Java/Spring, Spring Boot

Request(DTO)와 Domain의 검증 책임

열심히 사는 우진 2023. 5. 4. 08:46
반응형

클라이언트가 보낸 정보를 매핑한 Request 객체(DTO).

이 객체는 @Valid 애너테이션을 비롯해 간단하게 검증할 수 있는 방법이 많습니다.

 

그런데 Reqeust의 검증 책임은 어디까지일까요?

 


 

1) Request는 비즈니스 로직 외 간단한 검증만 한다.

Layered architecture를 기준으로, Request의 위치는 Presentation layer입니다.

더 자세하게는 Client(View)와 Controller 사이에 위치합니다.

Layered architecture는 계층 별로 관심사를 분리해 유지보수와 테스트를 편리하게 만드는 것이 핵심입니다.

이중 Presentation layer의 관심사는 필요한 데이터를 클라이언트에게 전달하거나, 클라이언트가 보낸 요청을 서버 내부로 가져오는 것입니다.

따라서 Request는 어떠한 비즈니스 로직도 적용하지 않고, 요청이 부적절하지는 않은지만 검증하는 것이 가장 상식적인 판단일 것입니다. (null 가능성 체크 등)

 

하지만 이 또한 Layered architecture를 만족하는 선에서의 정답일 뿐입니다.

어떤 상황에서는 맹점이 존재할 수도 있습니다.

 

 

2) Request가 도메인 검증 책임을 갖는다.

유연한 검증 책임

Image URL에 규칙을 추가하는 예시를 하나 들어보겠습니다.

class Product {
	private final String imageUrl;
	...
}

도메인 객체인 Product가 imageUrl이라는 필드를 갖는데,

해당 필드의 앞에 반드시 https 가 붙어야된다는 규칙이 추가되었다고 가정해보겠습니다.

Domain 객체의 검증 로직에 해당 내용을 추가한다면,

그 전의 기준이 적용된 DB의 데이터를 꺼내와 Domain 객체를 생성할 수가 없게 됩니다.

imageUrl에 https가 붙어야 한다는 것은 Domain 로직이 확실한데 말이죠.

 

이것을 해결하기 위해 아래와 같은 복잡한 해결책을 택해야 할 수도 있습니다.

  • 데이터베이스 수정 (마이그레이션 or 필드 추가)
  • 새로운 Domain 객체 생성

 

반면에, Request에 검증 책임을 추가한다면 비교적 쉽게 문제를 해결할 수 있습니다.

또, 검증 기준이 바뀌는 경우에도 쉽게 대응할 수 있습니다.

Request(DTO)만을 검증하는 Validator 객체를 만들 수도 있고,

Product를 검증하는 ProductValidator를 상속받거나 구현한 객체를 만들 수도 있습니다.

 

 

철저한 검증

안전한 데이터가 그 어떤 것보다 중요한 환경일 때 (금융 등의 도메인)

요청이 서버에 들어와서 응답을 반환하기까지,

서버 내부에 안전한 데이터만 유지시키고 싶을 수 있습니다.

 

Request에서 Web 환경만을 고려한 데이터를 받는다면,

Domain 객체가 생기기 전까지 Presentation ~ Business layer에 걸쳐

올바르지 않은 데이터가 존재할 수 있습니다.

이것이 잘못 사용되어 Domain에서의 검증을 거치지 않고 DB에 저장되는 등의 행위가 일어난다면,

운영 중인 서비스에 문제가 발생할 수 있습니다.

 

이런 경우, Request에서부터 Domain 기준으로 검증을 한다면

웹 요청 ~ 반환 전체 cycle에 대해 안전한 데이터만 존재하도록 만들 수 있습니다.

또한, 특정 요청에 대해 특수한 검증 로직이 적용되어야 한다면

Request에서 간단하게 수행할 수도 있습니다.

 

하지만 중복된 검증 로직을 별도로 작성하는 것은 유지보수(동기화)가 너무 어려울 수 있습니다.

이때, Domain에서 사용하는 Validator 객체를 사용하거나, 그것을 구현, 상속한 객체를 사용함으로써

관리를 일원화할 수 있습니다.

또한 테스트도 용이해집니다.

 

 

3. Domain과 Request의 검증 책임 정리

제 생각을 토대로 책임을 정리해보면 아래와 같습니다.

Domain 필수

  • Domain 객체로서 변하지 않을 성질
    • ex: Product의 price는 0 이상이어야 한다.
  • 논리를 통한 검증
    • ex: 로또 번호는 서로 같을 수 없다.
  • 저장된 정보와의 비교
    • ex: DB에 저장된 ID와의 중복 비교

 

Request 필수

  • 요청에 대한 올바른 정보인지
    • null 체크
    • 형식 비교 (전화번호, 이메일 등)

 

Request의 검증 로직 확대

  • 가능한 한 Domain에서 해결한다.
    • 10,000원 이하의 핫딜 상품 등록 → Request에서 검증하기 보다, Product를 구현한 HotDealProduct 생성 등
    • imageUrl에 https 필수 → DB 수정을 통해 해결 등
  • 추가될 검증 로직이 요청에 종속되고 변경 가능성이 높은 경우, Request에서 해결할 수 있다. (절충)
    • 특히 Domain의 검증 로직이 변경되기 매우 어려운 경우
  • 서버 내부에 안전한 데이터만 두고 싶을 때
    • 중복된 검증 로직을 동기화할 수 있는 방안이 마련되어야 함 (테스트 등)

 

 


 

결국 문제를 해결하기 위한 여러가지 방안을 떠올리고,

지금과 미래를 고려하여 최적의 방안을 선택하는 것이 중요하다는 생각이 듭니다.

 

의견과 지적을 환영합니다.

감사합니다!

 

 

 

 

 

 
반응형