📗 self-study/📗 KH정보교육원 당산지원

[Servlet & JSP] 기본적인 기능이 구현된 동적 웹 페이지 만들기 (member ver. )

천재강쥐 2022. 10. 13. 12:19

 

더보기

Servlet/JSP를 이용해 웹 페이지의 회원 관련 기능을 만들어 보자

 

 

 

회원가입  

✔️ 로그인, 회원가입, 마이페이지, 정보 및 비밀번호 변경, 회원 탈퇴

 

 

 

 

✔️ HttpServletRequest 객체와 HttpServletResponse 객체

👉🏻 request: 서버로 요청할 때의 정보들이 담겨 있음
(요청 시 전달값, 요청 전송 방식, 요청자의 ip 주소 등)
request.getParameter() / request.getParameterValues()로 값 뽑기


👉🏻 response: 요청에 대해 응답할 때 필요한 객체
자바 코드로 응답 페이지를 만들 때 주로 사용


✔️ GET 방식과 POST 방식

👉🏻 GET: 사용자가 입력한 값이 url에 노출 / 데이터의 길이 제한 / 대신 즐겨찾기가 편리
👉🏻 POST: 사용자가 입력한 값이 url에 노출 X / 데이터의 길이 제한 X / 대신 즐겨찾기가 불편 / Timeout이 존재

 

✔️ 응답 페이지에 전달할 값이 있다면 값을 어딘가에 담아야 함

담아 줄 수 있는 Servlet Scope 내장객체 4종류

👉🏻 1) application : application 객체에 담은 데이터는 웹 애플리케이션 전역에서 다 꺼내 쓸 수 있음
👉🏻 2) session : session 객체에 담은 데이터는 웹 애플리케이션 전역에서 다 꺼내 쓸 수 있음
단, 한 번 담은 데이터는 내가 직접 지우기 전까지 쓸 수 있음
한 번 담은 데이터는 서버가 멈추기 전까지 쓸 수 있음
한 번 담은 데이터는 브라우저가 종료되기 전까지 쓸 수 있음
👉🏻 3) request : request 객체에 담은 데이터는 해당 요청에 대한 응답 페이지에서만 사용 가능함
👉🏻 4) page : 해당 jsp 페이지에서만 데이터를 담고 꺼내 쓸 수 있음

=> session과 request를 주로 많이 씀
 
공통적으로 데이터를 담고자 한다면 request.setAttribute("키", 밸류);
                      데이터를 꺼내고자 한다면 request.getAttribute("키"); : Object 타입의 밸류 리턴
                      데이터를 지우고자 한다면 request.removeAttribute("키");

 

✔️ 응답 페이지 지정 방식

👉🏻 1. 포워딩 방식: 해당 선택된 jsp가 보여질 뿐 url 주소는 여전히 이 서블릿 url 매핑값으로 지정되어 있음
             http://localhost:8888/jsp/login.me
              
👉🏻 2. url 재요청 방식 (sendRedirect 방식)
    : 자바 코드로 url 주소를 요청하는 방식 (즉, 새로고침의 개념)
      자바스크립트 때 location.href = "~~~", 와 같은 원리

 

 

 

 

*로그인 기능

 

LoginController.java (login.me)

package com.kh.member.controller;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.kh.member.model.service.MemberService;
import com.kh.member.model.vo.Member;

/**
 * Servlet implementation class LoginController
 */
@WebServlet("/login.me")
public class LoginController extends HttpServlet {
	private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public LoginController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        // 1) 인코딩 처리해야 함 (post 방식일 경우)
        request.setCharacterEncoding("UTF-8");
        
        // 2) 요청 시 전달값을 꺼내기 (request의 parameter 영역으로부터)
        // 요청 시 전달값을 뽑아서 변수에 담기!
        // request.getParameter("키값"): String 타입의 밸류값 1개 리턴
        // request.getParameterValues("키값"): Strin[] 타입의 밸류값 여러 개가 묶여서 리턴
        
        // 요청 시 리턴값
        // userId: 아이디값
        String userId = request.getParameter("userId"); // "user01"
        // userPwd: 비밀번호값
        String userPwd = request.getParameter("userPwd"); // "password01"
        
        // 주의사항!
        // 키값을 제시 시 오타가 나면 없는 키값을 찾는 꼴이기 때문에 무조건 null이 리턴됨!
        
        // 여기서 콘솔에 잘 뜨는지 테스트 한 것이기 때문에 주석 처리
        // System.out.println(userId);
        // System.out.println(userPwd);
        
        // 3) 요청 시 전달값들을 VO 객체로 가공하기
        Member m = new Member();
        m.setUserId(userId);
        m.setUserPwd(userPwd);
        
        // 4) 가공한 VO 객체를 해당 요청을 처리하는 서비스 클래스의 메소드로 넘기기
        Member loginUser = new MemberService().loginMember(m);
        
        // 5) 처리된 결과를 가지고 사용자가 보게 될 응답 뷰 지정
        // System.out.println(loginUser);
		
		if(loginUser == null) { // 로그인 실패 => 에러 문구를 담아서 에러 페이지로 응답
		    
		    // 응답 페이지에서 필요로 하는 데이터
		    request.setAttribute("errorMsg", "로그인에 실패했습니다.");
		    
		    // 응답 페이지 지정 => RequestDispatcher 객체 생성 후 포워딩
		    RequestDispatcher view = request.getRequestDispatcher("views/common/errorPage.jsp");
		    view.forward(request, response);
		    
		    /*
		     * 포워딩 방식: 해당 url 경로로 선택된 뷰가 보여질 뿐
		     *          url은 절대 변경되지 않음 (요청했을 때 이 Servlet의 url이 주소창에 그대로 남아 있음)
		     */
		    
		} else { // 로그인 성공 => 응답 페이지에 loginUser 데이터 전달, 메인 페이지로 응답
		    
		    // 응답 페이지에서 필요로 하는 데이터
		    // 로그인한 회원의 정보를 로그아웃하기 전까지 계속 가져다 써야 함
		    // (글 작성, 마이페이지, 글 수정, 댓글 작성, ...)
		    // => session 객체에 담기
		    
		    // Servelt에서 JSP 내장 객체인 session에 접근하고자 한다면
		    // 우선 session 객체 (HttpSession)를 얻어와야 함
		    // => request 객체로부터 getSession() 메소드를 통해
		    HttpSession session = request.getSession();
		    session.setAttribute("loginUser", loginUser);
		    
		    // 성공 메시지도 담기
		    session.setAttribute("alertMsg", "성공적으로 로그인되었습니다.");

		    // response.sendRedirect("/jsp"); // 내가 요청한 url 주소로 이동하겠음
		    response.sendRedirect(request.getContextPath());
		    
		}
		
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

 

errorPage.jsp

에러페이지 응답 화면

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	String errorMsg = (String)request.getAttribute("errorMsg");
	// System.out.println(errorMsg);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1 align="center" style="color:red;"><%= errorMsg %></h1>

</body>
</html>

 

MemberService.java

package com.kh.member.model.service;

import java.sql.Connection;

import com.kh.common.JDBCTemplate;
import com.kh.member.model.dao.MemberDao;
import com.kh.member.model.vo.Member;

public class MemberService {

    // 로그인 요청 서비스
    public Member loginMember(Member m) {

        // 1) Connection 객체 생성
        Connection conn = JDBCTemplate.getConnection();

        // 2) 요청 시 전달값과 만들어진 Connection 객체를 DAO 의 메소드한테 전달 (호출)
        Member loginUser = new MemberDao().loginMember(conn, m);

        // 3) INSERT, UPDATE, DELETE 문이라면 commit / rollback 처리

        // 4) Connection 객체 반납
        JDBCTemplate.close(conn);

        // 5) 결과 반환
        return loginUser;
    }
}

 

MemberDao.java

USER_ID와 USER_PWD 일치, STATUS(계정활성화) 조건으로 검색할 것

package com.kh.member.model.dao;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import com.kh.common.JDBCTemplate;
import com.kh.member.model.vo.Member;

public class MemberDao {

    private Properties prop = new Properties();

    public MemberDao() {

        String fileName = MemberDao.class.getResource("/sql/member/member-mapper.xml").getPath();

        try {
            prop.loadFromXML(new FileInputStream(fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Member loginMember(Connection conn, Member m) {

        // SELECT 문 => ResultSet 객체 (unique 제약조건에 의해 많아봤자 한 건만 조회됨) => Member 객체

        // 1) 필요한 변수들 먼저 셋팅
        Member loginUser = null;
        PreparedStatement pstmt = null;
        ResultSet rset = null;

        String sql = prop.getProperty("loginMember");

        try {
            // 2) 쿼리문 실행에 필요한 PreparedStatement 객체를 생성하기
            pstmt = conn.prepareStatement(sql);

            // 3_1) 미완성된 쿼리문일 경우 완성시키기
            pstmt.setString(1, m.getUserId());
            pstmt.setString(2, m.getUserPwd());

            // 3_2) 쿼리문 실행 후 결과 받기
            rset = pstmt.executeQuery();

            // 4) rset 으로부터 커서를 움직여가며 값 뽑아내기
            if(rset.next()) {

                loginUser = new Member(rset.getInt("USER_NO"),
                                       rset.getString("USER_ID"),
                                       rset.getString("USER_PWD"),
                                       rset.getString("USER_NAME"),
                                       rset.getString("PHONE"),
                                       rset.getString("EMAIL"),
                                       rset.getString("ADDRESS"),
                                       rset.getString("INTEREST"),
                                       rset.getDate("ENROLL_DATE"),
                                       rset.getDate("MODIFY_DATE"),
                                       rset.getString("STATUS"));
            }

            // 이 시점 기준으로
            // 만약 일치하는 회원을 찾았다면 loginUser 에는 해당 회원의 정보가 다 담겨있을 것
            // 만약 일치하는 회원을 못찾았다면 loginUser 에는 null 값이 들어있을 것

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 5) 자원 반납 (생성된 순서의 역순)
            // pstmt -> rset 순서로 생성됐었음
            JDBCTemplate.close(rset);
            JDBCTemplate.close(pstmt);
        }

        // 6) 결과 리턴
        return loginUser;
    }
}

 

member-mapper.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>SQL</comment>
	
	<entry key="loginMember">
		SELECT *
		FROM MEMBER
		WHERE USER_ID = ?
			AND USER_PWD = ?
			AND STATUS = 'Y'
	</entry>
	
</properties>

 

menubar.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="com.kh.member.model.vo.Member"%>
<%
	// 로그인한 사용자의 정보를 session에 담았기 때문에
	// 이 웹 애플리케이션의 어디서든지 해당 키값을 제시해서 로그인한 사용자의 정보를 꺼내 올 수 있음
	Member loginUser = (Member)session.getAttribute("loginUser");
	
	// System.out.println(loginUser);
	// 로그인 전 menubar.jsp 로딩 시: null
	// 로그인 후 menubar.jsp 로딩 시: 로그인한 회원의 정보가 담긴 Member 객체
	
	// 동적 코딩으로 바꾸기
	// System.out.println(request.getContextPath()); // /jsp
	String contextPath = request.getContextPath();
	
	// 성공 시 알람 문구 또한 session에 담았기 때문에
	// session으로부터 뽑기
	String alertMsg = (String)session.getAttribute("alertMsg");
	// System.out.println(alertMsg);
	// 로그인 성공 전 menubar.jsp 로딩 시: null
	// 로그인 성공 후 menuber.jsp 로딩 시: alert로 띄워 주고자 하는 문구 출력
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
    #login-form, #user-info { float : right; }
    /* 오른손잡이가 많은 사용자 편의상 오른쪽이 좋다고 함 */
    
    #user-info a {
        color : black;
        text-decoration: none;
        font-size : 12px;
    }

    .nav-area { background-color : black; }

    .menu {
        display : table-cell; /* 인라인 요소처럼 배치 가능 */
        height : 50px;
        width : 150px;
    }

    .menu a {
        text-decoration : none;
        color : white;
        font-size : 20px;
        font-weight : bold;
        display : block;
        /* 원래 글자에만 마우스 클릭이 먹혔는데 얘 하면 그 공간 자체를 클릭 가능해짐 */
        width : 100%; 
        height : 100%;
        line-height : 50px;
    }

    .menu a:hover {
        background-color : darkgray;
    }

    button {
        background-color: black;
        color : white;
        border-color : white;
    }

</style>
</head>
<body>

	<script>
	
		// script 태그 내에도 스크립틀릿과 같은 jsp 요소를 쓸 수 있음
		// var msg = <%= alertMsg %>;
		// var msg = 성공적으로 로그인이 되었습니다.; // 현재 이런 꼴임!! 따옴표 꼭! 넣어 주기
		
		var msg = "<%= alertMsg %>"; // "성공적으로 로그인이 되었습니다." / "null"
		
		if(msg != "null") {
			alert(msg);
			// 알림창을 띄워 준 후 session에 담긴 해당 메시지를 지워 줘야 함
			// 안 그러면 menubar.jsp가 로딩될 때마다 매번 alert가 뜸!
			
			<% session.removeAttribute("alertMsg"); %>
		}
	
	</script>

	<h1 align="center">Welcome D Class</h1>

    <div class="lonin-area">
        <!-- 로그인 전에 보여지는 로그인 form -->
        <!-- action 값은 로그인 화면 처리 기능 만든 후 url 넣을 예정 -->
        <!-- 
        	현재 나의 위치: http://localhost:8888/jsp/
        	로그인 버튼을 클릭했을 때 이동하고자 하는 위치: http://localhost:8888/jsp/login.me
        	
        	절대경로: /jsp/login.me
        	상대경로(마지막 슬래시 기준 문구가 붙는 것):login.me 
         -->
         <% if(loginUser == null) { %>
        <form id="login-form" action="<%= contextPath %>/login.me" method="post">

            <table>
                <tr>
                    <th>아이디: </th>
                    <td><input type="text" name="userId" required></td>
                </tr>
                <tr>
                    <th>비밀번호: </th>
                    <td><input type="password" name="userPwd" required></td>
                </tr>
                <tr>
                    <th colspan="2">
                        <button type="submit">로그인</button>
                        <button type="button">회원가입</button>
                    </th>
                </tr>
            </table>
        </form>
        <% } else { %>

        <!-- 로그인 성공 후에 보여지는 프로필 화면 -->
        <div id="user-info">
            <b><%= loginUser.getUserName() %>님!</b> 환영합니다요 ^0^ <br><br>
            <div align="center">
                <a href="">마이페이지</a>
                <a href="">로그아웃</a>
            </div>
        </div>
        <% } %>
    </div>

    <br clear="both"> <!-- float 속성 해제 -->
    <br>

    <div class="nav-area" align="center">
        <!-- (div.menu>a)*4 + Enter -->
        <div class="menu"><a href="">HOME</a></div>
        <div class="menu"><a href="">공지사항</a></div>
        <div class="menu"><a href="">일반게시판</a></div>
        <div class="menu"><a href="">사진게시판</a></div>
    </div>

</body>
</html>
더보기

서버 재시작 후 DB 테이블에 있는 아이디와 비번을 입력해 봄

 

 

로그인 버튼을 눌러 봄

 

Console 상황
페이지 상황

 

확인 버튼을 눌러 봄

 

페이지 상황

내 상황

 

 

 

 

 

*로그아웃 기능

 

 

LogoutController(logout.me) Servlet 생성

캡처는 못 했는데 URL Mapping값을 logout.me로 설정함!

package com.kh.member.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class LogoutController
 */
@WebServlet("/logout.me")
public class LogoutController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public LogoutController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	    
	    // 로그아웃 요청 처리 2가지 방법!
	    // session에 담긴 회원 정보를 지워 주면 됨
	    // => 전역에 데이터를 꺼내 쓸 수 있되, 서버가 종료되기 전까지만, 브라우저가 종료되기 전까지만 
	    
	    // 1. session으로부터 키-밸류 세트 지우기 (중요한 정보를 같이 담았을 때 아주 확실하게 지우고 싶음)
	    // request.getSession().removeAttribute("loginUser");
	    
	    // 2. session 객체를 무효화 시키기(로그인 정보만 담았으니 위와 같은 보안까지는 필요 없다! 할 때)
	    request.getSession().invalidate();
	    
	    // 응답페이지 => /jsp
	    // url 재요청 방식(response.sendRedirect() 메소드 호출)
	    // response.sendRedirect("/jsp");
	    
	    // 앞으로는 응답 페이지 요청 시 contextRoot (== contextPath)를 직접 작성하는 것이 아니라
	    // request.getContextPath()메소드를 통해 반환해서 쓸 것!
	    // => 언제 바뀔지 모르기 때문에 하드 코딩 하지 않겠음!
	    response.sendRedirect(request.getContextPath());
	
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

menubar.jsp 로그아웃 부분에 경로 제시

       <!-- 로그인 성공 후에 보여지는 프로필 화면 -->
        <div id="user-info">
            <b><%= loginUser.getUserName() %>님!</b> 환영합니다요 ^0^ <br><br>
            <div align="center">
                <a href="">마이페이지</a>
                <a href="<%= contextPath %>/logout.me">로그아웃</a>
            </div>
        </div>
        <% } %>
    </div>
더보기

서버 재시작 후 로그인을 해 봄

 

 

로그아웃 버튼을 눌러 봄

 

 

 

 

 

 

 

*회원가입 기능

 

memberEnrollForm.jsp

오타나거나 경로 다르면 404 오류 바로 남 주의!

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
    .outer {
        background-color: black;
        color : white;
        width : 1000px;
        margin : auto;
        margin-top : 50px;
    }

    #enroll-form table { margin : auto;} /* 테이블 가운데 정렬 */
    #enroll-form input { margin : 5px; } /* input 태그들 사이 간격 */
</style>
</head>
<body>

	<%@ include file="../common/menubar.jsp" %>
    <!-- ../ : 현재 폴더로부터 한 겹 빠져나가겠다 -->

    <div class="outer">
        
        <br>
        <h2 align="center">회원가입</h2>
        
        <!--
        	현재 나의 주소: http://localhost:8888/jsp/enrollForm.me
        	내가 요청을 보내고자 하는 주소: http://localhost:8888/jsp/insert.me
        	
        	절대경로: /jsp/insert.me
        	상대경로: insert.me
         -->

        <form id="enroll-form" action="<%= contextPath %>/insert.me" method="post">

            <!-- 아이디, 비밀번호, (비밀번호 확인), 전화번호, 이메일, 주소, 취미 -->
            <table>
                <!-- (tr>td*3)*8 + Enter -->
                <tr>
                    <td>* 아이디</td>
                    <td><input type="text" name="userId" maxlength="12" required></td>
                    <td><button type="button">중복확인</button></td>
                    <!-- 아이디 중복 확인은 나중에 AJAX 라는 기술을 배운 뒤 할 것 -->
                </tr>
                <tr>
                    <td>* 비밀번호</td>
                    <td><input type="password" name="userPwd" maxlength="15" required></td>
                    <td></td>
                </tr>
                <tr>
                    <td>* 비밀번호 확인</td>
                    <td><input type="password" maxlength="15" required></td>
                    <!-- 프론트에서 읽고 일치하는지만 보면 되므로 서버에는 넘길 필요 없음 == name 값 필요 없음! -->
                    <td></td>
                </tr>
                <tr>
                    <td>* 이름</td>
                    <td><input type="text" name="userName" maxlength="6" required></td>
                    <td></td>
                </tr>
                <tr>
                    <td>&nbsp;&nbsp;전화번호</td>
                    <td><input type="text" name="phone" placeholder="- 포함해서 입력"></td>
                    <td></td>
                </tr>
                <tr>
                    <td>&nbsp;&nbsp;이메일</td>
                    <td><input type="email" name="email"></td>
                    <td></td>
                </tr>
                <tr>
                    <td>&nbsp;&nbsp;주소</td>
                    <td><input type="text" name="address"></td>
                    <td></td>
                </tr>
                <tr>
                    <td>&nbsp;&nbsp;관심분야</td>
                    <td colspan="2">
                        <!-- (input[type=checkbox name=interest id= value=]+label)*6 + Enter -->
                        <input type="checkbox" name="interest" id="sports" value="운동">
                        <label for="sports">운동</label>
                        <input type="checkbox" name="interest" id="hiking" value="등산">
                        <label for="hiking">등산</label>
                        <input type="checkbox" name="interest" id="fishing" value="낚시">
                        <label for="fishing">낚시</label>
                        <br>
                        <input type="checkbox" name="interest" id="cooking" value="요리">
                        <label for="cooking">요리</label>
                        <input type="checkbox" name="interest" id="game" value="게임">
                        <label for="game">게임</label>
                        <input type="checkbox" name="interest" id="movie" value="영화">
                        <label for="movie">영화</label>
                    </td>
                </tr>
            </table>

            <br><br>

            <div align="center">
                <button type="submit">회원가입</button>
                <button type="reset">초기화</button>
            </div>

            <br>

        </form>

    </div>

</body>
</html>

 

menubar.jsp

         <% if(loginUser == null) { %>
        <form id="login-form" action="<%= contextPath %>/login.me" method="post">

            <table>
                <tr>
                    <th>아이디: </th>
                    <td><input type="text" name="userId" required></td>
                </tr>
                <tr>
                    <th>비밀번호: </th>
                    <td><input type="password" name="userPwd" required></td>
                </tr>
                <tr>
                    <th colspan="2">
                        <button type="submit">로그인</button>
                        <button type="button" onclick="enrollPage();">회원가입</button>
                    </th>
                </tr>
            </table>
        </form>
        <script>
        	function enrollPage() {
        		
        		// location.href= "/jsp/views/member/memberEnrollForm.jsp";
        		// 웹 애플리케이션의 디렉토리 구조가 url에 노출되면 보안에 취약
        		
        		// 단순히 페이지 요청이라고 하더라도 반드시 Servlet을 거쳐 갈 것!
        		location.href = "<%= contextPath %>/enrollForm.me";

        	}
        </script>

 

MemberEnrollFormController Servlet 생성

controller 단에서 화면만 띄워 주고 말 용도로 생성하는 것

👉🏻 url에 내 디렉토리 구조가 그대로 포함되므로 보안성 향상을 위해서

 

 

URL Mapping: /enrollForm.me

 

package com.kh.member.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class MemberEnrollFormController
 */
@WebServlet("/enrollForm.me")
public class MemberEnrollFormController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public MemberEnrollFormController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
	    // 이 서블릿의 용도: 단순히 회원가입 페이지를 띄워 주는 용도
	    
	    // 내가 보여 주고자 하는 url : http://localhost:8888/jsp/enrollForm.me
	    // 실제 사용자에게 보여 주고자 하는 화면 : views/member/memberEnrollForm.jsp
	    
	    // 포워딩 방식
	    // RequestDispatcher view = request.getRequestDispatcher("views/member/memberEnrollForm.jsp");
	    // view.forward(request, response);
	    // 한 줄로 줄일 수 있음!
	    request.getRequestDispatcher("views/member/memberEnrollForm.jsp").forward(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

MemberInsertController Servlet 생성

진짜 회원 추가를 할 때 쓰일 놈

 

 

URL Mapping: /insert.me

 

package com.kh.member.controller;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.kh.member.model.service.MemberService;
import com.kh.member.model.vo.Member;

/**
 * Servlet implementation class MemberInsertController
 */
@WebServlet("/insert.me")
public class MemberInsertController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public MemberInsertController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	    
	    // 회원 가입 기능 구현
	    
	    // 1) 인코딩 변경
	    request.setCharacterEncoding("UTF-8");
	    
	    // 2) 요청 시 전달값들을 뽑아서 변수 및 객체에 기록하기
	    // 뽑아야 할 값
	    // userId: 아이디 (필수입력)
	    String userId = request.getParameter("userId");
	    // userPwd: 비밀번호 (필수입력)
	    String userPwd = request.getParameter("userPwd");
	    // userName: 이름 (필수입력)
	    String userName = request.getParameter("userName");
	    // phone: 휴대폰번호
	    String phone = request.getParameter("phone"); // 빈 문자열이 올 수도 있음
	    // email: 이메일 주소
	    String email = request.getParameter("email"); // 빈 문자열이 올 수도 있음
	    // address: 주소
	    String address = request.getParameter("address"); // 빈 문자열이 올 수도 있음
	    // interest: 관심분야 (배열)
	    String[] interestArr = request.getParameterValues("interest");
	                                                    // ["운동", "등산] / null
	    
	    // String[] --> String
	    // ["운동", "등산"] --> "운동, 등산"
	    String interest = "";
	    
	    if(interestArr != null) {
	        interest = String.join(", ", interestArr);
	    }
	    
	    // 매개변수 생성자를 이용해서 Member 객체에 담기
	    Member m = new Member(userId, userPwd, userName, phone, email, address, interest);
	    
	    // 3) 전달값을 Service에 전달하면서 요청 처리
	    int result = new MemberService().insertMember(m);
	    
	    // 4) 처리 결과를 가지고 사용자가 보게 될 응답페이지를 지정
	    if(result > 0) { // 성공 => 성공 메시지를 담아 메인페이지로 요청
	        
           HttpSession session = request.getSession();
           session.setAttribute("alertMsg", "회원가입에 성공했습니다.");
           
           // URL 재요청 방식
           // http://localhost:8888/jsp
           // response.sendRedirect("/jsp"); // 하드코딩 방식
           response.sendRedirect(request.getContextPath()); // 동적 바인딩
	           
	    } else { // 실패 => 실패 메시지를 담아서 에러 페이지로 포워딩
	        
	        request.setAttribute("errorMsg", "회원가입에 실패했습니다.");
	        
	        // 포워딩 방식
	        // 지금 경로: http://localhost:8888/jsp/insert.me
	        // 파일 경로: WebContent/views/common/errorPage.jsp
	        RequestDispatcher view = request.getRequestDispatcher("views/common/errorPage.jsp");
	        view.forward(request, response);
	    }
	    
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

Member 클래스에 회원가입용 생성자 추가

userNo, enrollDate, modifyDate, status 제외한 생성자를 추가

    // 회원가입용 생성자
    public Member(String userId, String userPwd, String userName, String phone, String email, String address,
            String interest) {
        super();
        this.userId = userId;
        this.userPwd = userPwd;
        this.userName = userName;
        this.phone = phone;
        this.email = email;
        this.address = address;
        this.interest = interest;
    }

 

MemberService

    // 회원가입용 서비스
    public int insertMember(Member m) {
        
        // 1) Connection 객체 생성
        Connection conn = JDBCTemplate.getConnection();
        
        // 2) 전달값과 만들어진 Connection 객체를 DAO에 전달하면서 요청
        int result = new MemberDao().insertMember(conn, m);
        
        // 3) 트랜잭션 처리
        if(result > 0) { // 성공
            JDBCTemplate.commit(conn);
            
        } else { // 실패
            JDBCTemplate.rollback(conn);
        }
        
        // 4) Connection 객체 반납
        JDBCTemplate.close(conn);
        
        // 5) 결과 반환
        return result;
        
    }

 

MemberDao

    public int insertMember(Connection conn, Member m) {
        
        // INSERT문 => 처리된 행의 개수 반환
        
        // 1) 필요한 변수 먼저 세팅
        int result = 0;
        PreparedStatement pstmt = null;
        
        String sql = prop.getProperty("insertMember");
        
        try {
            // 2) pstmt 객체 생성
            pstmt = conn.prepareStatement(sql);
            
            // 3_1) 미완성 쿼리문
            pstmt.setString(1, m.getUserId());
            pstmt.setString(2, m.getUserPwd());
            pstmt.setString(3, m.getUserName());
            pstmt.setString(4, m.getPhone());
            pstmt.setString(5, m.getEmail());
            pstmt.setString(6, m.getAddress());
            pstmt.setString(7, m.getInterest());
            
            // 3_2) 쿼리문 실행
            result = pstmt.executeUpdate();
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            
            // 4) 자원 반납 (생성된 순서의 역순으로)
            JDBCTemplate.close(pstmt);
        }
        
        // 5) 결과 반환
        return result;
    }

 

member-mapper.xml

	<!--
		선택적 INSERT 구문 쓸 것!
		USER_NO: 시퀀스로 딸 것
		ENROLL_DATE, MODIFY_DATE, STATUS: 기본값 사용
	-->
		
	<entry key="insertMember">
		INSERT INTO MEMBER(USER_NO
						 , USER_ID
						 , USER_PWD
						 , USER_NAME
						 , PHONE
						 , EMAIL
						 , ADDRESS
						 , INTEREST)
					VALUES(SEQ_UNO.NEXTVAL
						 , ?
						 , ?
						 , ?
						 , ?
						 , ?
						 , ?
						 , ?)
	</entry>

 

 

 

 

 

*마이페이지 기능

menubar.jsp

👉🏻 변경사항 있는 부분만 발췌

<head>
 <!-- 제이쿼리 연결 온라인 방식 -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>

<!-- 부트스트랩 링크 3개 -->
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">

<!-- Popper JS -->
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>

<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>

<body>
        <!-- 로그인 성공 후에 보여지는 프로필 화면 -->
        <div id="user-info">
            <b><%= loginUser.getUserName() %>님!</b> 환영합니다요 ^0^ <br><br>
            <div align="center">
                <a href="<%= contextPath %>/myPage.me">마이페이지</a>
                <a href="<%= contextPath %>/logout.me">로그아웃</a>
            </div>
        </div>
</body>

 

아직 myPage 서블릿을 안 만들어서 404 오류가 뜨긴 하지만 url 링크는 잘 잡힘!

 

myPage.jsp 생성

👉🏻 memberEnrollForm.jsp에서 outer form 전체 복사 후 진행하는 것이 편함

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>   
    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
    .outer {
        background-color : black;
        color : white;
        width : 1000px;
        margin : auto;
        margin-top : 50px;
    }

    #mypage-form>table { margin : auto; } /* 테이블 가운데 정렬 */
    #mypage-form input { margin : 5px; } /* 테이블 줄간격 */

</style>
</head>
<body>

	<%@ include file="../common/menubar.jsp" %>
	
	<%
	// 현재 로그인한 사용자의 정보를 자바 변수로 담기
	// => 아이디, 이름, 휴대폰, 이메일, 주소, 관심분야
	// 보통 스크립틀릿은 상단에 기술하지만,
	// 위에서 아래로 흐르는 코드 순서상 가장 위에 해당 구문을 기술하면 loginUser가 정의되지 않은 상태이므로 빨간 줄 뜸 
	// 그래서 여기로 옮겨서 씀!
	String userId = loginUser.getUserId();
	String userName = loginUser.getUserName();
	// String phone = loginUser.getPhone(); // "010-1111-2222" / null => ""
	// null값이 나와서는 안 됨(사용자가 알아듣지 못함), null이라면 빈 문자열을 보여 줘야 함
	String phone = (loginUser.getPhone() == null) ? "" : loginUser.getPhone(); // 삼항연산자 사용
	String email = (loginUser.getEmail() == null) ? "" : loginUser.getEmail();
	String address = (loginUser.getAddress() == null) ? "" : loginUser.getAddress();
	String interest = (loginUser.getInterest() == null) ? "" : loginUser.getInterest();

	%> 

    <div class="outer">
        
        <br>
        <h2 align="center">마이페이지</h2>
        
        <!--
        	현재 나의 주소: http://localhost:8888/jsp/enrollForm.me
        	내가 요청을 보내고자 하는 주소: http://localhost:8888/jsp/insert.me
        	
        	절대경로: /jsp/insert.me
        	상대경로: insert.me
         -->

        <form id="mypage-form" action="" method="post">

            <!-- 아이디, 비밀번호, (비밀번호 확인), 전화번호, 이메일, 주소, 취미 -->
            <table>
                <!-- (tr>td*3)*8 + Enter -->
                <tr>
                    <td>* 아이디</td>
                    <td><input type="text" name="userId" maxlength="12" required value="<%= userId %>" readonly></td>
                    <!-- 아이디의 수정을 막기 위해 readonly 속성 부여 -->
                    <td></td>
                    <!-- 아이디 중복 확인은 나중에 AJAX 라는 기술을 배운 뒤 할 것 -->
                </tr>
                <tr>
                    <td>* 이름</td>
                    <td><input type="text" name="userName" maxlength="6" required value="<%= userName %>"></td>
                    <td></td>
                </tr>
                <tr>
                    <td>&nbsp;&nbsp;전화번호</td>
                    <td><input type="text" name="phone" placeholder="- 포함해서 입력" value="<%= phone %>"></td>
                    <td></td>
                </tr>
                <tr>
                    <td>&nbsp;&nbsp;이메일</td>
                    <td><input type="email" name="email" value="<%= email %>"></td>
                    <td></td>
                </tr>
                <tr>
                    <td>&nbsp;&nbsp;주소</td>
                    <td><input type="text" name="address" value="<%= address %>"></td>
                    <td></td>
                </tr>
                <tr>
                    <td>&nbsp;&nbsp;관심분야</td>
                    <td colspan="2">
                        <!-- (input[type=checkbox name=interest id= value=]+label)*6 + Enter -->
                        <input type="checkbox" name="interest" id="sports" value="운동">
                        <label for="sports">운동</label>
                        <input type="checkbox" name="interest" id="hiking" value="등산">
                        <label for="hiking">등산</label>
                        <input type="checkbox" name="interest" id="fishing" value="낚시">
                        <label for="fishing">낚시</label>
                        <br>
                        <input type="checkbox" name="interest" id="cooking" value="요리">
                        <label for="cooking">요리</label>
                        <input type="checkbox" name="interest" id="game" value="게임">
                        <label for="game">게임</label>
                        <input type="checkbox" name="interest" id="movie" value="영화">
                        <label for="movie">영화</label>
                    </td>
                </tr>
            </table>
            
            <script>
            	$(function() {
            		var interest = "<%= interest %>"; // "" / "운동, 등산"
            		
            		// [input, input, input, input, input, input]
            		$("input[type=checkbox]").each(function() {
            			
            			// 순차적으로 접근한 input 요소에 value 속성값이 interest라는 변수에 포함되었을 경우
            			// => 해당 input 요소에 checked라는 속성 부여
            			if(interest.search($(this).val()) != -1) { // 포함됐다면
            				// interest라는 문자열로부터 현재 체크박스의 value 속성이 포함되지 않으면 -1 반환
            				$(this).attr("checked", true);
            			}
            			
            		});
            	});
            </script>
            
             <br><br>

            <div align="center">
                <button type="submit" class="btn btn-secondary btn-sm">정보 변경</button>
                <button type="button" class="btn btn-warning btn-sm">비밀번호 변경</button> <!-- 추후 모달창 띄워서 비밀번호 변경할 수 있게끔 할 것-->
                <button type="button" class="btn btn-danger btn-sm">회원 탈퇴</button>
                <!-- 부트스트랩 사용해서 버튼 색상을 주면 더 좋을 것 같음! 각종 링크를 menubar.jsp에 붙여 놓고 함께 사용하기로 함 -->
                <!-- 만들고 나서 VSCode 프리뷰어에는 바로 적용 안 되는 게 정상! 브라우저로 열어보면 적용될 것 -->
            </div>

            <br>

        </form>

    </div>

</body>
</html>

 

MyPageController 서블릿 생성

👉🏻 단지 마이페이지를 띄워 줄 용도로만사용

package com.kh.member.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet implementation class MyPageController
 */
@WebServlet("/myPage.me")
public class MyPageController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public MyPageController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	    
	    // 마이페이지 띄우기 => 로그인한 상태일 경우에만 보여지게끔
	    // 로그인 전 요청 시 : 알람 메시지를 담아 메인 페이지로 요청
	    // 로그인 후 요청 시 : 마이페이지를 포워딩
	    
	    // 현재 로그인한 상태인지 검사하는 방법 : session이 loginUser 키값이 존재하는지 검사!
	    HttpSession session = request.getSession();
	    
	    if(session.getAttribute("loginUser") == null) { // 로그인 전
	        session.setAttribute("alertMsg", "로그인 후 이용 가능한 서비스입니다.");
	        response.sendRedirect(request.getContextPath());
	        
	    } else {
	        // 포워딩 방식 이용
	        // 현재 경로: http://localhost:8888/jsp/myPage.me
	        // 파일 경로: WebContent/views/member/myPage.jsp
	        request.getRequestDispatcher("views/member/myPage.jsp").forward(request, response);
	    }
	 }

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

 

 

 

 

*정보 변경(마이페이지 버튼)

myPage.jsp

         <!-- 정보 변경
        	현재 나의 주소: http://localhost:8888/jsp/myPage.me
        	내가 요청을 보내고자 하는 주소: http://localhost:8888/jsp/update.me
        	
        	절대경로: /jsp/update.me
        	상대경로: update.me
         -->

        <form id="mypage-form" action="<%= contextPath %>/update.me" method="post">

 

Member.java

    // 회원 정보 수정용 생성자
    public Member(String userId, String userName, String phone, String email, String address, String interest) {
        super();
        this.userId = userId;
        this.userName = userName;
        this.phone = phone;
        this.email = email;
        this.address = address;
        this.interest = interest;
    }

 

MemberUpdateController 생성

package com.kh.member.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.kh.member.model.service.MemberService;
import com.kh.member.model.vo.Member;

/**
 * Servlet implementation class MemberUpdateController
 */
@WebServlet("/update.me")
public class MemberUpdateController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public MemberUpdateController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	    
	    // 1) 인코딩 설정
	    request.setCharacterEncoding("UTF-8");
	    
	    // 2) 요청 시 전달값들을 뽑아서 변수 및 객체에 담기
	    // 뽑아야 할 값
	    // userId: 아이디값 => 변경 불가한 값 (unique 제약조건에 의해 구분 용도)
	    String userId = request.getParameter("userId");
	    // userName: 이름
	    String userName = request.getParameter("userName");
	    // phone: 전화번호
	    String phone = request.getParameter("phone");
	    // email: 이메일
	    String email = request.getParameter("email");
	    // address: 주소
	    String address = request.getParameter("address");
	    // interest: 관심분야(배열)
	    String[] interestArr = request.getParameterValues("interest");
	    
	    // 배열 형태의 관심 분야들을 하나의 문자열로 연잇는 작업 추가
	    String interest = "";
	    
	    if(interestArr != null) {
	        interest = String.join(", ", interestArr);
	    }
	    
	    // Member 타입의 객체
	    Member m = new Member(userId, userName, phone, email, address, interest);
	    
	    // 3) 서비스단으로 요청 보내고 결과 받기
	    Member updateMem = new MemberService().updateMember(m);
	    
	    // 4) 돌려받은 처리 결과를 가지고 사용자가 보게 될 응답 페이지를 지정
	    if(updateMem == null) { // 실패 => 에러 문구를 담아서 에러페이지로 포워딩
	        
	        request.setAttribute("errorMsg", "회원 정보 수정에 실패했습니다.");
	        request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);    
	    } else { // 성공 => 갱신된 회원의 정보를 session에 덮어씌우기, 알람 문구 담기, 마이페이지로 url 재요청
	        
	        HttpSession session = request.getSession();
	        session.setAttribute("loginUser", updateMem); // 맵처럼 같은 키값은 존재 불가하므로 덮어쓰여짐
	        session.setAttribute("alertMsg", "성공적으로 회원 정보를 수정했습니다.");
	        
	        // 마이페이지로 url 재요청
	        // localhost:8888/jsp/myPage.me
	        response.sendRedirect(request.getContextPath() + "/myPage.me");
	        
	    }

	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

MemberService

   // 회원 정보 수정용 서비스
    public Member updateMember(Member m) {
        
        // 1) Connection 객체 생성
        Connection conn = JDBCTemplate.getConnection();
        
        // 2) 만들어진 Connection과 전달값을 DAO로 넘기면서 요청
        int result = new MemberDao().updateMember(conn, m);
        
        // DAO로 다녀온 result에는 1 또는 0이 담겨 있을 것
        
        Member updateMem = null;
        
        // 3) 트랜잭션 처리
        if(result > 0) { // 회원 정보 수정 성공 => commit
            JDBCTemplate.commit(conn);
            
            // 갱신된 회원 객체를 다시 조회해 오기
            updateMem = new MemberDao().selectMember(conn, m.getUserId());
            
        } else { // 회원 정보 수정 실패 => rollback
            JDBCTemplate.rollback(conn);
        }
        
        // 4) Connection 객체 반납
        JDBCTemplate.close(conn);
        
        // 5) 결과 리턴
        return updateMem; // 성공: 갱신된 회원의 정보, 실패: null
    }

 

MemberDao

     public int updateMember(Connection conn, Member m) {
         
         // UPDATE문 => int (처리된 행의 개수)
         
         // 1) 필요한 변수들 먼저 세팅
         int result = 0;
         PreparedStatement pstmt = null;
         
         String sql = prop.getProperty("updateMember");
         
         try {
             
            // 2) PreparedStatement 객체 생성
            pstmt = conn.prepareStatement(sql);
            
            // 3_1) 미완성된 쿼리문을 완성시키기
            pstmt.setString(1, m.getUserName());
            pstmt.setString(2, m.getPhone());
            pstmt.setString(3, m.getEmail());
            pstmt.setString(4, m.getAddress());
            pstmt.setString(5, m.getInterest());
            pstmt.setString(6, m.getUserId());
            
            // 3_2) 쿼리문 실행 후 결과받기
            result = pstmt.executeUpdate();
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 4) 자원 반납 (생성 역순)
            JDBCTemplate.close(pstmt);
        }
         
         // 5) 결과 리턴
         return result; // 처리된 행의 갯수 (성공: 1, 실패: 0)
     }
     
     public Member selectMember(Connection conn, String userId) {
         
         // SELECT문 => ResultSet 객체 (unique 제약조건에 의해 많아 봤자 1건 조회) => Member 객체
         
         // 1) 필요한 변수들 먼저 세팅
         Member m = null;
         PreparedStatement pstmt = null;
         ResultSet rset = null;
         
         String sql = prop.getProperty("selectMember");
         
         try {
            // 2) PreparedStatement 객체 생성
            pstmt = conn.prepareStatement(sql);
            
            // 3_1) 미완성된 쿼리문 완성시키기
            pstmt.setString(1, userId);
            
            // 3_2) 쿼리문 실행 후 결과 받기
            rset = pstmt.executeQuery();
            
            // 4) rset으로부터 값을 뽑아서 Member 객체에 담기
            if(rset.next()) {
                m = new Member(rset.getInt("USER_NO"),
                               rset.getString("USER_ID"),
                               rset.getString("USER_PWD"),
                               rset.getString("USER_NAME"),
                               rset.getString("PHONE"),
                               rset.getString("EMAIL"),
                               rset.getString("ADDRESS"),
                               rset.getString("INTEREST"),
                               rset.getDate("ENROLL_DATE"),
                               rset.getDate("MODIFY_DATE"),
                               rset.getString("STATUS"));
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            
            // 5) 자원 반납
            JDBCTemplate.close(rset);
            JDBCTemplate.close(pstmt);
            
        }
         
         // 6) 결과 반환
         return m;
         
     }

 

member-mapper.xml

	<entry key="updateMember">
		UPDATE MEMBER
			SET USER_NAME = ?
			  , PHONE = ?
			  , EMAIL = ?
			  , ADDRESS = ?
			  , INTEREST = ?
			  , MODIFY_DATE = SYSDATE
			WHERE USER_ID = ?
	</entry>

	<!-- 업데이트 된 유저 정보를 가지고 오기 위한 쿼리문 -->
	<entry key="selectMember">
		SELECT *
		FROM MEMBER
		WHERE USER_ID = ?
			AND STATUS = 'Y'
	</entry>
더보기
바꾼 정보가 덮어씌워져 있음

 

 

 

 

 

*비밀번호 변경(마이페이지 버튼)

myPage.jsp

            <div align="center">
                <button type="submit" class="btn btn-secondary btn-sm">정보 변경</button>
                <button type="button" class="btn btn-warning btn-sm" data-toggle="modal" data-target="#updatePwdForm">비밀번호 변경</button> <!-- 추후 모달창 띄워서 비밀번호 변경할 수 있게끔 할 것-->
                <button type="button" class="btn btn-danger btn-sm">회원 탈퇴</button>
                <!-- 부트스트랩 사용해서 버튼 색상을 주면 더 좋을 것 같음! 각종 링크를 menubar.jsp에 붙여 놓고 함께 사용하기로 함 -->
                <!-- 만들고 나서 VSCode 프리뷰어에는 바로 적용 안 되는 게 정상! 브라우저로 열어보면 적용될 것 -->
            </div>

            <br>

        </form>

    </div>
    
    <!-- 비밀번호 변경용 모달창(부트스트랩) -->
    <!-- The Modal -->
	<div class="modal" id="updatePwdForm">
	  <div class="modal-dialog">
	    <div class="modal-content">
	
	      <!-- Modal Header -->
	      <div class="modal-header">
	        <h4 class="modal-title">비밀번호 변경</h4>
	        <button type="button" class="close" data-dismiss="modal">&times;</button>
	      </div>
	
	      <!-- Modal body -->
	      <div class="modal-body">

            <!--
		                현재 나의 위치: localhost:8888/jsp/myPage.me
		                비밀번호 변경 요청 시 url: localhost:8888/jsp/updatePwd.me
                
               	절대 경로: /jsp/updatePwd.me
               	상대 경로: updatePwd.me
                
            -->
	        
			<form action="<%= contextPath %>/updatePwd.me" method="post">
				<!-- 현재 비밀번호, 변경할 비밀번호, 변경할 비밀번호 확인(재입력) -->
				<!-- 해당 회원의 아이디값을 애초에 숨겨서 보내기  -->
				<input type="hidden" name="userId" value="<%= userId %>">
				<table>
					<tr>
						<td>현재 비밀번호</td>
						<td><input type="password" name="userPwd" required></td>
					</tr>
                    <tr>
                        <td>변경할 비밀번호</td>
                        <td><input type="password" name="updatePwd" required></td>
                    </tr>
                    <tr>
                        <td>변경할 비밀번호 재입력</td>
                        <td><input type="password" name="checkPwd" required></td>
                    </tr>
				</table>
				
				<br>
				
				<button type="submit" class="btn btn-secondary btn-sm" onclick="return validatePwd();">비밀번호 변경</button>
				
			</form>

            <script>
                function validatePwd() {
                    if($("input[name=updatePwd]").val() != $("input[name=checkPwd]").val()) {
                        alert("비밀번호가 일치하지 않습니다.");
                        return false;
                    }
                }
            </script>

	      </div>
	
	    </div>
	  </div>
	</div>

 

MemberUpdatePwdController 서블릿 생성

package com.kh.member.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.kh.member.model.service.MemberService;
import com.kh.member.model.vo.Member;

/**
 * Servlet implementation class MemberUpdatePwdController
 */
@WebServlet("/updatePwd.me")
public class MemberUpdatePwdController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public MemberUpdatePwdController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	    
	    // 1) 인코딩 설정
	    request.setCharacterEncoding("UTF-8");
	    
	    // 2) 요청 시 전달값을 뽑아서 변수 및 객체에 담기
	    // 뽑아야 할 값
	    // userPwd : 기존 비밀번호
	    String userPwd = request.getParameter("userPwd");
	    // updatePwd: 새로운 비밀번호
	    String updatePwd = request.getParameter("updatePwd");
	    // checkPwd는 자바스크립트로 이미 유효성 검사를 했기 때문에 굳이 넘겨받지 않음!
	    
	    // 추가적으로 필요한 값: userId
	    // 로그인한 회원의 정보를 알아내는 방법
	    // 1. input type="hidden"으로 애초에 요청 시 숨겨서 전달
	    // 2. session 영역에 담겨 있는 회원 객체로부터 뽑기
	    
	    String userId = request.getParameter("userId");
	    
	    Member m = new Member();
	    m.setUserId(userId); // 해당 회원의 아이디값
	    m.setUserPwd(userPwd); // 해당 회원의 기존 비밀번호값
	    // 새로 바꿀 비밀번호를 담을 필드가 존재하지 않기 때문에 바꿀 비밀번호는 따로 객체로 가공하지 않음
	    
	    // 3) Service단으로 요청 시 전달값을 넘기면서 요청 처리, 결과 리턴받기
	    Member updateMem =  new MemberService().updatePwdMember(m, updatePwd);
	    
	    HttpSession session = request.getSession();
	    
	    // 4) 결과에 따른 응답페이지 지정
	    if(updateMem == null) { // 실패 => 실패 문구를 alertMsg 키값으로 보내기 => 마이페이지 url 요청
	        session.setAttribute("alertMsg", "비밀번호 변경에 실패했습니다.");
	    } else { // 성공 => 갱신된 회원의 정보를 loginUser라는 키값에 덮어씌우기, 성공 문구를 alertMsg 키값으로 보내기 => 마이페이지 url 요청
	        session.setAttribute("loginUser", updateMem);
	        session.setAttribute("alertMsg", "성공적으로 비밀번호가 변경되었습니다.");
	    }
	    
	    response.sendRedirect(request.getContextPath() + "/myPage.me");
	    
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

MemberService

    // 비밀번호 변경용 서비스
    public Member updatePwdMember(Member m, String updatePwd) {
        
        // 1) Connection 객체 생성
        Connection conn = JDBCTemplate.getConnection();
        
        // 2) 만들어진 Connection 객체와 전달값을 DAO로 넘겨서 요청 처리 후 결과받기
        int result = new MemberDao().updatePwdMember(conn, m, updatePwd);
        
        Member updateMem = null;
        
        // 3) 결과에 따른 트랜잭션 처리
        if(result > 0) { // 성공 => commit 
            JDBCTemplate.commit(conn);
            
            // 갱신된 회원의 정보 다시 조회하기
            updateMem = new MemberDao().selectMember(conn, m.getUserId());
            
        } else { // 실패 => rollback
            JDBCTemplate.rollback(conn);
        }
        
        // 4) Connection 객체 반납
        JDBCTemplate.close(conn);
        
        // 5) 결과 반환
        return updateMem; // 성공: 해당 갱신된 회원의 정보, 실패: null
        
    }

 

MemberDao

     public int updatePwdMember(Connection conn, Member m, String updatePwd) {
         
         // UPDATE문 => int (처리된 행의 개수)
         
         // 1) 필요한 변수들 먼저 세팅
         int result = 0;
         PreparedStatement pstmt = null;
         
         String sql = prop.getProperty("updatePwdMember");
         
         try {
            // 2) PreparedStatement 객체 생성
            pstmt = conn.prepareStatement(sql);
            
            // 3_1) 미완성된 쿼리문 완성시키기
            pstmt.setString(1, updatePwd); // 가공하지 않고 바로 들고 왔으니 넣을 때도 바로 넣음!
            pstmt.setString(2, m.getUserId());
            pstmt.setString(3, m.getUserPwd());
            
            // 3_2) 실행 후 결과받기
            result = pstmt.executeUpdate();
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            
            // 4) 자원 반납
            JDBCTemplate.close(pstmt);
        }
         
         // 5) 결과 리턴
         return result; // 성공: 1, 실패: 0         
     }

 

member-mapper.xml

	<entry key="updatePwdMember">
		UPDATE MEMBER
			SET USER_PWD = ?
			  , MODIFY_DATE = SYSDATE
		  WHERE USER_ID = ?
		  	AND USER_PWD = ?
	</entry>

 

 

 

 

 

 

*회원 탈퇴

버튼 클릭 시 '정말 탈퇴하시겠습니까?' 창 띄우고 비밀번호 입력 후 일치하면 탈퇴

myPage.jsp

👉🏻 비밀번호 변경용 모달창 복붙 해서 수정

            <div align="center">
                <button type="submit" class="btn btn-secondary btn-sm">정보 변경</button>
                <button type="button" class="btn btn-warning btn-sm" data-toggle="modal" data-target="#updatePwdForm">비밀번호 변경</button> <!-- 추후 모달창 띄워서 비밀번호 변경할 수 있게끔 할 것-->
                              <button type="button" class="btn btn-danger btn-sm" data-toggle="modal" data-target="#deleteForm">회원 탈퇴</button>
                <!-- 부트스트랩 사용해서 버튼 색상을 주면 더 좋을 것 같음! 각종 링크를 menubar.jsp에 붙여 놓고 함께 사용하기로 함 -->
                <!-- 만들고 나서 VSCode 프리뷰어에는 바로 적용 안 되는 게 정상! 브라우저로 열어보면 적용될 것 -->
            </div>
            <br>
        </form>
    </div>

    <!-- 회원 탈퇴용 모달창 - 버튼 클릭 시 뜨도록 -->
    <!-- The Modal -->
	<div class="modal" id="deleteForm">
        <div class="modal-dialog">
          <div class="modal-content">
      
            <!-- Modal Header -->
            <div class="modal-header">
              <h4 class="modal-title">회원 탈퇴</h4>
              <button type="button" class="close" data-dismiss="modal">&times;</button>
            </div>
      
            <!-- Modal body -->
            <div class="modal-body">
                <b>탈퇴 후 복구가 불가능합니다. <br>
                정말로 탈퇴하시겠습니까? <br><br></b>
                
                <!-- 
                	현재 나의 위치: http://localhost:8888/jsp/myPage.me
                	회원 탈퇴 요청 시 요청을 보내고자 하는 url 위치: http://localhost:8888/jsp/delete.me
                	
                	절대경로: /jsp/delete.me
                	상대경로: delete.me
                 -->

                <form action="<%= contextPath %>/delete.me" method="post">
                    <table>
                        <tr>
                            <td>비밀번호</td>
                            <td><input type="password" name="userPwd" required></td>
                        </tr>
                    </table>
                    <br>
                    <button type="submit" class="btn btn-danger btn-sm">탈퇴하기</button>
                </form>
  
            </div>     
          </div>
        </div>
    </div>

 

MemberDeleteController 서블릿 생성

package com.kh.member.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.kh.member.model.service.MemberService;
import com.kh.member.model.vo.Member;

/**
 * Servlet implementation class MemberDeleteController
 */
@WebServlet("/delete.me")
public class MemberDeleteController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public MemberDeleteController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	    
	    // 1) 인코딩 설정
	    request.setCharacterEncoding("UTF-8");
	    
	    // 2) 요청 시 전달값 뽑아서 밑 변수나 객체에 담기
	    // 뽑아야 할 값
	    // userPwd: 비밀번호
	    String userPwd = request.getParameter("userPwd");
	    
	    // 로그인한 회원의 정보를 알아내는 방법
	    // 1. input type="hidden"으로 애초에 넘기기
	    // 2. session에 담겨 있는 회원의 정보 뽑기
	    // => 모두 가능하나 이 기능에서는 session 방법을 써 볼 것!
	    //  비밀번호는 유니크값이 아니므로 회원 정보를 알아낼 수 없음
	    // => 추가적으로 필요한 값: userId
	    
	    // 세션 객체 얻어내기 => loginUser 키값에 해당되는 밸류로부터 아이디값만 getter로 뽑기
	    HttpSession session = request.getSession();
	    String userId = ((Member)session.getAttribute("loginUser")).getUserId();
	    
	    // Member 객체에 담기 => 몇 개 안 되니까 setter 메소드로
	    Member m = new Member();
	    m.setUserId(userId);
	    m.setUserPwd(userPwd);
	    
	    // 3) Service단으로 요청 및 결과 받기
	    // 해당 아이디와 일치하는 값은 단 1개뿐일 것이므로 int형 변수로 받아 줌
	    int result = new MemberService().deleteMember(m);
	    
	    // 4) 결과에 따른 응답 페이지 지정
	    if(result > 0) { // 성공 => 성공 문구를 담아서 메인 페이지로 url 요청 (단, 로그아웃되도록)
	        
	        session.setAttribute("alertMsg", "성공적으로 회원 탈퇴되었습니다. 그동안 이용해 주셔서 ㅠㅠ... 고마워요...");
	        
	        // invalidate() 메소드를 이용해서 세션을 무효화시킴으로써 로그아웃 처리 가능!
	        // session.invalidate();
	        // => invalidate() 메소드를 사용 시 세션 자체가 만료되어 alert가 되지 않기 때문에 (값을 담을 그릇 자체를 없애 버린 꼴)
	        // 즉, 이 방법을 사용하면  탈퇴 성공 시에도 알림창이 뜨지 않음! => 성공 문구를 담았던 그릇 자체를 없애 버리는 꼴
	        // 이 경우에는 removeAttribute("키값")을 이용하여 현재 로그인한 사용자의 정보를 지워 주는 방식으로 로그아웃시킴
	        session.removeAttribute("loginUser");
	        
	        response.sendRedirect(request.getContextPath());
	        
	    } else { // 실패 => 에러 문구를 request에 담아서 (errorMsg 키값) 에러페이지로 포워딩
	        
	        request.setAttribute("errorMsg", "비밀번호 불일치! 회원 탈퇴에 실패했습니다.");
	        request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);
	        // forward(request, response): 나 대신에 응답 좀 해 줘~ 라고 위임시키는 개념
	    }
	    
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

MemberService

    // 회원 탈퇴용 서비스
    public int deleteMember(Member m) {
        
        // 1) Connection 객체 생성
        Connection conn = JDBCTemplate.getConnection();
        
        // 2) 만들어진 Connection 객체와 전달값을 DAO로 넘기고 결과받기
        int result = new MemberDao().deleteMember(conn, m);
        
        // 3) 결과에 따른 트랜잭션 처리
        if(result > 0) { // 성공 => commit
            JDBCTemplate.commit(conn);
            
        } else { // 실패 => rollback
            JDBCTemplate.rollback(conn);
        }
        
        // 4) Connection 객체 반납
        JDBCTemplate.close(conn);
        
        // 5) 결과 반환
        return result;
    }

 

MemberDao 

     public int updatePwdMember(Connection conn, Member m, String updatePwd) {
         
         // UPDATE문 => int (처리된 행의 개수)
         
         // 1) 필요한 변수들 먼저 세팅
         int result = 0;
         PreparedStatement pstmt = null;
         
         String sql = prop.getProperty("updatePwdMember");
         
         try {
            // 2) PreparedStatement 객체 생성
            pstmt = conn.prepareStatement(sql);
            
            // 3_1) 미완성된 쿼리문 완성시키기
            pstmt.setString(1, updatePwd); // 가공하지 않고 바로 들고 왔으니 넣을 때도 바로 넣음!
            pstmt.setString(2, m.getUserId());
            pstmt.setString(3, m.getUserPwd());
            
            // 3_2) 실행 후 결과받기
            result = pstmt.executeUpdate();
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            
            // 4) 자원 반납
            JDBCTemplate.close(pstmt);
        }
         
         // 5) 결과 리턴
         return result; // 성공: 1, 실패: 0         
     }
     
     public int deleteMember(Connection conn, Member m) {
         
         // UPDATE문 => int 처리된 행의 개수
         
         // 1) 필요한 변수 먼저 세팅
         int result = 0;
         PreparedStatement pstmt = null;
         
         String sql = prop.getProperty("deleteMember");
         
         try {
            // 2) PreparedStatement 객체 생성
            pstmt = conn.prepareStatement(sql);
            
            // 3_1) 미완성된 쿼리문 완성시키기
            pstmt.setString(1, m.getUserId());
            pstmt.setString(2, m.getUserPwd());
            
            // 3_2) 쿼리문 실행 후 결과 받기
            result = pstmt.executeUpdate(); // 성공 시 1, 실패 시 0이 담길 것
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 4) 자원 반납
            JDBCTemplate.close(pstmt);
        }
         
         // 5) 결과 반환
         return result;
     }

 

member-mapper.xml

	<entry key="deleteMember">
		UPDATE MEMBER
			SET STATUS = 'N'
			  , MODIFY_DATE = SYSDATE
		WHERE USER_ID = ?
			AND USER_PWD = ?
	</entry>
더보기
회원 탈퇴 버튼 클릭 시 모달창 띄워 줌
일치하지 않는 비밀번호를 누르고 탈퇴하기 버튼 눌렀을 때
일치하는 비밀번호를 입력했을 때

이후에는 로그아웃된 상태로 기본 홈페이지가 뜨도록 설정함