07_Framework
Goolge : '다음 우편번호 서비스' 검색
<%@ 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층
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";
}
}
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);
}
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);
}
}
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);
}
}
<?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>
가입하기 버튼 클릭 시, 회원가입 완료!
새롭게 회원가입한 회원 : 로그인도 잘 됨.
마이페이지
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";
}
}
<%@ 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>