개발 slecs

공급 체인 환불 검증 로직 추가

목차

공급 체인 환불 검증 로직 추가가 생각보다 손이 많이 갔다. 파일 수만 보면 백엔드 1개, 쿼리 XML 1개라서 작아 보이는데, 실제론 환불 가능 여부를 판단하는 분기가 여러 조건을 물고 늘어지는 구조라 영향 범위가 꽤 넓었음.

공급 체인에서 환불 검증은 단순 "환불 가능/불가능" 플래그 하나로 끝나는 문제가 아니다. 발주 상태, 입고 처리 여부, 정산 단계 등 선행 조건들이 서로 맞물려 있고, 그 조합마다 허용해야 할 케이스와 막아야 할 케이스가 달라진다. 이걸 여러 곳에서 제각각 검사하고 있었던 게 기존 코드의 문제였음. 중복 검증 코드가 흩어져 있으면 하나 고쳤을 때 다른 곳이 빠져나가는 구멍이 생기기 쉽다.

핵심 변경 내용

이번 작업에서 건드린 레이어는 두 곳이다.

레이어 파일 수 주요 변경
백엔드 로직 1개 환불 가능 여부 검증 로직 단일화
쿼리 (XML) 1개 검증에 필요한 공급 체인 상태값 조회 최적화

백엔드 쪽에서 한 일은 결국 "조건 판단을 한 함수로 모으는 것"이었다. 기존엔 서비스 레이어 여기저기서 비슷한 체크를 반복하고 있었는데, 그 로직들을 뜯어보면 미묘하게 다른 부분이 있었음. 동일한 케이스를 서로 다른 기준으로 판단하면 어떤 경로로 들어오느냐에 따라 결과가 달라질 수 있다. 이건 버그가 재현이 안 된다는 소리고, 운영에서 제일 무서운 패턴이다.

XML 쿼리 쪽은 상태값을 가져오는 조인 구조를 정리했다. 기존 쿼리가 불필요한 서브쿼리를 끼고 있어서 검증에 필요한 컬럼만 정확히 가져오도록 다듬었음.

검증 로직을 단일화할 때 주의할 것

이런 류의 리팩터링에서 흔히 하는 실수가 "다 모아놨으니 이제 완벽하다"고 안심하는 거다. 실제로는 기존에 분산돼 있던 로직 각각이 조금씩 다른 케이스를 처리하고 있을 수 있어서, 단순히 하나를 기준으로 나머지를 없애면 엣지 케이스가 날아간다.

이번 작업에서 실제로 마주친 패턴을 일반화하면 이렇다.

// 기존 패턴 - 서비스 곳곳에 흩어진 검증
// ServiceA.java
if (order.getStatus() == OrderStatus.CONFIRMED) {
    // 환불 처리...
}

// ServiceB.java
if (order.getStatus() == OrderStatus.CONFIRMED
        && supply.isSettled() == false) {
    // 환불 처리...
}

// 단일화 후 - 검증 조건을 한 곳에서 관리
public boolean isRefundable(Order order, Supply supply) {
    if (order.getStatus() != OrderStatus.CONFIRMED) {
        return false;
    }
    if (supply.isSettled()) {
        return false;
    }
    // 추가 조건은 여기서만 관리
    return true;
}

ServiceB에만 있던 isSettled() 체크가 ServiceA에는 없었던 것처럼, 기존 코드를 모두 펼쳐놓고 합집합으로 조건을 확정하는 과정이 필요하다. 하나씩 지워가면서 "이 조건이 없어져도 되나?"를 물어봐야 한다.

에러 메시지도 이번에 정리했다. 환불이 왜 안 되는지 알 수 없는 메시지보다는 어떤 조건에서 막혔는지 로그에 남기는 게 낫다. 새벽에 이슈가 터졌을 때 로그 파악에 걸리는 시간이 진짜 다르다.

if (supply.isSettled()) {
    log.warn("환불 불가 - 정산 완료 상태. orderId={}, supplyId={}",
        order.getId(), supply.getId());
    return false;
}

이 로그 한 줄 차이가 나중에 디버깅 시간을 30분은 줄여준다. 과장이 아님.

작업 회고

당장 기능이 추가된 것도 아니고 화면이 바뀐 것도 아니라서, 배포 나간다고 누군가 박수 치는 작업은 아니다. 그런데 이런 작업들이 안 쌓이면 나중에 진짜 기능 개발할 때 속도가 안 나온다. 검증 로직 하나 추가하려는데 "이거 건드리면 저기가 터질 것 같아서"라는 말이 나오기 시작하면 이미 늦은 거다.

이번 작업 하면서 다시 생각한 기준들:

  • 6개월 후의 나: 이 코드를 다시 볼 때 왜 이렇게 짰는지 바로 이해할 수 있는가
  • 다음 개발자: 공급 체인 도메인을 처음 보는 사람이 이 함수 하나만 보고도 환불 조건을 파악할 수 있는가
  • 새벽 3시: 장애 상황에서 이 로직이 문제를 빠르게 좁혀주는가
좋은 코드 나쁜 코드의 신호
읽으면 의도가 바로 보임 주석 없으면 이해 불가
검증 조건이 한 곳에 모여 있음 같은 조건이 여러 파일에 흩어짐
변경이 한 곳에만 영향 한 줄 바꾸면 세 군데 따라감
테스트 작성이 자연스러움 테스트하려면 구조 먼저 바꿔야 함

변경 범위가 작아도 다음 사람이 헷갈리지 않게 만드는 것, 그게 이번 작업의 실질적인 목표였음. 코드 줄 수보다 그 다음 변경이 얼마나 쉬워졌느냐가 기준이다.

댓글 0

첫 댓글 달아줘.