ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 학원 day50. 회원가입, 로그인/로그아웃, 게시글 수정/삭제
    기록 2022. 11. 15. 12:54

    < SAMPLE_BOARD_USERS 테이블 생성 및 제약조건 설정하기 >

    CREATE TABLE SAMPLE_BOARD_USERS (
        USER_ID VARCHAR2(20) PRIMARY KEY,
        USER_PASSWORD VARCHAR2(20) NOT NULL,
        USER_NAME VARCHAR2(100) NOT NULL,
        USER_EMAIL VARCHAR2(255) UNIQUE,
        USER_DELETED CHAR(1) DEFAULT 'N',
        USER_CREATED_DATE DATE DEFAULT SYSDATE,
        USER_UPDATED_DATE DATE DEFAULT SYSDATE
    );
    
    DELETE FROM SAMPLE_BOARD_REVIEWS;
    DELETE FROM SAMPLE_BOARDS;
    COMMIT;
    
    -- SAMPLE_BOARDS 테이블의 BOARD_WRITER 컬럼에 외래키 제약조건을 추가한다.
    -- SAMPLE_BOARDS 테이블의 BOARD_WRITER 컬럼의 값은 SAMPLE_BOARD_USERS 테이블의 USER_ID를 참조한다.
    ALTER TABLE SAMPLE_BOARDS ADD CONSTRAINT BOARD_WRITER_FK 
    FOREIGN KEY (BOARD_WRITER) REFERENCES SAMPLE_BOARD_USERS (USER_ID);
    
    -- SAMPLE_BOARD_REVIEWS 테이블의 REVIEW_WRITER 컬럼에 외래키 제약조건을 추가한다.
    -- SAMPLE_BOARD_REVIEWS 테이블의 REVIEW_WRITER 컬럼의 값은 SAMPLE_BOARD_USERS 테이블의 USER_ID를 참조한다.
    ALTER TABLE SAMPLE_BOARD_REVIEWS ADD CONSTRAINT BOARD_REVIEW_WRITER_FK
    FOREIGN KEY (REVIEW_WRITER) REFERENCES SAMPLE_BOARD_USERS (USER_ID);

    sample_board_reviews의 review_writer와 sample_boards의 board_writer는 sample_board_users의 프라이머리키 제약조건이 걸려있는 user_id를 참조한다.


    < navbar 교체하기! >

     
    화면이 있는 (html코드가 있는) 모든 jsp파일에 아래 코드를 붙인다.
     
    <nav class="navbar navbar-expand-sm bg-dark navbar-dark">
        <div class="container">
            <ul class="navbar-nav me-auto">
                <li class="nav-item"><a class="nav-link" href="../home.jsp"></a></li>
                <li class="nav-item"><a class="nav-link active" href="../board/list.jsp">게시판</a></li>
            </ul>
            <ul class="navbar-nav">
                <li class="nav-item"><a class="nav-link disabled" href="loginform.jsp">로그인</a></li>
                <li class="nav-item"><a class="nav-link disabled" href="registerform.jsp">회원가입</a></li>
            </ul>
        </div>
    </nav>

    위에 코드를 지우고 

    방법1. <%@ include file="common/header.jsp" %>   

      // header.jsp include 포함시킨다는 의미이다.

    방법2. <jsp:include page="../common/header.jsp">  
                        <jsp:param name="menu" value="board"/>
               </jsp:include>

     

    위의 3줄의 코드로 대체할 수 있다.

    common폴더의 header.jsp를 이 jsp 페이지에 포함시킨다. 

    위의 3줄의 코드로 변경하면 header.jsp에게 parameter를 전해줄 수 있다.

    menu라는 이름으로 header.jsp에게 어쩔 때는 home이가고 어쩔 때는 board, register.. 등이 가는 것이다.

    방법1은 parameter를 전해줄 수 없다.

    header.jsp에게 사용자가 뭘 눌렀는지 알려주기 위해 쓴다.

    <header.jsp>
    
    <%
    	String menu = request.getParameter("menu");
    %>    
    <nav class="navbar navbar-expand-sm bg-dark navbar-dark">
    	<div class="container">
    		<ul class="navbar-nav me-auto">
    			<li class="nav-item"><a class="nav-link <%="home".equals(menu) ? "active bg-danger" : "" %>" href="/web-board/home.jsp">홈</a></li>
    			<li class="nav-item"><a class="nav-link <%="board".equals(menu) ? "active bg-danger" : "" %>" href="/web-board/board/list.jsp">게시판</a></li>
    		</ul>
    		<ul class="navbar-nav">
    			<li class="nav-item"><a class="nav-link <%="login".equals(menu) ? "active bg-danger" : "" %>" href="/web-board/user/loginform.jsp">로그인</a></li>
    			<li class="nav-item"><a class="nav-link <%="register".equals(menu) ? "active bg-danger" : "" %>" href="/web-board/user/form.jsp">회원가입</a></li>
    		</ul>
    	</div>
    </nav>


    < ../ 표시 의미 알아보기! >

     

    webapp/home.jsp

    webapp/user/form/jsp

     

    user랑 같은 폴더 아래에 위치해 있으면 <a href="home.jsp">라고 적을 수 있다. 그런데 webapp은 user보다 상위 폴더 아래에 있으니까 ../ 을 붙인다. 상위폴더로 가라는 의미이다.

    폴더가 나뉘어져 있으니까 같은 이름의 jsp파일이 있어도 충돌나지 않음. 


    < 아이디, 이메일 중복일 경우 회원가입 실패 기능 구현하기 >

    재요청응답에 에러정보를 담는다.

    서버에게 값을 전해줘야 오류임을 알려줄 수 있다. (자바의 모든 값은 name, value로 전해진다.)

    <form.jsp>
    
    	<form class="bg-light border p-3" method="post" action="register.jsp">
    		<div class="mb-3">
    			<label class="form-label">아이디</label>
    			<input type="text" class="form-control" name="id" />
    		</div>
    		<div class="mb-3">
    			<label class="form-label">비밀번호</label>
    			<input type="password" class="form-control" name="password" />
    		</div>
    		<div class="mb-3">
    			<label class="form-label">이름</label>
    			<input type="text" class="form-control" name="name" />
    		</div>
    		<div class="mb-3">
    			<label class="form-label">이메일</label>
    			<input type="text" class="form-control" name="email" />
    		</div>
    		<div class="text-end">
    			<a href="../home.jsp" class="btn btn-secondary">취소</a>
    			<button type="submit" class="btn btn-primary">회원가입</button>
    		</div>
    	</form>
    <register.jsp>
    
    <%
    	// 회원가입 폼에서 register.jsp를 실행요청할 때 제출한 폼 입력값을 요청객체에서 조회한다.
    	String id = request.getParameter("id");
    	String password = request.getParameter("password");
    	String name = request.getParameter("name");
    	String email = request.getParameter("email");
    	
    	// User 객체를 생성해서 조회된 값을 저장한다.
    	User user = new User();
    	user.setId(id);
    	user.setPassword(password);
    	user.setName(name);
    	user.setEmail(email);
    	
    	// UserDao 객체를 생성한다.
    	UserDao userDao = new UserDao();
    	
    	// 아이디로 사용자정보를 조회한다. 사용자정보가 null이 아니면 form.jsp를 재요청하는 URL을 응답으로 보낸다.
    	User savedUser = userDao.getUserById(id);
    	if (savedUser != null) {
    		response.sendRedirect("form.jsp?error=invalid");
    		return;   // 아래 코드가 실행안되도록 return을 꼭 적어둔다.
    	}
    	
    	// 이메일로 사용자정보를 조회한다. 사용자정보가 null이 아니면 form.jsp를 재요청하는 URL을 응답으로 보낸다.
    	savedUser = userDao.getUserByEmail(email);
    	if (savedUser != null) {
    		response.sendRedirect("form.jsp?error=invalid");
    		return;   // 아래 코드가 실행안되도록 return을 꼭 적어둔다.
    	}
    	
    	// insertUser(user)를 실행해서 데이터베이스 저장시킨다.
    	userDao.insertUser(user);
    	
    %>
    <form.jsp>
    
    	<%
    		// 회원가입 실패로 form.jsp?error=invalid로 재요청했을 때 error의 값은 invalid다.
    		String errorCode = request.getParameter("error");
    	%>
    	
    	<p>아이디, 비밀번호, 이름, 이메일을 입력해서 회원가입을 신청하세요</p>
    	<%
    		if ("invalid".equals(errorCode)) {   // 반대로 쓰면 오류난다. errorCode가 없을 때는 null이니까
    	%>
    	<div class="alert alert-danger">
    		<strong>회원가입 오류</strong> 아이디 혹은 이메일이 이미 사용중입니다.
    	</div>
    	<%
    		}
    	%>

    * 문자열하고 변수명을 비교할 때는 반드시 문자열, 상수를 .equals 앞에 적어야 한다.


    처음 접속했을 때는 요청 헤더정보에 JSESSIONID 정보가 없다. 

    따라서 pageContext의 getSession()메소드를 실행시켜서 HttpSession 객체를 만들고 생성된 HttpSession객체를 session에 대입시킨다.

    응답객체에 쿠키정보를 담고 그안에 name은 JSESSIONID value에는 세션아이디를 담아서 보낸다. 

     

    이후에는 이미 세션아이디가 있으니까 SESSION객체를 새로 만드는 대신에 검색하고 발견한다.

    그리고 발견된 SESSION객체를 session에 대입시킨다.

    로그인 이후에는 어떤 jsp가 실행되어도 고유한 세션 객체가 연결되어있다.

     

    로그인이 수행된 다음에는 세션객체안에 login.jsp에서 넣어놓은 사용자정보도 담겨있기 때문에 모든 jsp에서 사용자정보를 사용할 수 있게된다.

     

    < 로그인 기능 구현하기 > 

    * loginform.jsp로그인 화면을 제공해주는 jsp이고, 

       login.jsp 실제 로그인 처리해주는 jsp이다.

    * 로그인

    - 인증된 사용자정보를 서버의 HttpSession 객체(나만의 보관함)에 저장하는 것

    - HttpSession 객체는 고유한 아이디를 가지고 있고, 그 세션과 관련있는 클라이언트도 같은 아이디를 가지고 있기 때문에(쿠키파일로), 클라이언트마다 사용하는 세션객체가 고유하게 구별된다.

    - <login.jsp>의 session.setAttribute("loginedUser", savedUser);  인증이 완료된 사용자정보를 세션에 저장시킨다. 세션에서도 마찬가지로 name, value형식으로 값을 저장한다. "loginedUser"라는 이름으로 savedUser라는 값을 저장시킨다.

    - HttpSession 객체는 요청/응답이 완료되어도 사라지지 않고, 명시적으로 로그아웃하거나 일정시간 이상 해당 사이트를 이용하지 않을 때만 사라진다. 따라서, HttpSession에 저장한 정보는 여러 JSP에서 이용할 수 있다.

     - 브라우저마다 각각 다른 세션아이디를 갖고 있는 것이다.

    - 중복 로그인이 안되게 하는 방법 : 로그인할 때 로그인 한 사람의 정보를 db에 저장하고 같은 아이디로 로그인하면 둘다 로그아웃시키는 방법을 사용한다. 

    <loginform.jsp>
    
    <form class="bg-light border p-3" method="post" action="login.jsp">
    	<div class="mb-3">
    		<label class="form-label">아이디</label>
    		<input type="text" class="form-control" name="id" />
    	</div>
    	<div class="mb-3">
    		<label class="form-label">비밀번호</label>
    		<input type="password" class="form-control" name="password" />
    	</div>
    	<div class="text-end">
    		<a href="form.jsp" class="btn btn-secondary">회원가입</a>
    		<button type="submit" class="btn btn-primary">로그인</button>
    	</div>
    </form>

     

    <login.jsp>
    
    <%
    	// 로그인 폼에서 login.jsp를 실행요청할 때 제출한 폼 입력값을 요청객체에서 조회한다.
    	String id = request.getParameter("id");
    	String password = request.getParameter("password");
    			
    	// 사용자 정보 조회를 위해서 UserDao객체를 생성한다.
    	UserDao userDao = new UserDao();
    	
    	// 아이디로 사용자정보를 조회해서 사용자정보가 null이면 가입된 아이디가 아니므로 로그인화면을 재요청하는 URL을 응답으로 보낸다.
    	User savedUser = userDao.getUserById(id);
    	if (savedUser == null) {
    		response.sendRedirect("loginform.jsp?error=fail");
    		return;	
    	}
    	
    	// 조회된 사용자정보의 비밀번호와 입력한 비밀번호가 일치하지 않으면 로그인화면을 재요청하는 URL을 응답으로 보낸다.
    	if(!savedUser.getPassword().equals(password)) {
    		response.sendRedirect("loginform.jsp?error=fail");
    		return;
    	}
    	
    	// 사용자 인증이 완료된 경우, 세션객체에 사용자정보를 저장한다.
    	// HttpSession의 setAttribute(String name, Object value) 메소드를 사용해서 세션객체에 사용자 정보를 저장한다.
    	session.setAttribute("loginedUser", savedUser);  // "loginedUser"라는 이름으로 savedUser라는 값을 담는다.
    	
    	// 재요청 URL을 응답으로 보낸다.
    	response.sendRedirect("../home.jsp");
    %>

    자바에서 모든 값은 name,value의 형태로 관리되기 때문에 session에 담을 때에도 name,value로 저장됨.

    <loginform.jsp>
    
    <%
    	String errorCode = request.getParameter("error");
    %>	
    	
    	<p>아이디, 비밀번호 입력해서 로그인하세요</p>
    <%
    	if ("fail".equals(errorCode)) {
    %>	
    	<div class="alert alert-danger">
    		<strong>로그인 실패</strong> 아이디 혹은 비밀번호가 일치하지 않습니다.
    	</div>
    <%
    	}
    %>

    * 로그아웃 : 세션을 없애는 것, session.invalidate();

    <logout.jsp>
    
    <%
    	// 로그아웃은 기존 세션객체를 무효화시킨다.
    	session.invalidate();
    
    	// 재요청 URL을 응답으로 보낸다.
    	response.sendRedirect("../home.jsp");
    %>

    < 로그인한 사용자만 게시글 등록하기 기능 구현하기 > 

    <list.jsp>
    
    	<%
    		// HttpSession객체에 저장된 인증된 사용자 정보를 조회한다.
    		User user = (User) session.getAttribute("loginedUser");
    		// 인증된 사용자 정보가 존재하면, 새글 등록 링크를 출력한다.
    		if (user != null) {
    	%>
    		<a href="form.jsp" class="btn btn-primary btn-sm float-end">새 글 등록</a>
    	<%
    		}
    	%>

    만약, 로그인을 한 상태에서 새 글 등록을 들어갔을 때 주소를 복사해둔다면, 로그아웃한 상태에서도 주소로 접속이 가능하기 때문에 게시글 등록하는 form.jsp에서 인증된 사용자 정보를 확인하고 null이면 로그인 폼으로 돌아가도록 구현해야 한다.

    <board/form.jsp>
    
    <%
    	// HttpSession객체에 저장된 인증된 사용자정보를 조회한다
    	User user = (User) session.getAttribute("loginedUser");
    	// 인증된 사용자 정보가 존재하지 않으면, 로그인 폼을 재요청하게 한다.
    	if (user == null) {
    		response.sendRedirect("../user/loginform.jsp?error=deny");
    		return;
    	}
    %>
    <loginform.jsp>
    
    	<p>아이디, 비밀번호 입력해서 로그인하세요</p>
    <%
    	if ("fail".equals(errorCode)) {
    %>	
    	<div class="alert alert-danger">
    		<strong>로그인 실패</strong> 아이디 혹은 비밀번호가 일치하지 않습니다.
    	</div>
    <%
    	} else if ("deny".equals(errorCode)) {
    %>	
    	<div class="alert alert-danger">
    		<strong>요청 거부</strong> 로그인한 사용자만 이용가능한 페이지입니다.
    	</div>
    <%
    	}
    %>


    < 작성자 정보를 사용자가 입력하지 않고 세션에서 가져오기 > 

    첫페이지 들어갈 때 생성한 세션 객체로부터 세션아이디를 가져와서 사용한다.

    세션 아이디에는 개인정보가 포함되어 있지 않다.

    세션아이디를 안다고 해서 이전 세션 아이디나 이후 세션아이디를 유추할 수 없다.

    매우 안전하다.

    <register.jsp>
    
    <%
    	// 로그인 여부 체크
    	User user = (User) session.getAttribute("loginedUser");
    	if (user == null) {
    		response.sendRedirect("../user/loginform.jsp?error=deny");
    	return;
    	}
    
    	// 새 게시글을 등록시키세요
    	String title = request.getParameter("title");
    	String content = request.getParameter("content");
    
    	// Board객체를 생성해서 board정보를 담는다.
    	Board board = new Board();
    	board.setTitle(title);
    	board.setWriter(user.getId());   // 작성자에 로그인한 사용자의 아이디를 저장한다.
    	board.setContent(content);
    	
    	// BoardDao객체를 생성하고, insertBoard 메소드를 실행시켜서 Board정보를 저장시킨다.
    	BoardDao boardDao = new BoardDao();
    	boardDao.insertBoard(board);
    	
    	// 추가/삭제/변경 작업 중 하나를 수행하였기 때문에 재 요청 URL을 응답으로 보낸다.
    	response.sendRedirect("list.jsp");
    	
    %>

    session.getAttribute("loginedUser")로 사용자 정보 가져오고, user.getId()로 작성자를 찾아서 저장한다.


    < 본인의 게시글만 수정/삭제할 수 있도록 구현하기 > 

    <detail.jsp>
    
    	<%
    		// 인증된 사용자 정보를 조회한다.
    		User user = (User)session.getAttribute("loginedUser");		
    	%>        
    
    	<%
    		// 인증된(로그인한) 사용자 정보가 존재하고, 
    		// 로그인한 사용자의 아이디와 게시글 작성자의 아이디가 일치하는 경우 삭제/수정 링크 출력
    		if(user != null && user.getId().equals(board.getWriter())) {   // user가 null일 경우에 equals 앞이 null이 되어서 오류남.
    	%>
    		<a href="delete.jsp?no=<%=board.getNo() %>" class="btn btn-danger">삭제</a>
    		<a href="modifyform.jsp?no=<%=board.getNo() %>" class="btn btn-warning">수정</a>
    	<%
    		}
    	%>
    <delete.jsp>
    
    <%
    	// 인증된 사용자정보를 조회한다. 인증된 사용자정보가 존재하지 않으면 loginform.jsp를 재요청하는 응답을 보낸다.
    	User user = (User) session.getAttribute("loginedUser");
    	if (user == null) {
    		response.sendRedirect("../user/loginform.jsp?error=deny");
    		return;
    	}
    
    	// 게시글 정보를 삭제하세요. sample_boards의 board_deleted를 'Y'으로 변경하세요
    	int boardNo = StringUtils.stringToInt(request.getParameter("no"));
    
    	BoardDao boardDao = new BoardDao();
    	Board board = boardDao.getBoardByNo(boardNo);
    
    	// 로그인한 사용자의 아이디와 게시글 작성자의 아이디가 서로 다르면 게시글을 삭제할 수 없다.
    	// detail.jsp를 재요청하는 URL을 응답으로 보낸다.
    	if (!user.getId().equals(board.getWriter())) {
    		response.sendRedirect("detail.jsp?no=" + boardNo + "&error=deny");
    		return;
    	}
    
    %>
    <detail.jsp>
    
    	<%
    		String errorCode = request.getParameter("error");
    		if ("deny".equals(errorCode)) {
    	%>
    			<div class="alert alert-danger">
    				<strong>수정/삭제 거부</strong> 다른 사용자가 작성한 게시글은 수정/삭제할 수 없습니다.
    			</div>
    	<%
    		}
    	%>

     

     

     

    * 게시글의 작성자는 게시글 번호로 게시글 정보를 데이터베이스에서 조회하면 획득 가능하다.

    * 현재 로그인 사용자는 서버의 HttpSession 객체에서 조회하면 획득 가능하다.

    * 클라이언트에서 서버로 사용자정보를 전달할 필요가 없다.

    댓글

Designed by Tistory.