@valid vs @validated

📌@Valid, @Validated

우리가 웹사이트에서 회원가입을 할 때, 이런 걸 검사해야 합니다.


✔ 이름을 입력했는지?
✔ 나이가 너무 어리진 않은지?
✔ 이메일 형식이 맞는지?

 

이런 검사를 백엔드(Spring) 에서 자동으로 해주는 게 @Valid랑 @Validated입니다.

@Valid 사용법 (기본 검사)

예를 들어 회원가입할 때, 이름이 비어있으면 안 되고, 나이는 18살 이상이어야 한다는 조건을 붙인다고 생각합시다.

UserDTO (데이터 저장하는 클래스)

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public class UserDTO {

    @NotBlank(message = "이름을 입력하세요!")
    private String name;

    @NotNull(message = "나이를 입력하세요!")
    @Min(value = 18, message = "나이는 18살 이상이어야 합니다.")
    private Integer age;

    // 기본 생성자, getter, setter 필요
}

UserController (검사 적용)

import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping("/create")
    public String createUser(@Valid @RequestBody UserDTO user) {
        return "회원가입 완료! " + user.getName();
    }
}

 

📌 결과
✅ {"name": "홍길동", "age": 20} → 회원가입 성공
❌ {"name": "", "age": 17} → 이름을 입력하세요!, 나이는 18살 이상이어야 합니다.

 

@Validated 사용법 (그룹 검사)

예를 들어,

  • 회원가입할 때(CreateGroup)는 이름과 나이 둘 다 필수!
  • 정보 수정할 때(UpdateGroup)는 이름만 필수!

그룹 인터페이스 만들기

public interface CreateGroup {}  
public interface UpdateGroup {}
이 인터페이스는 단순히 "이름표" 역할만 하면 되기 때문에, 별도의 코드가 필요하지 않습니다.

 

UserDTO에 그룹 적용

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;

@Validated
public class UserDTO {

    @NotBlank(groups = {CreateGroup.class, UpdateGroup.class}, message = "이름을 입력하세요!")
    private String name;

    @NotNull(groups = {CreateGroup.class}, message = "나이를 입력하세요!")
    @Min(value = 18, groups = {CreateGroup.class}, message = "나이는 18살 이상이어야 합니다.")
    private Integer age;

    // 기본 생성자, getter, setter 필요
}

UserController에서 특정 그룹만 검사

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping("/create")
    public String createUser(@Validated(CreateGroup.class) @RequestBody UserDTO user) {
        return "회원가입 완료! " + user.getName();
    }

    @PutMapping("/update")
    public String updateUser(@Validated(UpdateGroup.class) @RequestBody UserDTO user) {
        return "회원정보 수정 완료! " + user.getName();
    }
}

 

📌 결과
✅ POST /create (회원가입)
{ "name": "홍길동", "age": 20 } → 회원가입 성공!
{ "name": "홍길동" } → ❌ "나이를 입력하세요!"

✅ PUT /update (정보 수정)
{ "name": "홍길동" } → 수정 성공!
{ "age": 20 } → ❌ "이름을 입력하세요!"

정리

✔ @Valid: 모든 필드 검사
✔ @Validated: 필요한 것만 골라서 검사

그룹이 필요 없으면 @Valid 사용
검사를 다르게 하고 싶으면 @Validated 사용

 

실사용 예제

DTO 클래스

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class UserRequestDto {
    
    @NotBlank(message = "이름은 필수 입력 값입니다.")
    @Size(min = 2, max = 30, message = "이름은 2자 이상 30자 이하로 입력해주세요.")
    private String name;

    @NotBlank(message = "이메일은 필수 입력 값입니다.")
    @Email(message = "유효한 이메일 형식을 입력해주세요.")
    private String email;

    @NotBlank(message = "비밀번호는 필수 입력 값입니다.")
    @Size(min = 6, message = "비밀번호는 최소 6자 이상이어야 합니다.")
    private String password;
}

 

컨트롤러

import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
@Validated
public class UserController {

    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@Valid @RequestBody UserRequestDto userRequestDto) {
        return ResponseEntity.ok("회원가입 성공: " + userRequestDto.getName());
    }
}

 

요청 및 성공시 응답

POST /users/register
Content-Type: application/json

{
  "name": "홍길동",
  "email": "hong@example.com",
  "password": "password123"
}
HTTP/1.1 200 OK
회원가입 성공: 홍길동

 

요청 및 실패시 응답

POST /users/register
Content-Type: application/json

{
  "name": "",
  "email": "not-an-email",
  "password": "123"
}
HTTP/1.1 400 Bad Request
{
  "message": "이름은 필수 입력 값입니다.",
  "message": "유효한 이메일 형식을 입력해주세요.",
  "message": "비밀번호는 최소 6자 이상이어야 합니다."
}

'Spring' 카테고리의 다른 글

@PostConstruct , @PreDestory  (0) 2025.03.26
스프링의 메세지, 국제화  (0) 2025.02.13
@Scheduled  (0) 2025.01.24
@Controller 와 @RestController  (2) 2025.01.22
ResponseEntity  (0) 2025.01.16