카카오테크캠퍼스에서 선물하기 API를 구현하면서 단순히 DB에 저장되느냐보다, 어떤 계층에서 어떤 검증을 책임질지가 계속 고민이었다. 특히 상품 수정이나 위시리스트 추가처럼 이미 존재하는 데이터를 기준으로 판단해야 하는 API에서 이 문제가 자주 나왔다.
처음에는 DB 제약조건에 맡기면 되는 것 아닌가 생각할 수 있었다. 유니크 제약조건이나 FK 제약조건이 있으면 잘못된 데이터는 결국 DB에서 막힌다. 하지만 사용자에게 어떤 에러를 줄지, 비즈니스 규칙을 어디에 드러낼지까지 생각하면 DB 에러에만 기대기는 어려웠다.
이 글은 DB 제약조건과 애플리케이션 검증 사이에서 어떤 기준으로 판단했는지 정리한 기록이다.
처음 보인 문제
상품 수정 API를 만들 때는 먼저 해당 상품이 존재하는지 확인해야 했다. 방법은 크게 두 가지였다.
첫 번째는 바로 update 쿼리를 날리고, 변경된 row 수가 0이면 존재하지 않는 상품으로 판단하는 방식이다. 쿼리 수를 줄일 수 있다는 장점이 있다.
두 번째는 먼저 조회한 뒤, 존재하면 수정하는 방식이다. 쿼리는 하나 더 나갈 수 있지만, 비즈니스 흐름이 명확해졌다.
처음에는 성능만 보면 바로 update가 더 좋아 보였다. 하지만 API 관점에서는 존재하지 않는 상품을 수정하려는 요청과, 수정은 됐지만 값이 같아서 변화가 없는 상황을 구분해야 했다.
그래서 단순히 쿼리 수만 기준으로 보지 않고, 에러 의미를 명확히 표현할 수 있는지를 함께 확인했다.
원인을 확인한 과정
위시리스트에 상품을 추가할 때도 비슷한 고민이 있었다. 이미 담긴 상품을 다시 추가할 수 없게 해야 했고, 존재하지 않는 상품도 담을 수 없어야 했다.
이때 DB의 유니크 제약조건이나 FK 제약조건을 믿고 예외를 잡을 수도 있다. 하지만 그렇게 하면 어떤 비즈니스 규칙이 깨졌는지 애플리케이션 코드에서 잘 드러나지 않는다.
반대로 애플리케이션에서 먼저 확인하면 의도가 명확해졌다.
- 사용자가 존재하는 상품을 요청했는지 확인한다.
- 이미 위시리스트에 있는 상품인지 확인한다.
- 그에 맞는 도메인 예외를 반환한다.
쿼리가 조금 늘어날 수는 있지만, API 응답과 도메인 규칙이 더 선명해졌다.
해결 방안
DB 제약조건은 마지막 방어선으로 중요하다. 하지만 모든 검증을 DB에만 맡기면 애플리케이션의 의도가 흐려질 수 있다.
특히 사용자에게 내려줄 에러 메시지나 에러 코드가 중요한 API에서는, DB 예외를 그대로 해석하기보다 애플리케이션 계층에서 먼저 도메인 규칙을 확인하는 편이 나았다.
이때 확인한 기준은 아래와 같았다.
- 이 검증이 비즈니스 규칙인가
- 사용자가 이해할 수 있는 에러로 내려줘야 하는가
- DB 예외만으로 문제의 의미가 충분히 드러나는가
- 성능 비용이 현재 요구사항에서 감당 가능한가
모든 경우에 먼저 조회하는 것이 답은 아니지만, 사용자 요청의 의미를 명확히 검증해야 하는 경우에는 애플리케이션 계층 검증이 더 적절했다.
적용하면서 확인한 기준
검증을 추가하면 쿼리 수가 늘어날 수 있다. 그래서 무조건 애플리케이션에서 먼저 확인하는 방식이 항상 좋은 것은 아니다.
다만 이 프로젝트의 해당 API에서는 검증으로 늘어나는 비용보다, 명확한 에러 처리와 코드 의도가 더 중요하다고 판단했다. 트래픽 규모와 기능 성격을 고려했을 때, 한 번 더 조회하는 비용은 수용 가능하다고 봤다.
반대로 대량 처리나 성능 민감 구간이라면 다른 선택을 할 수 있었다. 중요한 것은 DB 제약조건과 애플리케이션 검증 중 하나를 절대적인 답으로 보는 것이 아니라, API의 목적과 실패 응답의 의미를 기준으로 판단하는 것이었다.
정리한 기준
이 작업 이후로는 검증 위치를 정할 때 아래 기준을 더 자주 확인한다.
- DB 제약조건은 마지막 방어선으로 둔다.
- 비즈니스 규칙은 가능하면 애플리케이션 코드에서 드러낸다.
- 사용자에게 의미 있는 에러를 내려줘야 하면 직접 검증한다.
- 성능 비용이 큰 경우에는 별도로 측정하고 판단한다.
검증은 단순히 잘못된 입력을 막는 일이 아니라, API가 어떤 계약을 제공하는지 드러내는 일이었다.
마무리
이 경험을 통해 DB가 막아줄 수 있다는 것과 애플리케이션이 책임져야 한다는 것은 다르다는 점을 확인했다.
DB 제약조건은 여전히 중요하다. 하지만 사용자 요청을 도메인 규칙에 맞게 해석하고, 실패 이유를 명확히 돌려주는 책임은 애플리케이션 계층에도 있었다.