모든 글

경계에서 검증하고 핵심 흐름은 단순하게

API 입력을 프레젠테이션 경계에서 온전한 비즈니스 값으로 바꾸고, 널을 안쪽에 넘기지 않으며, 저장 데이터는 들어올 때 검증하는 방법입니다.

출처 및 AI 안내: 이 글은 제미니의 개발실무 유튜브를 기반으로 작성되었습니다. gpt-5.6-sol 모델을 사용해 생성·편집했습니다.

같은 값을 모든 계층이 같은 방식으로 의심하면 검증 코드가 반복된다. 각 관심사가 들어오는 경계에서 검증을 끝내고, 안쪽에는 온전한 객체를 넘겨 핵심 흐름이 앞선 경계 작업을 신뢰하게 하는 편을 선호한다.

HTTP 요청에서는 프레젠테이션 계층이 첫 번째 경계다. API 스펙을 소유하므로 요청을 비즈니스 값으로 바꾸기 전에 그 스펙을 만족하지 않는 입력을 거절해야 한다.

변환 결과를 바로 쓸 수 있는 객체로 만든다

NewOrderRequest에 외부 요청에서 생략 가능한 널 필드가 있다고 하자. 이 널이 서비스와 중요한 도메인 객체까지 이동하게 두지 않는다. 프레젠테이션 계층에서 확인하고, 허용된 변환을 적용한 뒤, 필수 값이 채워진 안쪽 객체를 만든다.

스펙 규칙도 이곳에 둔다. API가 사용자 키의 최대 길이를 정했다면 요청 검증기가 바로 실패를 알려줄 수 있다. 단순히 빈 문자열을 확인하는 수준을 넘어 API가 공개한 계약을 지키는 일이다.

나는 규칙이 한곳에 드러나고 테스트하기 쉽다는 이유로 명시적인 검증 코드를 선호하는 편이다. 어노테이션 기반 검증도 가능한 선택이다. 취향과 테스트 방식의 차이지 어노테이션이 잘못됐다는 주장은 아니다.

서비스마다 같은 성벽을 다시 세우지 않는다

요청을 검증하고 변환했다면 서비스가 같은 빈 값, 길이, 널 검사를 이유 없이 반복할 필요는 없다. 서비스는 유스케이스의 흐름을 담당한다. 다만 데이터를 조회해야만 알 수 있는 검증은 안쪽에 남는다. 요청한 상품이 존재하지 않는다는 사실은 요청 객체만 보고 확인할 수 없다.

다음처럼 나누면 이해하기 쉽다.

  • 프레젠테이션 검증은 외부 스펙을 확인한다.
  • 변환은 안쪽에서 쓸 온전한 값을 만든다.
  • 구현이나 도메인 작업은 실행 중 새로 발견되는 사실을 확인한다.

마지막 검증이 놓이는 위치는 프로젝트의 레이어에 따라 달라진다. 내가 쓰는 구조에서는 바깥 검사를 되풀이하는 서비스보다 저장소와 비즈니스 객체를 조율하는 구현 레이어에 이런 검증이 많이 나타난다.

신뢰되는 상태가 되는 순간 검증한다

저장 데이터에도 같은 원칙을 적용할 수 있다. 상품을 넣을 때 기본 형태를 검증했다면 읽을 때마다 같은 형태를 처음부터 다시 증명할 필요는 적다. 쓰기 경계가 잘못된 상태의 유입을 막아야 한다. 조회에서는 데이터 부재와 현재 행위에 필요한 비즈니스 조건을 여전히 처리하지만, 객체 전체를 매번 다시 검증하지는 않는다.

이 판단은 경계가 실제로 지켜진다는 조건에 기대고 있다. 다른 경로가 검증되지 않은 행을 쓸 수 있다면 안쪽이 신뢰해도 된다는 전제는 틀린다. 신뢰는 얻은 불변 조건이지 불안정한 입력을 무시하기 위한 구실이 아니다.

목표는 모든 검증을 컨트롤러에 몰아넣는 것이 아니다. 각 규칙을 그 규칙의 주인이 있는 경계에서 끝내는 것이다. 외부 모양은 API 가장자리에서, 저장 상태는 들어올 때, 유스케이스에서 드러나는 사실은 실행 중에 확인한다. 이 책임이 나뉘면 안쪽 코드는 모든 성벽을 복제하지 않고 실제 흐름을 보여줄 수 있다.