로그인 검증 부분을 어떻게 설계할까 고민을 많이 했습니다. 하드코딩과 @validation 두 가지를 고려했습니다.
1. 하드코딩
하드코딩으로 만든다면 각각의 계층에서 연속적으로 검증을 해줘야 하는 번거로움이 있습니다. 또한 검증 로직이 변경될 경우 각 계층을 같이 봐야하는 어려움이 있습니다.
2.Bean Validation
java는 Bean Validation라는 유효성검사 프레임워크를 지원하고 있습니다. 각 계층에서 검증하는 것이 아닌 빈 어노테이션을 통해 공통적으로 필요한 부분만 검증할 수 있게 되었습니다.
시작
gradle 설정
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
의존성을 추가해줍니다.
Dependencies를 확인해보면 잘 주입된 것을 확인할 수 있습니다.
로그인 폼을 사용할 loginDto입니다.
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Getter
@Setter
public class LoginDto {
@NotBlank(message = "아이디를 입력해주세요")
@NotNull(message = "아이디를 입력해주세요")
private String id;
@NotBlank(message = "비밀번호를 입력해주세요")
@NotNull(message = "비밀번호를 입력해주세요")
private String password;
}
빈 값일 경우와 Null이 아닐 경우 두 가지 validation을 추가해줍니다. 여러 가지 검증 어노테이션이 있습니다. 더 자세한 것들은 아래 공식문서를 확인해 보시면 됩니다.
Controller를 확인해봅시다.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
@Controller
@RequiredArgsConstructor
@Slf4j
public class LoginController {
private final LoginService loginService;
@GetMapping("/login")
public String loginForm(Model model, HttpSession session) {
model.addAttribute("loginDto", new LoginDto());
if (session != null) {
return "login/main";
}
return "index";
}
@PostMapping("/login")
public String loginCheck(@ModelAttribute("loginDto") @Valid LoginDto loginDto, BindingResult bindingResult, HttpServletRequest request){
HttpSession session;
if (bindingResult.hasErrors()) {
return "login/main";
}
AdminDto adminDto = loginService.adminFindByLoginId(loginDto);
if (adminDto == null) {
return "login/main";
}
.
.
.
session = request.getSession();
session.setAttribute("user" , employee);
session.setAttribute("id", adminDto.getId());
return "redirect:/index";
}
@GetMapping("/logout")
public String logout(Model model, HttpServletRequest request) {
model.addAttribute("loginDto", new LoginDto());
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate(); // 세션 날림
}
return "login/main";
}
}
Get :/login
로그인 페이지로 넘길 때 model에 loginDto를 같이 넘겨줍니다. 그래야 타임리프에서 form을 정상적으로 인식할 수 있습니다.
POST:/login
가장 중요한 로직입니다. 천천히 살펴보자면,
@PostMapping("/login")
public String loginCheck(@ModelAttribute("loginDto") @Valid LoginDto loginDto, BindingResult bindingResult,
HttpServletRequest request){
HttpSession session;
if (bindingResult.hasErrors()) {
return "login/main";
}
...
}
@ModelAttribute를 통해 form에서 loginDto객체를 가져옵니다. 이때 중요한 점은 @Valid를 붙여주고 BindingResult bindingResult을 검증할 해당 객체 뒤에 넣어줍니다. hasErrors()가 있을 경우 다시 로그인폼을 반환해줍니다.
BindingResult이 뭐지?
BindingResult은 객체 바인딩을 반환해주는 메서드입니다. 객체를 까 보면
Errors를 extends 하고 있어 Errors에 있는 hasErrors()가 바인딩 객체 에러 처리를 해줍니다.
마지막으로 view(Thymeleaf)를 살펴보겠습니다.
...
<form action="/login" th:object="${loginDto}" method="post" class="loginForm">
<div class="idForm">
<input type="text" th:field="*{id}" class="id" placeholder="ID"/>
<div th:each="msg : ${#fields.errors('id')}">
<p th:text="${msg}"></p>
</div>
</div>
</div>
<div class="passForm">
<input type="password" th:field="*{password}" class="pw" placeholder="PW"/>
<div th:each="msg : ${#fields.errors('password')}">
<p th:text="${msg}"></p>
</div>
</div>
...
controller에서 model에 loginDto를 넘겨줬습니다. 롬복을 사용하여 getter와 setter를 사용합니다. 각 필드에 입력값을 지정해줍니다.
에러 처리를 위해 메시지를 작성해줍니다.
이런 식으로 입력 값이 없을 경우 Dto에서 지정한 메시지를 출력하게 됩니다.
잘못된 부분이 있으면 언제든지 피드백해주세요~!
감사합니다.
'혼자 공부하는 것들 > Spring' 카테고리의 다른 글
Spring Boot 이미지업로드 (환경: gradle, java11,Spring Boot 2.6.2, thymeleaf) (0) | 2022.02.14 |
---|---|
Spring 빈 스코프를 알아보자♻️ (1) | 2022.01.31 |
Spring Boot gradle 환경에서 logback을 적용해보자. 🔥 (2) | 2022.01.27 |
Bean의 생명주기 콜백 🌱 (0) | 2022.01.25 |
Bean 자동 주입 VS 수동 주입 어떤 걸 사용해야할까? 🌱 (0) | 2022.01.20 |
댓글