결제 API 응답에 플랫폼 사용거래번호 항목 추가
목차
결제 API 응답에 welcomeUseTrstnId를 추가하는 작업인데, 겉보기엔 단순하다. 필드 하나 더 내려주는 것. 근데 웹훅 기반 결제 플로우에서 이 거래번호 하나가 없으면 운영이 꽤 골치 아파진다.
결제 플랫폼이 거래를 식별하는 ID와 우리 내부 주문 ID는 다르다. 사용자 입장에서 결제가 성공하든 실패하든, 플랫폼 측에서 "이 거래는 welcomeUseTrstnId로 관리한다"고 통보해주는 구조다. 이게 응답에 없으면 이후 취소·환불·재조회 시 플랫폼 API를 한 번 더 쳐야 하거나, 플랫폼 콘솔을 열고 수작업으로 매핑해야 한다. 처음엔 그냥 넘어갔다가 운영 중에 이 부재가 티가 나는 케이스가 생겼을 가능성이 높다.
결제 웹훅에서 거래번호 추적이 왜 중요한가
웹훅은 결제 플랫폼이 거래 상태 변경을 우리 서버로 밀어주는 방식이다. 보통 흐름은 이렇다.
- 사용자가 결제 요청 → 플랫폼으로 리다이렉트
- 플랫폼에서 결제 처리 완료 → 우리 webhook endpoint에 POST
- 우리 서버가 POST를 받아 DB 업데이트 → 결과 응답 반환
이 3번 단계에서 우리가 돌려주는 응답 바디에 welcomeUseTrstnId가 포함되어야, 이후 거래 추적 시 내부 주문번호와 플랫폼 거래번호를 별도 조회 없이 연결할 수 있다. 없으면 특정 건을 조회할 때 내부 주문번호 - 플랫폼 거래번호 간 매핑 작업을 따로 해야 한다.
장애 대응 상황에서 이게 없으면 특히 느려진다. "이 주문 취소해야 하는데 플랫폼 거래번호가 뭐야?"를 찾으러 다른 테이블을 뒤지거나 플랫폼 어드민에 로그인해야 하는 상황이 생긴다. 필드 하나가 이후의 운영 편의성을 꽤 크게 바꾸는 지점이다. 작은 수정이지만 인지 부하가 줄어드는 건 실제로 티가 난다.
webhook-api.jsp 수정에서 챙긴 것들
수정 파일은 webhook-api.jsp. Spring MVC + MyBatis 스택에서 웹훅 요청을 처리하는 진입점 역할을 한다. JSP가 뷰가 아니라 API 엔드포인트로 쓰이는 구조인데, 레거시 프로젝트에서 흔한 패턴이다. 응답은 JSON이나 플랫폼 지정 포맷으로 나간다.
추가 작업은 크게 세 줄기였다.
- SQL 조회 결과에
welcomeUseTrstnId컬럼 포함 확인 (MyBatis resultMap 또는 select 절 수정) - 내부 클래스에서 바인딩 후 응답 객체에 필드 세팅
- 응답 직렬화 시 해당 필드가 누락 없이 포함되는지 검증
코드 패턴으로 보면 대략 이런 흐름이다.
// MyBatis 매퍼 결과 → 응답 객체 바인딩
PaymentResult result = paymentMapper.selectByOrderId(orderId);
response.setWelcomeUseTrstnId(result.getWelcomeUseTrstnId());
// 응답 직렬화 (JSON 또는 플랫폼 규격 포맷)
out.print(toJson(response));
단순해 보이지만 null 처리를 빠뜨리면 플랫폼에 따라 응답 파싱 오류로 이어질 수 있다. welcomeUseTrstnId가 빈 문자열이냐 null이냐를 플랫폼이 다르게 해석할 수 있어서, 값이 없을 때 어떻게 내보낼지 플랫폼 스펙을 먼저 확인했다.
예외 처리 선택지는 상황마다 달랐다.
| 상황 | 처리 방식 |
|---|---|
| DB 조회 결과 null | 빈 문자열 또는 필드 제외 (플랫폼 스펙 따름) |
| DB 연결 실패 | RuntimeException 전파 → 상위에서 에러 응답 |
| 플랫폼 재시도 요청 (중복 웹훅) | 멱등성 보장 - 동일 응답 재반환 |
웹훅은 플랫폼이 실패 시 재전송하는 경우가 많아서 멱등성이 중요하다. 같은 웹훅이 두 번 들어와도 DB 상태가 두 번 바뀌면 안 된다. 이 부분은 기존 로직에서 이미 처리하고 있었고, 이번 필드 추가가 그 흐름을 깨지 않는지 추적하는 게 주 확인 포인트였다.
트랜잭션 범위는 비즈니스 로직을 담당하는 내부 클래스에서 @Transactional로 묶여 있는 구조를 그대로 유지했다. JSP 레이어는 트랜잭션 바깥에서 응답 렌더링만 하는 역할이어서 범위 침범 없이 필드 추가만 했다. 여기서 트랜잭션 경계를 흐리게 하면 나중에 롤백 범위를 추론하기 어려워지기 때문에, 기존 경계를 그대로 존중하는 게 맞다고 판단했다.
로그는 INFO 레벨로 welcomeUseTrstnId 값을 입력과 출력 양쪽에 남겼다. 운영 이슈가 생겼을 때 "플랫폼에서 받은 거래번호가 뭐였고, 우리가 응답으로 돌려준 게 뭐였는지"를 로그에서 바로 확인할 수 있어야 한다. 결제 관련 로그는 과할 정도로 남기는 게 낫다는 게 경험칙이다. 디버깅할 때 로그가 없어서 답답했던 기억이 한두 번이 아니라서.
배포는 로컬 기동 확인 → 개발 DB 반영 → 배포 순서로 처리했다. 작업 규모 자체는 작았지만, 결제 플로우 한가운데에 있는 파일이라 동선 검증은 생략하지 않았다. 작은 수정일수록 "이 정도면 됐겠지" 하고 넘어가다가 엣지 케이스를 놓치는 경우가 생기기 때문이다. 특히 기존 로직과의 호환성은 코드만 봐서는 확인하기 어려운 부분이 있어서, 실제로 돌려보는 게 안심된다.
이런 작업이 반복되다 보면 결제 관련 웹훅 설계에서 몇 가지 습관이 생긴다. 플랫폼 거래번호는 처음부터 양쪽이 공유하는 키로 저장해두고, 응답에도 일관되게 포함시키는 것. 그리고 중복 요청 대응은 나중에 추가하려면 훨씬 복잡해지니까 처음 설계에 넣어두는 것. 이번 수정이 그 방향으로 가는 한 걸음이었다.
댓글 0
첫 댓글 달아줘.