승인된 상품 수정 시 재승인 흐름 버그 수정
목차
APPROVED 상태인 상품을 수정하면 재승인 요청이 자동으로 올라가야 한다. 흐름 자체는 단순해 보이지만, 실제로는 상태 전이 로직과 SQL 집계, 화면 렌더링이 서로 물려 있어서 어느 한 곳이 틀리면 이상한 결과가 나온다. 이번에 딱 그 경우였다.
상품 상태를 간단히 정리하면 이렇다.
| 상태 | 설명 |
|---|---|
| DRAFT | 작성 중, 미제출 |
| PENDING | 승인 대기 중 |
| APPROVED | 승인 완료, 노출 중 |
| REJECTED | 반려됨 |
APPROVED 상품을 수정하면 PENDING 으로 돌아가고, 다시 승인 절차를 밟아야 한다. 이건 의도된 설계다. 문제는 이 전이 과정에서 내부 클래스가 상태를 제대로 핸들링하지 못하는 엣지 케이스가 있었다는 것. 조건 분기를 보면 DRAFT → PENDING 경로와 APPROVED → PENDING 경로를 같은 코드로 처리하고 있었는데, 두 경우가 실제로는 다른 컨텍스트를 가진다. APPROVED 상품 수정에는 기존 승인 이력이 붙어 있고, 그걸 고려하지 않으면 SQL 집계가 엇나간다.
버그의 구조
내부 클래스 로직, SQL 매퍼, 뷰 렌더링 세 군데를 건드렸다. 각각이 독립적으로 잘못된 게 아니라 서로 잘못된 가정을 공유하고 있었다.
내부 클래스에서는 상품이 재승인 대기 상태일 때 isReapproval 같은 플래그를 따로 세우지 않고 status == PENDING 조건 하나로 뭉뚱그려 처리하고 있었다. SQL 매퍼 쪽에서는 집계 쿼리에 approval_count 나 이전 승인 이력을 고려하는 조건이 빠져 있어서, 재승인 대기 건과 신규 승인 대기 건이 구분 없이 카운팅됐다. 화면에서는 이 숫자를 그대로 렌더링하니 당연히 어긋났다.
수정 방향은 세 가지였다.
- 내부 클래스에서 APPROVED → PENDING 전이를 명시적으로 분기
- SQL 매퍼에서 재승인 여부를 구분하는 조건 추가
- 뷰에서 상태 플래그에 따라 표시 분기
코드 패턴 예시를 간단히 보면, 수정 전에는 이런 식이었다.
if (product.getStatus() == ProductStatus.PENDING) {
// 신규/재승인 구분 없이 동일 처리
approvalService.request(product);
}
수정 후에는 진입 경로를 먼저 구분했다.
boolean isReapproval = product.getPreviousStatus() == ProductStatus.APPROVED;
if (isReapproval) {
approvalService.requestReapproval(product);
} else {
approvalService.request(product);
}
SQL 쪽도 비슷한 방향으로, 재승인 여부를 구분하는 컬럼 조건을 WHERE 절에 명시했다. 집계할 때 두 케이스를 섞으면 숫자 자체가 의미를 잃는다.
버그 수정할 때 챙기는 것들
증상만 보고 픽스하면 같은 버그가 다른 경로로 다시 온다. 이번처럼 상태 전이가 얽혀 있으면 특히 그렇다. 몇 가지 습관이 있다.
- 같은 분기 로직이 다른 경로에도 있는지 먼저 확인한다. 한 곳 고치고 끝이 아닐 수 있다.
- 수정 전에 현재 동작을 수치나 스크린샷으로 메모해 둔다. 수정 후 같은 케이스로 다시 확인할 때 비교 기준이 필요하다.
- 관련 화면이 있으면 숫자 cross-check 를 한다. 상세 화면과 목록 화면의 카운트가 다르면 어딘가 집계가 잘못된 것이다.
- 회귀 여부를 반드시 본다. 재승인 케이스를 고치면서 신규 승인 케이스가 깨지면 안 된다.
커밋은 논리적으로 독립된 단위로 쪼갠다. 이번에도 내부 클래스 수정, SQL 매퍼 수정, 뷰 수정을 커밋으로 분리했다. 한 커밋에 몰아넣으면 나중에 어느 변경이 문제였는지 찾기 훨씬 어렵다. 커밋 메시지는 "무엇을 바꿨는가" 보다 "왜 바꿨는가"를 담으려 한다. fix: 재승인 요청 로직 수정 보다 fix: APPROVED 상품 수정 시 신규/재승인 PENDING 혼합 집계 오류 쪽이 나중에 git log 볼 때 훨씬 낫다.
상태 머신이 있는 기능의 특성
이런 상태 전이 로직을 다룰 때 드는 생각은, 상태 머신이 복잡해질수록 각 전이에 명확한 이름을 붙이는 게 유지보수에 결정적이라는 것이다. status == PENDING 이 신규 대기인지 재승인 대기인지 코드 문맥을 모르면 알 수 없는 상황이 되면, 그 코드를 처음 보는 사람은 버그를 만들 가능성이 높아진다.
상태가 늘어날수록 이 비용은 지수적으로 올라간다. 비슷한 구조의 기능이 있다면, 상태 전이마다 명시적인 메서드나 플래그를 갖도록 미리 정리해 두는 게 낫다. 사후에 리팩터링하는 게 가능하긴 하지만, 그 시점엔 이미 다른 코드가 잘못된 가정 위에 쌓여 있는 경우가 많다.
supplier 사이드 기능은 화면에서 보면 단순해 보여도, 내부적으로 상태 전이 규칙과 권한 체크가 같이 엮여 있다. 어느 하나가 잘못되면 supplier 입장에서는 이유를 알 수 없는 이상한 동작이 나온다. 그게 불신으로 이어지기 전에 잡는 게 맞다. 이번 픽스는 그 지점에서 찾아낸 것이었고, 비슷한 패턴이 없는지 관련 코드를 한 번 더 훑고 마무리했다.
댓글 0
첫 댓글 달아줘.