[Framework] TIL 058 - 23.10.11

유진·2023년 10월 10일
0

07_Framework

Goolge : '다음 우편번호 서비스' 검색

Site : https://postcode.map.daum.net/guide


signUp.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 lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>회원가입</title>

    <link rel="stylesheet" href="/resources/css/member/signUp-style.css">
</head>
<body>
    <main>
        <%-- header.jsp include --%>
        <%-- 다른 JSP 코드를 현재 위치에 포함
            webapp 부터의 jsp 경로를 작성--%>
        <jsp:include page="/WEB-INF/views/common/header.jsp"/>

        <section class="signUp-content">

            <form action="/member/signUp" method="POST" name="signUpFrm" id="signUpFrm">

                <!-- 이메일 입력 -->
                <label for="memberEmail">
                    <span class="required">*</span> 아이디(이메일)
                </label>

                <div class="signUp-input-area">
                    <input type="text" name="memberEmail" id="memberEmail" 
                    placeholder="아이디(이메일)" maxlength="30" autocomplete="off">
                    
                    <button id="sendAuthKeyBtn" type="button">인증번호 받기</button>
                </div>
                <span class="signUp-message" id="emailMessage">메일을 받을 수 있는 이메일을 입력해주세요.</span>



                <!-- 인증번호 입력 -->
                <label for="emailCheck">
                    <span class="required">*</span> 인증번호
                </label>

                <div class="signUp-input-area">
                    <input type="text" name="authKey" id="authKey" s placeholder="인증번호 입력" maxlength="6" autocomplete="off" >
                    
                    <button id="checkAuthKeyBtn" type="button">인증하기</button>
                </div>
                <span class="signUp-message" id="authKeyMessage"></span>
                                <!-- 인증번호가 일치하지 않습니다 -->
                

                <!-- 비밀번호/비밀번호 확인 입력 -->
                <label for="memberPw">
                    <span class="required">*</span> 비밀번호
                </label>

                <div class="signUp-input-area">
                    <input type="password" name="memberPw" id="memberPw" 
                    placeholder="비밀번호" maxlength="20" >
                </div>
                <div class="signUp-input-area">
                    <input type="password" name="memberPwConfirm" id="memberPwConfirm" 
                    placeholder="비밀번호 확인" maxlength="20" >
                </div>

                <span class="signUp-message" id="pwMessage">영어,숫자,특수문자(!,@,#,-,_) 6~20글자 사이로 입력해주세요.</span>


                <!-- 닉네임 입력 -->
                <label for="memberNickname">
                    <span class="required">*</span> 닉네임
                </label>

                <div class="signUp-input-area">
                    <input type="text" name="memberNickname" id="memberNickname" placeholder="닉네임" maxlength="10" >
                </div>

                <span class="signUp-message" id="nickMessage">한글,영어,숫자로만 2~10글자</span>


                <!-- 전화번호 입력 -->
                <label for="memberTel">
                    <span class="required">*</span> 전화번호
                </label>

                <div class="signUp-input-area">
                    <input type="text" name="memberTel" id="memberTel" placeholder="(- 없이 숫자만 입력)" maxlength="11">
                </div>

                <span class="signUp-message" id="telMessage">전화번호를 입력해주세요.(- 제외)</span>



                <!-- 주소 입력 -->
                <label for="memberAddress">주소</label>

                <div class="signUp-input-area">
                    <input type="text" name="memberAddress" id="sample6_postcode" placeholder="우편번호" maxlength="6">
                    
                    <button type="button" onclick="sample6_execDaumPostcode()">검색</button>
                </div>

                <div class="signUp-input-area">
                    <input type="text" name="memberAddress" id="sample6_address" placeholder="도로명/지번 주소">
                </div>

                <div class="signUp-input-area">
                    <input type="text" name="memberAddress" id="sample6_detailAddress" placeholder="상세 주소">
                </div>


                <button id="signUpBtn">가입하기</button>
            </form>
        </section>

    </main>

    <%-- footer.jsp include --%>
    <jsp:include page="/WEB-INF/views/common/footer.jsp"/>
    
    
    <script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
	<script>
	    function sample6_execDaumPostcode() {
	        new daum.Postcode({
	            oncomplete: function(data) {
	                // 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
	
	                // 각 주소의 노출 규칙에 따라 주소를 조합한다.
	                // 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
	                var addr = ''; // 주소 변수
	
	                //사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
	                if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
	                    addr = data.roadAddress;
	                } else { // 사용자가 지번 주소를 선택했을 경우(J)
	                    addr = data.jibunAddress;
	                }
	
	                // 우편번호와 주소 정보를 해당 필드에 넣는다.
	                document.getElementById('sample6_postcode').value = data.zonecode;
	                document.getElementById("sample6_address").value = addr;
	                // 커서를 상세주소 필드로 이동한다.
	                document.getElementById("sample6_detailAddress").focus();
	            }
	        }).open();
	    }
	</script>
    
    
    


    <script src="/resources/js/member/signUp.js"></script>
</body>
</html>


검색 버튼 클릭 시,
주소 입력하면 주소 자동으로 기입됨


가입하기 버튼 눌렀을 때

name 값이 동일할 경우 String[] memberAddress: 배열로 넘어옴 ex) ['04746', '서울 어쩌구', '2층']
                	만약, 주소가 선택사항이라 사용자가 기입하지 않을 경우 구분자 '' 따옴표만 넘어옴
                	Controller 단에서 ''(따옴표로 넘어오면) -> null로 db에 입력되도록 처리해줄 것임
                	ex) 04746^^^서울 어쩌구^^^2층으로 만들어줄 것임 -> 이유: , 구분자와 헷갈릴 수 있기 때문
                	
                	ex) 04157,서울시 성동구 어쩌구동,(구분자로서 역할을 제대로 하지 못함) 무슨빌라,2층
                	->  0416^^^서울 성동구, 어쩌구^^^2층

MemberController.java

package edu.kh.project.member.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import edu.kh.project.member.model.dto.Member;
import edu.kh.project.member.model.service.MemberService;

@Controller // 요청/응답 클래스 + bean 등록(Spring 관리하는 객체)
@RequestMapping("/member") // 공통된 주소 앞부분 작성, member로 시작하는 요청은 해당 컨트롤러에서 처리
@SessionAttributes({"loginMember"}) // Model의 이름(key)를 적으면 session으로 추가
									// 올리고 싶은게 여러 개일 수 있기 때문에 나열하고 싶으면 {} 사용하여 적기
public class MemberController {
	
	// 등록된 Bean 중에서 필드와 타입이 일치하는 Bean 주입
	// -> MemberService를 구현한 MemberServiceImpl의 Bean 주입
	@Autowired
	private MemberService service;
	
	

	// 로그인 : /member/login
	// 로그아웃 : /member/logout
	
	// /member/login, post 처리방식
	
	
	// @RequestMapping : 요청 주소에 맞는 클래스/메서드 연결
	// @RequestMapping("요청주소") : -> GET / POST 구분 X , 주소만 맞으면 연결하지만 GET요청시 사용
	// @RequestMapping(value="/login", method=RequestMethod.GET/POST) -> GET/POST 방식을 구분
	
    // @RequestMapping(value="/login", method=RequestMethod.POST)
	public String login(HttpServletRequest req) {
		
		// 파라미터 전달 방법 1 : HttpServletRequest를 이용하는 방법
		// -> Controller 메서드에 매개변수로 HttpServletRequest를 작성
		
		// 매개변수에 적으면 사용 가능한 이유
		// Spring 제공하는 Argument Resolver(매개변수해결사)가 자동으로
		// 대입해주고 해결해줌.
		
		String inputEmail = req.getParameter("inputEmail");
		String inputPw = req.getParameter("inputPw");
		
		System.out.println("inputEmail : " + inputEmail);
		System.out.println("inputPw : " + inputPw);
		
		
		// ******************* redirect 방법! *******************
		// "redirect:요청주소"
		
		return "redirect:/";
	}
	
	
	// @PostMapping : @RequestMapping 자식으로
	//				Post 방식 요청을 연결하는 어노테이션
	//@PostMapping("/login")
	public String login(/*@RequestParam("inputEmail")*/ String inputEmail,
						/*@RequestParam("inputPw")*/ String inputPw/*,
						@RequestParam(value="saveId", required="fasle", defaultValue="1")*/) {
		
		// 파라미터 전달 방법 2 : @RequestParam 어노테이션 이용(+생략방법)
		
		// @RequestParam 어노테이션
		// - request 객체를 이용한 파라미터 전달 어노테이션
		// - 매개변수 앞에 해당 어노테이션 작성하면, 매개변수에 값이 주입됨.
		
		// ** 파라미터의 name 속성값과
		//	매개변수명이 같으면 @RequestParam 생략 가능! **
		
		
		// @RequestParam(value="name", required="fasle", defaultValue="1")
		// [속성]
		// value : 전달 받은 input 태그의 name 속성값
		
		// required : 입력된 name 속성값 파라미터 필수 여부 지정(기본값 true)
		// -> required = true인 파라미터가 존재하지 않는다면 400 Bad Request 에러 발생
		// -> required = true인 파라미터가 null인 경우에도 400 Bad Request

		// defaultValue : 파라미터 중 일치하는 name 속성 값이 없을 경우에 대입할 값 지정.
		// -> required = false인 경우 사용
		
		
		System.out.println("inputEmail : " + inputEmail);
		System.out.println("inputPw : " + inputPw);

		
		return "redirect:/";
	}
	
	
	//@PostMapping("/login")
	public String login(/*@ModelAttribute*/ Member inputMember) {
		
		// 파라미터 전달 방법 3 : @ModelAttribute 이용한 방법
		
		// - DTO(또는 VO)와 같이 사용하는 어노테이션
		
		// - 전달 받은 파라미터의 name 속성 값이
		//   같이 사용되는 DTO의 필드명과 같다면
		//   자동으로 setter를 호출해서 필드에 값을 세팅
		
		System.out.println(inputMember);
		
		// ** @ModelAttribute 사용 시 주의 사항 **
		// - DTO에 기본 생성자가 필수로 존재해야 한다
		// - DTO에 setter가 필수로 존재해야 한다
		
		// ** ModelAttribute 어노테이션은 생략이 가능하다 ! ** 
		
		// ** '@ModelAttribute'를 이용해 값이 필드에 세팅된 객체를
		//	'커맨드 객체' 라고 부른다.
		
		
		// Member [ memberEmail = user123, memberPw = pass123.. ]
		
		
		return "redirect:/";
	}
	
	
	/* 찐 로그인 메서드 */
	
	// Session : '서버' -> 보안에 더 유리
	// Cookie : '클라이언트'(브라우저) -> 속도는 조금 더 빠름
	
	@PostMapping("/login")
	public String login(Member inputMember, Model model, // 파라미터 추가할때마다 , 적어주기!
						@RequestHeader("referer") String referer,
						RedirectAttributes ra,
						@RequestParam(value="saveId", required=false) String saveId,
						HttpServletResponse resp
						) {
		
		// @RequestHeader(value="referer") String referer
		// -> 요청 HTTP header에서 "referer" (이전 주소) 값을 얻어와
		//  매개 변수 String referer에 저장
		
		
		// Model : 데이터 전달용 객체
		// -> 데이터를 K : V 형식으로 담아 전달
		// -> 기본적으로 request scope
		// -> @SessionAttributes 어노테이션과 함께 사용 시 session scope
		
		// @RequestParam(value="saveId", required=false) String saveId
		// -> name 속성값이 saveId인 파라미터를 전달받아서 저장
		// required 미작성 시 기본 값 true
		// required = false : 필수 아님(null 허용) 
		
//		System.out.println(saveId);

		// 로그인 서비스 호출
		Member loginMember = service.login(inputMember);
		
		// DB 조회 결과 확인
		System.out.println(loginMember);
		
		// 로그인 결과에 따라 리다이렉트 경로를 다르게 지정
		String path = "redirect:";
		
		if(loginMember != null) {  // 로그인 성공시
			path += "/";  // 메인페이지로 리다이렉트
			
			// session loginMember 추가
			
			// Session에 로그인한 회원 정보 추가
			// Servlet : HttpSession.setAttribute(key, value)
			// Spring  : Model + @SessionAttributes
			
			// 1) model에 로그인한 회원 정보 추가
			model.addAttribute("loginMember", loginMember);
			// -> 현재는 request scope
			
			// 2) 클래스 위에 @SessionAttributes 추가
			// -> 이제 session scope
			
			// --------------------------------------
			
			// 아이디 저장 (Cookie)
			
			/* Cookie란?
			 * - 클라이언트 측(브라우저)에서 관리하는 파일
			 * 
			 * - 쿠키파일에 등록된 주소 요청 시 마다
			 * 	 자동으로 요청에 첨부되어 서버로 전달됨.
			 * 
			 * - 서버로 전달된 쿠키에
			 * 	 값 추가, 수정, 삭제 등을 진행한 후
			 * 	 다시 클라이언트에게 반환
			 * 
			 */
			
			// 쿠키 생성(해당 쿠키에 담을 데이터를 k:v 로 지정)
			Cookie cookie = new Cookie("saveId", loginMember.getMemberEmail());
			
			if(saveId != null) { // 체크가 되었을 때
				
				// 한 달(30일) 동안 유지되는 쿠키 생성
				cookie.setMaxAge(60*60*24*30); // 초단위 지정
				
				
			} else { // 체크가 안되었을 때
				
				// 0초 동안 유지되는 쿠키 생성
				// -> 기존에 쿠키가 지정되어 있었다면 해당 쿠키를 삭제
				cookie.setMaxAge(0);
				
			}
			
			// 클라이언트가 어떤 요청을 할 때 쿠키가 첨부될지 경로(주소)를 지정
			cookie.setPath("/"); // localhost:/ 이하 모든 주소
								// ex) / , /member/login , /member/logout 등
								// 모든 요청에 쿠키를 첨부
			
			// 응답 객체(HttpServletResponse) 를 이용해서
			// 만들어진 쿠키를 클라이언트에게 전달
			resp.addCookie(cookie);
			
			
		} else {  // 로그인 실패
			path += referer;
			
			// message 추가 (아이디 또는 비밀번호 불일치)
			
			/* redirect(재요청) 시
			 * 기존 요청(request)이 사라지고
			 * 새로운 요청(request)을 만들게 되어
			 * redirect된 페이지에서는 이전 요청이 유지 되지 않는다!
			 * -> 유지 하고 싶으면 어쩔 수 없이 session scope를 이용
			 * 
			 * RedirectAttributes를 스프링에서 제공
			 * - 리다이렉트 시 데이터를 request scope로 전달할 수 있게하는 객체
			 * 
			 * 응답 전 : request scope
			 * 
			 * 응답 중 : session scope로 잠시 이동
			 * 
			 * 응답 후 : request scope로 복귀
			 * 
			 */
			
			// addFlashAttribute : 잠시 session 에 추가
			ra.addFlashAttribute("message", "아이디 또는 비밀번호 불일치");
		}
		
		return path;
	}
	
	
	@GetMapping("/logout")
	public String logout(SessionStatus status/*HttpSession session*/) { // 두 방법 중 하나 사용하면 됨!
		
		// SessionStatus : 세션 상태를 관리하는 객체
		
		//session.invalidate(); // 세션 무효화
		status.setComplete();
		
		return "redirect:/";
	}
	
	
	// 회원 가입 페이지 이동
	@GetMapping("/signUp")
	public String signUp() {
		
		return "member/signUp";
	}
	
	
	// 회원 가입 진행
	@PostMapping("/signUp")
	public String signUp( Member inputMember,
						String[] memberAddress,
						RedirectAttributes ra ) {
		
		// Member inputMember : 커맨드 객체 (제출된 파라미터가 저장된 객체)
		
		// String[] memberAddress : 
		//	input name="memberAddress" 3개가 저장된 배열
		
		// RedirectAttributes ra :
		// 리다이렉트 시 데이터를 request scope로 전달하는 객체
		
		System.out.println("주소 : " + inputMember.getMemberAddress());
		
		// 주소 입력 시 : 01234,서울 성동구 어쩌구,2층
		// 만약에 입력하지 않았다면 ,, 이런식으로 구분자만 나옴
		// 주소를 입력하지 않은 경우 null 로 변경
		
		// 주소가 입력되지 않았다면
		if(inputMember.getMemberAddress().equals(",,")) {
			inputMember.setMemberAddress(null);
		// 주소를 입력한 경우 ,, -> ^^^
		} else {
			// String.join("구분자", String[])
			// 배열의 요소를 하나의 문자열로 변경
			// 요소 사이에 구분자를 추가함
			String addr = String.join("^^^", memberAddress); // ['12345','서울시어쩌구','2층'] -> ["12345^^^서울시어쩌구^^^2층"]
			inputMember.setMemberAddress(addr);
			
		}
		
		
		
		// 회원 가입 서비스 호출
		int result = service.signUp(inputMember);
		
		
		// 가입 성공 여부에 따라서 주소 결정
		String path = "redirect:";
		String message = null;
		
		if(result > 0) { // 가입 성공
			path += "/"; // 메인페이지로
			
			message = inputMember.getMemberNickname() + "님의 가입을 환영합니다";
			
		} else { // 가입 실패
			
			// 회원 가입 페이지
			//path += "/member/signUp"; // 절대경로
			path += "signUp"; // 상대경로
			
			message = "회원 가입 실패";
			
		}
		
		// 리다이렉트 시 session에 잠깐 올라갔다 request로 복귀하도록 세팅
		ra.addFlashAttribute("message", message);
			
		return path;
	}
	
	
	
	/*
	 *  스프링 예외 처리 방법(3종류, 중복 사용 가능)
	 * 
	 *  1 순위 : 메서드 단위로 처리
	 * 		    -> try - catch / throws
	 * 
	 * 
	 *  2 순위 : 클래스 단위로 처리
	 * 		    -> @ExceptionHandler
	 * 
	 * 
	 *  3 순위 : 프로그램 단위(전역) 처리
	 *          -> @ControllerAdvice
	 * 
	 */
	
	// 현재 클래스에서 발생하는 모든 예외를 모아서 처리
	//@ExceptionHandler(Exception.class /* 예외처리 하고 싶은 것 넣어주기 */)
	public String exceptionHandler(Exception e, Model model) {
		
		// Exception e : 예외 정보를 담고있는 객체
		// Model model : 데이터 전달용 객체 (request scope 기본)
		
		e.printStackTrace(); // 예외 내용/발생 메서드 확인
		
		// request 범위? 현재페이지, 위임받은 페이지
		model.addAttribute("e", e); // 예외 발생 시 forward되는 페이지로 e를 전달함. (request scope도 가능하다)
		
		
		// 누구에 의해서?
		// View Resolver의 prefix, suffix를 붙여 JSP 경로를 만든것
		// return "/WEB-INF/views/common/error.jsp";
		return "common/error";
	}
	
}

MemberService.java

package edu.kh.project.member.model.service;

import edu.kh.project.member.model.dto.Member;

// Service Interface 사용 이유

// 1. 프로젝트에 '규칙성'을 부여하기 위해서 == 모든 클래스가 같은 형태를 띔(협업 위함)

// 2. 클래스간의 '결합도를 약화' 시키기 위해서 (객체 지향적 설계) == MemberService service = new MemberServiceImpl();
// -> 유지보수성 향상								   MemberService service = new MemberServiceImpl2();
//												   MemberService service = new MemberServiceImpl3();

// 3. Spring AOP 사용을 위해서
// -> AOP는 spring-proxy를 이용해서 동작하는데
//  이 때 Service 인터페이스가 필요하다!

public interface MemberService {
	
	/** 로그인 서비스
	 * @param inputMember (email, pw)
	 * @return email, pw가 일치하는 회원정보 또는 null
	 */
	Member login(Member inputMember);

	/** 회원 가입 서비스
	 * @param inputMember
	 * @return result
	 */
	int signUp(Member inputMember);

}

MemberServiceImpl.java

package edu.kh.project.member.model.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import edu.kh.project.member.model.dao.MemberDAO;
import edu.kh.project.member.model.dto.Member;

@Service // 비즈니스 로직(데이터 가공, DAO 호출, 트랜잭션 제어)처리하는 클래스 명시
		// + Bean으로 등록하는 어노테이션
public class MemberServiceImpl implements MemberService{
	
	// @Autowired : 작성된 필드와
	// Bean으로 등록된 객체 중 타입이 일치하는 Bean을
	// 해당 필드에 자동 주입(Injection) 하는 어노테이션.
	// == DI(Dependency Injection, 의존성 주입)
	// -> 객체를 직접 만들지 않고, Spring이 만든걸 주입함(Spring에 의존)
	
	@Autowired
	private MemberDAO dao;
	
	@Autowired // bean으로 등록된 객체 중 타입이 일치하는 객체를 DI(의존성 주입)
	private BCryptPasswordEncoder bcrypt;
	
	// 암호화가 필요한 곳? 로그인, 회원가입
	
	@Override
	public Member login(Member inputMember) {
		
		// 암호화가 다 다른지 test 용도
//		System.out.println("암호화 확인 1:"  +  bcrypt.encode(  inputMember.getMemberPw()  )  );
//		System.out.println("암호화 확인 2:"  +  bcrypt.encode(  inputMember.getMemberPw()  )  );
//		System.out.println("암호화 확인 3:"  +  bcrypt.encode(  inputMember.getMemberPw()  )  );
//		System.out.println("암호화 확인 4:"  +  bcrypt.encode(  inputMember.getMemberPw()  )  );
//		System.out.println("암호화 확인 5:"  +  bcrypt.encode(  inputMember.getMemberPw()  )  );
		
		
		// dao 메서드 호출
		Member loginMember = dao.login(inputMember);
		
		// 입력받은 비밀번호(평문) --비교-- DB에서 조회한 비밀번호(암호화)
		
		if(loginMember != null) { // 아이디가 일치하는 회원이 조회된 경우
			
			// 입력한 pw, 암호화된 pw 같은지 확인
			
			// 같을 경우            // 로그인 시 입력             // DB에서 조회한 것
			if(bcrypt.matches(inputMember.getMemberPw(), loginMember.getMemberPw())) {
				
				// 비밀번호를 유지하지 않기 위해서 로그인 정보에서 제거
				loginMember.setMemberPw(null);
				
			} else { // 다를 경우(비밀번호 잘못쳤을 경우)
				loginMember = null;
			}
			
		} 
		
		return loginMember;
	}

	
	// 회원 가입 서비스
	@Transactional
	@Override
	public int signUp(Member inputMember) {
		
		// 비밀번호 암호화 (Bcrypt) 후 다시 inputMember 세팅
		String encPw = bcrypt.encode(inputMember.getMemberPw());
		inputMember.setMemberPw(encPw);
		
		return dao.signUp(inputMember);
	}
	
	
}

MemberDAO.java

package edu.kh.project.member.model.dao;

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

import edu.kh.project.member.model.dto.Member;

@Repository // Persistence Layer, 영속성 관련 클래스 // 영속성 == 영원히 지속되는 성질
			// (DB관련 클래스) + Bean(== Spring 객체화) 등록
public class MemberDAO { // IOC == 제어의 역전
	// DAO는 DB랑 연결하기 위한 Connection이 공통적으로 필요하다
	// + Mybatis 영속성 프레임워크를 이용하려면 Connection을 이용해서 만들어진 객체
	// SqlSessionTemplate을 사용
	@Autowired
	private SqlSessionTemplate sqlSession;
	
	

	/** DAO
	 * @param inputMember
	 * @return 회원 정보 또는 null
	 */
	public Member login(Member inputMember) {
		
		// 마이바티스를 이용해서 1행 조회(selectOne)
		
		// sqlSession.selectOne("namespace값.id값", 전달할 값);
		// -> namespace가 일치하는 Mapper에서
		// id가 일치하는 SQL 구문을 수행 후
		// 결과를 1행 반환
		
		return sqlSession.selectOne("memberMapper.login", inputMember);
	}



	/** 회원가입 DAO
	 * @param inputMember
	 * @return result
	 */
	public int signUp(Member inputMember) {
		
		return sqlSession.insert("memberMapper.signUp", inputMember);
	}

}

member-mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="memberMapper">
	<!-- namespace : 해당 공간의 이름 지정 -->

	<!-- mapper 파일 생성 시 아래 태그 반드시 삭제 -->
	<!--   <cache-ref namespace=""/>   -->
	
	<!-- 작성예시 -->
	<!-- <select id=""></select>  -->
	<!-- <insert id=""></insert>  -->
	
	<!-- 
		resultMap  
		- SELECT 조회 결과(ResultSet) 컬럼명과
		  컬럼 값을 옮겨 담을 DTO의 필드명이 같지 않을 때
		  이를 매핑 시켜 SELECT시 자동으로 담기게하는 역할
	 
	 
	 	-속성
	 	type : 연결할 DTO (패키지명 + 클래스명 또는 별칭)
	 	id : 만들어진 resultMap을 지칭할 식별명(이름)
	 	
	 	<id> 태그 : PK 역할 컬럼 - 필드 매핑
	 	<result> 태그 : <id>제외 나머지
	 -->
	 
	<resultMap type="Member" id="member_rm">
	
	<!-- property가 java, column이 db라고 생각하면 됨. -->
	
		<!-- DB의 기본 키(복합키면 여러 개 작성) -->
		<id property="memberNo" column="MEMBER_NO" />

		<!-- DB의 일반 컬럼들 -->
		<result property="memberEmail" column="MEMBER_EMAIL" />
		<result property="memberPw" column="MEMBER_PW" />
		<result property="memberNickname" column="MEMBER_NICKNAME" />
		<result property="memberTel" column="MEMBER_TEL" />
		
		<result property="memberAddress" column="MEMBER_ADDR" />
		
		<result property="profileImage" column="PROFILE_IMG" />
		<result property="enrollDate" column="ENROLL_DATE" />
		<result property="memberDeleteFlag" column="MEMBER_DEL_FL" />
		<result property="authority" column="AUTHORITY" />
	</resultMap>
	
	<!--  
  		SQL 관련 태그의 속성
  		
  		- parameterType : 전달 받은 값의 자료형
  						기본 : 패키지명 + 클래스명
  						별칭 : Mybatis 별칭 또는 사용자 지정 별칭
  						
		- parameterMap : (사용 안함)
		
		- resultType : select 결과를 담아서 반환할 자료형
					단, DTO를 작성할 경우 필드명 = 컬럼명 인 경우만 가능
										memberNo   MEMBER_NO
		
		- resultMap : select 결과의 컬럼명과 
					 결과를 저장할 DTO 필드명이 다를 경우
					 이를 알맞게 매핑(연결)시켜주는 <resultMap> id 작성
  	-->
  	
  	
  	<!-- 
  		** 마이바티스에서 전달 받은 값을 SQL에 작성하는 방법 **
  		
  		#{변수명|필드명} : PreparedStatement -> placeholder 대체하여 사용
  						 : SQL에 값 대입 시 양쪽에 '' 붙여서 대입
  		
  		${변수명|필드명} : Statement -> '숫자' 쓸 때 많이 사용
  						 : SQL에 값 대입 시 양쪽에 아무것도 붙이지 않음
 						 
		사용 예시)
		test1 = "user01"
		test2 = MEMBER_EMAIL
  	
  		- MEMBER_EMAIL이 'user01'인 회원 조회
  		SELECT * FROM MEMBER WHERE ${test2} = #{test1}
  								 MEMBER_EMAIL = 'user01'
  	 -->
	
	<!-- 
		<select></select>
		<insert></insert>
		<update></update>
		<delete></delete>
		사용 가능한 속성도 조금씩 다름
	 -->
	<select id="login" parameterType="Member" resultMap="member_rm">
  		SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME, MEMBER_PW,
			MEMBER_TEL, MEMBER_ADDR, PROFILE_IMG, AUTHORITY,
			TO_CHAR(ENROLL_DATE, 'YYYY"년" MM"월" DD"일" HH24"시" MI"분" SS"초"') AS ENROLL_DATE 
		FROM "MEMBER"
		WHERE MEMBER_DEL_FL = 'N'
		AND MEMBER_EMAIL = #{memberEmail}
		<!-- AND MEMBER_PW = #{memberPw} -->
  	</select>
  	
  	
  	<insert id="signUp" parameterType="Member">
  		INSERT INTO "MEMBER"
  		VALUES(SEQ_MEMBER_NO.NEXTVAL
  			, #{memberEmail}
  			, #{memberPw}
  			, #{memberNickname}
  			, #{memberTel}
  			, #{memberAddress}
  			, NULL, DEFAULT, DEFAULT, DEFAULT)
  	</insert>
  	
	
</mapper>


가입하기 버튼 클릭 시, 회원가입 완료!
새롭게 회원가입한 회원 : 로그인도 잘 됨.




마이페이지


MyPageController.java

package edu.kh.project.myPage;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping("/myPage")
@Controller
public class MyPageController {
	
	// 내 정보 페이지로 이동
	@GetMapping("/info")
	public String info() {
		return "myPage/myPage-info";
	}
	
	// 프로필 페이지 이동
	@GetMapping("/profile")
	public String profile() {
		return "myPage/myPage-profile";
	}
	
	
	// 비밀번호 변경 페이지 이동
	@GetMapping("/changePw")
	public String changePw() {
		return "myPage/myPage-changePw";
	}
	
	// 탈퇴 페이지 이동
	@GetMapping("/secession")
	public String secession() {
		return "myPage/myPage-secession";
	}

}

sideMenu.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!-- 왼쪽 사이드 메뉴 -->
<section class="left-side">
    사이드메뉴

    <ul class="list-group">
        <li> <a href="/myPage/profile">프로필</a> </li>
        
        <li> <a href="/myPage/info">내 정보</a> </li>
        
        <li> <a href="/myPage/changePw">비밀번호 변경</a> </li>
        
        <li> <a href="/myPage/secession">회원 탈퇴</a> </li>
        
    </ul>
    
    
</section>

0개의 댓글