2020년 12월 28일
업데이트:
프로젝트 구성
- 빨간 박스 파일은 새로 생성한 파일
- 파란 박스 파일은 수정한 파일
Cookie 설정
- Cookie 저장(아이디 저장 체크)
- 로그인 시 아이디 저장 체크
- 쿠키 확인
- 로그아웃 시 쿠키 확인
- 로그인 시 아이디 저장 체크
- Cookie 삭제(아이디 저장 해제)
- 로그인 시 아이디 저장 해제
- 쿠키 확인
- 로그아웃 시 쿠키 확인
- 로그인 시 아이디 저장 해제
LoginServlet.java
// 로그인이 성공했을 때 Session에 로그인 정보 추가하기
if(loginMember != null){
session.setMaxInactiveInterval(60 * 30); // 현재 세션 30분 후 만료
// session에 로그인 정보를 추가함
session.setAttribute("loginMember", loginMember);
// cookie -> 사용자 컴퓨터에 저장
// session -> 서버에 저장
// 서버가 접속한 브라우저마다 session을 구분하는 방법
// --> 해당 브라우저의 쿠키 파일에 session을 구분할 수 잇는
// session id를 저장시켜 두었다가 접속될 때마다 쿠키에서
// 자동으로 session id를 얻어간다.
// 쿠키 객체 생성
Cookie cookie = new Cookie("saveId", memberId);
// 아이디 저장 체크박스 체크 확인
if(save != null) { // 체크 했으면
cookie.setMaxAge(60 * 60 * 24 * 7); // 생명주기 1주일
}else{
cookie.setMaxAge(0); // 생명주기가 0이면 삭제된다.
}
// 쿠키 유효 디렉토리 지정
cookie.setPath(request.getContextPath());
// setPath(String path) : 쿠키의 유효한 디렉토리 설정.
// ex) setPath("/") : 모든 문서(디렉토리)에서 쿠키 사용이 가능하다.
// 위의 코드는 요청한 페이지 '/wsp'에서 사용이 가능하다는 얘기
// 생성된 쿠키를 클라이언트로 전달(응답)
response.addCookie(cookie);
}else{
// 로그인이 실패했을 때
// 생략
}
response.sendRedirect(request.getHeader("referer"));
header.jsp
<!-- cookie에 저장된 아이디가 있을 경우 checkd 속성을 추가 -->
<input type="checkbox" name="save" is="save"
<c:if test="${!empty cookie.saveId.value}">
checked
</c:if>
> 아이디 저장
c:if문으로 인해 cookie 값이 비어있지 않으면 input 태그에 checked라는 속성이 추가된다.
회원가입(페이지 이동)
- 로그인 모달창에서 회원가입 클릭
- 회원가입 페이지
header.jsp
<!-- 회원가입 버튼을 누르면 회원가입 페이지로 이동할 수 있도록
href 부분을 변경한다. -->
<!-- 로그인 모달창 부분 -->
<a class="btn btn-lg btn-secondary btn-block"
href="${contextPath}/member/signUpForm.do">회원가입</a>
SignUpFormServlet.java
package com.kh.wsp.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;
@WebServlet("/member/signUpForm.do")
public class SignUpFormServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 회원가입 화면을 보여주는 서블릿
String path ="/WEB-INF/views/member/signUpForm.jsp";
RequestDispatcher view = request.getRequestDispatcher(path);
view.forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
signUpForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입</title>
<style>
/* number 태그 화살표 제거 */
input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button
{
-webkit-appearance: none;
margin: 0;
}
</style>
</head>
<body>
<div class="container">
<%-- <%@ include file="../common/header.jsp"%> --%>
<jsp:include page="../common/header.jsp"></jsp:include>
<div class="py-5 text-center">
<h2>회원 가입</h2>
</div>
<div class="row">
<div class="col-md-6 offset-md-3">
<form method="POST" action="signUp.do" class="needs-validation" name="signUpForm" onsubmit="return validate();">
<%-- action="${contextPath}/member/signUp.do" --%>
<!-- 아이디 -->
<div class="row mb-5 form-row">
<div class="col-md-3">
<label for="id">* 아이디</label>
</div>
<div class="col-md-6">
<input type="text" class="form-control" name="id" id="id" maxlength="12" placeholder="아이디를 입력하세요" autocomplete="off" required>
<!-- required : 필수 입력 항목으로 지정 -->
<!-- autocomplete="off" : input 태그 자동완성 기능을 끔 -->
<!-- 팝업창 중복체크 여부 판단을 위한 hidden 타입 요소 -->
<input type="hidden" name="idDup" id="idDup" value="false">
</div>
<!-- ajax 중복검사 시 필요없음 -->
<div class="col-md-3">
<button type="button" class="btn btn-primary" id="idDupCheck">중복검사</button>
</div>
<div class="col-md-6 offset-md-3">
<span id="checkId"> </span>
</div>
</div>
<!-- 비밀번호 -->
<div class="row mb-3 form-row">
<div class="col-md-3">
<label for="pwd1">* 비밀번호</label>
</div>
<div class="col-md-6">
<input type="password" class="form-control" id="pwd1" name="pwd1" maxlength="12" placeholder="비밀번호를 입력하세요" required>
</div>
<div class="col-md-6 offset-md-3">
<span id="checkPwd1"> </span>
</div>
</div>
<!-- 비밀번호 확인 -->
<div class="row mb-3 form-row">
<div class="col-md-3">
<label for="pwd2">* 비밀번호 확인</label>
</div>
<div class="col-md-6">
<input type="password" class="form-control" id="pwd2" maxlength="12" placeholder="비밀번호 확인" required>
</div>
<div class="col-md-6 offset-md-3">
<span id="checkPwd2"> </span>
</div>
</div>
<br>
<!-- 이름 -->
<div class="row mb-3 form-row">
<div class="col-md-3">
<label for="name">* 이름</label>
</div>
<div class="col-md-6">
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="col-md-6 offset-md-3">
<span id="checkName"> </span>
</div>
</div>
<!-- 전화번호 -->
<div class="row mb-3 form-row">
<div class="col-md-3">
<label for="phone1">* 전화번호</label>
</div>
<!-- 전화번호1 -->
<div class="col-md-3">
<select class="custom-select" id="phone1" name="phone1" required>
<option>010</option>
<option>011</option>
<option>016</option>
<option>017</option>
<option>019</option>
</select>
</div>
<!-- number타입의 input태그에는 maxlength를 설정할 수 없음 -->
<!-- 전화번호2 -->
<div class="col-md-3">
<input type="number" class="form-control phone" id="phone2" name="phone2" required>
</div>
<!-- 전화번호3 -->
<div class="col-md-3">
<input type="number" class="form-control phone" id="phone3" name="phone3" required>
</div>
<div class="col-md-6 offset-md-3">
<span id="checkPhone"> </span>
</div>
</div>
<!-- 이메일 -->
<div class="row mb-3 form-row">
<div class="col-md-3">
<label for="email">* Email</label>
</div>
<div class="col-md-6">
<input type="email" class="form-control" id="email" name="email" autocomplete="off" required>
</div>
<div class="col-md-6 offset-md-3">
<span id="checkEmail"> </span>
</div>
</div>
<br>
<!-- 주소 -->
<!-- 오픈소스 도로명 주소 API -->
<!-- https://www.poesis.org/postcodify/ -->
<div class="row mb-3 form-row">
<div class="col-md-3">
<label for="postcodify_search_button">우편번호</label>
</div>
<div class="col-md-3">
<input type="text" name="post" class="form-control postcodify_postcode5">
</div>
<div class="col-md-3">
<button type="button" class="btn btn-primary" id="postcodify_search_button">검색</button>
</div>
</div>
<div class="row mb-3 form-row">
<div class="col-md-3">
<label for="address1">도로명 주소</label>
</div>
<div class="col-md-9">
<input type="text" class="form-control postcodify_address" name="address1" id="address1">
</div>
</div>
<div class="row mb-3 form-row">
<div class="col-md-3">
<label for="address2">상세주소</label>
</div>
<div class="col-md-9">
<input type="text" class="form-control postcodify_details" name="address2" id="address2">
</div>
</div>
<!-- 관심분야 -->
<hr class="mb-4">
<div class="row">
<div class="col-md-3">
<label>관심 분야</label>
</div>
<div class="col-md-9 custom-control custom-checkbox">
<div class="form-check form-check-inline">
<input type="checkbox" name="memberInterest" id="sports" value="운동" class="form-check-input custom-control-input">
<label class="form-check-label custom-control-label" for="sports">운동</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" name="memberInterest" id="movie" value="영화" class="form-check-input custom-control-input">
<label class="form-check-label custom-control-label" for="movie">영화</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" name="memberInterest" id="music" value="음악" class="form-check-input custom-control-input">
<label class="form-check-label custom-control-label" for="music">음악</label>
</div>
<br>
<div class="form-check form-check-inline">
<input type="checkbox" name="memberInterest" id="cooking" value="요리" class="form-check-input custom-control-input">
<label class="form-check-label custom-control-label" for="cooking">요리</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" name="memberInterest" id="game" value="게임" class="form-check-input custom-control-input">
<label class="form-check-label custom-control-label" for="game">게임</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" name="memberInterest" id="etc" value="기타" class="form-check-input custom-control-input">
<label class="form-check-label custom-control-label" for="etc">기타</label>
</div>
</div>
</div>
<hr class="mb-4">
<button class="btn btn-primary btn-lg btn-block" type="submit">가입하기</button>
</form>
</div>
</div>
<br>
<br>
<!-- jQuery와 postcodify 를 로딩한다. -->
<script src="//d1p7wdleee1q2z.cloudfront.net/post/search.min.js"></script>
<!-- 회원 관련 Javascript 코드를 모아둘 wsp_member.js 파일을 작성 -->
<script src="${contextPath}/resources/js/wsp_member.js"></script>
</div>
<jsp:include page="../common/footer.jsp"></jsp:include>
</body>
</html>
회원가입 유효성 검사
유효성 검사를 위해 Javascript 파일을 생성한다.
아이디
- 형식 불일치 시
-
형식 일치 시
- wsp_member.js
var validateCheck = {
"id" : false,
"pwd1" : false,
"pwd2" : false,
"name" : false,
"phone2" : false,
"email" : false,
}
// 아이디 유효성 검사
// 첫 글자는 영어 소문자, 나머지 글자는 영어 대, 소문자 + 숫자, 총 6~12글자
$("#id").on("input", function(){
var regExp = /^[a-z][a-zA-Z\d]{5,11}$/;
var value = $("#id").val();
if(!regExp.test(value)){
$("#checkId").text("아이디 형식이 유효하지 않습니다.")
.css("color", "red");
validateCheck.id = false;
} else{
$("#checkId").text("유효한 아이디 형식입니다.")
.css("color", "green");
validateCheck.id = true;
}
});
비밀번호 유효성 검사
- 비밀번호 유효성 검사를 통과하지 않고 비밀번호 확인을 입력할 시
- 비밀번호 두개가 일치하지 않을 시
- 비밀번호가 일치할 시
// 비밀번호 유효성 검사
// 영어 대, 소문자 + 숫자, 총 6~12글자
// + 비밀번호, 비밀번호 확인의 일치 여부
// + 비밀번호를 입력하지 않거나 유효하지 않은 상태로
// 비밀번호 확인을 작성하는 경우
$("#pwd1, #pwd2").on("input", function(){
// 비밀번호 유효성 검사
var regExp = /^[a-zA-Z\d]{6,12}$/;
var v1 = $("#pwd1").val();
var v2 = $("#pwd2").val();
if(!regExp.test(v1)){
$("#checkPwd1").text("비밀번호 형식이 유효하지 않습니다.")
.css("color", "red");
validateCheck.pwd1 = false;
} else{
$("#checkPwd1").text("유효한 비밀번호 형식입니다.")
.css("color", "green");
validateCheck.pwd1 = true;
}
// 비밀번호가 유효하지 않은 상태에서 비밀번호 확인 작성 시
if(!validateCheck.pwd1 && v2.length > 0){
swal("유효한 비밀번호를 먼저 작성해주세요.");
$("#pwd2").val(""); // 비밀번호 확인에 입력한 값 삭제
$("#pwd1").focus();
}else {
// 비밀번호, 비밀번호 확인의 일치 여부
if(v1.length == 0 || v2.length == 0){
$("#checkPwd2").text("");
}else if(v1 == v2){
$("#checkPwd2").text("비밀번호 일치")
.css("color", "green");
validateCheck.pwd2 = true;
}else{
$("#checkPwd2").text("비밀번호 불일치")
.css("color", "red");
validateCheck.pwd2 = false;
}
}
});
이름 유효성 검사
- 형식에 맞지 않을 시
- 형식에 맞을 시
/*
이름
/^[가-힣]{2,}$/; // 한글 두 글자 이상
*/
$("#name").on("input", function(){
var regExp = /^[가-힣]{2,}$/;
var value = $("#name").val();
if(!regExp.test(value)){
$("#checkName").text("이름 형식이 유효하지 않습니다.")
.css("color", "red");
validateCheck.name = false;
}else {
$("#checkName").text("유효한 이름 형식입니다.")
.css("color", "green");
validateCheck.name = true;
}
});
전화번호 유효성 검사
- 형식에 맞지 않을 시
- 형식에 맞을 시
// 전화번호 유효성 검사
$(".phone").on("input", function(){
// 전화번호 input 태그에 4글자 초과 입력하지 못하게 하는 이벤트
if ($(this).val().length > 4) {
$(this).val( $(this).val().slice(0, 4));
// this : 이벤트가 발생한 요소
// $(this)
}
var regExp1 = /^\d{3,4}$/;
var regExp2 = /^\d{4}$/;
var v1 = $("#phone2").val();
var v2 = $("#phone3").val();
if(!regExp1.test(v1) || !regExp2.test(v2)){
$("#checkPhone").text("전화번호가 유효하지 않습니다.")
.css("color", "red");
validateCheck.phone2 = false;
}else{
$("#checkPhone").text("유효한 형식의 전화번호 입니다.")
.css("color", "green");
validateCheck.phone2 = true;
}
});
이메일 유효성 검사
- 형식에 맞지 않을 시
- 형식에 맞을 시
/*
이메일
/^[\w]{4,}@[\w]+(\.[\w]+){1,3}$/; // 4글자 아무단어 @ 아무단어 . * 3
*/
$("#email").on("input", function(){
var regExp = /^[\w]{4,}@[\w]+(\.[\w]+){1,3}$/;
var value = $("#email").val();
if(!regExp.test(value)){
$("#checkEmail").text("이메일 형식이 유효하지 않습니다.")
.css("color", "red");
validateCheck.email = false;
}else {
$("#checkEmail").text("유효한 이메일 형식입니다.")
.css("color", "green");
validateCheck.email = true;
}
});
전체적인 유효성 검사
function validate(){
// 유효성 검사 여부 확인
for(var key in validateCheck){
if(!validateCheck[key]){
var msg;
switch(key){
case "id" : msg="아이디가"; break;
case "pwd1" :
case "pwd2" : msg = "비밀번호가"; break;
case "name" : msg="이름이"; break;
case "phone2" : msg="전화번호가"; break;
case "email" : msg="이메일이"; break;
}
swal(msg + " 유효하지 않습니다.");
$("#" + key).focus();
return false;
}
}
}
// 주소
$(function(){
$("#postcodify_search_button").postcodifyPopUp();
});
회원가입(sql 반영)
- 유효성 검사를 모두 통과하지 않을 시
- 유효성 검사를 모두 통과할 시
-
회원가입이 성공 후 DB
- Member.java
// 회원가입용 생성자 추가
public Member(String memberId, String memberPwd, String memberName, String memberPhone, String memberEmail,
String memberAddress, String memberInterest) {
super();
this.memberId = memberId;
this.memberPwd = memberPwd;
this.memberName = memberName;
this.memberPhone = memberPhone;
this.memberEmail = memberEmail;
this.memberAddress = memberAddress;
this.memberInterest = memberInterest;
}
- SignUpServlet.java
@WebServlet("/member/signUp.do")
public class SignUpServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// get보다 post를 쓰는 이유
// 1. 정보의 양이 많아서
// 2. queryString을 숨기기 위해
// POST 방식 전송 시 한글이 깨지는 문제를 해결하기 위해 인코딩 변경
request.setCharacterEncoding("UTF-8");
// 전달 받은 파라미터를 모두 변수에 저장
String memberId = request.getParameter("id");
String memberPwd = request.getParameter("pwd1");
String memberName = request.getParameter("name");
String memberEmail = request.getParameter("email");
String phone1 = request.getParameter("phone1");
String phone2 = request.getParameter("phone2");
String phone3 = request.getParameter("phone3");
// 전화번호를 '-' 구분자로 하여 하나로 합치기
String memberPhone = phone1 + "-" + phone2 + "-" + phone3;
String post = request.getParameter("post"); // 우편번호
String address1 = request.getParameter("address1"); // 도로명주소
String address2 = request.getParameter("address2"); // 상세 주소
String memberAddress = post + "," + address1 + "," + address2;
String[] interest = request.getParameterValues("memberInterest");
String memberInterest = null;
if(interest != null) memberInterest = String.join(", ", interest);
// Member 객체를 생성하여 파라미터 모두 저장
Member member = new Member(memberId, memberPwd, memberName,
memberPhone, memberEmail,
memberAddress, memberInterest);
// 비즈니스 로직 수행
try {
// 비즈니스 로직 수행 후 결과를 반환 받아 저장
int result = new MemberService().signUp(member);
String swalIcon = null;
String swalTitle = null;
String swalText = null;
if(result > 0) { // 성공
swalIcon = "success";
swalTitle = "회원가입 성공!";
swalText = memberName + "회원가입을 환영합니다.";
}else { // 실패
swalIcon = "error";
swalTitle = "회원가입 실패";
swalText = memberName + "문제가 지속될 경우 고객센터로 문의 바랍니다.";
}
HttpSession session = request.getSession();
session.setAttribute("swalIcon", swalIcon);
session.setAttribute("swalTitle", swalTitle);
session.setAttribute("swalText", swalText);
// 회원가입 진행 후 메인 페이지로 이동(메인 화면 재요청)
response.sendRedirect(request.getContextPath());
}catch (Exception e) {
e.printStackTrace();
request.setAttribute("errorMsg", "회원가입 과정에서 오류가 발생했습니다.");
String path = "/WEB-INF/views/common/errorPage.jsp";
RequestDispatcher view = request.getRequestDispatcher(path);
view.forward(request, response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- MemberService.java
/** 회원가입 Service
* @param member
* @return result
* @throws Exception
*/
public int signUp(Member member) throws Exception {
// 1) Connection 얻어오기
Connection conn = getConnection();
// getConnection 기존에 있던 커넥션 풀에서 하나를 가져옴. 새로 생성 X
// 2) DAO 메소드 호출 후 결과 반환
int result = dao.signUp(conn, member);
// 3) 트랜잭션 처리
if(result > 0) commit(conn);
else rollback(conn);
// 4) Connection 반환
close(conn);
// 5) 결과값 반환
return result;
}
- member-query.xml
<entry key="signUp">
INSERT INTO MEMBER
VALUES(SEQ_MNO.NEXTVAL, ?, ?, ?, ?, ?, ?, ?,
DEFAULT, DEFAULT, DEFAULT)
</entry>
- MemberDAO.java
/** 회원가입 DAO
* @param conn
* @param member
* @return result
* @throws Exception
*/
public int signUp(Connection conn, Member member) throws Exception {
//1) 결과를 저장할 변수 선언
int result = 0;
// 2) SQL 구문 준비
try {
// 3) PreparedStatement 객체를 얻어와 SQL 구문 세팅
String query = prop.getProperty("signUp");
pstmt = conn.prepareStatement(query);
// 4) 위치 홀더(?)에 알맞은 값 세팅
//MEMBER_ID, MEMBER_PWD, MEMBER_NM,
//MEMBER_PHONE, MEMBER_EMAIL,
//MEMBER_ADDR, MEMBER_INTEREST
pstmt.setString(1, member.getMemberId());
pstmt.setString(2, member.getMemberPwd());
pstmt.setString(3, member.getMemberName());
pstmt.setString(4, member.getMemberPhone());
pstmt.setString(5, member.getMemberEmail());
pstmt.setString(6, member.getMemberAddress());
pstmt.setString(7, member.getMemberInterest());
// 5) SQL 구문 수행 후 반환값 저장
result = pstmt.executeUpdate();
}finally {
// 6) 사용한 JDBC 자원 반환
close(pstmt);
}
// 7) 결과 반환
return result;
}
댓글남기기