• ABOUT
  • PORTFOLIO
  • POSTS
  • GUESTBOOK

ยฉ 2025-2026 BlueCool12 All rights reserved.

2025.07.17Spring

๐Ÿ‘ท @Valid๋กœ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ ๊ฐ’์„ ์„œ๋น„์Šค ๋กœ์ง์—์„œ if ๋ฌธ์œผ๋กœ ํ•˜๋‚˜์”ฉ ๊ฒ€์‚ฌํ•˜๋ฉด ๊ฒ€์ฆ ์ฝ”๋“œ๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋’ค์„ž์ด๊ณ , ์ปจํŠธ๋กค๋Ÿฌ๋งˆ๋‹ค ๋น„์Šทํ•œ ์ฝ”๋“œ๊ฐ€ ๋ฐ˜๋ณต๋œ๋‹ค.

Spring Boot์—์„œ๋Š” @Valid์™€ Bean Validation์„ ์‚ฌ์šฉํ•˜์—ฌ DTO์— ๊ฒ€์ฆ ๊ทœ์น™์„ ์„ ์–ธํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์ž˜๋ชป๋œ ์š”์ฒญ์„ ์ปจํŠธ๋กค๋Ÿฌ ์ง„์ž… ์ „์— ๊ฑธ๋Ÿฌ๋‚ด๊ณ , ์ผ๊ด€๋œ ํ˜•์‹์˜ ์—๋Ÿฌ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

1. Bean Validation ์˜์กด์„ฑ ์ถ”๊ฐ€

Spring Boot 2.3๋ถ€ํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ์ด spring-boot-starter-web์—์„œ ๋ถ„๋ฆฌ๋˜์—ˆ๋‹ค. @Valid ๊ธฐ๋ฐ˜ ๊ฒ€์ฆ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด spring-boot-starter-validation ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

[Gradle]

// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
}

[Maven]

// pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

์ด ์Šคํƒ€ํ„ฐ์—๋Š” Jakarta Bean Validation ํ‘œ์ค€ API์™€ ๊ตฌํ˜„์ฒด์ธ Hibernate Validator๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์–ด ๋ณ„๋„ ์„ค์ • ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Spring Boot 3.x๋ถ€ํ„ฐ๋Š” javax.validation ๋Œ€์‹  jakarta.validation ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.


2. DTO์— ๊ฒ€์ฆ ์ œ์•ฝ ์กฐ๊ฑด ์„ ์–ธ

ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ๋ฐ›์„ DTO ํ•„๋“œ์— ๊ฒ€์ฆ ๊ทœ์น™์„ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์„ ์–ธํ•œ๋‹ค.

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;โ€‹
import jakarta.validation.constraints.Size;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class CommentCreateRequest {
@NotNull(message = "postId๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.")
@Positive(message = "postId๋Š” 1 ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
private Long postId;

// null ํ—ˆ์šฉ
private Long parentId;

@NotBlank(message = "๋‹‰๋„ค์ž„์„ ์ž…๋ ฅํ•˜์„ธ์š”.")
@Size(max = 10, message = "๋‹‰๋„ค์ž„์€ ์ตœ๋Œ€ 10์ž์ž…๋‹ˆ๋‹ค.")
private String nickname;

@NotBlank(message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.")
@Pattern(regexp = "^\\d{4}$", message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 4์ž๋ฆฌ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
private String password;

@NotBlank(message = "๋Œ“๊ธ€ ๋‚ด์šฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
@Size(max = 250, message = "๋Œ“๊ธ€์€ ์ตœ๋Œ€ 250์ž๊นŒ์ง€ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
private String content;
}


ํ•„์ˆ˜๊ฐ’ ๊ฒ€์ฆ ์–ด๋…ธํ…Œ์ด์…˜ ๋น„๊ต

@NotNull, @NotEmpty, @NotBlank๋Š” ๋ชจ๋‘ ํ•„์ˆ˜ ๊ฐ’์„ ๊ฒ€์ฆํ•˜์ง€๋งŒ ๋นˆ ๋ฌธ์ž์—ด๊ณผ ๊ณต๋ฐฑ ๋ฌธ์ž์—ด์„ ๋‹ค๋ฃจ๋Š” ๋ฐฉ์‹์ด ๋‹ค๋ฅด๋‹ค.

์–ด๋…ธํ…Œ์ด์…˜

null

""

" "

์ ์šฉ ํƒ€์ž…

@NotNull

๊ฑฐ๋ถ€

ํ—ˆ์šฉ

ํ—ˆ์šฉ

๋ชจ๋“  ํƒ€์ž…

@NotEmpty

๊ฑฐ๋ถ€

๊ฑฐ๋ถ€

ํ—ˆ์šฉ

String, ๋ฐฐ์—ด, ์ปฌ๋ ‰์…˜, Map

@NotBlank

๊ฑฐ๋ถ€

๊ฑฐ๋ถ€

๊ฑฐ๋ถ€

String

๋ฌธ์ž์—ด ํ•„์ˆ˜ ๊ฐ’์—๋Š” ๊ณต๋ฐฑ ์ž…๋ ฅ๊นŒ์ง€ ๋ง‰๋Š” @NotBlank๋ฅผ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.


๋ฒ”์œ„ / ํ˜•์‹ ๊ฒ€์ฆ ์–ด๋…ธํ…Œ์ด์…˜

๊ฐ’์˜ ํฌ๊ธฐ, ๋ฒ”์œ„, ํ˜•์‹, ์‹œ์ ์„ ์ œํ•œํ•  ๋•Œ๋Š” ํ•„๋“œ ํƒ€์ž…์— ๋งž๋Š” ์–ด๋…ธํ…Œ์ด์…˜์„ ์„ ํƒํ•œ๋‹ค.

์–ด๋…ธํ…Œ์ด์…˜

๊ฒ€์ฆ ๋‚ด์šฉ

์ ์šฉ ํƒ€์ž…

@Size(min, max)

๊ธธ์ด, ํฌ๊ธฐ๊ฐ€ ๋ฒ”์œ„ ์•ˆ์— ์žˆ๋Š”์ง€

String, ๋ฐฐ์—ด, ์ปฌ๋ ‰์…˜, Map

@Min(value) / @Max(value)

์ง€์ •ํ•œ ๊ฐ’ ์ด์ƒ / ์ดํ•˜์ธ์ง€

์ •์ˆ˜ ํƒ€์ž…, BigDecimal

@Positive / @PositiveOrZero

0๋ณด๋‹ค ํฐ์ง€ / 0 ์ด์ƒ์ธ์ง€

์ˆซ์ž ํƒ€์ž…

@Past / @PastOrPresent

๊ณผ๊ฑฐ์ธ์ง€ / ๊ณผ๊ฑฐ ๋˜๋Š” ํ˜„์žฌ์ธ์ง€

๋‚ ์งœ, ์‹œ๊ฐ„ ํƒ€์ž…

@Future / @FutureOrPresent

๋ฏธ๋ž˜์ธ์ง€ / ๋ฏธ๋ž˜ ๋˜๋Š” ํ˜„์žฌ์ธ์ง€

๋‚ ์งœ, ์‹œ๊ฐ„ ํƒ€์ž…

@Pattern(regexp)

์ •๊ทœ ํ‘œํ˜„์‹๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€

String

@Email

์ด๋ฉ”์ผ ํ˜•์‹์ธ์ง€

String

์ด ์–ด๋…ธํ…Œ์ด์…˜๋“ค์€ null์„ ์œ ํšจํ•œ ๊ฐ’์œผ๋กœ ํŒ๋‹จํ•œ๋‹ค. ๊ฐ’์ด ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•˜๋Š” ํ•„๋“œ๋ผ๋ฉด @NotNull์ด๋‚˜ @NotBlank๋ฅผ ํ•จ๊ป˜ ์„ ์–ธํ•œ๋‹ค.


3. ์ปจํŠธ๋กค๋Ÿฌ์—์„œ @Valid๋กœ ๊ฒ€์ฆ ์‹คํ–‰

DTO์— ์ œ์•ฝ ์กฐ๊ฑด์„ ์„ ์–ธํ•ด๋„ ๊ทธ ์ž์ฒด๋กœ๋Š” ๊ฒ€์ฆ์ด ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์— @Valid๋ฅผ ๋ถ™์—ฌ์•ผ Spring์ด ์š”์ฒญ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ฆํ•˜๋ฉฐ, ์ฃผ๋กœ @RequestBody / @ModelAttribute์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค.

@PostMapping
public CommentCreateResponse createComment(
@Valid @RequestBody CommentCreateRequest request
) {
// ...์ปจํŠธ๋กค๋Ÿฌ ๋กœ์ง
}

์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด Spring์€ ์š”์ฒญ ๋ฐ์ดํ„ฐ๋ฅผ DTO๋กœ ๋ฐ”์ธ๋”ฉํ•œ ๋’ค Bean Validation์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ œ์•ฝ ์กฐ๊ฑด์„ ํ•˜๋‚˜๋ผ๋„ ์œ„๋ฐ˜ํ•˜๋ฉด ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.


4. @Valid๋กœ ์ค‘์ฒฉ ๊ฐ์ฒด์™€ ์ปฌ๋ ‰์…˜ ๊ฒ€์ฆ

Bean Validation์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ์ฒด์˜ ์ง์ ‘ ํ•„๋“œ๋งŒ ๊ฒ€์‚ฌํ•œ๋‹ค. DTO ์•ˆ์— ๋‹ค๋ฅธ ๊ฐ์ฒด๊ฐ€ ์žˆ๊ฑฐ๋‚˜ ์ปฌ๋ ‰์…˜ ์š”์†Œ๊นŒ์ง€ ๊ฒ€์‚ฌํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๊ฒ€์ฆ ๋ฒ”์œ„๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋„“ํ˜€์•ผ ํ•œ๋‹ค.

์ค‘์ฒฉ DTO ๊ฒ€์ฆ

DTO ์•ˆ์— ๋‹ค๋ฅธ DTO๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ํ•„๋“œ์— @Valid๋ฅผ ๋ถ™์—ฌ ๊ฒ€์ฆ์„ ํ•˜์œ„ ๊ฐ์ฒด๋กœ ์ „ํŒŒํ•œ๋‹ค.

@Getter
public class OrderRequest {
@Valid
@NotNull
private AddressRequest address;
}

@Getter
public class AddressRequest {
@NotBlank
private String city;

@NotBlank
private String street;
}

address ํ•„๋“œ์—์„œ @Valid๋ฅผ ๋นผ๋ฉด AddressRequest์˜ city, street ๊ฒ€์ฆ์€ ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. @NotNull์€ address ์ž์ฒด๊ฐ€ null์ธ ์š”์ฒญ์„ ๋ง‰๋Š”๋‹ค.


์ปฌ๋ ‰์…˜ ์š”์†Œ ๊ฒ€์ฆ

์ œ๋„ค๋ฆญ ํƒ€์ž… ์ธ์ž์— ์ œ์•ฝ ์กฐ๊ฑด์„ ์„ ์–ธํ•˜๋ฉด ์ปฌ๋ ‰์…˜์˜ ๊ฐ ์š”์†Œ๊นŒ์ง€ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค. Bean Validation 2.0๋ถ€ํ„ฐ ์ง€์›ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ ์š”์†Œ ์ œ์•ฝ(container element constraint) ๊ธฐ๋Šฅ์ด๋‹ค.

@Getter
public class PostRequest {
@NotEmpty
private List<@NotBlank String> tags;
}

@NotEmpty๋Š” ๋ฆฌ์ŠคํŠธ๊ฐ€ null์ด๊ฑฐ๋‚˜ ๋น„์–ด ์žˆ์ง€ ์•Š์€์ง€ ๊ฒ€์‚ฌํ•˜๊ณ , @NotBlank๋Š” ๋ฆฌ์ŠคํŠธ์˜ ๊ฐ ์š”์†Œ๊ฐ€ null, ๋นˆ ๋ฌธ์ž์—ด, ๊ณต๋ฐฑ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค.


5. @Valid ๊ฒ€์ฆ ์‹คํŒจ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

@Valid ๊ฒ€์ฆ์ด ์‹คํŒจํ•˜๋ฉด ์š”์ฒญ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š” ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๋ฐ”์ธ๋”ฉ ๋ฐฉ์‹

๋ฐœ์ƒ ์˜ˆ์™ธ

@RequestBody

MethodArgumentNotValidException

@ModelAttribute

BindException

@RequestParam, @PathVariable ๊ฐ™์€ ๋‹จ์ผ ๊ฐ’ ๊ฒ€์ฆ์€ @Valid๊ฐ€ ์•„๋‹Œ ๋ฉ”์„œ๋“œ ๊ฒ€์ฆ์œผ๋กœ ๋™์ž‘ํ•˜๋ฉฐ, ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋„ ๋‹ค๋ฅด๋‹ค.

์˜ˆ์™ธ๋ฅผ ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด Spring Boot๊ฐ€ 400 Bad Request์™€ ๊ธฐ๋ณธ ์—๋Ÿฌ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ํด๋ผ์ด์–ธํŠธ์— ์›ํ•˜๋Š” ํ˜•์‹์˜ ์—๋Ÿฌ ์‘๋‹ต์„ ์ฃผ๋ ค๋ฉด ์ด ์˜ˆ์™ธ๋ฅผ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.


BindingResult๋กœ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ง์ ‘ ์ฒ˜๋ฆฌ

@Valid ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”๋กœ ๋’ค์— BindingResult๋ฅผ ์„ ์–ธํ•˜๋ฉด ๊ฒ€์ฆ์ด ์‹คํŒจํ•ด๋„ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋Œ€์‹  ์˜ค๋ฅ˜ ์ •๋ณด๊ฐ€ BindingResult์— ๋‹ด๊ธฐ๊ณ , hasErrors()๋กœ ์‹คํŒจ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

@PostMapping
public ResponseEntity<?> createComment(
@Valid @RequestBody CommentCreateRequest request,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest().body(bindingResult.getAllErrors());
}
return ResponseEntity.ok().build();
}

๊ฐ„๋‹จํ•œ API์—์„œ๋Š” ๋น ๋ฅด๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ปจํŠธ๋กค๋Ÿฌ๋งˆ๋‹ค ๊ฐ™์€ ์ฒ˜๋ฆฌ ์ฝ”๋“œ๊ฐ€ ๋ฐ˜๋ณต๋˜๊ณ  ์—๋Ÿฌ ์‘๋‹ต ํ˜•์‹์„ ํ†ต์ผํ•˜๊ธฐ ์–ด๋ ต๋‹ค.


@RestControllerAdvice๋กœ ์ „์—ญ ์ฒ˜๋ฆฌ

@RestControllerAdvice๋กœ ๊ฒ€์ฆ ์˜ˆ์™ธ๋ฅผ ํ•œ๊ณณ์—์„œ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๊ฐ™์€ ํ˜•์‹์˜ ์—๋Ÿฌ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๊ฒ€์ฆ ์ฒ˜๋ฆฌ ์ฝ”๋“œ๋ฅผ ์—†์•จ ์ˆ˜ ์žˆ๋‹ค.

@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidationException(
MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.toList();
return ResponseEntity.badRequest().body(errors);
}
}

Spring 5.3๋ถ€ํ„ฐ MethodArgumentNotValidException์€ BindException์„ ์ƒ์†ํ•œ๋‹ค. @ModelAttribute ์š”์ฒญ์˜ ๊ฒ€์ฆ ์˜ˆ์™ธ๊นŒ์ง€ ํ•จ๊ป˜ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด BindException์„ ํ•ธ๋“ค๋Ÿฌ ๋Œ€์ƒ์œผ๋กœ ์ง€์ •ํ•œ๋‹ค.

ๅ‰ใฎ่จ˜ไบ‹
๐Ÿ”Ž Next.js๋ฅผ ์ด์šฉํ•œ SEO ์ตœ์ ํ™”
ๆฌกใฎ่จ˜ไบ‹
๐ŸŒœ Next.js๋กœ ๋‹คํฌ๋ชจ๋“œ ๊ตฌํ˜„ํ•˜๊ธฐ (next-themes)
่ฃ…้ฃพใƒญใ‚ด