해당 코드는 Github를 확인해주세요.
스프링은 컨트롤러에서 클라이언트에서 넘겨받은 값에 대한 검증을 JSR-303 기반으로 쉽고 강력하게 할 수 있습니다. 또 한 커스텀 한 어노테이션을 확장도 쉽게 구현할 수 있습니다.
아래에서 작성하는 어노테이션은 해당 이메일이 유니크한지 검증을 하고 유니크하지 않은 이메일일 경우 Bad Request를 응답하는 어노테이션 입니다.
어노테이션 정의
1 2 3 4 5 6 7 8 9 10 11 12
| @Documented @Constraint(validatedBy = EmailDuplicationValidator.class) @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface EmailUnique {
String message() default "Email is Duplication";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {}; }
|
Validator 로직 작성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Component @RequiredArgsConstructor public class EmailDuplicationValidator implements ConstraintValidator<EmailUnique, String> {
private final MemberRepository memberRepository;
@Override public void initialize(EmailUnique emailUnique) {
}
@Override public boolean isValid(String email, ConstraintValidatorContext cxt) {
boolean isExistEmail = memberRepository.existsByEmail(email);
if (isExistEmail) { cxt.disableDefaultConstraintViolation(); cxt.buildConstraintViolationWithTemplate( MessageFormat.format("Email {0} already exists!", email)) .addConstraintViolation(); } return !isExistEmail; } }
|
넘겨 받은 email이 존재하는지 조회하고 중복되느 값이면 예외 메시지를 추가하고 isValid(...)
메서드에서 false를 리턴합니다.
Test
API Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class SignUpRequest { @EmailUnique @Email private String email; }
public class MemberApi {
private final MemberRepository memberRepository;
@PostMapping public Member create(@RequestBody @Valid final SignUpRequest dto) {
return memberRepository.save(Member.builder() .email(dto.getEmail()) .build()); }
}
|
Test Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class MemberApiTest {
@Test public void signUp_test_이메일이_중복된_경우() throws Exception { final SignUpRequest dto = new SignUpRequest("yun@test.com");
final ResultActions resultActions = requestSignUp(dto);
resultActions .andExpect(status().isBadRequest()); }
private ResultActions requestSignUp(SignUpRequest dto) throws Exception { return mockMvc.perform(post("/members") .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(dto))) .andDo(print()); }
}
|
Response
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| { "timestamp": "2019-03-19T17:11:26.919+0000", "status": 400, "error": "Bad Request", "errors": [ { "codes": [ "EmailUnique.signUpRequest.email", "EmailUnique.email", "EmailUnique.java.lang.String", "EmailUnique" ], "arguments": [ { "codes": [ "signUpRequest.email", "email" ], "arguments": null, "defaultMessage": "email", "code": "email" } ], "defaultMessage": "Email yun@test.com already exists!", "objectName": "signUpRequest", "field": "email", "rejectedValue": "yun@test.com", "bindingFailure": false, "code": "EmailUnique" } ], "message": "Validation failed for object='signUpRequest'. Error count: 1", "path": "/members" }
|
테스트 코드를 실행해보면 EmailDuplicationValidator 로직이 정상 동작하는지 확인할 수 있습니다.