ㅁ 회원서비스
- 회원서비스 개발 세팅
- 회원서비스_로그인 - 회원서비스_로그아웃 <- 여기까지 했음. - 회원서비스_회원가입페이지로이동
- 회원서비스_회원가입요청 - 회원서비스_마이페이지요청 - 회원서비스_정보변경요청 - 회원서비스_비번변경요청 - 회원서비스_회원탈퇴요청
ㅁ 회원가입페이지로 이동
- 아래는 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객체
%>
< 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" > 회원가입 </ button >
</ 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 = " < %=contextPath%>" > Home </ a >
</ li >
< li class = "nav-item" >
< a class = "nav-link" href = " < %= contextPath %>/list.no" > 공지사항 </ 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 >
- header.jsp에는 변수가 둘 선언되어 있다.
ㅁ src/main/webapp/views/member에 "회원가입페이지.html"(화면 구현한 것)이 있다.
- 이걸 jsp 파일로 만들어야 한다.
- src/main/webapp/views/member 우클릭 - new - jsp에서 signUp.jsp 파일을 만든다.
- 회원가입 페이지를 화면 구현한 "회원가입페이지.html"를 열어서 <body> 태그 안의 구문들을
"signUp.jsp" 파일의 <body> 태그에 전체 복사해서 붙여넣기 한다.
- 아래는 signUp.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 -->
<!-- header.jsp include로 표현할 예정 -->
<!-- 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" required ></ td >
< td >< button type = "button" class = "btn btn-secondary btn-sm" > 중복확인 </ button ></ td >
</ tr >
< tr >
< th > * 비밀번호 </ th >
< td >< input type = "password" class = "form-control" placeholder = "Enter Your Password" required ></ td >
< td ></ td >
</ tr >
< tr >
< th > * 비밀번호 확인 </ th >
< td >< input type = "password" class = "form-control" placeholder = "Enter Your Password Check" required ></ td >
< td ></ td >
</ tr >
< tr >
< th > * 이름 </ th >
< td >< input type = "text" class = "form-control" placeholder = "Enter Your Name" required ></ td >
< td ></ td >
</ tr >
< tr >
< th > 전화번호 </ th >
< td >< input type = "text" class = "form-control" placeholder = "Enter Your Phone (- include)" ></ td >
< td ></ td >
</ tr >
< tr >
< th > 이메일 </ th >
< td >< input type = "text" class = "form-control" placeholder = "Enter Your Email (@ include)" ></ td >
< td ></ td >
</ tr >
< tr >
< th > 주소 </ th >
< td >< input type = "text" class = "form-control" placeholder = "Enter Your Address" ></ 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 >
< td ></ td >
</ tr >
</ table >
< br >
< div align = "center" >
< button type = "submit" class = "btn btn-primary btn-sm" > 회원가입 </ button >
< button type = "reset" class = "btn btn-danger btn-sm" > 초기화 </ button >
</ div >
</ form >
</ div >
</ section >
<!-- Section end -->
<!-- Footer start -->
<!-- footer.jsp include 할 예정 -->
<!-- Footer end -->
</ div >
</ body >
</ html >
- header와 footer를 include 지시어를 사용해서 include 한다.
- header에 있는 '회원가입' 버튼 클릭시 'signUp.jsp' 페이지로 이동되어야 한다.
ㅁ header.jsp의 '회원가입' 버튼 클릭시 'signUp.jsp'로 이동되게끔 한다.
- 아래는 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객체
%>
< 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 = " < %=contextPath%>" > Home </ a >
</ li >
< li class = "nav-item" >
< a class = "nav-link" href = " < %= contextPath %>/list.no" > 공지사항 </ 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 >
- 로그인 전에는 회원가입할 수 있는 버튼이 보여진다.
- a 태그가 아니라 button 태그이기 때문에 href 속성에 요청할 url을 바로 작성할 수는 없다.
자바스크립트 방식으로 url을 요청한다.
- 회원가입 버튼에 id를 주고 <script> 태그로 동적인 효과를 부여한다.
자바스크립트 방식으로 '회원가입' 버튼 요소를 선택하고 이벤트를 부여한다.
- location.href="요청할url";
url을 요청하고 싶으면 location 객체의 href 속성을 사용한다.
- '회원가입' 버튼 클릭시 '회원가입' 페이지로 이동시킨다.
이 페이지에는 db로부터 조회해올 데이터는 없다. 그냥 정적인 페이지다.
단순한 정적페이지 이동은 그 페이지에 대한 경로를 곧바로 url로 요청해서 띄워줄 수도 있지만,
그러면 이 프로젝트의 디렉토리 구조가 url에 노출되므로 forward 한다.
- 단순한 페이지 이동도 무조건 서블릿을 호출시켜서 그 서블릿에서 forwarding 방식으로 이동한다.
- 자바스크립트 내에서도 표현식 사용이 가능하다.
"/signup.me"라는 url을 요청한다. 서블릿 호출이다.
ㅁ src/main/java/com.br.web.member.controller에 MemberSignUpController라는 서블릿 클래스를 만든다.
- 아래는 MemberSignUpController.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 ( "/signup.me" )
public class MemberSignUpController extends HttpServlet {
private static final long serialVersionUID = 1L ;
public MemberSignUpController () {
super ();
}
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) throws ServletException , IOException {
// 단순 페이지 이동 요청
// 응답페이지 : /web/views/member/signUp.jsp
request . getRequestDispatcher ( "/views/member/signUp.jsp" ). forward (request, response);
}
protected void doPost ( HttpServletRequest request , HttpServletResponse response ) throws ServletException , IOException {
doGet (request, response);
}
}
- 회원가입 페이지로 이동시켜주는 "/signup.me"라는 url mapping 값을 가지는 서블릿 클래스를 만든다.
- doGet() 메소드 내부를 지우고, 단순한 페이지 이동 요청이므로 요청처리할 것 없이 바로 응답페이지에 대해 생각하면 된다.
- 요청시 전달값도 없고 요청처리할 것도 없다. 그냥 페이지 이동만 되게끔 하면 된다.
만약에 응답 페이지에서 필요한 데이터가 있다면 이동하기 전에 조회해서 request 객체에 담아서 전달하면 된다.
- forward로 이동할 경로에는 context path를 제외하고 쓴다.
- '회원서비스'의 '회원가입페이지 이동 요청' 기능은 '/signup.me'라는 url로 요청했고 get방식이었다.
요청시 전달값은 없었고 실행할 쿼리도 없었다. forward 방식으로 이동해서 응답페이지를 보여줬다.
- 다른 페이지에서도 이제 '회원가입페이지로 이동 요청' 하고 싶으면 이제부터 '/signup.me'라는 url을 redirect하면 된다.
ㅁ signUp.jsp로 돌아온다.
- 아래는 signUp.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 %>/insert.me" method = "post" class = "m-4" >
< table class = "table" >
< tr >
< th > * 아이디 </ th >
< td >< input type = "text" name = "userId" class = "form-control" placeholder = "Enter Your ID" required ></ td >
< td >< button type = "button" class = "btn btn-secondary btn-sm" > 중복확인 </ button ></ td >
</ tr >
< tr >
< th > * 비밀번호 </ th >
< td >< input type = "password" name = "userPwd" class = "form-control" placeholder = "Enter Your Password" required ></ td >
< td ></ td >
</ tr >
< tr >
< th > * 비밀번호 확인 </ th >
< td >< input type = "password" class = "form-control" placeholder = "Enter Your Password Check" required ></ td >
< td ></ td >
</ tr >
< tr >
< th > * 이름 </ th >
< td >< input type = "text" name = "userName" class = "form-control" placeholder = "Enter Your Name" required ></ td >
< td ></ td >
</ tr >
< tr >
< th > 전화번호 </ th >
< td >< input type = "text" name = "phone" class = "form-control" placeholder = "Enter Your Phone (- include)" ></ td >
< td ></ td >
</ tr >
< tr >
< th > 이메일 </ th >
< td >< input type = "text" name = "email" class = "form-control" placeholder = "Enter Your Email (@ include)" ></ td >
< td ></ td >
</ tr >
< tr >
< th > 주소 </ th >
< td >< input type = "text" name = "address" class = "form-control" placeholder = "Enter Your Address" ></ 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 >
< td ></ td >
</ tr >
</ table >
< br >
< div align = "center" >
< button type = "submit" class = "btn btn-primary btn-sm" > 회원가입 </ button >
< button type = "reset" class = "btn btn-danger btn-sm" > 초기화 </ button >
</ div >
</ form >
</ div >
</ section >
<!-- Section end -->
<!-- Footer start -->
< %@ include file="/views/common/footer.jsp" %>
<!-- Footer end -->
</ div >
</ body >
</ html >
- 각각의 input 요소 텍스트 상자에 name 속성을 추가해서 key값을 부여해준다. 그래야 데이터가 넘어간다.
- 안의 내용은 form 요소로 구성되어 있다.
개인정보니까 post 방식으로 요청하고, url은 context path를 동적으로 따와서 작성하고 서블릿을 호출한다.
- signUp.jsp에는 contextPath라는 변수가 선언되어 있지 않지만, header.jsp를 include하고 있기 때문에 사용 가능하다.
header.jsp를 include하는 include 지시어 아래쪽이어서 사용 가능한거고,
include 지시어 위쪽에서는 contextPath라는 변수 사용이 불가능하다.
- 비밀번호 확인은 name 속성을 추가하지 않아도 된다.
그냥 화면단에서 이 두 개가 일치하는지 비교하기 위해서 둔거기 때문에 굳이 데이터를 넘길 필요는 없다.
일치하는지 유효성 체크하는거 자바스크립트 때 했으니까 한번 적용을 해보시고,
잘 입력했다는 가정 하에 데이터 넘겨서 insert하는 것만 진행한다.
- (참고) 아이디 옆의 '중복확인' 버튼은 나중에 AJAX 배워서 적용시킨다.
ㅁ src/main/java/com.br.web.member.controller에 MemberInsertController라는 서블릿 클래스를 만든다.
- 아래는 MemberInsertController.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 com.br.web.member.model.service.MemberService ;
import com.br.web.member.model.vo.Member ;
@ WebServlet ( "/insert.me" )
public class MemberInsertController extends HttpServlet {
private static final long serialVersionUID = 1L ;
public MemberInsertController () {
super ();
}
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) throws ServletException , IOException {
// 1. 요청
// 요청시 전달값 뽑기
request . setCharacterEncoding ( "utf-8" );
String userId = request . getParameter ( "userId" ); // "user03"
String userPwd = request . getParameter ( "userPwd" ); // "pass03"
String userName = request . getParameter ( "userName" ); // "강개똥"
String phone = request . getParameter ( "phone" ); // "010-1111-2222" | ""
String email = request . getParameter ( "email" ); // "ssss@sss.com" | ""
String address = request . getParameter ( "address" ); // "서울시" | ""
String [] interestArr = request . getParameterValues ( "interest" ); // ["운동", "등산"] | null
String interest = "" ;
if (interestArr != null ) {
interest = String . join ( "," , interestArr);
}
Member m = new Member (userId, userPwd, userName, phone, email, address, interest);
// 서비스 호출 (쿼리실행)
int result = new MemberService (). insertMember (m);
// 2. 응답
if (result > 0 ) {
} else {
}
}
protected void doPost ( HttpServletRequest request , HttpServletResponse response ) throws ServletException , IOException {
doGet (request, response);
}
}
- "/insert.me"라는 url mapping 값을 가지는 서블릿 클래스를 만든다.
- "/insert.me" 요청시 다수의 데이터가 넘어온다.
항상 요청 관련한 내용, 응답 관련한 내용만 써주면 된다.
- 요청은 요청시 전달값 뽑아서 sql문을 실행한다.
요청시 전달값 뽑고 service 호출하기. 이게 jdbc 과정이다.
- insert 쿼리는 처리된 행 수가 돌아온다.
0보다 크면 insert가 성공한 거고 0이면 실패다.
- post 방식 요청이므로 utf-8로 인코딩한다.
- 요청시 전달한 데이터가 다수이므로 vo 객체에 담아서 service로 넘긴다.
dao까지 넘겨서 db에 insert를 수행한다.
- 그런데 Member 객체의 매개변수 생성자로 저 7개의 매개변수를 받아주는 생성자가 없다. 없으면 만들면 된다.
- /webApp/src/main/java/com/br/web/member/model/vo/Member.java 에서 alt shift s + o 눌러서 생성자를 만들어 준다.
- 내가 데이터를 new Member(~, ~, ...)로 전달하는 순서 와 생성자의 매개변수 순서가 일치하는지도 확인한다.
- 그런데 db에 자바에서의 배열객체를 저장시킬 수는 없다. interestArr은 String형 배열이다.
관심분야의 경우 db에 '운동,등산' 이렇게 하나의 문자열로 만들어서 기록을 하기로 했다.
즉 String 타입의 배열을 하나의 문자열로 합쳐서 넘겨줘야 한다.
- 그런데 String 타입의 배열이 null일 수 있다.
null pointer exception을 방지하기 위해 조건문으로 String 배열이 null인지 체크한다.
- db에 기록할 데이터를 가공하는 과정이다. 데이터 가공 처리는 controller 단에서 한다.
ㅁ MemberService 클래스에 insertMember() 메소드를 만든다.
- 아래는 MemberService.java이다.
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;
}
}
- MemberService 클래스에서는 MemberInsertController 클래스에서 보낸 Member 객체를
매개변수로 전달받을 수 있는 insertMember()메소드가 필요하다.
- JDBCTemplate 클래스의 commit()과 rollback() 메소드를 그냥 이름만으로 호출하고 싶다면 import static 해줘야 한다.
ㅁ MemberDao 클래스에 insertMember() 메소드를 만든다.
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;
}
}
- insert 쿼리를 실행하면 처리된 행 수가 반환된다.
- 최종적으로 반환된 행 수가 1이라면 성공적으로 insert된 거고, 아니면(0이라면) insert에 실패한 것이다.
그러면 문법문제일 수도 있기 때문에 절대 오류가 나선 안 된다.
제약 조건에 위배될 수도 있지만 그 제약 조건에 위배가 안되게 애초에 화면단에서부터 유효성 체크를 해줬어야 한다.
(지금 유효성 체크 과정은 스킵했지만)
ㅁ /webApp/src/main/java/db/mappers/member-mapper.xml에 insert 쿼리문을 작성한다.
<? 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 >
</ properties >
- 메소드명과 동일하게 "insertMember"라는 key 값을 가지는 entry 객체를 만든다.
- 총 11개의 컬럼이 있는 테이블인데 8개의 컬럼만 insert 했다.
나머지 3개는 ENROLL_DATE, MODIFY_DATE, STATUS인데 다 default 값이 설정되어 있다.
걔네 들을 굳이 쓰지 않아도 알아서 값이 들어간다.
- 8개의 컬럼을 선택했으니 8개의 값을 쓰면 된다.
USER_NO 컬럼은 SEQ_UNO.NEXTVAL로 시퀀스로 새로운 번호를 발생시켜서 넣어야만 한다.
절대 겹치면 안되기 때문이다.
- 나머지 7개 값들은 ?로 두고 사용자가 입력한 값으로 INSERT 한다.
ㅁ /webApp/src/main/java/com/br/web/member/controller/MemberInsertController.java로 돌아온다.
- 아래는 MemberInsertController.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 com.br.web.member.model.service.MemberService;
import com.br.web.member.model.vo.Member;
@ WebServlet ( "/insert.me" )
public class MemberInsertController extends HttpServlet {
private static final long serialVersionUID = 1L ;
public MemberInsertController () {
super ();
}
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) throws ServletException , IOException {
// 1. 요청
// 요청시 전달값 뽑기
request . setCharacterEncoding ( "utf-8" );
String userId = request . getParameter ( "userId" ); // "user03"
String userPwd = request . getParameter ( "userPwd" ); // "pass03"
String userName = request . getParameter ( "userName" ); // "강개똥"
String phone = request . getParameter ( "phone" ); // "010-1111-2222" | ""
String email = request . getParameter ( "email" ); // "ssss@sss.com" | ""
String address = request . getParameter ( "address" ); // "서울시" | ""
String [] interestArr = request . getParameterValues ( "interest" ); // ["운동", "등산"] | null
String interest = "" ;
if (interestArr != null ) {
interest = String . join ( "," , interestArr);
}
Member m = new Member (userId, userPwd, userName, phone, email, address, interest);
// 서비스 호출 (쿼리실행)
int result = new MemberService (). insertMember (m);
// 2. 응답
if (result > 0 ) {
/*
* * 회원가입 성공
* ㄴ 응답페이지 : 메인페이지
* ㄴ 응답데이터 : "성공적으로 회원가입 되었습니다." alert 메세지
*/
request . getSession (). setAttribute ( "alertMsg" , "성공적으로 회원가입 되었습니다." );
response . sendRedirect ( request . getContextPath ());
} else {
/*
* * 회원가입 실패
* ㄴ 응답페이지 : 에러페이지
* ㄴ 응답데이터 : "회원가입 실패" 메세지 (해당 응답페이지에서만 필요)
*/
request . setAttribute ( "msg" , "회원가입 실패" );
request . getRequestDispatcher ( "/views/common/errorPage.jsp" ). forward (request, response);
}
}
protected void doPost ( HttpServletRequest request , HttpServletResponse response ) throws ServletException , IOException {
doGet (request, response);
}
}
- 회원가입에 실패한 경우 이미 만들어 놓은 에러페이지가 응답페이지로 뜨게끔 한다.
포워딩으로 만들어 놓은 에러페이지로 이동한다. 포워딩은 context path를 제외하고 입력한다.
"회원가입 실패" 메세지는 응답 페이지에서만 필요하기 때문에 request에 담는다.
- 이때 아무 key 값으로 담으면 안된다.
errorPage.jsp에서 <%=request.getAttribute("msg") %>로 에러메시지를 출력하고 있기 때문에 "msg"를 key로 한다.
- 이렇게 회원가입 실패에 대해 처리를 했는데,
코드만 잘 작성되어 있으면 절대 실패할 일이 없다. 에러페이지는 혹시 모르니 만드는 것이다.
- 회원가입에 성공한 경우 response.sendRedirect("/web");으로 메인페이지 url을 재요청하면 된다.
"/web"이라는 context path는 언제 바뀔지 모르기 때문에 request.getContextPath()로 동적으로 context path를 알아온다.
- 회원가입 성공시 응답데이터는 응답페이지인 메인페이지에서 한번 alert로 띄워주고 끝이다.
그런데 request에 담긴 데이터는 포워딩으로 전달이 되어야만 이동한 페이지에서 쓸 수 있다.
url을 요청하는 방식(redirect)에서는 request에 담을 수가 없다. 그러면 session에 담으면 된다.
- 어떤 페이지를 응답하는 url이 존재하면 포워딩하면 안 되고 반드시 url을 재요청하는 방식으로 해야한다.
- redirect 하는 경우에는 request에 데이터를 담아도 의미가 없습니다.
redirect는 클라이언트(브라우저)에게 새로운 URL로 이동하라는 명령 을 보내는 것이기 때문에, 클라이언트는 서버로부터 받은 새로운 URL로 다시 요청을 보냅니다. 이때, 기존의 request 객체는 사라지고 새로운 요청이 생성되므로, 이전 요청에 담았던 데이터는 유지되지 않습니다.
ㅁ 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 >
- session으로부터 alert 메세지를 뽑아서 script 구문을 통해 출력되게 한다.
- 코드가 실행되는 시점을 생각해야 한다.
header.jsp가 로드될 때마다 alertMsg 데이터가 있는 것은 아니다.
- script 구문은 alert로 띄워줄 메세지가 존재할 때만 script 요소가 만들어지게끔 조건문으로 작성한다.
- alert() 메소드 안에 표현식을 쓸 때도, ' '로 문자열로 취급되게끔 해야 한다.
- session에 담긴 데이터는 서버가 종료되기 전까지 쭉 유지가 된다.
필요없어졌다면 직접 지워줄 수도 있어야 한다.
회원가입 성공 메세지는 한 번만 출력되고 더 이상 출력이 되어서는 안 된다. 일회성 메세지다. 그래서 session을 비워준다.
- header.jsp의 변수가 3개가 되었다. contextPath, loginUser, alertMsg.
ㅁ 서버를 재시작하고, 메인페이지를 켜서 회원가입하고 회원가입 성공 메세지가 alert로 뜨는지 확인한다.
- 그리고 로그인도 해보고, sql developer를 켜서 데이터가 db에 잘 담겼는지도 확인한다. 컬럼에 맞게 잘 담겼는지.
ㅁ 회원가입 서비스뿐 아니라 정보변경 서비스 등 다른 서비스 요청에 성공했을 때도 alert 메세지를 활용할 예정이다.