Select-then-Update vs 단일 Update: 성능보다 중요한 것
Select-then-Update vs 단일 Update: 성능보다 중요한 것
문제 상황
카카오테크캠퍼스에서 선물하기 API를 클론 코딩하다가 상품 정보 수정 API를 구현해야 했다. JDBC 환경에서 상품을 수정하는 방법은 크게 두 가지가 있었다.
방법 1: 단일 Update 쿼리
UPDATE product SET ... WHERE id = ?한 방에 처리- 쿼리 1회로 끝난다
방법 2: Select 후 Update
- 먼저 조회하고, 객체 수정 후 저장
- 쿼리 2회 발생한다
성능만 보면 방법 1이 유리하지만, 방법 2를 선택했다.
방법 1의 한계: affected rows의 모호함
단일 Update 쿼리의 결과는 affected rows, 즉 영향받은 행의 수다. 이 값이 0이면 “수정 실패”라고 판단할 수 있다.
문제는 왜 0인지 알 수가 없다는 점이다.
- ID가 존재하지 않아서인지
- 권한이 없어서인지
- 비즈니스 조건(status, deleted_at 등)이 맞지 않아서인지
클라이언트 입장에서 “존재하지 않는 상품입니다”와 “수정 권한이 없습니다”는 완전히 다른 의미인데, affected rows만으로는 이걸 구분할 수가 없었다.
방법 2의 장점: 명확한 예외 분리
조회 단계에서 도메인 객체를 확보하면, 검증 로직을 애플리케이션 레벨에서 분리할 수 있다.
- 존재 여부 확인 → 없으면
EntityNotFoundException - 권한 확인 → 없으면
AccessDeniedException - 상태 확인 → 삭제된 상품이면
IllegalStateException
이렇게 하니 각 예외가 명확히 분리되어서 디버깅도 쉬워지고, API 응답 메시지도 명확해졌다.
성능 오버헤드는 괜찮은가?
조회 쿼리 때문에 오버헤드가 걱정될 수 있는데, 다음과 같이 판단했다.
- PK 기반 인덱스 조회 비용은 낮다 — B-Tree 인덱스에서 PK 조회는 O(log n)이고, 보통 3~4번의 디스크 I/O면 충분하다.
- 대부분의 수정 요청은 성공한다 — 존재하지 않는 상품을 수정하려는 요청은 예외적인 상황이다.
- 성능 최적화는 병목 지점에서 하면 된다 — 실제로 성능 이슈가 발생했을 때 최적화해도 늦지 않다.
정리
| 기준 | 단일 Update | Select-then-Update |
|---|---|---|
| 성능 | 빠르다 | 쿼리 1회 추가 |
| 예외 구분 | 불가능 | 명확하다 |
| 유지보수 | 어렵다 | 쉽다 |
| 디버깅 | 불편하다 | 편하다 |
결론: 성능만 바라보는 것보다 예측 가능하고 안전한 시스템을 설계하는 게 더 중요하다는 걸 깨달았다.
카카오테크캠퍼스 3기 선물하기 API 클론 코딩 중 고민했던 내용 정리.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.