로그인 검증 부분을 어떻게 설계할까 고민을 많이 했습니다. 하드코딩과 @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을 추가해줍니다. 여러 가지 검증 어노테이션이 있습니다. 더 자세한 것들은 아래 공식문서를 확인해 보시면 됩니다.
Hibernate Validator 6.2.1.Final - Jakarta Bean Validation Reference Implementation: Reference Guide
Validating data is a common task that occurs throughout all application layers, from the presentation to the persistence layer. Often the same validation logic is implemented in each layer which is time consuming and error-prone. To avoid duplication of th
docs.jboss.org
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 |
댓글