개발 slecs

결제 입금통지 응답 구조 오류 4건 교정

목차

결제 플랫폼의 입금통지(deposit notification) 응답 형식이 PDF 스펙과 불일치하는 4가지 오류를 발견하고 수정했다. 외부 시스템과의 연동에서 매우 흔한 실수인데, 이번 경험을 통해 배운 점이 많아서 정리해 본다.

외부 스펙 대조의 어려움

결제 플랫폼과 연동할 때는 항상 PG사의 API 스펙 문서를 따라야 한다. 우리 시스템이 보내는 요청(request)도, 받는 응답(response)도 스펙에 맞아야 상대방 시스템이 정확하게 해석할 수 있다. 특히 입금통지 같은 webhook 응답은 상대방의 결제 시스템이 자동으로 수신 및 파싱하는 구조라서, 조금의 오류도 심각한 결과를 초래할 수 있다.

그런데 실제 개발하다 보면 이런 불일치가 어떻게 발생할까? 주로 두 가지 원인이다.

스펙 문서를 완전하게 읽지 못함 — 특히 "선택사항", "deprecated 필드", "조건부 필드"는 놓치기 쉽다. PDF로 배포되는 스펙 같은 경우 검색도 어렵고, 내용 구조도 일관성이 없는 경우가 많다. "이 항목이 어디 챕터에 있는지 기억 안 남" 하다가 재개발할 때 다시 놓친다.

스펙은 맞는데 코드에서 실수 — 복사-붙여넣기, 필드명 오타, 조건문 실수, 혹은 리팩토링 중 변수명만 바뀌고 JSON 직렬화 규칙은 안 바뀐 경우다. 이번 수정도 후자였다. 스펙은 명확했지만 구현 과정에서 놓친 부분들이 있었던 것 같다.

발견한 4가지 불일치와 영향

항목 문제 상대방 시스템에 미치는 영향
봉투(Envelope) 구조 응답 JSON 루트 필드가 스펙과 다름 파서가 필드를 찾지 못해 전체 응답 해석 실패
필드명(Field Name) 특정 필드들의 이름이 스펙과 불일치 데이터 매핑 오류, 값이 누락되고 기본값으로 처리됨
빈 바디(Empty Body) 특정 조건에서 응답 본문이 완전히 비어있음 상대 시스템이 NPE나 validation error로 요청 재시도
추가 필드 스펙에 없는 필드가 응답에 포함됨 strict 파서일 경우 예상치 못한 필드로 인한 오류

가장 심각한 것은 봉투 구조 오류였다. JSON 응답의 최상위 구조가 잘못되면 상대방 시스템이 response 자체를 파싱할 수 없게 된다. 자동화된 webhook 수신 로직이 있는데 필드가 없으면 그냥 무시되거나 에러 로그만 남는다. 상대방 운영팀은 "어? 통지가 안 들어왔네?" 하고 수동으로 확인을 요청하는 식으로 나중에 발견되곤 한다.

코드 레벨에서의 수정

변경 파일들을 보면 세 부분이 영향을 받았다:

  • web 컨트롤러 클래스: webhook endpoint의 response 구조를 수정했다. 봉투 필드명과 계층 구조를 스펙과 일치시켰다.
  • util 클래스: 응답 본문을 생성하는 빌더/포매터 로직을 수정했다. 필드명 오타, 조건부 필드 누락, empty body 처리를 보강했다.
  • KpPayWebhookControllerIdempotencyTest: webhook의 멱등성을 검증하는 테스트를 강화했다.

특히 주목할 점은 멱등성 테스트가 함께 수정됐다는 것이다. webhook은 네트워크 재시도나 상대방의 중복 발송으로 같은 요청이 여러 번 들어올 수 있다. 이때 응답이 매번 동일해야 한다는 것을 확인하는 테스트다. 스펙 오류를 수정할 때 멱등성 테스트까지 함께 강화했다면, 결과적으로 외부 시스템과의 안정성이 한층 높아진 셈이다.

회고: 이런 버그를 어떻게 방지할까

1. 외부 스펙은 정확하게, 충분히 읽어야 한다
마크다운이나 PDF 문서를 여러 번 읽고, 예시 값(example)을 직접 따라 만들어 보자. 내가 이해한 게 맞는지 확인하려면 스펙의 예시 JSON을 복사해서 우리 시스템의 응답과 필드 대조를 하나하나 해본다.

2. 자동화 테스트의 한계를 인식하자
단위 테스트는 우리 로직이 맞는지 검증하지만, "상대 시스템이 정말 파싱할 수 있는가"는 통합 테스트나 수동 검증이 필요하다. 실제로 PG사 테스트 환경에 webhook을 날려 보고 상대방이 정상 수신했는지 확인했는가?

3. JSON 필드명은 명시적으로 관리하자
자바의 리플렉션 기반 직렬화(예: Jackson)를 쓸 때, @JsonProperty 애노테이션을 명시적으로 붙이자. 변수명과 JSON 필드명을 같게 가져가려는 욕심이 실수의 원인이 될 수 있다. 특히 리팩토링할 때 변수명만 바꾸고 @JsonProperty는 누락하는 경우가 흔하다.

4. webhook 응답은 멱등성을 먼저 생각하자
구조 오류를 고칠 때도, 같은 요청에 대해 매번 같은 응답을 주는지 꼭 확인하자. 상태를 변경하는 로직(예: 거래 상태 업데이트)이 webhook 처리에 포함돼 있다면, 중복 요청으로 인한 double-processing을 방지해야 한다.

외부 시스템 연동은 우리 시스템 내부 버그보다 발견하기 어렵다. 로그를 분석하거나 상대방 운영팀과 소통하다가 "어? 응답이 이상한데?"라는 피드백이 올 때쯤이면 이미 꽤 많은 거래가 영향을 받은 상태다. 그래서 이런 스펙 대조 작업은 지루하지만 정말 중요하다. 다음에는 이런 불일치를 초기에 캐치하기 위해 스펙 체크리스트를 만들고, PR 리뷰할 때 외부 스펙과의 일치성을 항상 점검하는 습관을 들여야겠다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.