이번 9강에서는 글 작성 API에서 작성자(user1 고정) 방식 대신,
클라이언트가 username을 전달하면 그 값으로 작성자를 찾도록 변경했다.
그리고 @NotBlank, @Size 같은 파라미터 검증이 실패할 때 발생하는 예외인
ConstraintViolationException을 GlobalExceptionHandler에서 응답 처리하도록 추가했다.
user1로 고정username을 요청 파라미터로 받아서 작성자를 조회@RestController
@Validated
@RequestMapping("/api/v1/posts")
public class ApiV1PostController {
@PostMapping
public RsData<PostDto> write(
@Valid @RequestBody PostWriteReqBody reqBody,
@NotBlank @Size(min = 2, max = 30) String username
) {
Member actor = memberService.findByUsername(username).get();
Post post = postService.write(actor, reqBody.title(), reqBody.content());
...
}
}
username은 Request Parameter로 전달받는다.@NotBlank, @Size로 입력 검증을 걸었다.@Validated를 컨트롤러 클래스에 붙여서 파라미터 검증이 실제로 동작하게 했다.파라미터 검증(@NotBlank, @Size) 실패 시 발생하는 예외를 잡아서
일관된 응답으로 내려주도록 GlobalExceptionHandler에 추가했다.
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<RsData<Void>> handle(ConstraintViolationException ex) {
String message = ex.getConstraintViolations()
.stream()
.map(violation -> {
String field = violation.getPropertyPath().toString().split("\\.")[2];
String[] messageTemplateBits = violation.getMessageTemplate().split("\\.");
String code = messageTemplateBits[messageTemplateBits.length - 2];
String _message = violation.getMessage();
return "%s-%s-%s".formatted(field, code, _message);
})
.sorted(Comparator.comparing(String::toString))
.collect(Collectors.joining("\n"));
return new ResponseEntity<>(
new RsData<>("400-1", message, null),
BAD_REQUEST
);
}
username에 @NotBlank, @Size 붙이려면 @Validated가 꼭 필요해?✅ 거의 “필요하다”가 맞다.
@Valid는 주로 @RequestBody(DTO) 검증에서 동작한다.@NotBlank, @Size 같은 어노테이션을 메서드 파라미터(@RequestParam 등) 에 붙이면@Validated가 있어야 한다.즉,
@Valid @RequestBody DTO ✅ (Validated 없어도 동작하는 경우가 많음)@RequestParam String username에 @NotBlank @Size ✅ → 보통 @Validated 필요URL 예시:
POST {{baseURL}}/api/v1/posts?username=user3
이건 “쿼리 스트링(query string)”이고, 스프링에서는 보통 @RequestParam으로 받는다.
@PostMapping
public RsData<PostDto> write(
@RequestParam String username
) { ... }
근데 너 코드에서는 @RequestParam을 안 붙였는데도 받는 이유?
@RequestParam처럼 바인딩해주는 경우가 있다.✅ 추천 형태(명확하게):
public RsData<PostDto> write(
@Valid @RequestBody PostWriteReqBody reqBody,
@RequestParam @NotBlank @Size(min = 2, max = 30) String username
) { ... }
예: 요청 바디(JSON)에 username을 넣는 경우
{
"title": "제목",
"content": "내용",
"username": "user3"
}
이건 @RequestBody DTO로 받는다.
public record PostWriteReqBody(
@NotBlank String title,
@NotBlank String content,
@NotBlank @Size(min = 2, max = 30) String username
) {}
그리고 컨트롤러는:
public RsData<PostDto> write(@Valid @RequestBody PostWriteReqBody reqBody) {
String username = reqBody.username();
}
즉 정리하면:
@RequestParam@RequestBody DTO@Valid가 실패하면 어떤 예외가 터져?@Valid @RequestBody DTO 검증 실패MethodArgumentNotValidException 발생@ExceptionHandler(MethodArgumentNotValidException.class)
...
@NotBlank @Size 같은 “파라미터 검증” 실패ConstraintViolationException 발생@ExceptionHandler(ConstraintViolationException.class)
...
username을 파라미터로 받아 작성자를 결정하도록 변경@NotBlank, @Size 적용ConstraintViolationException을 전역 예외 처리로 응답 통일