배경
비즈니스 로직을 구현하다 보면, 특정 로직이 수행되기 전에 입력값이나 상태를 검증해야 하는 경우가 있다. 어떤 작업을 처리하기 위해 입력 데이터가 특정 조건을 충족해야 하는 경우를 생각해볼 수 있다.
Spring Boot 을 사용하는 환경이라면 일반적으로 각 검증을 별도 Validator 로 구현하고, 이를 스프링 빈으로 등록하여 사용할 수 있다. 이 방식에서 Validation 결과를 Errors 객체에 담아 처리하거나, 조건을 만족하지 않으면 로직 수행을 반려하는 형태로 동작한다. 하지만 이 방식에는 몇 가지 한계점이 존재하는데, Validator 를 AND 조건으로 평가해야 하며, OR 조건 등 다른 Operation 이 필요한 경우에 대한 확장성이 좀 떨어진다.
입맛에 맞게 검증 로직을 처리하기 위해서 커스텀하게 Validator 를 구현해보았다. 최대한 가독성이 좋게 구현하고자 했고, SOLID 원칙도 지켜보려고 했다. 원하는 느낌의 validation 은 아래와 같이 구현하는 것이다.
validator1.and(validator2).or(validator3).and(validator4.not())
위와 같은 느낌으로 만들면 각 validator 에 검증 로직을 분리하여 넣을 수 있고, 서로 연결하기도 쉬울 것이다.
Composite 패턴으로 Validator 설계
먼저 인터페이스로 Validator 를 만든다. 모든 데이터 타입을 처리할 수 있도록 Generic 으로 만든다. 검증 로직을 표현하는 validate 함수가 있고, 메소드 체이닝 (method chaining) 방식으로 각 validator 를 연결할 수 있도록 구현한다. 이번 예시에서는 and, or, not 을 구현했으며, 필요한 경우에 더 많은 operation 을 구현할 수 있다.
각 AndValidator, OrValidator, NotValidator 는 별도 클래스로 구현해야 한다.
AndValidator, OrValidator, NotValidator
AndValidator 는 두 개의 validator 를 받아서 and 조건으로 검증을 한다.
OrValidator 는 두 개의 validator 를 받아서 or 조건으로 검증을 한다.
NotValidator 는 하나의 validator 를 받아서 반대 검증을 한다.
Concrete 클래스
인터페이스를 만들었으니, 이제 각 validator 의 구현체를 만들면 된다. 이번 예시에서는 PrimeNumberValidator, LargeNumberValidator, EvenNumberValidator 총 3개의 validator 를 만들어보려고 한다.
Validator 인터페이스를 상속받아서 클래스를 만들고, validate 메소드를 구현하면 된다. 구현 방식은 아래를 참고하자.
Validator 조립
Validator 를 사용해야 하는 곳에서 Validator 클래스를 선언하고, and, or, not operation 을 사용해서 각 validator 를 조립한다.
숫자 500 이 짝수이고 100 보다 커야 하고 소수가 아니어야 하는 조건에 충족하는지 판단한다.
(짝수) && (100보다 크고) && (소수가 아니고)
위 조건을 코드로 표현하면 아래와 같다.
'프로그래밍' 카테고리의 다른 글
Interface 를 활용한 Enum 리팩토링 (0) | 2023.06.21 |
---|---|
[번역] 반복적인 DTO-Domain 변환 처리하기 (feat. Kotlin Flow) (0) | 2023.03.31 |
[티스토리] highlight.js 를 활용해서 티스토리 코드블럭 꾸미기 (0) | 2023.02.10 |
[오픈소스] MapStruct 소개와 사용법 (0) | 2023.02.08 |
[디자인패턴] 실무에서 사용해본 전략 패턴 (Strategy Pattern) (0) | 2023.02.02 |
댓글