JUN0.DEV
JUN0.DEV

애플리케이션 계층에서 직접 검증이 필요한 이유

Published on
  • avatarJunyoung Yang

카카오테크캠퍼스에서 선물하기 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 제약조건은 여전히 필요하다. 하지만 사용자 요청을 도메인 규칙에 맞게 해석하고, 실패 이유를 알아보기 쉽게 돌려주는 일은 애플리케이션에서도 신경 써야 했다.