๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
BE/๐Ÿƒ Spring

[Validation] spring boot validation ์‚ฌ์šฉ๋ฒ•๊ณผ ์ข…๋ฅ˜

by ํ‹ด๋”” 2024. 11. 7.
๋ฐ˜์‘ํ˜•

Validation

Spring Boot์—์„œ๋Š” Validation์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ์—์„œ ์š”์ฒญ์‹œ ์ „์†กํ•œ ๋ฐ์ดํ„ฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.

 

์™œ Validation์„ ์‚ฌ์šฉํ• ๊นŒ

  • ์ฃผ๋กœ Controller์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ถ€ํ„ฐ ์ „์†ก๋œ ๋ฐ์ดํ„ฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•จ
  • ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, request์‹œ ๋“ค์–ด์˜ค๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ๊ฒ€์ฆ ์ฝ”๋“œ๋กœ ์ธํ•ด ์ฝ”๋“œ์˜ ์–‘์ด ๋งŽ์•„ ์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ ์„œ๋น„์Šค ๋กœ์ง์— ๋ฐฉํ•ด๋  ์ˆ˜ ์žˆ์Œ
  • ์š”์ฒญ ํ•ญ๋ชฉ์ด ๋งŽ์•„์งˆ ์ˆ˜๋ก ๊ฒ€์ฆ ๊ณผ์ •์ด ๊ธธ์–ด์ง
  • ์—ฌ๋Ÿฌ๋ช…์˜ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฒ€์ฆ ๋กœ์ง์„ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒฝ์šฐ ์ผ๊ด€์„ฑ์ด ์—†์–ด์งˆ ์ˆ˜ ์žˆ์Œ
  • Validation์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฒ€์ฆ ๋กœ์ง์„ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์ž‡์Œ
  • Validation์„ ์‚ฌ์šฉํ•ด์„œ ๊ฒ€์ฆ ๋กœ์ง์„ ๋‹จ์ˆœํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Œ

 

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด์„ฑ ์ถ”๊ฐ€

implementation 'org.springframework.boot:spring-boot-starter-validation'

 

Spring Boot์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ. ์ž…๋ ฅ ๊ฐ’ ๊ฒ€์ฆ์„ ์œ„ํ•œ ์˜์กด์„ฑ๋“ค์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค. 

Validation ์ข…๋ฅ˜

์–ด๋…ธํ…Œ์ด์…˜ ์„ค๋ช… ํŠน์ง•
@Size ๋ฌธ์ž์—ด ๊ธธ์ด ๊ฒ€์ฆ int ํƒ€์ž…์—์„œ ์‚ฌ์šฉ ๋ถˆ๊ฐ€
@NotNull null ๋ถˆ๊ฐ€  
@NotEmpty null, ""(๋นˆ ๋ฌธ์ž์—ด) ๋ถˆ๊ฐ€  
@NotBlank null, ""(๋นˆ ๋ฌธ์ž์—ด), " "(๊ณต๋ฐฑ ๋ฌธ์ž์—ด) ๋ถˆ๊ฐ€  
@Pattern ์ •๊ทœ์‹ ๊ฒ€์ฆ  
@Max ์ตœ๋Œ€๊ฐ’  
@Min ์ตœ์†Œ๊ฐ’  
@Past ๊ณผ๊ฑฐ ๋‚ ์งœ์ธ์ง€ ๊ฒ€์ฆ Date ํƒ€์ž…์—์„œ ์‚ฌ์šฉ. request ์ „์†ก์‹œ "yyyy-MM-dd" ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉ

 

https://jakarta.ee/specifications/bean-validation/3.0/jakarta-bean-validation-spec-3.0.html#builtinconstraints

 

Jakarta Bean Validation specification

BeanNode, PropertyNode and ContainerElementNode host getContainerClass() and getTypeArgumentIndex(). If the node represents an element that is contained in a container such as Optional, List or Map, the former returns the declared type of the container and

jakarta.ee

  • Validation์—์„œ ์ œ๊ณตํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ ์ŠคํŽ™์€ jakarta ๋ฌธ์„œ์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ
  • Built-In Constraint definitions ์„น์…˜ ํ™•์ธ

 

์˜ˆ์‹œ์ฝ”๋“œ

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRegisterRequest {
    @NotBlank(message = "username์€ ๋นˆ ๊ฐ’์ด ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
    @Size(min = 4, max = 10, message = "username์€ 4~10์ž๋ฆฌ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
    private String username;

    private String password;

    @Min(value = 19, message = "age๋Š” 19์‚ด ์ด์ƒ ๋ถ€ํ„ฐ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.")
    private Integer age;

    @Past(message = "์ƒ๋…„์›”์ผ์€ ๊ณผ๊ฑฐ์˜ ๋‚ ์งœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
    private Date birth;

    @Pattern(regexp = "^01([0|1|6|7|8|9])-?([0-9]{3,4})-?([0-9]{4})$", message = "์œ ํšจํ•œ ํœด๋Œ€ํฐ ํ˜•์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.")
    private String phoneNumber;

    public User toEntity() {
        return User.builder()
                .username(username)
                .password(password)
                .role(Role.MEMBER)
                .build();
    }
}

 

  • request ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•˜๋ฏ€๋กœ ์ฃผ๋กœ entity๊ฐ€ ์•„๋‹Œ dto์— validation annotation์„ ์ถ”๊ฐ€

 

RequestBody

    @PostMapping
    public  String register(@Valid @RequestBody UserRegisterRequest request) {
        log.info("init : {}", request);
        return request.toString();
    }

 

  • @Valid ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์„œ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆ

RequestParam

@RestController
@RequiredArgsConstructor
@RequestMapping("api/v1/user")
@Validated // ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ์ƒ๋žต ๊ฐ€๋Šฅ
class UserApiController {
		@GetMapping(path = "/book")
        public String queryParam(
            @RequestParam("category") @Size(min = 2) String category,
            @RequestParam("issued-day") @Past  @DateTimeFormat(pattern = "yyyy-MM-dd") Date issuedDay
        ) {
            return category + " " + issuedDay;
        }
}

 

http://localhost:8080/api/book?category=fiction&issued-day=2020-01-01

 

PathVariable

 

@RestController
@RequiredArgsConstructor
@RequestMapping("api/v1/user")
@Validated // ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ์ƒ๋žต ๊ฐ€๋Šฅ
class UserApiController {
        @GetMapping(path = "/book/{category}/{issued-day}")
        public String queryParam(
            @PathVariable("category") @Size(min = 2) String category,
            @PathVariable("issued-day") @Past @DateTimeFormat(pattern = "yyyy-MM-dd") Date issuedDay
        ) {
            return category + " " + issuedDay;
        }
}

 

http://localhost:8080/api/book/fiction/2020-01-01

 

๊ธฐ์กด์—๋Š” RequestParam, PathVariable ์‚ฌ์šฉ์‹œ @Validated ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ์œผ๋‚˜ ์Šคํ”„๋ง๋ถ€ํŠธ 3.2์—์„œ ์• ๋…ธํ…Œ์ด์…˜์ด ์—†์–ด๋„ ์ž๋™์œผ๋กœ ์ธ์‹ํ•˜๋„๋ก ์—…๋ฐ์ดํŠธ ๋จ (์•„๋ž˜ ์ฐธ๊ณ  ์‚ฌ์ดํŠธ ์ฐธ๊ณ )

 

 

์ฐธ๊ณ  ์‚ฌ์ดํŠธ

https://mangkyu.tistory.com/379

 

[SpringBoot] ์Šคํ”„๋ง ๋ถ€ํŠธ3.2(SpringBoot3.2)์— ์ถ”๊ฐ€๋œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ(MethodValidator) ํŒŒํ—ค์น˜๊ธฐ

1. ์Šคํ”„๋ง ๋ถ€ํŠธ3.2์— ์ถ”๊ฐ€๋œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ(MethodValidator) ํŒŒํ—ค์น˜๊ธฐ[ ์Šคํ”„๋ง ๋ถ€ํŠธ3.2์— ์ถ”๊ฐ€๋œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ ํŒŒํ—ค์น˜๊ธฐ ]์Šคํ”„๋ง ๋ถ€ํŠธ 2๋ฅผ ๊ธฐ์ค€์œผ๋กœ, ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฐฉ๋ฒ•์ด ํฌ๊ฒŒ 2๊ฐ€์ง€

mangkyu.tistory.com

 

728x90
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€