개발 slecs

결제 어댑터 응답·에러 표준화와 다중 WAS 동기화 개선

목차

이번 Phase 5B.1 + 5B.2 작업은 결제 어댑터 레이어 전반의 응답/에러 표준화, 그리고 다중 WAS 환경에서의 동기화 처리를 한 스프린트에 묶어서 처리한 꽤 묵직한 커밋이었다.


왜 지금 이 작업이 필요했나

결제 어댑터 레이어가 쌓이다 보면 어느 순간 꼭 터지는 문제가 있다. 각 PG사 혹은 내부 결제 모듈마다 에러를 던지는 방식이 제각각이고, 응답 구조도 조금씩 다르다. 처음에는 "어차피 케이스별로 처리하면 되지" 하고 넘어가지만, 이 어댑터를 소비하는 상위 서비스 레이어가 점점 늘어나면 상황이 달라진다. 서비스 A는 code 필드를 보고, 서비스 B는 resultCode를 보고, 에러 핸들링 로직이 호출 지점마다 다르게 흩어진다. 이 상태에서 다중 WAS 환경까지 끼어들면 특정 노드에서만 재현되는 타이밍 이슈가 발생하기 시작하고, 디버깅 비용이 기하급수적으로 올라간다.

팀 내부에서 이 문제는 이미 몇 번 언급이 됐었다. 코드리뷰 때마다 "이 에러 처리 왜 이렇게 됐어요?"라는 코멘트가 반복됐고, 결국 Phase 5B에서 본격적으로 건드리기로 결정했다.


작업 내용: 표준화 + 동기화 두 트랙

이번 커밋에서 핵심적으로 변경된 파일들을 보면 구조가 보인다.

파일 역할
PaymentException.java 어댑터 레이어 전용 예외 클래스 정의
PaymentResult.java 응답 표준 모델 정의
PaymentFacade.java 어댑터 진입점 / 오케스트레이션
내부 클래스 (adapter) 개별 구현체 / 내부 처리 로직
내부 클래스 (sysconfig/web) WAS 설정 관련 동기화 처리 지점

크게 두 트랙으로 나눠서 봤다.

5B.1 — 응답·에러 표준화

PaymentResult를 응답 표준 모델로 확정하고, 어댑터 내부에서 올라오는 모든 예외를 PaymentException 하나로 wrapping하도록 정리했다. PaymentFacade가 그 집결 지점이 된다. 외부에서 보면 어떤 구현체를 쓰든 동일한 결과 타입과 동일한 예외 타입만 받으면 된다.

// Before: 구현체마다 다른 예외가 튀어나오던 상황
try {
    SomePgResult raw = pgClient.request(params);
    // 구현체마다 필드명, 성공 판단 조건 상이
} catch (PgApiException | HttpClientException | RuntimeException e) {
    // 호출부마다 다르게 처리
}

// After: Facade를 통해 단일 타입으로 정규화
try {
    PaymentResult result = paymentFacade.process(request);
    if (!result.isSuccess()) { /* 표준 처리 */ }
} catch (PaymentException e) {
    // 에러 코드, 메시지 구조 통일
}

이렇게 하면 상위 레이어는 구현 세부사항을 몰라도 되고, 새로운 결제 수단이 붙을 때 PaymentException/PaymentResult 계약만 지키면 된다. 팀에 새로 합류한 개발자 입장에서도 "일단 이 두 클래스만 이해하면 흐름이 보인다"는 진입장벽 낮추기 효과가 있다.

5B.2 — 다중 WAS 동기화

이 부분이 사실 더 까다로웠다. 단일 WAS에서는 문제없이 동작하던 상태 관리 코드가 WAS를 두 대 이상 띄우면 서로 다른 메모리 상태를 보는 상황이 생긴다. sysconfig/web 쪽 내부 클래스 변경이 이 부분이다. 설정이나 상태가 노드마다 달라지면 결제 요청이 어느 노드로 라우팅되느냐에 따라 동작이 달라지고, 재현이 안 되는 버그가 생긴다. 운영 입장에서는 최악의 시나리오다.


회고: Phase 단위 작업의 트레이드오프

이번 작업을 하면서 다시 한번 느낀 건 "표준화 작업은 빠를수록 좋다"는 거다. 구현체가 두 개일 때 정리하는 것과 다섯 개일 때 정리하는 건 난이도가 완전히 다르다. 이미 흩어진 에러 처리 코드를 역추적하면서 "이게 맞는 건지"를 확인하는 데 쓴 시간이 꽤 됐다.

Phase 5B.1과 5B.2를 하나의 커밋으로 묶은 것도 의사결정 포인트였다. 사실 두 트랙은 분리해서 PR을 올릴 수도 있었는데, 응답 표준화와 WAS 동기화가 PaymentFacade 레벨에서 강하게 결합되어 있어서 분리하면 중간 상태에서 테스트가 불안정해지는 문제가 있었다. 결국 한 번에 묶어서 검증하는 게 낫다고 판단했고, 리뷰어한테는 변경 의도를 트랙별로 설명하는 방식으로 보완했다.

  • 표준화 작업은 테스트 커버리지를 먼저 확보하고 들어가는 게 맞다
  • 다중 WAS 이슈는 로컬에서 재현이 어렵기 때문에 스테이징에서의 검증 시간을 충분히 확보해야 한다
  • PaymentException 같은 도메인 전용 예외 클래스는 이른 시점에 만들어두는 편이 나중에 확실히 이득이다

멘토링 때도 자주 하는 말이지만, 어댑터 레이어 설계는 "지금 당장"보다 "이 인터페이스를 처음 보는 사람이 얼마나 빨리 이해하는가"를 기준으로 잡아야 한다. 이번 작업이 그 방향으로 한 걸음 더 간 것 같아서 나름 만족스럽다.

끝.


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

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

댓글 0

첫 댓글 달아줘.