sm 기술 블로그

[스프링 부트] 유효값과 예외 처리 본문

스프링부트

[스프링 부트] 유효값과 예외 처리

sm_hope 2022. 7. 23. 10:38

유효값 사용처

우리는 엔티티에 NotNull 혹은 Size와 같은 어노테이션을 통해서 바로바로 에러 처리가 가능했다.
그것은 @Valid에서 제공하는 어노테이션 덕분이다.(NotNull, Size)
하지만 유효성 검사에서 제공하는 어노테이션은 한정적이다. (아래의 표를 보자)

따라서 다른 에러가 발생하면 유효성으로 확인을 못하니 컨트롤러 혹은 서비스 등에서 에러를 처리해준다.

유효성 검사에 없는 에러

예를 들어 회원가입시 비밀번호 1과 비밀번호 2가 다를경우, 유저 이름과 아이디가 중복으로 발생한 경우에는 에러처리가 따로 필요하다.

1. 비밀번호가 다른 경우 에러 처리


비밀번호 1과 비밀번호 2가 다른경우 bindingResult.rejectValue를 통해서 에러 메시지를 표시한다.

bindingResult.rejectValue의 경우에 형식은 다음과 같다.

void rejectValue(@Nullable String field, String errorCode, String defaultMessage);

따라서 field 값은 굳이 넣어줄 필요가 없다.

bindingResult.rejectValue("password1","passwordInCorrect","2개의 패스워드가 일치하지 않습니다.");
bindingResult.rejectValue("","passwordInCorrect","2개의 패스워드가 일치하지 않습니다.");
// 같은 의미이다.

2. 중복 혹은 다른 에러가 발생할 경우


중복 혹은 다른에러가 발생한 경우 왜 if가 아닌 try를 이용해서 에러를 처리하는지 궁금할 수 있다.
그 이유는 중복이나 다른에러가 발생하는지에 대한 여부는 시도를 해봐야 에러가 발생할지 안 할지, 어떤 에러가 발생했는지에 대해 알 수 있기 때문이다.

catch(DataIntegrityViolationException e) {
            e.printStackTrace();
            bindingResult.reject("signupFailed", "이미 등록된 사용자입니다.");
            return "usr/user/signup_form.html";
        }

위 구문은 중복이 발생했을 경우에 대한 에러 처리이다.

  • e.printStackTrace(); 에러를 출력해줌
  • bindingResult.reject("signupFailed", "이미 등록된 사용자입니다."); 에러 발생시 오른쪽 메시지를 출력한다.
  • bindingResult.rejectValue 와 bindingResult.reject는 같은 것으로 차이점은 어디서 에러가 발생했는지 알려주는 field 유무의 차이이다.
catch(Exception e) {
            e.printStackTrace();
            bindingResult.reject("signupFailed", e.getMessage());
            return "usr/user/signup_form.html";
        }

위 구문은 예상치 못한 에러가 발생했을 경우에 대한 에러 처리 이다.

요약하면 1,2 에러 처리를 해준 것은 유효성 검사에 처리할 수 없는 에러들 이기때문이다.

유효성 검사를 안하고 에러처리가 가능할까?

결론부터 말하면 가능하다.

예시를 보여주겠다.

import lombok.Getter;
import lombok.Setter;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;

@Getter
@Setter
public class UserCreateForm {
    @Size(min = 3, max = 25)
    @NotEmpty(message = "사용자ID는 필수항목입니다.")
    private String username;

//    @NotEmpty(message = "비밀번호는 필수항목입니다.")
    private String password1;

//    @NotEmpty(message = "비밀번호 확인은 필수항목입니다.")
    private String password2;

    @NotEmpty(message = "이메일은 필수항목입니다.")
    @Email
    private String email;
}

다음과 같이 비밀 번호 부분에는 NotEmpty라는 어노테이션을 주석 처리해 주었다.

    @PostMapping("/signup")
    public String signup(@Valid UserCreateForm userCreateForm, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "usr/user/signup_form.html";
        }

        if(userCreateForm.getPassword1() == null || userCreateForm.getPassword1().trim().length() == 0){
            bindingResult.reject("signupFailed", "비밀번호는 필수 기입 입니다.");
            return "usr/user/signup_form.html";
        }
        ...(생략)...

if 문을 통해 비밀번호가 비어있는 경우 조건에 들어가도록 했다.
userCreateForm.getPassword1().trim().length() == 0 를 사용한 이유는 비밀번호에 공백을 집어 넣었을 때 공백 또한 null 값이기 때문에 trim()으로 정리한 후
length를 통해 길이가 0이라면 조건에 들어가도록 했다. (그냥 기입을 안했다는 뜻)

그러면,


다음과 같이 기입을 한다면


에러를 정상적으로 처리하는 것을 볼 수 있다.

즉 유효성 검사에서 제공하는건 컨트롤러, 서비스, 폼, 엔티티 어디든 사용 가능하지만 유효성 검사를 제공하지 않는건 폼이나 엔티티에서 사용 불가능 하다

유효성 검사로 작성하는 이유

    @NotEmpty(message = "비밀번호는 필수항목입니다.")
    private String password1;

한줄 짜리 코드가

        if(userCreateForm.getPassword1() == null || userCreateForm.getPassword1().trim().length() == 0){
            bindingResult.reject("signupFailed", "비밀번호는 필수 기입 입니다.");
            return "usr/user/signup_form.html";
        }

세줄로 바뀌기 때문에 코드의 간결성으로 유효성 검사로 제공하는 것들은 유효성 검사를 통해 에러 처리를 하는것이 좋다.

Comments