어플리케이션의 규모가 커지고, API의 종류가 다양해지면서 입력값 검증 로직이 여러 레이어에 흩어져 관리되는 문제가 발생했습니다.
특히 같은 도메인 필드에 대한 검증 코드가 각기 다른 DTO에 중복 작성되면서, 유지보수성과 일관성에 어려움이 뒤따랐습니다.
이러한 문제를 해결하기 위해, 검증 책임의 위치를 조정하고 통합해가는 과정을 다음과 같이 정리해보았습니다
검증 방식 발전 과정
1. Request에 대해서만 검증하자.
1-1. 구현 방식 : DTO에 대한 검증
일반적인 스프링 MVC 어플리케이션에서 입력값 검증을 위해 Jakarta Validation API
를 제공합니다.
입력값에 대한 검증을 위해 아래와 같이 해당 API를 사용했습니다.
public class MemberRegisterRequest {
@Length(min = 2, max = 10)
@Pattern(regexp = "^윤.*")
private String name;
}
예제인 만큼, 모든 이름은 '윤'으로 시작하도록 설정해봤습니다.
이렇게 구성함으로써, MemberRegisterRequest
에 유효성 검증을 완료할 수 있었습니다.
1-2. 문제 인식 : DRP 위베
이제, 다음과 같은 UPDATE ( PUT ) API가 추가됐다고 해봅시다.
같은 도메인에 대해 RequestDTO가 2개로 늘어나자, @Length(min = 2, max = 10)
@Pattern(regexp = "^윤.*")
어노테이션이 두 군데에 반복되기 시작헀습니다.
public class MemberRegisterRequest {
@Length(min = 2, max = 10)
@Pattern(regexp = "^윤.*")
private String name;
}
public class MemberNameUpdateRequest {
@NotNull
private final Long memberId;
@Length(min = 2, max = 10)
@Pattern(regexp = "^윤.*")
private final String name;
}
이에 따라, MemberName
에 대한 도메인 규칙을 적용하기 위해 두 DTO에 코드를 작성해야 했으며, 이는 DRY 원칙에 어긋나는 상황이라 판단, 유지 보수성을 떨어뜨린다고 생각했습니다.
2. API Request들에 대해 중복되는 검증들을 제거하기 위해 도메인에서 검증하자.
2-1. 구현 방식 : Domain에서 통합된 검증
분산되는 검증로직을 통합하고자, 기존의 Jakarta Validation API
를 사용하지 않고, 내부 도메인 레벨에서 직접 검증을 수행했습니다.
도메인 모델을 다음과 같이 구현합니다.
@Getter
public class MemberName {
private final String name;
public MemberName(final String name) {
validate(name);
this.name = name;
}
public static void validate(final String name) {
if (name.length() < 20 && name.length() >= 2) {
throw new IllegalArgumentException();
}
}
}
이전의 MemberRegisterRequest
, MemberNameUpdateRequest
에선 더 이상 validation을 수행하지 않았습니다.
이를 통해, 도메인 관련 로직을 한 클래스로 집중하도록 하여, DRY 원칙에 부합할 수 있었습니다.
2-2. 문제 인식 : dirty 비즈니스 로직
도메인 모델이 위와 같이 간단한 로직들로 인해 더럽혀지는 상황이 발생했습니다.
3. 이미 존재하는 API를 활용하자. 중복을 제거하기 위해 메타 어노테이션을 활용하자.
3-1. 구현 방식 : 다시 DTO에서의 검증
결국, 다시 원점으로 돌아왔습니다. Jakarta Validation API
를 최대한으로 활용하던 (1)의 방식을 다시한번 돌아봤습니다. 기존 방식에서의 문제점은 도메인 규칙 변경에 따른 수정 작업이 MemberNameUpdateRequest
MemberRegisterRequest
모두에 일어나는 점이였습니다.
이를 해결하기 위해 도메인 규칙이 담긴 어노테이션을 구성합니다.
@Length(min = 2, max = 10)
@Pattern(regexp = "^윤.*")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy = {})
public @interface MemberNameRestriction {
String message() default "사용자 이름 형식 에러";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
이를 통해, 도메인 검증로직을 하나의 클래스로 집중할 수 있었고, 도메인 로직이 간단한 검증에 대해 신경쓰지 않도록 할 수 있었습니다.
3-2. 문제 인식
MemberRegisterRequest
, MemberNameUpdateRequest
에서 모두 동일한 검증을 수행할 수 있었지만,
API마다 다른 규약을 적용하긴 어려웠습니다.
어노테이션을 오버라이딩 할 수 있다면 이와같은 문제를 쉽게 해결할 수 있을 것으로 보이나, Jakarta Validation API
에서 해당 기능을 명시하지 않고 있는 만큼, 다른 방법을 더 고민해보도록 하겠습니다.
긴 글 읽어주셔서 감사합니다.
결과적으로 원하던 방식을 구현완료했습니다.
하지만, DTO에 약간의 수정이 필요한경우가 발생
참고
https://catsbi.oopy.io/c61342dd-26c5-4cd8-a462-957cd5787525
[5. 검증2 - Bean Validation
목차
catsbi.oopy.io](https://catsbi.oopy.io/c61342dd-26c5-4cd8-a462-957cd5787525)
https://meetup.nhncloud.com/posts/223
[Validation 어디까지 해봤니? : NHN Cloud Meetup
TOAST Cloud의 메시징 플랫폼 상품인 Notification은 메시지, 이메일 주소 형식, 수신/발신자의 번호 등 클라이언트의 입력값에 대해 많은 검증을 진행합니다.
meetup.nhncloud.com](https://meetup.nhncloud.com/posts/223)
https://www.sitepoint.com/effective-domain-model-validation-with-hibernate-validator/
[Effective Domain Model Validation with Hibernate Validator — SitePoint
A hands-on guide to domain model validation with the Java Beans Validation standard (JSR 303) and Hibernate Validator (Apache BVal is very similar.)
[Where we should put validation for domain model
I still looking best practice for domain model validation. Is that good to put the validation in constructor of domain model ? my domain model validation example as follows: public class Order ...
softwareengineering.stackexchange.com](https://softwareengineering.stackexchange.com/questions/119778/where-we-should-put-validation-for-domain-model)
https://www.reddit.com/r/DomainDrivenDesign/comments/1631nkr/double_validation_when_applying_ddd/
[From the DomainDrivenDesign community on Reddit
Explore this post and more from the DomainDrivenDesign community
https://stackoverflow.com/questions/62390008/bean-validation-inside-the-domain-abstraction
[Bean validation inside the domain abstraction
I have been reading Clean Architecture by R. C. Martin. I'm trying to make sense of it, by developing a small project where I'm trying to apply its concepts. One core concept in the domain layer i...
stackoverflow.com](https://stackoverflow.com/questions/62390008/bean-validation-inside-the-domain-abstraction)
'프로젝트 일기 > 한편의 수학 학원' 카테고리의 다른 글
데코레이터 패턴을 통한 비동기 처리의 안정적 도입 (0) | 2025.04.14 |
---|---|
이미지 업로드 API에서의 트랜잭션 병목과 비동기 처리 전략 (0) | 2025.04.13 |
벌크연산을 통한 쿼리 최적화 (0) | 2025.04.04 |
로그인 시도 횟수 제한 기능 구현하기 (0) | 2025.04.03 |