※ 본문은 혼자 공부한 내용을 기록한 글입니다. 오개념이 있다면 댓글로 알려주세요!
2023.01.29 - [나에 대한 기록] - [독서 기록] 스프링5 프로그래밍 입문 - 최범균 저
[독서 기록] 스프링5 프로그래밍 입문 - 최범균 저
https://search.shopping.naver.com/book/catalog/32458958626 스프링5 프로그래밍 입문 : 네이버 도서 네이버 도서 상세정보를 제공합니다. search.shopping.naver.com 지난 한 주 동안 최범균님의 스프링 5 프로그래밍
krchoish.tistory.com
※ 해당 책을 참고하여 프로젝트를 진행하였습니다.
[ 1 ] Service 구현
MySQL Member table에 담겨있는 정보를 바탕으로 로그인 기능의 로직을 제공하는 LoginService를 구현하자.
LoginService의 로직은 다음과 같다.
- DAO.selectByEmail()로 email에 해당하는 member 객체를 받는다.
- member 객체의 email이 DB에 저장되어있는지, 저장된 email이라면 email과 password가 일치하는지 확인한다.
- DB에 저장된 email이 아니라면 NoMemberException을, email과 password가 일치하지 않다면 WrongIdPasswordException을 throw한다.
- Exception이 발생하지 않으면 해당 member 객체의 id, email, name을 생성자 파라미터로 갖는 LoginInfo객체를 LoginController로 return한다. (LoginInfo객체는 뒤에서 설명한다)
package service;
import command.LoginInfo;
import dao.MemberDao;
import dto.Member;
import exception.NoMemberException;
import exception.WrongIdPasswordException;
public class LoginService {
private MemberDao memberDao;
public LoginService(MemberDao memberDao) {
this.memberDao = memberDao;
}
public LoginInfo authenticate(String email, String password) {
Member member = memberDao.selectByEmail(email);
if(member == null) {
throw new NoMemberException();
}
if(!member.matchPassword(password)) {
throw new WrongIdPasswordException();
}
return new LoginInfo(member.getId(), member.getEmail(), member.getName());
}
}
[ 2 ] LoginCommand 객체 및 검증 구현
로그인 정보를 받는 login/form.jsp에서는 email, password, 그리고 이메일 기억하기 여부를 <form>에 넣어 전달한다. 해당 정보를 Controller에서 받기 위해 LoginCommand 클래스를 다음과 같이 작성한다.
package command;
public class LoginCommand {
private String email;
private String password;
private boolean rememberEmail;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isRememberEmail() {
return rememberEmail;
}
public void setRememberEmail(boolean rememberEmail) {
this.rememberEmail = rememberEmail;
}
}
해당 커맨드 객체를 검증하는 Validator를 구현한 클래스는 다음과 같다.
package validator;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import command.LoginCommand;
public class LoginCommandValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return LoginCommand.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "required");
}
}
로그인 시 입력한 email과 password가 null이거나 공백문자로만 되어 있을 경우 에러 코드로 "required"를 추가한다.
Validator 인터페이스의 supports, validate 메서드는 이전 게시글을 참고하자.
[ 3 ] LoginController 구현
"/login" 요청을 처리하는 LoginController의 전체 코드는 다음과 같다.
package controller;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import command.LoginCommand;
import command.LoginInfo;
import exception.NoMemberException;
import exception.WrongIdPasswordException;
import service.LoginService;
import validator.LoginCommandValidator;
@Controller
@RequestMapping("/login")
public class LoginController {
private LoginService loginService;
public LoginController(LoginService loginService) {
this.loginService = loginService;
}
@GetMapping
public String handlerForm(LoginCommand loginCommand,
@CookieValue(value="REMEMBER", required = false) Cookie rememberCookie) {
if(rememberCookie != null) {
loginCommand.setEmail(rememberCookie.getValue());
loginCommand.setRememberEmail(true);
}
return "login/form";
}
@PostMapping
public String handlerSubmit(LoginCommand loginCommand, Errors errors,
HttpSession session, HttpServletResponse response) {
new LoginCommandValidator().validate(loginCommand, errors);
if(errors.hasErrors()) {
return "login/form";
}
try {
LoginInfo loginInfo = loginService.authenticate(loginCommand.getEmail(), loginCommand.getPassword());
session.setAttribute("loginInfo", loginInfo);
Cookie rememberCookie = new Cookie("REMEMBER", loginCommand.getEmail());
rememberCookie.setPath("/");
if(loginCommand.isRememberEmail()) {
rememberCookie.setMaxAge(60 * 60 * 24 * 30);
} else {
rememberCookie.setMaxAge(0);
}
response.addCookie(rememberCookie);
return "login/success";
} catch(NoMemberException e) {
errors.rejectValue("email", "nomember");
return "login/form";
} catch(WrongIdPasswordException e) {
errors.reject("idPasswordNotMatching");
return "login/form";
}
}
}
우선, LoginController는 LoginService에 의존하므로 의존을 설정한다.
private LoginService loginService;
public LoginController(LoginService loginService) {
this.loginService = loginService;
}
※ Controller를 사용하기 위해서는 반드시 Config에 @Bean으로 등록하고, 의존 주입을 해야 한다.
LoginController는 "/login" 요청만 처리하며, Get 요청 시에는 로그인 정보를 입력하는 login/form.jsp을 forward하고, Post 요청 시에는 form.jsp의 <form>에서 받은 정보를 LoginCommand에 저장, 해당 커맨드 객체 검증, 로그인 정보 Session에 저장하기 등을 수행한다.
Get 요청 시 로그인 정보를 입력하는 login/form.jsp을 forward하기 위해 아래 코드를 작성한다.
@GetMapping
public String handlerForm(LoginCommand loginCommand,
@CookieValue(value="REMEMBER", required = false) Cookie rememberCookie) {
if(rememberCookie != null) {
loginCommand.setEmail(rememberCookie.getValue());
loginCommand.setRememberEmail(true);
}
return "login/form";
}
form.jsp에서 Spring의 <form> 태그를 사용하기 위해 매개변수에 LoginCommand를 추가했다.
@CookieValue는 로그인 할 때 '이메일 기억하기'를 클릭했을 경우 해당 email이 Cookie에 저장되는데, 그 Cookie에 저장된 정보를 사용하기 위한 매개변수이다. Cookie가 생성되는 과정은 Post 요청 처리 메서드에서 살펴보자.
Post 요청 처리 메서드는 다음과 같다.
@PostMapping
public String handlerSubmit(LoginCommand loginCommand, Errors errors,
HttpSession session, HttpServletResponse response) {
new LoginCommandValidator().validate(loginCommand, errors);
if(errors.hasErrors()) {
return "login/form";
}
try {
LoginInfo loginInfo = loginService.authenticate(loginCommand.getEmail(), loginCommand.getPassword());
session.setAttribute("loginInfo", loginInfo);
Cookie rememberCookie = new Cookie("REMEMBER", loginCommand.getEmail());
rememberCookie.setPath("/");
if(loginCommand.isRememberEmail()) {
rememberCookie.setMaxAge(60 * 60 * 24 * 30);
} else {
rememberCookie.setMaxAge(0);
}
response.addCookie(rememberCookie);
return "login/success";
} catch(NoMemberException e) {
errors.rejectValue("email", "nomember");
return "login/form";
} catch(WrongIdPasswordException e) {
errors.reject("idPasswordNotMatching");
return "login/form";
}
}
(1) 매개변수 - form.jsp에서 입력한 정보를 받기 위한 LoginCommand, 커맨드 객체를 검증하기 위한 Errors(반드시 커맨드 객체 바로 뒤에 위치해야 한다), 성공적으로 로그인 시 해당 정보를 session에 저장하기 위한 HttpSession, '이메일 기억하기'가 true일 때 해당 이메일을 쿠키에 저장하고 그 쿠키를 response 객체에 담기 위한 HttpServletResponse.
(2) 우선, LoginCommand를 검증한다.
new LoginCommandValidator().validate(loginCommand, errors);
if(errors.hasErrors()) {
return "login/form";
}
(3) try{} 내부 : LoginService의 authenticate 메서드로 LoginInfo 객체를 받는다. 그 후 로그인 상태 정보를 유지하기 위해 session에 LoginInfo를 담는다.
LoginInfo loginInfo = loginService.authenticate(loginCommand.getEmail(), loginCommand.getPassword());
session.setAttribute("loginInfo", loginInfo);
(4) try{} 내부 : name이 "REMEMBER"인 쿠키를 생성한다. 만약, form.jsp에서 '로그인 기억하기'를 체크하여 loginCommand.isRememberEmail()이 true라면 cookie를 저장할 기간을 설정한다. 만약 '로그인 기억하기'를 체크하지 않았다면 cookie 저장 기간을 0으로 설정한다. 마지막으로 response 객체에 cookie를 담는다.
Cookie rememberCookie = new Cookie("REMEMBER", loginCommand.getEmail());
rememberCookie.setPath("/");
if(loginCommand.isRememberEmail()) {
rememberCookie.setMaxAge(60 * 60 * 24 * 30);
} else {
rememberCookie.setMaxAge(0);
}
response.addCookie(rememberCookie);
(5) NoMemberException이 발생하면 "email" 프로퍼티에 "nomember" 에러 코드를 추가하고, WrongIdPasswordException이 발생하면 에러 코드가 "idPasswordNotMatching"인 글로벌 에러를 추가한다.
catch(NoMemberException e) {
errors.rejectValue("email", "nomember");
return "login/form";
} catch(WrongIdPasswordException e) {
errors.reject("idPasswordNotMatching");
return "login/form";
}
[ 4 ] LogoutController 구현
Logout을 하기 위해 session 설정값을 제거한다.
package controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LogoutController {
@RequestMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/main";
}
}
[ 코드 테스트 ]
성공적으로 동작함을 확인할 수 있었다. 이제 여러가지 에러 상황을 확인하자.
(1) required 에러 코드
(2) idPasswordNotMatching 에러 코드
(3) nomember 에러 코드
모두 정상적으로 작동하였다!
다음은 로그인 시 비밀번호 변경 로직을 구현해 보도록 하자!
❗️전체 코드는 github에서 확인할 수 있습니다❗️
'Project > [Spring] 게시판' 카테고리의 다른 글
[Spring] 게시판 만들기 - 커맨드 객체 검증 및 에러 코드 지정 (1) | 2023.02.11 |
---|---|
[Spring] 게시판 만들기 - 회원가입 구현 (1) | 2023.02.10 |
[Spring] 게시판 만들기 - gradle 설정, Spring MVC 설정, DispatcherServlet 설정 (1) | 2023.02.07 |