본문 바로가기
Project/소경관

[소경관] : 회원가입 로직 검증 다듬어주기, Spring Bean Validator 사용

by 오주현 2022. 4. 18.
반응형

오늘은 검증 로직을 살짝 다듬어주었다. 전에는 완전하게 적용을 하지 않아서 엉망이었는데 오늘 다 정리해 버렸다. 이해가 잘 안 되는 부분이 있어서 하루 종일 찾아보다 저녁 되서야 완성시키고 로직 정리를 했다.

 

내용은 아래와 같다.

 

@PostMapping("/user/regUser/insert")
public String InsertRegUser(@Validated @ModelAttribute UserVo userVo, BindingResult bindingResult) throws Exception{

    log.info(this.getClass().getName() + "회원가입 로직 처리 시작");

    if (bindingResult.hasErrors()) {
        log.info(" 회원가입 로직 처리 중 Errors 처리 bindingResult ={}", bindingResult);
        return "user/regUser";
    }

    UserDTO userDTO = new UserDTO(userVo.getUserNo(), userVo.getUserName(), userVo.getUserPn(), userVo.getUserEmail(), userVo.getUserId(), userVo.getUserPw(), userVo.getUserAddr());

    log.info("UserDTO ={}", userDTO);

    userService.InsertUser(userDTO);

    return "/user/logIn";
}

회원가입 기능을 만들면서 Controller에 다 떄려 넣은 검증 로직을 따로 빼서 관리하기로 했다.

그 결과 Controller가 깔끔해지고 있는 중이다.

 

<div class="form-floating">
    <input th:type="text" id="userName" th:field="*{userName}" th:errorclass="field-error" th:class="form-control" placeholder="이름">
    <label for="userName" th:text="#{label.userVo.userName}">이름</label>
    <div th:class="field-error" th:errors="*{userName}">이름 오류</div>
</div>

프론트를 먼저 보면 label에 th:text="#{label.userVo.userName}" 코드를 추가해 주었다.

 

label.userVo.userName=이름을 입력해주세요.
label.userVo.userPn=휴대폰 번호를 입력해주세요.
label.userVo.userEmail=이메일을 입력해주세요.
label.userVo.userId=아이디를 입력해주세요.
label.userVo.userPw=비밀번호를 입력해주세요.
label.userVo.userAddr=주소를 입력해주세요.

errors.properties를 만들어 주었다. messages.properties에서 관리해도 되는데 일반 메시지랑 오류랑 구분하는 게 좋을 것 같아서 따로 빼서 관리했다.

 

# error 메시지 파일 생성 및 등록
spring.messages.basename=messages,errors

application.properties에서 errors.properties를 등록해주었다. 원래는 기본 값으로 messages를 사용하는데 기본 메시지랑 에러 메시지랑 구분하기 위해 따로 errors.properties를 만들어 주었으므로 위 코드처럼 설정해 주어야 한다.

 

@Slf4j
@Component
public class UserValidator implements Validator {

먼저 UserValidator를 만들어주고 Validator를 구현했다.

 

Validator interface는 2개의 메소드를 지원한다.

 

boolean supports(Class<?> clazz); 와 void validate(Object target, Errors errors); 를 지원하고 있다.

 

@Override
public boolean supports(Class<?> clazz) {
    return UserVo.class.isAssignableFrom(clazz);
}

supports에서 UserVo (View에서 회원가입 정보를 가지고 오는 Model 객체)를 리턴한다.

 

아직 supports를 제대로 사용해 보지는 못 했는데 만약 여러 검증이 있을 때 supports에서 해당 검증에 맞는 객체가 들어왔는지 체크한다고 한 것 같다. 즉, 들어온 객체가 검증 대상 타입인지 체크를 한다.

 

아직 추가로 공부가 필요한 부분인데 아마 로그인 검증을 짤 때 사용하지 않을까 싶다.

 

@Override
public void validate(Object target, Errors errors) {

    UserVo userVo = (UserVo) target;

    /**
     * 1. @Valid 검증 로직 실행
     */

    if (!StringUtils.hasText(userVo.getUserName())) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "label");
    }

    if (!StringUtils.hasText(userVo.getUserPn())) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userPn", "label");
    }

    if (!StringUtils.hasText(userVo.getUserEmail())) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userEmail", "label");
    }

    if (!StringUtils.hasText(userVo.getUserId())) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userId", "label");
    }

    if (!StringUtils.hasText(userVo.getUserPw())) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userPw", "label");
    }

    if (!StringUtils.hasText(userVo.getUserAddr())) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userAddr", "label");
    }
}

validate 메소드는 파라미터로 target을 사용하는데 검증 값을 담고있는 userVo를 target으로 등록해 검증해야 할 객체임을 알게 해 준다.

 

ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "label"); userName 검증만 보면 처음에는 errors와 필드, 에러코드를 적게 되어있다. 여기에 들어가는 에러 코드는 아래를 참고한다.

 

label.userVo.userName=이름을 입력해주세요.

errors.properties에서 정의한 에러 코드를 가지고 온다. 이 에러 코드는 View에서 설정한 것과 일치한다.

 

<div class="form-floating">
    <input th:type="text" id="userName" th:field="*{userName}" th:errorclass="field-error" th:class="form-control" placeholder="이름">
    <label for="userName" th:text="#{label.userVo.userName}">이름</label>
    <div th:class="field-error" th:errors="*{userName}">이름 오류</div>
</div>

<label for="userName" th:text="#{label.userVo.userName}">이름</label> th:text로 에러 코드를 적어주었다. 검증에 걸리면 여기에 errors.properties에서 설정한 labe.userVo.uerName 에러 코드가 나오게 된다.

 

에러 코드는 이름을 입력해주세요.라고 적어두었다.

 

<style>
    .field-error {
        border-color: #dc3545;
        color: #dc3545;
    }
</style>

에러 코드가 눈에 잘 안 띄어서 빨간색 컬러를 넣어주었다.

 

@Slf4j
@Controller
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;
    private final UserValidator userValidator;

    @InitBinder
    public void init(WebDataBinder dataBinder) {
        log.info("init binder {}", dataBinder);
        dataBinder.addValidators(userValidator);
    }

@RequiredArgsConstructor를 사용해서 단일 생성자인 경우 파라미터 값이 여러개라도 @Autowired 를 사용하지 않아도 되도록 설정해 주었고 private final UserValidator userValidator;를 등록해 주었다.

 

@InitBinder 를 통해서 해당 Controller에서 @Validated 어노테이션이 있는 곳이 검증 로직을 거쳐가도록 설정해 주었다.

이렇게 회원가입에 대한 검증 처리는 마쳤고, 로그인 처리는 찾아보니 몇 가지 방법이 있는 것 같다.

 

groups를 사용하거나 전송 객체를 분리하거나인데 가능하면 객체 분리식으로 사용하려고 한다. (로그인 검증을 하기 위해서 사용하는 게 아니라 회원가입, 로그인이 서로 다른 검증이 들어가야 하기 때문에 선택지가 늘어난 것이다.)

반응형

댓글