검증 관련한 컨트롤러 테스트 코드 작성 중,
검증 시 적절한 검증을 통해 에러 메시지까지 같아야 테스트가 통과하도록 코드를 짰었다.
(ex - 빈 값 입력 시 @NotBlank에 설정한 message가 출력되도록)
하지만, 빈 값 입력 시 @NotBlank에 설정한 message가 아닌,
@Pattern에서 설정한 message가 출력되어 테스트가 실패했었다.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserSignUpRequestDto {
@NotBlank(message = "이메일을 입력해주세요.")
@Pattern(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]+$",
message = "이메일 형식에 맞게 입력해주세요.")
private String email;
@NotBlank(message = "비밀번호를 입력해주세요.")
@Size(min = 4, max = 12, message = "비밀번호는 4자 이상 12자 이하여야합니다.")
private String password;
@NotBlank(message = "닉네임을 입력해주세요.")
@Pattern(regexp = "^[a-zA-Z0-9가-힣]+$", message = "닉네임은 숫자, 한글, 영어만 가능합니다.")
@Size(min = 2, max = 10, message = "닉네임은 2자 이상 10자 이하여야합니다.")
private String nickname;
}
빈 값을 입력 했을 때, @NotBlank는 당연히 검증 실패가 되고,
@Pattern이나 @Size에서도 정규식과 최소 길이가 있기 때문에 검증 실패가 된다.
이때, 내가 원하는 순서인 @NotBlank가 먼저 동작한 뒤, @Size나 @Pattern이 동작되는
@NotBlank → @Size or @Pattern 순서로 실행되지 않아 테스트가 실패했다.
구글링을 해보니, @Valid의 여러 어노테이션에 순서를 지정해줄 수 있다는 것을 배웠다.
1. Validation 어노테이션 종류별로 인터페이스 생성
각 검증 어노테이션을 지정하기 위한 인터페이스를 생성해줘야하는데,
각각 생성하게 되면 파일이 늘어나므로 클래스 하나를 만들고 그 안에 중첩 인터페이스 형태로 선언한다.
public class ValidationGroups {
public interface NotBlankGroup {}
public interface PatternGroup {}
public interface SizeGroup {}
}
2. @GroupSequence를 사용하여 그룹별 인터페이스 순서 정의
이 과정에서 검증 어노테이션의 순서를 지정할 수 있다.
@GroupSequence의 파라미터 배열 순서로 검증 어노테이션이 차례대로 실행되고,
하나의 그룹 검증이 성공하면 그 다음 파라미터의 그룹 검증으로 이어진다.
import javax.validation.GroupSequence;
import static com.today.todayproject.global.validation.ValidationGroups.*;
@GroupSequence({NotBlankGroup.class, PatternGroup.class, SizeGroup.class})
public interface ValidationSequence {
}
내가 검증하고자 하는 순서대로 아래와 같이 진행할 수 있게 구현했다.
NotBlankGroup(@NotBlank) → PatternGroup(@Pattern) → SizeGroup(@Size)
3. 해당 Validation 어노테이션에 각각 groups = ‘인터페이스명’ 지정
Validation 어노테이션(@NotBlank, @Pattern, @Size)이 사용되는 곳(ex - DTO)에
groups 속성으로 해당 검증에 해당하는 인터페이스를 설정해준다.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserSignUpRequestDto {
@NotBlank(message = "이메일을 입력해주세요.", groups = NotBlankGroup.class)
@Pattern(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]+$",
message = "이메일 형식에 맞게 입력해주세요.", groups = PatternGroup.class)
private String email;
@NotBlank(message = "비밀번호를 입력해주세요.")
@Size(min = 4, max = 12, message = "비밀번호는 4자 이상 12자 이하여야합니다.",
groups = SizeGroup.class)
private String password;
@NotBlank(message = "닉네임을 입력해주세요.", groups = NotBlankGroup.class)
@Pattern(regexp = "^[a-zA-Z0-9가-힣]+$", message = "닉네임은 숫자, 한글, 영어만 가능합니다.",
groups = PatternGroup.class)
@Size(min = 2, max = 10, message = "닉네임은 2자 이상 10자 이하여야합니다.",groups = SizeGroup.class)
private String nickname;
}
검증 어노테이션 각각에 groups로 그룹을 설정해주었다.
4. 컨트롤러 @Valid → @Validated로 교체
그룹 시퀀스를 사용하기 위해서는, 스프링에서 제공하는 @Validated로 어노테이션을 바꿔야한다.
또, 파라미터로 해당 시퀀스를 지정한 클래스를 설정해주면 된다.
@PostMapping("/sign-up")
public BaseResponse<UserSignUpResponseDto> signUp(
@RequestPart(required = false) MultipartFile profileImg,
@Validated(ValidationSequence.class) @RequestPart UserSignUpRequestDto userSignUpRequestDto) throws Exception {
Long createUserId = userService.signUp(userSignUpRequestDto, profileImg);
UserSignUpResponseDto userSignUpResponseDto = new UserSignUpResponseDto(createUserId);
return new BaseResponse<>(userSignUpResponseDto);
}
@Validated(ValidationSequence.class) 이렇게 지정해서 설정해주면 된다.
이렇게 하면, 검증하고 싶은 순서대로 검증되어 테스트 코드가 통과했다.
※ 아쉬운 점
위의 과정 중에서 다른 과정들은 그래도 코드를 한 줄 추가하거나, 수정하면 돼서
새로 검증 추가 시에나 유지보수 시에 불편함이 적어보였다.
하지만 3번 과정인, 각 검증 어노테이션에 groups로 해당 그룹을 지정해주는 부분에서,
검증하는 모든 필드 위에 지정해줘야하므로,
상당히 불편하고 귀찮게 느껴졌다.
지금은 작은 프로젝트라 검증 부분이 상당히 적은데,
규모가 큰 프로젝트라면 유지 보수가 상당히 어려워보였다.
만약, 그룹 이름이 NotBlankGroup → NotBlankCheckGroup으로 바뀌게 되면
모든 @NotBlank의 이름을 변경해줘야하는 것이다.
이러한 부분때문에, 구글링을 해서 원리를 이해한 뒤에 사용하기가 싫어졌는데
지금은 규모가 작은 사이드 프로젝트이고, 순서를 지정하는 방법이 내가 못 찾은 것인지
구글링을 해도 @Validated, @GroupSequence로 그룹 지정하는 것 밖에 안나와서
어쩔 수 없이 적용해보기로 했다.
Reference
https://dncjf64.tistory.com/302
'Spring > 기타' 카테고리의 다른 글
[Spring] @ResponseBody VS ResponseEntity<T> : 무엇을 사용할까? (1) | 2023.04.16 |
---|---|
[Spring] JdbcTemplate 스프링 빈은 어떻게 자동으로 등록될까?(feat.DataSource) (4) | 2023.04.16 |
[Spring] MockMvc @RequestPart MultipartFile 파일 테스트 시 에러 트러블 슈팅 (0) | 2023.01.06 |
[Spring] Postman multipart/form-data 여러 개 파일 보내기 (0) | 2023.01.05 |
[Spring] SpringBoot 테스트 시 @WebMvcTest와 @SpringBootTest의 차이 (0) | 2022.11.15 |