Spring

[Spring] 필드의 유효성 검증 3가지 방법

2025. 3. 27. 16:22
목차
  1. ✅ 1. DTO 레벨에서 @Valid 어노테이션 활용
  2. 📌 DTO에서 유효성 검증
  3. 📌 Controller에서 @Valid 사용
  4. ✅ 장점
  5. ❌ 단점
  6. ✅ 2. Value Object에서 검증 로직을 캡슐화
  7. 📌 Value Object 정의
  8. 📌 User 엔티티에서 Value Object 사용
  9. ✅ 장점
  10. ❌ 단점
  11. ✅ 3. User 엔티티 생성 시, 생성자 내부에서 검증
  12. ✅ 장점
  13. ❌ 단점
  14. 🎯 결론: 어떤 방법이 가장 좋은가?
728x90
반응형

도메인 필드의 유효성 검증(validation)을 할 때 가장 좋은 방법은 각 필드에 대한 검증을 한 곳에서 관리하는 거야.
이를 위해 3가지 방법을 고려할 수 있어:

  1. DTO 레벨에서 @Valid 어노테이션을 활용한 검증 (Spring Validation)
  2. Value Object를 활용하여 검증 로직을 캡슐화
  3. User 엔티티 생성 시, 생성자 내부에서 검증 실행

User 도메인을 예로 들어서 설명해볼게.


✅ 1. DTO 레벨에서 @Valid 어노테이션 활용

가장 일반적인 방식으로, Spring의 @Valid 및 @NotBlank, @Size, @Email 등을 활용하는 방법이야.

📌 DTO에서 유효성 검증

import jakarta.validation.constraints.*;
@Getter
public class UserRequestDto {
@Email(message = "Invalid email format")
@NotBlank(message = "Email cannot be empty")
private String email;
@Size(min = 8, message = "Password must be at least 8 characters long")
@NotBlank(message = "Password cannot be empty")
private String password;
@NotBlank(message = "Name cannot be empty")
private String name;
@NotBlank(message = "Address cannot be empty")
private String address;
}

📌 Controller에서 @Valid 사용

@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
public ResponseEntity<UserResponseDto> createUser(@Valid @RequestBody UserRequestDto dto) {
UserResponseDto response = userService.createUser(dto);
return ResponseEntity.ok(response);
}
}

✅ 장점

  • 코드가 간결하고 유지보수하기 쉬움
  • Spring Boot의 검증 기능을 최대한 활용 가능
  • 필드별로 검증 메시지를 쉽게 설정할 수 있음

❌ 단점

  • DTO에서만 검증이 이루어짐 → 즉, 도메인 객체(User)에서 직접 생성될 때는 유효성 검증이 적용되지 않음
  • DTO를 거치지 않고 직접 엔티티를 조작하면 검증이 무시될 수 있음

✅ 2. Value Object에서 검증 로직을 캡슐화

모든 필드에 대해 **VO (Value Object)**를 만들어서 내부에서 검증을 수행하는 방식이야.
이 방식은 도메인 계층에서 유효성 검증을 강제할 수 있어.


📌 Value Object 정의

 
@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Email {
private String value;
public Email(String value) {
if (value == null || !value.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
throw new IllegalArgumentException("Invalid email format");
}
this.value = value;
}
}
@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Password {
private String value;
public Password(String value) {
if (value == null || value.length() < 8) {
throw new IllegalArgumentException("Password must be at least 8 characters long");
}
this.value = value;
}
}
@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Name {
private String value;
public Name(String value) {
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
this.value = value;
}
}
@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Name {
private String value;
public Name(String value) {
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
this.value = value;
}
}

📌 User 엔티티에서 Value Object 사용

@Getter
@Entity
@Table(name = "users")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Embedded
private Email email;
@Embedded
private Password password;
@Embedded
private Name name;
@Embedded
private Address address;
private LocalDateTime createTime;
private LocalDateTime updateTime;
public User(Email email, Password password, Name name, Address address) {
this.email = email;
this.password = password;
this.name = name;
this.address = address;
this.createTime = LocalDateTime.now();
this.updateTime = LocalDateTime.now();
}
public void updateInfo(Name name, Address address) {
this.name = name;
this.address = address;
this.updateTime = LocalDateTime.now();
}
}

✅ 장점

  • 검증 로직이 엔티티 내부에서 수행되므로 비즈니스 로직과 일관성이 유지됨
  • 어디서든 User 객체를 생성할 때 유효성 검증이 강제됨
  • 불변성 보장 (Value Object는 생성 후 변경이 불가능)

❌ 단점

  • 모든 필드에 대해 VO를 만드는 것은 코드가 많아지고 복잡해질 수 있음
  • 엔티티가 복잡해질 수 있음

✅ 3. User 엔티티 생성 시, 생성자 내부에서 검증

위 방식이 너무 무겁다고 생각되면, 엔티티의 생성자 내부에서 검증하는 방법도 가능해.
이 경우, Value Object를 만들지 않고, User 내부에서 직접 검증 로직을 처리할 수 있어.

 
@Getter
@Entity
@Table(name = "users")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String password;
private String name;
private String address;
private LocalDateTime createTime;
private LocalDateTime updateTime;
public User(String email, String password, String name, String address) {
validateEmail(email);
validatePassword(password);
validateName(name);
validateAddress(address);
this.email = email;
this.password = password;
this.name = name;
this.address = address;
this.createTime = LocalDateTime.now();
this.updateTime = LocalDateTime.now();
}
private void validateEmail(String email) {
if (email == null || !email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
throw new IllegalArgumentException("Invalid email format");
}
}
private void validatePassword(String password) {
if (password == null || password.length() < 8) {
throw new IllegalArgumentException("Password must be at least 8 characters long");
}
}
private void validateName(String name) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
}
private void validateAddress(String address) {
if (address == null || address.trim().isEmpty()) {
throw new IllegalArgumentException("Address cannot be empty");
}
}
}

✅ 장점

  • 검증 로직이 엔티티 내부에 있어 한눈에 보기 좋음
  • 코드가 상대적으로 간결함

❌ 단점

  • VO보다 재사용성이 떨어짐
  • 불변성(immutability)을 완벽히 보장할 수 없음

🎯 결론: 어떤 방법이 가장 좋은가?

✅ 유효성 검증을 강제해야 한다면 → "Value Object" 방식 추천
✅ DTO에서만 검증하면 충분하다면 → "Spring Validation(@Valid)" 방식 추천
✅ 간단한 검증이라면 → "User 생성자에서 검증" 방식 추천

🚀 상황에 따라 가장 적절한 방법을 선택하면 돼! 😃

 

나는 DTO, Controller에서 @Valid를 써서 들어올 때 검증하고, 필요한 경우만 생성자에서 검증하는 방법을 택함.

 

728x90
반응형
저작자표시 (새창열림)

'Spring' 카테고리의 다른 글

[Spring] Spring Cloud Eureka와 Zuul을 사용하여 마이크로서비스 생성하기  (1) 2025.04.11
[Spring] sqlExceptionHelper() is null 에러  (0) 2024.07.01
[Spring] 정적파일 경로  (0) 2024.04.05
[Spring] Spring MVC 설정(XML방식)  (0) 2024.04.05
[springboot] springboot 3.0의 새로운 점!  (1) 2024.01.11
  1. ✅ 1. DTO 레벨에서 @Valid 어노테이션 활용
  2. 📌 DTO에서 유효성 검증
  3. 📌 Controller에서 @Valid 사용
  4. ✅ 장점
  5. ❌ 단점
  6. ✅ 2. Value Object에서 검증 로직을 캡슐화
  7. 📌 Value Object 정의
  8. 📌 User 엔티티에서 Value Object 사용
  9. ✅ 장점
  10. ❌ 단점
  11. ✅ 3. User 엔티티 생성 시, 생성자 내부에서 검증
  12. ✅ 장점
  13. ❌ 단점
  14. 🎯 결론: 어떤 방법이 가장 좋은가?
'Spring' 카테고리의 다른 글
  • [Spring] Spring Cloud Eureka와 Zuul을 사용하여 마이크로서비스 생성하기
  • [Spring] sqlExceptionHelper() is null 에러
  • [Spring] 정적파일 경로
  • [Spring] Spring MVC 설정(XML방식)
우주유령
우주유령
우주유령
우주
우주유령
전체
오늘
어제
반응형
  • 분류 전체보기 (131)
    • javascript (28)
    • java (26)
    • Web (16)
    • React (7)
    • SQL (2)
    • Spring (20)
    • IntelliJ (6)
    • JSP (1)
    • Notion (1)
    • VScode (8)
    • Vue (3)
    • css (1)
    • tistory (1)
    • jpa (8)
    • 책 (1)

블로그 메뉴

  • 홈
  • 태그

공지사항

인기 글

최근 글

hELLO · Designed By 정상우.
우주유령
[Spring] 필드의 유효성 검증 3가지 방법
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.