2020년 02월 03일

업데이트:


로그아웃


  • 로그아웃 : 세션에 있는 회원정보를 없애는 것.
  • Model + @SessionAttributes 방법으로 등록된 세션은 SessionStatus 객체를 이용해야만 만료 가능하다.
    SessionStatus : 세션의 상태를 관리할 수 있는 객체
    setComplete() : @SessionAttributes로 Session에 등록된 값을 모두 지운다 == session의 내용을 없앤다.

header.jsp

header.jsp 로그아웃 버튼 클릭

로그아웃




MemberController.java

MemberController.java 로그아웃 동작 Controller


// 로그아웃 동작 Controller
@RequestMapping("logout")
public String logout(SessionStatus status) {
    // 로그아웃 : 세션에 있는 회원 정보를 없애거나 만료시키는 것.
    
    // 기존방법 : HttpSession의 invalidate() 메소드를 이용해 세션 무효화.
    // 매개변수에 HttpSession session를 작성한 후 
    // session.invalidate(); 을 이용해서 세션 무효화
    
    // loginMember 세션을 어떻게 등록했는가?
    // model.addAttribute("loginMember",loginMember)
    // -> Model 을 이용해서 값을 추가 + @SessionAttributes
    // --> 해당 방법으로 등록된 세션은 기존 방법으로 없애거나 만료 시킬 수 없음
    // --> SessionStatus 객체를 이용해야만 만료 가능하다.
    
    // SessionStatus : 세션의 상태를 관리할 수 있는 객체
    // setComplete() : @SessionAttributes로 Session에 등록된 값을 모두 지운다 == session의 내용을 없앤다.
    
    status.setComplete();
    
    return "redirect:/"; // 메인화면 재요청
}





쿠키(아이디 저장)


  • cookie 와 Session 의 차이점

    session : Server 쪽에서 관리
    어떤 세션 정보를 업로드를 해도 사용자 컴퓨터가 아닌 서버에 저장된다.
    브라우저가 종료되거나 지정된 시간이 되면 사라짐.
    cookie : 클라이언트 쪽에 파일 형태로 저장된다.
    사용자가 직접 파일을 지우거나, 지정된 시간이 되면 사라지지만 설정된 시간이 길다.
    (왜? 오래 저장되기 위해, 보통 한달로 지정한다.)

login.jsp의 아이디저장 체크박스에 별도의 value가 없음.
체크가 되면 파라미터로 on이 넘어가고, 체크가 안되어있으면 null이 넘어감.

아이디 저장


MemberController.java

MemberController.java 의 로그인 동작에서 진행함.

매개변수의 inputMember에 saveId는 담겨있지 않음.
@RequestParam(“saveId”) String saveId 를 매개변수로 파라미터를 전달받음.



쿠키 생성은 어느 시점에?
비즈니스 로직 수행 후 결과 반환 받은 후 로그인이 성공 했을 때 쿠키가 생성됨.
(loginMember !=null) 일 때

@RequestMapping("loginAction")
public String loginAction(Member inputMember, @RequestParam(value="saveId", required=false) String saveId, 
                            HttpServletResponse response, Model model ) {
    // Model = 전달하고자 하는 데이터를 Map 형식(K:V)으로 담아서 전달하는 용도의 객체
    // Model 객체는 기본적으로 request scope 이지만, 클래스 위쪽에 작성된 @SessionAttributes를 이용하면 session scope로 변경됨.
    
    // System.out.println(inputMember);
    
    // 비즈니스 로직 수행 후 결과 반환받기
    Member loginMember = service.loginAction(inputMember);
    System.out.println(loginMember);
    
    if(loginMember !=null) {
        model.addAttribute("loginMember", loginMember);
        
        
        // Cookie  K:V 의 형태로 저장이 된다.
        Cookie cookie = new Cookie("saveId", loginMember.getMemberId());
        
        // 쿠키 유지 시간 지정(초 단위)
        // 아이디저장 체크를 했을 때는 한 달 저장, 체크를 안 했을 경우 쿠키 저장 안 함(0)
        if(saveId != null) {// 아이디 저장이 체크 되었을 경우
            cookie.setMaxAge(60 * 60 * 24 * 30); // 한 달
        }else { // 아이디 저장이 체크 되어있지 않은 경우
            cookie.setMaxAge(0); // 쿠키를 생성하지 않겠다. 기존에 있던 쿠키도 없앤다.
        }
    
        // Spring만의 방법으로 쿠키를 내보낼 수 있는 방법이 없다. response 객체를 사용해야 된다.(매개변수에 등록)
        // 생성된 쿠키 객체를 응답객체에 담아서 내보내기.
        response.addCookie(cookie);
        
    }
    return "redirect:/";
}



login.jsp

쿠키 생성 후 login.jsp 화면에 뿌려주기.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<c:set var="contextPath" value="${pageContext.servletContext.contextPath}" scope="application" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="canonical" href="https://getbootstrap.com/docs/4.5/examples/floating-labels/">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<title>Spring Project</title>
<style>
.bd-placeholder-img {
	font-size: 1.125rem;
	text-anchor: middle;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}

@media ( min-width : 768px) {
	.bd-placeholder-img-lg {
		font-size: 3.5rem;
	}
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js"></script>
<!-- Third party plugin JS-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js"></script>
<!-- Custom styles for this template -->
<link href="${contextPath}/resources/css/floating-labels.css" rel="stylesheet">
</head>
<body>

<div id="login-container">
<div style="margin: auto; width: 200px; margin-bottom: 20px">
	<a class="navbar-brand js-scroll-trigger mx-0" href="${contextPath}"> 
		<img class="img-fluid img-profile rounded-circle mx-auto mb-2" src="https://github.com/Baek-dh/image_repository/blob/main/Spring_logo(green).png?raw=true" width="200px" height="200px" />
	</a>
</div>

<%-- 로그인 form --%>
<form action="loginAction" method="post" class="form-signin">
<!--  /나  /..이 없으면 상대경로!! 제일 뒤에 부분만 바뀌는 것임.  -->

	<!-- form-label-group : input태그와 연결된 label의 내용을 input의 placeholder처럼 보이게해주고, 
		값이 들어올 경우사라지지 않고 글씨가 작아진 상태로 위쪽으로 올라가게 하는 부트스트랩-->
	<div class="form-label-group">
		<input type="text" id="memberId" name="memberId" placeholder="ID" class="form-control" value="${cookie.saveId.value }" required autofocus> 
		<label for="memberId">ID</label>
	</div>

	<div class="form-label-group">
		<input type="password" id="memberPwd" name="memberPwd" placeholder="Password" class="form-control" required> 
		<label for="memberPwd">Password</label>
	</div>

	<div class="checkbox mb-3">
		<label> 
			<input type="checkbox" name="saveId"
				<c:if  test="${!empty cookie.saveId.value }"> checked </c:if>
			> 아이디 저장
		</label>
	</div>
	<button class="btn btn-lg btn-success btn-block" type="submit">Login</button>
	<a class="btn btn-lg btn-success btn-block" href="signUp">Sign Up</a>
</form>
</div>
<jsp:include page="/WEB-INF/views/common/footer.jsp" />
</body>
</html>




로그인 시 아이디/비밀번호 틀렸을 때 경고창


Model 은 request scope 영역에 올라가기 때문에 redirct 에서는 사용할 수 없다.
왜? 넘어가는 도중에 폐기돼서 사라지기 때문에
하지만 Model + @SessionAttributes 방법으로 등록된 세션에 String이 넘어가면 쿼리스트링 형태로 넘어가는 오류가 발생한다.

오류1

오류2

오류3


해결 방법 : RedirectAttributes 객체 사용

RedirectAttributes 객체 : 리다이렉트 시 값을 전달하는 용도로 사용할 수 있는 객체.
RedirectAttributes는 세팅이 될때는 request 였다가 redirect 에는 session에 등록되었다가 redirect가 완료되면 다시 request가 된다..
일회성으로 session에 등록할 수 있음.

  • 응답 전 : request scope
  • redirect 중 : session scope
  • 응답 후 : request scope
  • addFlashAttributes()를 사용해서 값을 세팅 한다.



MemberController.java 의 로그인 동작 Controller

// 5. @ModelAttribute 어노테이션 생략
	@RequestMapping("loginAction")
	public String loginAction(Member inputMember, @RequestParam(value="saveId", required=false) String saveId, 
							  HttpServletResponse response, RedirectAttributes ra, Model model ) {
		// Model = 전달하고자 하는 데이터를 Map 형식(K:V)으로 담아서 전달하는 용도의 객체
		// Model 객체는 기본적으로 request scope 이지만, 클래스 위쪽에 작성된 @SessionAttributes를 이용하면 session scope로 변경됨.
		
		// System.out.println(inputMember);
		
		// 비즈니스 로직 수행 후 결과 반환받기
		Member loginMember = service.loginAction(inputMember);
		System.out.println(loginMember);
		
		if(loginMember !=null) {
			model.addAttribute("loginMember", loginMember);
			 
			 
			// Cookie  K:V 의 형태로 저장이 된다.
			Cookie cookie = new Cookie("saveId", loginMember.getMemberId());
			 
			// 쿠키 유지 시간 지정(초 단위)
			// 아이디저장 체크를 했을 때는 한 달 저장, 체크를 안 했을 경우 쿠키 저장 안 함(0)
			if(saveId != null) {// 아이디 저장이 체크 되었을 경우
				 cookie.setMaxAge(60 * 60 * 24 * 30); // 한 달
			}else { // 아이디 저장이 체크 되어있지 않은 경우
				 cookie.setMaxAge(0); // 쿠키를 생성하지 않겠다. 기존에 있던 쿠키도 없앤다.
			}
			
			// Spring만의 방법으로 쿠키를 내보낼 수 있는 방법이 없다. response 객체를 사용해야 된다.(매개변수에 등록)
			// 생성된 쿠키 객체를 응답객체에 담아서 내보내기.
			response.addCookie(cookie);
		 
		}else { // 로그인 실패
			
			ra.addFlashAttribute("swalIcon", "error");
			ra.addFlashAttribute("swalTitle", "로그인 실패");
			ra.addFlashAttribute("swalText", "아이디 또는 비밀번호가 일치하지 않습니다.");
			
		}
		return "redirect:/";
	}



header.jsp

	<c:if test="${!empty swalTitle}">
		<script>
			swal({icon : "${swalIcon}",
				title : "${swalTitle}",
				text : "${swalText}"});
		</script>
		
		<!-- 세션에 올라가지 않고, 한번만 사용 한 다음에 없어져서 remove 필요 없음. 주석처리  -->
<%-- <c:remove var="swalIcon"/>
		<c:remove var="swalTitle"/>
		<c:remove var="swalIcon"/> --%>
	</c:if>



로그인 성공/실패시 요청 경로 다르게 지정.

MemberController.java

// 5. @ModelAttribute 어노테이션 생략
@RequestMapping("loginAction")
public String loginAction(Member inputMember, @RequestParam(value="saveId", required=false) String saveId, 
                            HttpServletResponse response, RedirectAttributes ra, Model model ) {
    // Model = 전달하고자 하는 데이터를 Map 형식(K:V)으로 담아서 전달하는 용도의 객체
    // Model 객체는 기본적으로 request scope 이지만, 클래스 위쪽에 작성된 @SessionAttributes를 이용하면 session scope로 변경됨.
    
    // System.out.println(inputMember);
    
    // 비즈니스 로직 수행 후 결과 반환받기
    Member loginMember = service.loginAction(inputMember);
    System.out.println(loginMember);
    
    // 로그인 성공 시 메인페이지 , 로그인 실패 시 로그인 화면으로 요청 경로를 지정하고 싶음.
    String url = null; // 요청 경로를 저장할 변수
    
    
    if(loginMember !=null) {
        model.addAttribute("loginMember", loginMember);
            
            
        // Cookie  K:V 의 형태로 저장이 된다.
        Cookie cookie = new Cookie("saveId", loginMember.getMemberId());
            
        // 쿠키 유지 시간 지정(초 단위)
        // 아이디저장 체크를 했을 때는 한 달 저장, 체크를 안 했을 경우 쿠키 저장 안 함(0)
        if(saveId != null) {// 아이디 저장이 체크 되었을 경우
                cookie.setMaxAge(60 * 60 * 24 * 30); // 한 달
        }else { // 아이디 저장이 체크 되어있지 않은 경우
                cookie.setMaxAge(0); // 쿠키를 생성하지 않겠다. 기존에 있던 쿠키도 없앤다.
        }
        
        // Spring만의 방법으로 쿠키를 내보낼 수 있는 방법이 없다. response 객체를 사용해야 된다.(매개변수에 등록)
        // 생성된 쿠키 객체를 응답객체에 담아서 내보내기.
        response.addCookie(cookie);
        
        url = "/";
        
    }else { // 로그인 실패
        
        ra.addFlashAttribute("swalIcon", "error");
        ra.addFlashAttribute("swalTitle", "로그인 실패");
        ra.addFlashAttribute("swalText", "아이디 또는 비밀번호가 일치하지 않습니다.");
        
        url = "login";
    }
    return "redirect:" + url;
}



login.jsp

로그인 실패 시 로그인 화면에서 sweetAlert를 사용하기 위해 sweetalert라이브러리와 코드를login.jsp에 추가 해 준다.
왜? 현재 header.jsp 에만 추가가 되어있는데, login페이지에는 header가 없다.

sweet alert1

sweet alert2



예외처리

스프링 예외 처리 방법

  1. 메소드 별로 예외 처리(특별한 기능을 사용할 때 거기서 발생하는 예외가 특별할 때 사용함) –> 기존 try,catch, throws
  2. 컨트롤러 별로 예외 처리 : @ExceptionHandler (특정 메소드에 작성해 두면, 모든 예외가 그 메소드에서 잡힌다)
    사용 조건 : DispatcherServlet 에서 이 수행 되어야만 사용 가능하다.
  3. 전역(모든 클래스) 예외 처리 : 예외만을 처리하는 컨트롤러를 생성해서 컨트롤러 위에 @ControllerAdvice를 작성해 줌.
    겉으로 보기에는 제일 편해 보이지만, 세분화하기가 제일 힘들다.



MemberController.java

  • 모든 예외를 처리해주는 메소드
// ()안에 어떤 예외를 처리할 건지 작성해 줌. 모든 예외의 최상위 부모는 Exception.class이다.
@ExceptionHandler(Exception.class)
public String etcException(Exception e, Model model) {
    // 특정 예외를 제외한 나머지 예외 처리
    
    // 에러 내용을 보기 위한 printStackTrace() 를 표시할 변수 = Exception e
    // 에러메세지를 담을 변수  = Model model
    
    e.printStackTrace(); // 예외 내용 출력
    model.addAttribute("errorMsg", "회원 관련 서비스 처리 중 오류 발생"); 
    // 에러 메세지(Member컨트롤러여서 회원 관련 서비스)
    
    // 에러 페이지로 forward (errorPage.jsp)
    return  "common/errorPage";
}



  • DB관련 예외만 처리해주는 메소드
// 데이터 베이스 관련된 예외만 처리하는 메소드
@ExceptionHandler(SQLException.class)
public String DBException(Exception e, Model model) {
    e.printStackTrace();
    model.addAttribute("errorMsg", "데이터베이스 관련 오류 발생");
    return  "common/errorPage";
}


모든 예외를 처리해주는 메소드가 있으면 다른 자식 관련 예외만 처리해주는 메소드가 실행되지 않는다.





회원가입


  • 회원가입 시 중점적으로 봐야 하는 부분
    1. 아이디 중복검사 (AJAX 사용)
    2. 암호화 (bcrypt 사용)
    3. 가입 (파라미터를 DB까지 어떻게 넘길지)

login.jsp

  • 로그인과 다른 컨트롤러를 이용해 회원가입을 진행 함. (member2로 매핑)

회원가입

MemberController2.java

회원가입 화면으로 전환

package com.kh.spring.member.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.kh.spring.member.model.service.MemberService2;

@Controller // 컨트롤러임을 알려줌과 동시에 bean으로 등록해서 스프링 컨테이너가 제어할 수 있게 만든다.
@RequestMapping("/member2/*") // 매핑 주소
public class MemberController2 {
	
	@Autowired // 해당 자료형과 일치하는 bean을 자동으로 주입함. 의존성 주입 (DI)
	private MemberService2 service;
	
	
	// 회원가입 화면 전환용 Controller
	@RequestMapping("signUp")
	public String signUpView() {
		return "member/signUpView";
	}
}



signUpView.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;
	
}
	.requiredInput{ color : red; }
</style>
</head>
<body>
<jsp:include page="/WEB-INF/views/common/header.jsp" />
<div class="container">
<script>

</script>

<div class="py-5 text-center">
<h2>회원 가입</h2>
</div>

<div class="row">
<div class="col-md-7 offset-md-3">

	<form method="POST" action="signUpAction" class="needs-validation" name="signUpForm" onsubmit="return validate();">

		<!-- 아이디 -->
		<div class="row mb-3 form-row">
			<div class="col-md-3">
				<label for="id"><span class="requiredInput">*</span> 아이디</label>
			</div>
			<div class="col-md-6">
				<input type="text" class="form-control" id="id" name="memberId" maxlength="12" placeholder="아이디를 입력하세요" autocomplete="off" required> <input type="hidden" name="idDup" id="idDup" value="false">
				<!-- required : 필수 입력 항목으로 지정 -->
				<!-- autocomplete="off" : input 태그 자동완성 기능을 끔 -->
			</div>

			<div class="col-md-6 offset-md-3">
				<span id="checkId">&nbsp;</span>
			</div>
		</div>


		<!-- 비밀번호 -->
		<div class="row mb-3 form-row">
			<div class="col-md-3">
				<label for="pwd1"><span class="requiredInput">*</span> 비밀번호</label>
			</div>
			<div class="col-md-6">
				<input type="password" class="form-control" id="pwd1" name="memberPwd" maxlength="12" placeholder="비밀번호를 입력하세요" required>
			</div>

			<div class="col-md-6 offset-md-3">
				<span id="checkPwd1">&nbsp;</span>
			</div>
		</div>

		<!-- 비밀번호 확인 -->
		<div class="row mb-3 form-row">
			<div class="col-md-3">
				<label for="pwd2"><span class="requiredInput">*</span> 비밀번호 확인</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">&nbsp;</span>
			</div>
		</div>
		<br>

		<!-- 이름 -->
		<div class="row mb-3 form-row">
			<div class="col-md-3">
				<label for="name"><span class="requiredInput">*</span> 이름</label>
			</div>
			<div class="col-md-6">
				<input type="text" class="form-control" id="name" name="memberName" required>
			</div>

			<div class="col-md-6 offset-md-3">
				<span id="checkName">&nbsp;</span>
			</div>
		</div>

		<!-- 전화번호 -->
		<div class="row mb-3 form-row">
			<div class="col-md-3">
				<label for="phone1"><span class="requiredInput">*</span> 전화번호</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>

			<!-- 전화번호2 -->
			<div class="col-md-3">
				<input type="number" class="form-control phone" id="phone2" maxlength="4" name="phone2" required>
			</div>

			<!-- 전화번호3 -->
			<div class="col-md-3">
				<input type="number" class="form-control phone" id="phone3" maxlength="4" name="phone3" required>
			</div>

			<div class="col-md-6 offset-md-3">
				<span id="checkPhone">&nbsp;</span>
			</div>
		</div>

		<!-- 이메일 -->
		<div class="row mb-3 form-row">
			<div class="col-md-3">
				<label for="email"><span class="requiredInput">*</span> Email</label>
			</div>
			<div class="col-md-6">
				<input type="email" class="form-control" id="email" name="memberEmail" autocomplete="off" required>
			</div>

			<div class="col-md-6 offset-md-3">
				<span id="checkEmail">&nbsp;</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" id="post" class="form-control postcodify_postcode5">
			</div>
			<div class="col-md-3">
				<button type="button" class="btn btn-success" 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-success btn-lg btn-block" type="submit">가입하기</button>
	</form>
</div>
</div>
<br>
<br>
</div>
<jsp:include page="/WEB-INF/views/common/footer.jsp"/>



<!-- jQuery와 postcodify 를 로딩한다. -->
<script src="//d1p7wdleee1q2z.cloudfront.net/post/search.min.js"></script>
<script>
	// 검색 단추를 누르면 팝업 레이어가 열리도록 설정한다.
	$(function() {
		$("#postcodify_search_button").postcodifyPopUp();
	});
</script>

<script>
	// 각 유효성 검사 결과를 저장할 객체
	var signUpCheck = {
		"id" : false,
		"pwd1" : false,
		"pwd2" : false,
		"name" : false,
		"phone2" : false,
		"email" : false
	};

	//********** 실시간 유효성 검사  ************/
	// 정규표현식
	// jQuery 변수 : 변수에 직접적으로 jQuery메소드를 사용할 수 있음.
	var $id = $("#id");
	var $pwd1 = $("#pwd1");
	var $pwd2 = $("#pwd2");
	var $name = $("#name");
	var $phone2 = $("#phone2");
	var $phone3 = $("#phone3");
	var $email = $("#email");

	// 아이디  유효성 검사
	$id.on("input", function() {
		// 첫글자는 영어 소문자, 나머지 글자는 영어 대,소문자 + 숫자, 총 6~12글자
		var regExp = /^[a-z][a-zA-z\d]{5,11}$/;
		if (!regExp.test($id.val())) {
			$("#checkId").text("아이디 형식이 유효하지 않습니다.").css("color", "red");
			signUpCheck.id = false;

		} else {
			signUpCheck.id = true;
			$("#checkId").text("유효한 아이디 형식입니다.").css("color", "green");
			
			//******************************** 아이디 중복 검사(ajax) *******************************************
			
			
			//******************************** 아이디 중복 검사(ajax) *******************************************
		}
	});

	// 비밀번호 유효성 및 일치 검사
	$("#pwd1, #pwd2").on("input",function() {
		//영어 대,소문자 + 숫자, 총 6~12글자
		var regExp = /^[A-Za-z0-9]{6,12}$/;

		// 비밀번호1 유효성 검사
		if (!regExp.test($("#pwd1").val())) {
			$("#checkPwd1").text("비밀번호 형식이 유효하지 않습니다.").css("color", "red");
			signUpCheck.pwd1 = false;
		} else {
			$("#checkPwd1").text("유효한 비밀번호 형식입니다.").css("color", "green");
			signUpCheck.pwd1 = true;
		}

		// 비밀번호1이 유효하지 않은 상태로 비밀번호 2를 작성하는 경우
		if (!signUpCheck.pwd1 && $pwd2.val().length > 0) {
			swal("유효한 비밀번호를 작성해 주세요.");
			$pwd2.val("");
			$pwd1.focus();
		} else if (signUpCheck.pwd1 && $pwd2.val().length > 0) {
			if ($("#pwd1").val().trim() != $("#pwd2").val().trim()) {
				$("#checkPwd2").text("비밀번호 불일치").css("color", "red");
				signUpCheck.pwd2 = false;
			} else {
				$("#checkPwd2").text("비밀번호 일치").css("color", "green");
				signUpCheck.pwd2 = true;
			}
		}

	});

	// 이름 유효성 검사
	$name.on("input", function() {
		var regExp = /^[가-힣]{2,}$/; // 한글 두 글자 이상

		if (!regExp.test($(this).val())) { // 이름이 정규식을 만족하지 않을경우
			$("#checkName").text("한글 두 글자 이상을 입력하세요").css("color", "red");
			signUpCheck.name = false;
		} else {
			$("#checkName").text("정상입력").css("color", "green");
			signUpCheck.name = true;
		}
	});

	// 전화번호 유효성 검사
	$(".phone").on("input", function() {

		// 전화번호 input 태그에 4글자 이상 입력하지 못하게 하는 이벤트
		if ($(this).val().length > $(this).prop("maxLength")) {
			$(this).val($(this).val().slice(0, $(this).prop("maxLength")));
		}

		// 전화번호 유효성 검사
		var regExp1 = /^\d{3,4}$/; // 숫자 3~4 글자
		var regExp2 = /^\d{4,4}$/; // 숫자 4 글자

		if (!regExp1.test($phone2.val()) || !regExp2.test($phone3.val())) {
			$("#checkPhone").text("전화번호가 유효하지 않습니다.").css("color", "red");
			signUpCheck.phone2 = false;
		} else {
			$("#checkPhone").text("유효한 전화번호입니다.").css("color", "green");
			signUpCheck.phone2 = true;
		}
	});

	
	// 이메일 유효성 검사
	$email.on("input",function() {
		var regExp = /^[\w]{4,}@[\w]+(\.[\w]+){1,3}$/; // 4글자 아무단어 @ 아무단어 . * 3

		if (!regExp.test($(this).val())) { // 이메일이 정규식을 만족하지 않을경우
			$("#checkEmail").text("이메일 형식이 유효하지 않습니다.").css("color", "red");
			signUpCheck.email = false;
		} else {
			$("#checkEmail").text("유효한 이메일 형식입니다.").css("color", "green");
			signUpCheck.email = true;
		}
	});

	
	// 유효성 검사 
	function validate() {

		for ( var key in signUpCheck) {
			if (!signUpCheck[key]) {
				alert("일부 입력값이 잘못되었습니다.");
				var id = "#" + key;
				$(id).focus();
				return false;
			}
		}

		// 입력된 전화번호, 주소 조합하여 form태그에 hidden으로 추가 하기
		// 왜? -> 커맨드 객체를 이용하여 파라미터를 한번에 받기 쉽게 하기 위하여
		$memberPhone = $("<input>", {type : "hidden", name : "memberPhone",
				value : $("#phone1").val() + "-" + $("#phone2").val() + "-" + $("#phone3").val()
		});

		$memberAddress = $("<input>", {type : "hidden", name : "memberAddress",
				value : $("#post").val() + "," + $("#address1").val() + "," + $("#address2").val()
		});

		$("form[name='signUpForm']").append($memberPhone).append($memberAddress);
	}
	
</script>
</body>
</html>




ID 중복검사

유효성 검사가 완료 된 후 아이디 중복검사 진행.
AJAX 이용한 코드

signUpview.jsp

$.ajax({
url : "idDupCheck",   // idDupCheck로 요청을 보낸다.(상대경로   --> spring/member2/idDupCheck)
data : {"memberId" : $id.val()}, // data를 전달한다. memberId라는 키값으로 현재 input태그에 작성된 값을 전달.
type : "post", // 전달 타입 지정
success : function(result){ // 성공 시
	// console.log(result); 

	if(result == 0) { // 중복 아님 == 사용 가능
		$("#checkId").text("사용 가능한 아이디입니다.").css("color","green");
		signUpCheck.id=true;
	}else{ // 중복 == 사용 불가
		$("#checkId").text("중복되는 아이디입니다.").css("color", "red");
		signUpCheck.id = false;
	}
},
error : function(result){ // 실패 시
	console.log("ajax 통신 실패");
}
});




url : idDupCheck 와 매핑되는 Controller 생성

  • 기본적인 스프링 컨트롤러에서 반환되는 형식.

    컨트롤러에서 반횐되는 값은 forward 또는 redirect를 위한 경로/주소가 작성되는게 일반적이다.
    ->컨트롤러에서 반환 시 반환 값이 Dispatcher Servlet으로 이동해 View Resolver 또는 Handler Mapping 으로 연결 됨.
    하지만 이 컨트롤러에서는 result 가 호출한 AJAX(signUpView.jsp)로 넘어가게 하고 싶음.
    AJAX에서 반환값이 주소/경로가 아닌 값 자체로 인식해서 요청 부분으로 돌아가게 하는 별도의 방법이 존재한다.


MemberController2.java

// 아이디 중복 체크 Controller (AJAX)
@RequestMapping("idDupCheck")
@ResponseBody
public int idDupCheck(@RequestParam("memberId") String memberId) {  
                        // 키값과 변수명이 동일해서 @RequestParam("memberId") 생략 가능하지만 가독성 측면해서 남겨두는게 좋음.
    // AJAX에서 요청을 보낼때 받아오는 데이터 = memberId
    
    //System.out.println(memberId); // AJAX 통신이 제대로 실행되는지 확인
    
    // 중복검사 후 어떤식으로 반환할 건지 결정 해 줘야 됨 (True/False 인지  중복되는 아이디를 가져올건지 ..등등)
    // 아이디 중복 검사 서비스 호출
    int result = service.idDupCheck(memberId);
    
    // 기본적인 스프링 컨트롤러에서 반환되는 형식.
    // 컨트롤러에서 반횐되는 값은 forward 또는 redirect를 위한 경로/주소가 작성되는게 일반적이다.
    // -> 컨트롤러에서 반환 시 반환 값이 Dispatcher Servlet으로 이동해 View Resolver 또는 Handler Mapping 으로 연결 됨. 
    
    // 하지만 이 컨트롤러에서는 result 가  호출한 AJAX(signUpView.jsp)로 넘어가게 하고 싶음.
    // AJAX에서 반환값이 주소/경로가 아닌 값 자체로 인식해서 요청 부분으로 돌아가게 하는 별도의 방법이 존재한다.
    // == @ResponseBody
    
    return result;
}



MemberService2.java

package com.kh.spring.member.model.service;

public interface MemberService2 {
	
	/** 아이디 중복 검사 Service
	 * @param memberId
	 * @return result
	 */
	int idDupCheck(String memberId);
	// 앞에 public abstract 가 없음.
	// 왜?? 인터페이스 내에 작성되는 모든 메소드는 묵시적으로 public abstract 이다.
	// 인터페이스 내에 작성되는 모든 필드는 public static final이다.

}



MemberService2Impl.java

package com.kh.spring.member.model.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.kh.spring.member.model.dao.MemberDAO2;

@Service
public class MemberService2Impl implements MemberService2 {
	
	@Autowired
	private MemberDAO2 dao;

	
	// 아이디 중복 체크 Service 구현
	@Override
	public int idDupCheck(String memberId) {
		return dao.idDupCheck(memberId);
	}
}



MemberDAO2.java

package com.kh.spring.member.model.dao;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class MemberDAO2 {
	
	@Autowired
	private SqlSessionTemplate sqlSession;
	
	
	/** 아이디 중복 체크 DAO
	 * @param memberId
	 * @return result
	 */
	public int idDupCheck(String memberId) {
		return sqlSession.selectOne("memberMapper2.idDupCheck",memberId);
	}

}



member-mapper2.xml

  <!-- 아이디 중복 체크   -->
  <select id="idDupCheck" parameterType="string" resultType="_int">
  	SELECT COUNT(*) FROM MEMBER 
  	WHERE MEMBER_ID = #{memberId} 
  	AND MEMBER_STATUS = 'Y'
  </select>



태그: , ,

카테고리:

업데이트:

댓글남기기