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
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 테이블에 있는 아이디와 비번을 입력해 봄
![](https://blog.kakaocdn.net/dn/miBX3/btrOtHZukKd/Akd8Z8euESi5sY2Q5dZYy1/img.png)
로그인 버튼을 눌러 봄
![](https://blog.kakaocdn.net/dn/4fQ5D/btrOtcleKoA/Tfe9fPomfyUx0uKGKElGL1/img.png)
![](https://blog.kakaocdn.net/dn/dRc3GF/btrOxlU7aWM/u0jkndzgaX8JgDDYxbHPP1/img.png)
확인 버튼을 눌러 봄
![](https://blog.kakaocdn.net/dn/3icGT/btrOw6jmV7r/MWuKo7BQfhw4Fnm4Ktzxxk/img.png)
![](https://t1.daumcdn.net/keditor/emoticon/niniz/large/043.gif)
내 상황
*로그아웃 기능
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>
서버 재시작 후 로그인을 해 봄
![](https://blog.kakaocdn.net/dn/PaK1u/btrOrRBPvSc/tzrVDBU1uyY8z0h2TYJ2M1/img.png)
로그아웃 버튼을 눌러 봄
![](https://blog.kakaocdn.net/dn/biSV7b/btrOwk3Q56v/XnxMCzjKY1J9bOFqY47Al0/img.png)
![](https://t1.daumcdn.net/keditor/emoticon/niniz/large/001.gif)
*회원가입 기능
memberEnrollForm.jsp
<%@ 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> 전화번호</td>
<td><input type="text" name="phone" placeholder="- 포함해서 입력"></td>
<td></td>
</tr>
<tr>
<td> 이메일</td>
<td><input type="email" name="email"></td>
<td></td>
</tr>
<tr>
<td> 주소</td>
<td><input type="text" name="address"></td>
<td></td>
</tr>
<tr>
<td> 관심분야</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.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> 전화번호</td>
<td><input type="text" name="phone" placeholder="- 포함해서 입력" value="<%= phone %>"></td>
<td></td>
</tr>
<tr>
<td> 이메일</td>
<td><input type="email" name="email" value="<%= email %>"></td>
<td></td>
</tr>
<tr>
<td> 주소</td>
<td><input type="text" name="address" value="<%= address %>"></td>
<td></td>
</tr>
<tr>
<td> 관심분야</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>
![](https://blog.kakaocdn.net/dn/cqKIPM/btrO5tMouYS/9mETplrKA3KNKwb8qAnWwK/img.png)
![](https://blog.kakaocdn.net/dn/beKvlM/btrOQvxjW2y/R5DBOUh2u45yrbdvx8Y4Ck/img.png)
![](https://blog.kakaocdn.net/dn/cd8dGp/btrO8zSySsZ/BnP1wTSCPQ97Kj7nFtx7e0/img.png)
*비밀번호 변경(마이페이지 버튼)
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">×</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">×</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>
![](https://blog.kakaocdn.net/dn/vhvnv/btrO78Og1ZB/bQie2YUY4LmcbpDw6cpqo1/img.png)
![](https://blog.kakaocdn.net/dn/bma78v/btrO6NcxBnh/LYEVlKflfJ0yCCbDBgsweK/img.png)
![](https://blog.kakaocdn.net/dn/bfmwEn/btrO45Zi5se/eYpseTJN4tanl2ZzOC38w0/img.png)
이후에는 로그아웃된 상태로 기본 홈페이지가 뜨도록 설정함