ㅁ 회원서비스
- 회원서비스 개발 세팅
- 회원서비스_로그인
- 회원서비스_로그아웃
- 회원서비스_회원가입페이지로이동
- 회원서비스_회원가입요청 <- 여기까지 했음.
- 회원서비스_마이페이지요청
- 회원서비스_정보변경요청
- 회원서비스_비번변경요청
- 회원서비스_회원탈퇴요청
ㅁ header.jsp에서 마이페이지로 이동 요청한다.
<%@ page import="com.br.web.member.model.vo.Member" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- Bootstrap 사용을 위한 CDN -->
<!-- ------------------------- -->
<style>
header{height: 150px}
header a{color:black;}
</style>
<%
String contextPath = request.getContextPath(); // "/web"
Member loginUser = (Member)session.getAttribute("loginUser");
// 해당 구문이 실행되는 시점
// 로그인 요청 전 페이지 로드시 : null
// 로그인 성공 후 페이지 로드시 : 조회된 데이터가 담겨있는 Member객체
String alertMsg = (String)session.getAttribute("alertMsg");
// 해당 구문이 실행되는 시점
// 특정 서비스 요청 전 페이지 로드시 : null
// 특정 서비스 요청 성공 후 페이지 로드시 : alert로 띄워줄 메세지
%>
<% if(alertMsg != null) { %>
<script>
alert('<%=alertMsg%>');
</script>
<% session.removeAttribute("alertMsg"); } %>
<header class="row m-3">
<div class="col-3 d-flex justify-content-center align-items-center">
<a href="<%=contextPath%>"><img src="<%= contextPath %>/assets/image/goodee_logo.png"></a>
</div>
<div class="col-6"></div>
<div class="col-3 d-flex justify-content-center align-items-center">
<% if(loginUser == null) { %>
<!-- case1. 로그인전 -->
<form action="<%= contextPath %>/login.me" method="post">
<table>
<tr>
<th>ID</th>
<td><input type="text" name="userId" class="form-control form-control-sm" placeholder="Enter Your ID" required></td>
</tr>
<tr>
<th>PWD</th>
<td><input type="password" name="userPwd" class="form-control form-control-sm" placeholder="Enter Your PWD" required></td>
</tr>
<tr>
<td colspan="2" align="center">
<button type="submit" class="btn btn-secondary btn-sm">로그인</button>
<button type="button" class="btn btn-secondary btn-sm" id="signup-btn">회원가입</button>
<script>
document.getElementById("signup-btn").addEventListener("click", () => {
location.href = "<%=contextPath%>/signup.me";
})
</script>
</td>
</tr>
</table>
</form>
<% }else { %>
<!-- case2. 로그인후 -->
<div>
<b><%= loginUser.getUserName() %></b>님 환영합니다. <br><br>
<a href="<%=contextPath%>/myinfo.me">마이페이지</a>
<a href="<%=contextPath%>/logout.me">로그아웃</a>
</div>
<% } %>
</div>
</header>
<nav class="navbar m-3 navbar-expand-sm bg-dark navbar-dark d-flex justify-content-center">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">공지사항</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">일반게시판</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">사진게시판</a>
</li>
</ul>
</nav>
- 로그인 후 보여지는 '마이페이지' 링크를 클릭하면 "/myinfo.me"라는 url mapping 값을 가지는 서블릿 클래스가 호출되게끔 작성한다.
ㅁ /webApp/src/main/java/com/br/web/member/controller에 MemberInfoController.java라는 서블릿을 만든다.
package com.br.web.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;
@WebServlet("/myinfo.me")
public class MemberInfoController extends HttpServlet {
private static final long serialVersionUID = 1L;
public MemberInfoController() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 응답페이지 : /web/views/member/myInfo.jsp
// 응답데이터 : 로그인한 회원 데이터 (HttpSession에 이미 담겨있음)
request.getRequestDispatcher("/views/member/myInfo.jsp").forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 요청시 전달한 값이나 실행할 쿼리는 없다. 응답만 하면 된다.
- 마이페이지 요청을 보낸다는 것은 현재 로그인이 되어있다는 뜻이다.
header.jsp에서 '로그인 후'에 '마이페이지' 링크가 보여지기 때문이다.
- 마이페이지에서 응답할 데이터는 로그인한 회원 데이터인데,
로그인을 하면서 이미 session에 loginUser라는 key 값으로 로그인한 회원 객체가 담겨있다.
그래서 db에서 조회할 필요가 없다.
- 응답페이지로 /web/views/member/myInfo.jsp를 띄워준다.
이 페이지를 응답하는 url은 따로 존재하지 않는다. 최초로 이동할 때는 무조건 포워딩 방식으로 이동해야 한다.
- 이 응답페이지에서 필요한 응답데이터인 로그인한 회원 데이터는 session에 이미 담겨있다.
응답페이지에 가서 session에서 데이터를 꺼내서 각각의 영역에 출력만 해주면 된다.
ㅁ src/main/webapp/views/member에 myInfo.jsp를 만든다.
- 아래는 myInfo.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>
</head>
<body>
<div class="container p-3">
<!-- Header, Nav start -->
<%@ include file="/views/common/header.jsp" %>
<!-- Header, Nav end -->
<!-- Section start -->
<section class="row m-3" style="min-height: 500px">
<div class="container border p-5 m-4 rounded">
<h2 class="m-4">마이페이지</h2>
<form action="" method="" class="m-4">
<table class="table">
<tr>
<th>* 아이디</th>
<td><input type="text" class="form-control" placeholder="Enter Your ID" name="userId" value="<%= loginUser.getUserId() %>" readonly></td>
<td></td>
</tr>
<tr>
<th>* 이름</th>
<td><input type="text" class="form-control" placeholder="Enter Your Name" name="userName" value="<%= loginUser.getUserName() %>" required></td>
<td></td>
</tr>
<tr>
<th> 전화번호</th>
<td><input type="text" class="form-control" placeholder="Enter Your Phone (- include)" name="phone" value='<%= loginUser.getPhone() == null ? "" : loginUser.getPhone() %>'></td>
<td></td>
</tr>
<tr>
<th> 이메일</th>
<td><input type="text" class="form-control" placeholder="Enter Your Email (@ include)" name="email" value='<%= loginUser.getEmail() == null ? "" : loginUser.getEmail() %>'></td>
<td></td>
</tr>
<tr>
<th> 주소</th>
<td><input type="text" class="form-control" placeholder="Enter Your Address" name="address" value='<%= loginUser.getAddress() == null ? "" : loginUser.getAddress() %>'></td>
<td></td>
</tr>
<tr>
<th> 관심분야</th>
<td>
<input type="checkbox" name="interest" value="운동" id="sports">
<label for="sports">운동</label>
<input type="checkbox" name="interest" value="등산" id="climibing">
<label for="climibing">등산</label>
<input type="checkbox" name="interest" value="낚시" id="fishing">
<label for="fishing">낚시</label>
<input type="checkbox" name="interest" value="요리" id="cooking">
<label for="cooking">요리</label>
<input type="checkbox" name="interest" value="게임" id="game">
<label for="game">게임</label>
<input type="checkbox" name="interest" value="영화" id="movie">
<label for="movie">영화</label>
</td>
<script>
$(function() {
let interest = '<%=loginUser.getInterest() == null ? "" : loginUser.getInterest()%>'; // "" | "운동,등산"
$(":checkbox").each(function(idx, el) { // el : 순차적으로 접근되는 체크박스 요소 객체
// el.value : 접근되는 체크박스의 value값("운동", "등산", "낚시", ...)
if(interest.indexOf(el.value) != -1){
$(el).prop("checked", true);
}
})
})
</script>
<td></td>
</tr>
</table>
<br>
<div align="center">
<button type="submit" class="btn btn-outline-primary btn-sm">정보변경</button>
<button type="button" class="btn btn-outline-warning btn-sm" data-toggle="modal" data-target="#changePwdModal">비밀번호변경</button>
<button type="button" class="btn btn-outline-danger btn-sm" data-toggle="modal" data-target="#resignModal">회원탈퇴</button>
</div>
</form>
</div>
</section>
<!-- Section end -->
<!-- Footer start -->
<%@ include file="/views/common/footer.jsp" %>
<!-- Footer end -->
</div>
<!-- 비밀번호 변경용 Modal -->
<div class="modal" id="changePwdModal">
<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">
<form action="<%= contextPath %>/updatePwd.me" method="post">
<input type="hidden" name="userId" value="<%= loginUser.getUserId() %>">
<table align="center">
<tr>
<th>* 현재 비밀번호</th>
<td><input type="password" class="form-control" name="userPwd" required></td>
</tr>
<tr>
<th>* 변경할 비밀번호</th>
<td><input type="password" class="form-control" name="updatePwd" required></td>
</tr>
<tr>
<th>* 변경할 비밀번호 재입력</th>
<td><input type="password" class="form-control" required></td>
</tr>
<tr>
<td colspan="2" style="text-align:center; padding-top: 10px;">
<button type="submit" class="btn btn-warning btn-sm">비밀번호 변경</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
</div>
<!-- 회원탈퇴용 Modal -->
<div class="modal" id="resignModal">
<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">
<form action="<%= contextPath %>/delete.me" method="post">
<table align="center">
<tr>
<th colspan="2">
탈퇴 후 복구가 불가능합니다. <br>
정말로 탈퇴하시겠습니까?
</th>
</tr>
<tr>
<th>현재 비밀번호</th>
<td><input type="password" class="form-control" name="userPwd" required></td>
</tr>
<tr>
<td colspan="2" style="text-align:center; padding-top: 10px;">
<button type="submit" class="btn btn-danger btn-sm">회원탈퇴</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
- header와 footer에 include 지시어를 작성한다.
- 화면구현하며 만들었던 "마이페이지.html"의 body 태그 안 내용들을 복사해서 붙여넣기 한다.
- 아이디, 이름, 전화번호, 이메일, 주소 부분의 value 속성값을 'user01', '홍길동' 이렇게 정적으로 작성했었는데,
출력식을 사용해서 loginUser 객체에서 getXXX 메소드로 뽑는다.
- 전화번호, 이메일, 주소는 필수 입력값이 아니다.
db로부터 조회했을 때 데이터가 없으면 이 필드들에는 null이 있다.
회원가입시 입력을 안했다면 db로부터 조회했을 때 데이터가 없으면 이 필드에 null이 있다.
- 그러면 value="null"이 되어서 null이 불필요하게 노출된다. 그러므로 삼항연산자를 사용한다.
- 관심분야는 텍스트 상자가 아니라 체크박스 형태로 되어있다.
이런 경우엔 script 구문을 사용해야 한다.
- 텍스트 상자의 경우 value 속성값만 주면 되지만,
체크박스나 라디오버튼은 그 요소들에 checked 속성을 부여해줘야 한다.
- script 구문을 이용해서 사용자가 선택했던 각각의 체크박스에 checked된 상태로 보여준다.
- jQuery 방식으로 $(function() { })으로 요소가 다 만들어지고 나서 바로 실행되게끔 한다.
- 체크박스들을 순차적으로 탐색하면서 로그인한 회원의 관심분야인지를 비교한다.
맞다면 checked 속성을 부여한다.
- 6개의 타입이 체크박스인 요소들에 일일이 접근하면서 비교한다.
순차 접근하고자 한다면 jQuery 메소드 중에 each() 메소드를 사용한다.
each() 메소드로 각각의 요소에 순차적으로 접근하면서 그때마다 실행시킬 function()을 작성할 수 있다.
- : 는 jQuery에서 필터 선택자를 나타내는 문자입니다. 필터 선택자는 특정 조건에 맞는 요소를 선택할 때 사용됩니다.
- :checkbox 는 이 필터 선택자 중 하나로, 현재 문서에서 모든 체크박스 요소를 선택합니다.
- interest.indexOf(el.value) != -1는 interest 문자열에 el.value가 포함되어 있는지 확인하는 조건입니다.
(interest는 문자열이다. String 타입의 배열을 컴마로 하나의 문자열로 만들어서 interest 변수에 대입했었다.)
- interest 문자열에서 el.value 값이 처음 나타나는 위치의 인덱스를 반환합니다.
- 만약 el.value가 interest에 포함되어 있다면, 그 위치의 인덱스를 반환하고, 포함되어 있지 않다면 -1을 반환합니다.
- interest.indexOf(el.value)는 자바스크립트 방식이고, jQuery 방식으로 선택하고 jQuery 메소드를 사용해서 interest.indexOf($(el).val()) 이렇게 작성해도 된다.
- $(el).prop("checked", true);는 jQuery 방식이고, 자바스크립트 방식으로 하면 el.checked = true; 이다.
ㅁ 메인페이지에서 로그인해보고 마이페이지를 클릭해서 localhost:8888/web/myinfo.me로 url이 잘 뜨는지, 마이페이지에 회원정보가 잘 뜨는지 확인한다.
======================================================================================
ㅁ 마이페이지에서 '회원정보변경' 버튼 클릭시 "/update.me"라는 url mapping 값을 가지는 서블릿 클래스를 호출한다.
- 아래는 myInfo.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>
</head>
<body>
<div class="container p-3">
<!-- Header, Nav start -->
<%@ include file="/views/common/header.jsp" %>
<!-- Header, Nav end -->
<!-- Section start -->
<section class="row m-3" style="min-height: 500px">
<div class="container border p-5 m-4 rounded">
<h2 class="m-4">마이페이지</h2>
<form action="<%= contextPath %>/update.me" method="post" class="m-4">
<table class="table">
<tr>
<th>* 아이디</th>
<td><input type="text" class="form-control" placeholder="Enter Your ID" name="userId" value="<%= loginUser.getUserId() %>" readonly></td>
<td></td>
</tr>
<tr>
<th>* 이름</th>
<td><input type="text" class="form-control" placeholder="Enter Your Name" name="userName" value="<%= loginUser.getUserName() %>" required></td>
<td></td>
</tr>
<tr>
<th> 전화번호</th>
<td><input type="text" class="form-control" placeholder="Enter Your Phone (- include)" name="phone" value='<%= loginUser.getPhone() == null ? "" : loginUser.getPhone() %>'></td>
<td></td>
</tr>
<tr>
<th> 이메일</th>
<td><input type="text" class="form-control" placeholder="Enter Your Email (@ include)" name="email" value='<%= loginUser.getEmail() == null ? "" : loginUser.getEmail() %>'></td>
<td></td>
</tr>
<tr>
<th> 주소</th>
<td><input type="text" class="form-control" placeholder="Enter Your Address" name="address" value='<%= loginUser.getAddress() == null ? "" : loginUser.getAddress() %>'></td>
<td></td>
</tr>
<tr>
<th> 관심분야</th>
<td>
<input type="checkbox" name="interest" value="운동" id="sports">
<label for="sports">운동</label>
<input type="checkbox" name="interest" value="등산" id="climibing">
<label for="climibing">등산</label>
<input type="checkbox" name="interest" value="낚시" id="fishing">
<label for="fishing">낚시</label>
<input type="checkbox" name="interest" value="요리" id="cooking">
<label for="cooking">요리</label>
<input type="checkbox" name="interest" value="게임" id="game">
<label for="game">게임</label>
<input type="checkbox" name="interest" value="영화" id="movie">
<label for="movie">영화</label>
</td>
<script>
$(function() {
let interest = '<%=loginUser.getInterest() == null ? "" : loginUser.getInterest()%>';
// "" | "운동,등산"
$(":checkbox").each(function(idx, el) { // el : 순차적으로 접근되는 체크박스 요소
// el.value : 접근되는 체크박스의 value값
if(interest.indexOf(el.value) != -1){
$(el).prop("checked", true);
}
})
})
</script>
<td></td>
</tr>
</table>
<br>
<div align="center">
<button type="submit" class="btn btn-outline-primary btn-sm">정보변경</button>
<button type="button" class="btn btn-outline-warning btn-sm" data-toggle="modal" data-target="#changePwdModal">비밀번호변경</button>
<button type="button" class="btn btn-outline-danger btn-sm" data-toggle="modal" data-target="#resignModal">회원탈퇴</button>
</div>
</form>
</div>
</section>
<!-- Section end -->
<!-- Footer start -->
<%@ include file="/views/common/footer.jsp" %>
<!-- Footer end -->
</div>
<!-- 비밀번호 변경용 Modal -->
<div class="modal" id="changePwdModal">
<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">
<form action="<%= contextPath %>/updatePwd.me" method="post">
<input type="hidden" name="userId" value="<%= loginUser.getUserId() %>">
<table align="center">
<tr>
<th>* 현재 비밀번호</th>
<td><input type="password" class="form-control" name="userPwd" required></td>
</tr>
<tr>
<th>* 변경할 비밀번호</th>
<td><input type="password" class="form-control" name="updatePwd" required></td>
</tr>
<tr>
<th>* 변경할 비밀번호 재입력</th>
<td><input type="password" class="form-control" required></td>
</tr>
<tr>
<td colspan="2" style="text-align:center; padding-top: 10px;">
<button type="submit" class="btn btn-warning btn-sm">비밀번호 변경</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
</div>
<!-- 회원탈퇴용 Modal -->
<div class="modal" id="resignModal">
<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">
<form action="<%= contextPath %>/delete.me" method="post">
<table align="center">
<tr>
<th colspan="2">
탈퇴 후 복구가 불가능합니다. <br>
정말로 탈퇴하시겠습니까?
</th>
</tr>
<tr>
<th>현재 비밀번호</th>
<td><input type="password" class="form-control" name="userPwd" required></td>
</tr>
<tr>
<td colspan="2" style="text-align:center; padding-top: 10px;">
<button type="submit" class="btn btn-danger btn-sm">회원탈퇴</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
- 마이페이지를 감싸고 있는 form에 action 속성값으로 "<%= contextPath %>/update.me"을, method 속성 값으로 "post"를 준다.
ㅁ/webApp/src/main/java/com/br/web/member/controller에 MemberUpdateController.java라는 서블릿 클래스를 만든다.
- 아래는 MemberUpdateController.java다.
package com.br.web.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.br.web.member.model.service.MemberService;
import com.br.web.member.model.vo.Member;
@WebServlet("/update.me")
public class MemberUpdateController extends HttpServlet {
private static final long serialVersionUID = 1L;
public MemberUpdateController() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 요청
request.setCharacterEncoding("utf-8");
String userId = request.getParameter("userId");
String userName = request.getParameter("userName");
String phone = request.getParameter("phone");
String email = request.getParameter("email");
String address = request.getParameter("address");
String[] interestArr = request.getParameterValues("interest");
String interest = interestArr == null ? "" : String.join(",", interestArr);
Member m = new Member(userId, userName, phone, email, address, interest);
Member updateMem = new MemberService().updateMember(m);
// 2. 응답
if(updateMem == null) { // 실패
// 응답페이지 : 에러페이지
// 응답데이터 : "회원 정보 변경 실패" 메세지
request.setAttribute("msg", "회원 정보 변경 실패");
request.getRequestDispatcher("/views/common/errorPage.jsp").forward(request, response);
}else { // 성공
// session에 담겨있는 loginUser를 갱신된 회원객체로 변경
HttpSession session = request.getSession();
session.setAttribute("loginUser", updateMem);
// 응답페이지 : 다시 마이페이지
// 응답데이터 : "성공적으로 회원정보를 수정했습니다." alert 메세지
session.setAttribute("alertMsg", "성공적으로 회원정보를 수정했습니다.");
// /web/myinfo.me url 재요청 => 마이페이지 포워딩
response.sendRedirect(request.getContextPath() + "/myinfo.me");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 회원가입하는 MemberInsertController 클래스와 비슷하다.
차이점은 비밀번호 변경은 따로 빼놔서 비밀번호 부분이 없고, insert가 아닌 update 쿼리를 실행한다.
- SQL에서 UPDATE 쿼리를 실행하면, 데이터베이스는 수정된 행의 수를 반환합니다.
- userId는 readonly를 줘서 회원정보 변경이 불가능한 상태였다.
db에 update 쿼리문을 사용할 때 회원정보를 변경할 회원을 찾기 위한 조건으로 userId를 사용하기 위해 필요하다.
- userName은 required를 줘서 필수입력하게끔 했었다. 뭐라도 값이 넘어온다.
- 핸드폰번호와 이메일, 주소는 텍스트상자가 비어있으면 빈문자열이 넘어온다.
- 관심분야는 null일 수도 있고 문자열 배열일 수 있다. null이 아니라면 하나의 문자열로 합쳐준다.
- 다수의 데이터를 담기 위해 String 배열도 되지만 Member 객체에 담는다.
기본 생성자로 Member 객체를 생성하고 setXXX 메소드로 담아도 되고,
내가 담을 데이터의 순서와 개수를 맞춰서 생성자를 또 만들어서 매개변수로 담아도 된다.
- Member.java에 가서 alt shift s + o로 생성자를 만들어준다.
ㅁ MemberService 메소드에 updateMember() 메소드를 만든다.
package com.br.web.member.model.service;
import static com.br.web.common.template.JDBCTemplate.close;
import static com.br.web.common.template.JDBCTemplate.commit;
import static com.br.web.common.template.JDBCTemplate.getConnection;
import static com.br.web.common.template.JDBCTemplate.rollback;
import java.sql.Connection;
import java.util.Map;
import com.br.web.member.model.dao.MemberDao;
import com.br.web.member.model.vo.Member;
public class MemberService {
private MemberDao mDao = new MemberDao();
public Member loginMember(String userId, String userPwd) {
Connection conn = getConnection();
Member loginUser = mDao.loginMember(conn, userId, userPwd);
close(conn);
return loginUser;
}
public int insertMember(Member m) {
Connection conn = getConnection();
int result = mDao.insertMember(conn, m);
if(result > 0) {
commit(conn);
}else {
rollback(conn);
}
close(conn);
return result;
}
public Member updateMember(Member m) {
Connection conn = getConnection();
// 1. 회원정보 변경 (update)
int result = mDao.updateMember(conn, m);
Member updateMem = null;
if(result > 0) {
commit(conn);
// 2. 갱신된 회원 조회 (select)
updateMem = mDao.selectMemberById(conn, m.getUserId());
}else {
rollback(conn);
}
close(conn);
return updateMem; // null(정보변경실패) | 갱신된회원객체(정보변경성공)
}
}
- 정보 변경과 관련된 비즈니스 로직을 서비스 메소드 하나에서 다 진행하면 된다.
- service 클래스에서 각각의 기능별 비즈니스 로직을 처리하는 구문을 작성해야 한다.
- 항상 기능 단위로 service를 묶는다. 지금은 정보 변경이라는 기능을 구현하고 있다.
정보 변경 기능을 서비스의 하나의 메소드로 작업한다.
- 항상 기능당 service 메소드는 하나여야 한다. 그런데 실행쿼리는 여럿일 수 있다.
- 기능별로 주로 하나의 쿼리만을 실행하지만,
한 기능에 여러 쿼리가 실행될 때는 서비스 메소드 하나에, 각각의 쿼리를 실행하는 dao를 여러번 호출한다.
- service 클래스의 updateMember 메소드 하나에서
dao의 updateMember() 메소드로 update 쿼리(회원정보 변경)를 실행하고,
dao의 selectMemberById() 메소드로 select 쿼리(갱신된 회원 조회)를 실행한다.
- 현재 session의 "loginUser"라는 key값에 담겨있는 Member 객체는 정보 변경이 되기 전의 Member 객체다.
그래서 정보변경이 된 후에 갱신된 회원 객체를 다시 조회해 온다.
그래야 session에 담겨있는 Member 객체를 바꿔치기할 수 있다.
- commit 이후에 session의 Member 객체를 바꿔치기 해야 한다.
commit 이후에 db로부터 갱신된 데이터를 조회해 온다.
- 만약 commit() 메소드를 호출하지 않으면, 데이터베이스 삽입, 수정, 삭제가 반영되지 않아서
다른 사용자가나 시스템이 이러한 변경 사항을 볼 수 없습니다.
- 이미 member-mapper.xml에 "loginMember"라는 key값을 가지는 entry는 있지만,
그 쿼리로 회원데이터를 조회하기 위해서는 id와 비밀번호가 다 필요하다.
지금은 id로만 조회해야하기 때문에 "selectMemberById"라는 dao 메소드를 만들어야 한다.
- id도 UNIQUE 제약조건 이기 때문에 회원번호와 같이 식별자로 쓸 수 있다.
ㅁ MemberDao 클래스에 updateMember() 메소드와 selectMemberById() 메소드를 만든다.
package com.br.web.member.model.dao;
import static com.br.web.common.template.JDBCTemplate.close;
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.Map;
import java.util.Properties;
import com.br.web.member.model.vo.Member;
public class MemberDao {
private Properties prop = new Properties();
public MemberDao() {
String filePath = MemberDao.class.getResource("/db/mappers/member-mapper.xml").getPath();
try {
prop.loadFromXML(new FileInputStream(filePath));
} catch (IOException e) {
e.printStackTrace();
}
}
public Member loginMember(Connection conn, String userId, String userPwd) {
// select문 => ResultSet (한 행, 한 회원) => Member객체
Member m = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("loginMember");
try {
pstmt = conn.prepareStatement(sql); // 미완성된 sql문
pstmt.setString(1, userId);
pstmt.setString(2, userPwd);
rset = pstmt.executeQuery();
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 {
close(rset);
close(pstmt);
}
return m;
}
public int insertMember(Connection conn, Member m) {
// insert => 처리된 행 수
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("insertMember");
try {
pstmt = conn.prepareStatement(sql);
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());
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
public int updateMember(Connection conn, Member m) {
// update문 => 처리된 행수가 돌아온다.
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("updateMember");
try {
pstmt = conn.prepareStatement(sql);
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());
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
public Member selectMemberById(Connection conn, String userId) {
// select => ResultSet (한행, 한회원) => Member
Member m = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectMemberById");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, userId);
rset = pstmt.executeQuery();
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 {
close(rset);
close(pstmt);
}
return m;
}
}
- update, select 쿼리를 dao의 한 메소드에서 하지 않고, 각각 따로 메소드를 만들어서 한다.
dao의 한 메소드가 하나의 쿼리만을 담당해야 한다. 그래야 나중에 재사용할 수 있다.
- update문을 실행하면 처리된 행 수가 돌아온다.
- select문을 실행하면 여기서는 한 행(한 회원)만 돌아온다.
ㅁ /webApp/src/main/java/db/mappers/member-mapper.xml에 쿼리문을 2개 작성한다.
<?xml version="1.0" encoding="UTF-8"?>
<properties>
<entry key="loginMember">
SELECT
USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, PHONE
, EMAIL
, ADDRESS
, INTEREST
, ENROLL_DATE
, MODIFY_DATE
, STATUS
FROM
MEMBER
WHERE
USER_ID = ?
AND USER_PWD = ?
AND STATUS IN ('A', 'U')
</entry>
<entry key="insertMember">
INSERT
INTO MEMBER
(
USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, PHONE
, EMAIL
, ADDRESS
, INTEREST
)
VALUES
(
SEQ_UNO.NEXTVAL
, ?
, ?
, ?
, ?
, ?
, ?
, ?
)
</entry>
<entry key="updateMember">
UPDATE
MEMBER
SET
USER_NAME = ?
, PHONE = ?
, EMAIL = ?
, ADDRESS = ?
, INTEREST = ?
, MODIFY_DATE = SYSDATE
WHERE
USER_ID = ?
</entry>
<entry key="selectMemberById">
SELECT
USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, PHONE
, EMAIL
, ADDRESS
, INTEREST
, ENROLL_DATE
, MODIFY_DATE
, STATUS
FROM
MEMBER
WHERE
USER_ID = ?
</entry>
</properties>
- 메소드명을 따서 entry의 key값을 작성한다.
- MEMBER 테이블에 MODIFY_DATE라는 컬럼이 있다.
언제 마지막으로 정보 변경을 했는지 기록을 위해서 MODIFY_DATE도 SYSDATE로 UPDATE 한다.
ㅁ MemberUpdateController.java로 돌아온다.
package com.br.web.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.br.web.member.model.service.MemberService;
import com.br.web.member.model.vo.Member;
@WebServlet("/update.me")
public class MemberUpdateController extends HttpServlet {
private static final long serialVersionUID = 1L;
public MemberUpdateController() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 요청
request.setCharacterEncoding("utf-8");
String userId = request.getParameter("userId");
String userName = request.getParameter("userName");
String phone = request.getParameter("phone");
String email = request.getParameter("email");
String address = request.getParameter("address");
String[] interestArr = request.getParameterValues("interest");
String interest = interestArr == null ? "" : String.join(",", interestArr);
Member m = new Member(userId, userName, phone, email, address, interest);
Member updateMem = new MemberService().updateMember(m);
// 2. 응답
if(updateMem == null) { // 실패
// 응답페이지 : 에러페이지
// 응답데이터 : "회원 정보 변경 실패" 메세지
request.setAttribute("msg", "회원 정보 변경 실패");
request.getRequestDispatcher("/views/common/errorPage.jsp").forward(request, response);
}else { // 성공
// session에 담겨있는 loginUser를 갱신된 회원객체로 변경
HttpSession session = request.getSession();
session.setAttribute("loginUser", updateMem);
// 응답페이지 : 다시 마이페이지
// 응답데이터 : "성공적으로 회원정보를 수정했습니다." alert 메세지
session.setAttribute("alertMsg", "성공적으로 회원정보를 수정했습니다.");
// /web/myinfo.me url 재요청 => 마이페이지 포워딩
response.sendRedirect(request.getContextPath() + "/myinfo.me");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 에러페이지로 이동은 redirect가 아닌 forward로 한다.
갱신된 데이터를 보여줘야하는 것도 아닌 단순 페이지 요청이다.
redirect를 하면 새로운 요청이다.
- forward 아니면 redirect인데 이미 이 페이지를 응답하는 서블릿이 만들어져 있으면 서블릿을 재호출하면 된다.
- header.jsp에 String alertMsg = (String)session.getAttribute("alertMsg");가 있다.
alertMsg가 null이 아닐 경우에 <script>구문으로 한번 alert가 뜨고
session.removeAttribute("alertMsg");로 다시 비워지게끔 작성해놨다.
- alert 메세지를 띄우고 싶으면 session에 "alertMsg"라는 key값으로 문자열을 담으면 된다.
ㅁ 자바코드를 수정했으니 서버를 restart 한다.
- 서버가 재시작되면 session이 만료되므로 다시 로그인부터 시작해야 한다.
- 로그인하고 마이페이지로 이동해서 '회원 정보 변경' 버튼을 클릭해서 정상적으로 반영되었는지 확인한다.