여는 글

“타임아웃이 났다고요? 그런데 결제는 성공했대요!”

새벽 3시, 긴급 호출을 받았습니다. 실시간 정산 시스템에서 TimeoutException이 발생했는데, 정작 고객의 계좌에는 돈이 들어왔다는 것이었죠. 이 순간부터 시작된 타임아웃과의 전쟁, 그리고 금융 결제 시스템의 복잡한 계층 구조에서 발생하는 다양한 타임아웃 이슈들을 정리해보았습니다.

사건의 발단 - 불일치의 시작

실시간 정산과 지급대행 시스템을 개발하면서 다음과 같은 아키텍처를 구성했습니다:

CLIENT(가맹점/사용자) → PG(우리 시스템) → 금융 VAN(HYPEN/DOZN) → BANK

평화로워 보이는 이 구조에서 발생한 문제의 핵심은 이것이었습니다. 우리는 타임아웃으로 실패 처리했지만, 실제로는 은행에서 정산이 성공했다는 것입니다. 로그를 확인해보니 Read Timeout이 발생한 지 5분 후에 VAN사로부터 성공 콜백이 들어와 있었습니다.

TimeoutException의 삼위일체 - Connection, Read, Socket

1. Connection Timeout: 문을 두드리는 시간

Connection Timeout은 TCP 3-way handshake를 완료하는 데 걸리는 시간입니다. 쉽게 말해 맛집 앞에서 입장을 기다리는 시간과 같습니다.

금융 VAN 연동에서 Connection Timeout이 발생하는 주요 원인들:

  • 방화벽 이슈: 금융권은 보안이 생명입니다. IP 화이트리스트에서 누락되면 아예 연결조차 되지 않습니다.
  • 네트워크 라우팅 문제: 금융 전용선 연결에 문제가 생기면 우회 경로를 찾느라 시간이 소요됩니다.
  • VAN사 서버 다운: 새벽 점검 시간을 놓쳤거나 예기치 않은 장애가 발생한 경우입니다.

2. Read Timeout: 주문한 요리를 기다리는 시간

Read Timeout은 연결은 성공했지만 응답이 오지 않을 때 발생합니다. 맛집에 들어가서 주문은 했는데 요리가 나오지 않는 상황입니다.

펌뱅킹에서 Read Timeout이 가장 골치 아픈 이유:

  • 은행 처리 지연: 대용량 이체나 의심 거래는 은행 내부의 추가 검증 프로세스를 거칩니다.
  • VAN사 큐잉: 월말이나 급여일에는 거래가 몰려 처리 대기열이 길어집니다.
  • 네트워크 패킷 손실: 금융 전용선도 100% 완벽하지는 않습니다.

3. Socket Timeout: 패킷 간 숨 쉬는 시간

Socket Timeout은 개별 패킷 간의 시간 간격을 의미합니다. 서버가 응답을 여러 패킷으로 나누어 보낼 때, 각 패킷 사이의 간격이 너무 길면 발생합니다. 대용량 정산 데이터를 전송받을 때 주로 문제가 됩니다.

계층별 타임아웃 설정의 함정

실제 운영 중 발견한 가장 큰 문제는 타임아웃 체인의 역전 현상이었습니다.

이상적인 타임아웃 체인:

  • Client Timeout: 60초
  • PG Timeout: 50초
  • VAN Timeout: 40초
  • Bank Timeout: 30초

하위 계층으로 갈수록 타임아웃이 짧아야 상위 계층에서 적절히 대응할 수 있습니다. 그런데 실제로는 VAN 타임아웃이 40초인데 PG에서 30초만 기다리는 설정 실수가 있었습니다. 결과적으로 VAN은 아직 은행 응답을 기다리는 중인데, 우리는 이미 타임아웃으로 실패 처리를 해버린 것입니다.

금융 VAN별 타임아웃 특성과 대응

HYPEN (하이픈)

HYPEN은 평소에는 빠른 응답을 보이지만 피크 시간에는 불안정한 모습을 보입니다. 특히 오전 9-10시 출근 시간대와 오후 3-4시 은행 마감 시간대에는 평소보다 3-5배 느린 응답을 보였습니다. Connection은 3초, Read는 20초 정도로 설정하되, 피크 시간대에는 동적으로 늘려주는 전략이 필요했습니다.

DOZN (더즌)

DOZN은 HYPEN보다 안정적이지만 전반적으로 응답이 느린 편입니다. 특히 대용량 거래나 다건 이체 시에는 처리 시간이 선형적으로 증가하는 특성을 보였습니다. Connection 5초, Read 30초를 기본으로 하되, 거래 금액이나 건수에 따라 타임아웃을 조정하는 것이 효과적이었습니다.

타임아웃 발생 시 재처리 전략

일반적으로 금융 VAN사들은 타임아웃 건에 대한 재처리 가이드와 이체 결과 조회 API를 제공합니다. 하지만 단순히 VAN사 가이드를 따르는 것을 넘어, PG 측에서 어떻게 데이터를 핸들링하고 비즈니스 연속성을 보장할 것인지가 핵심입니다.

1. 트랜잭션 상태 관리 아키텍처

가장 먼저 구축해야 할 것은 정교한 상태 관리 시스템입니다. 단순히 성공/실패 두 가지 상태만으로는 타임아웃 상황을 제대로 다룰 수 없습니다. PENDING, PROCESSING, TIMEOUT, SUCCESS, FAILED와 같은 세분화된 상태를 정의하고, 각 상태 전이마다 타임스탬프와 사유를 기록해야 합니다.

특히 중요한 것은 TIMEOUT 상태입니다. 이는 “아직 끝나지 않은 거래”를 의미하며, 주기적인 상태 확인과 재처리가 필요한 대상입니다.

2. 지능형 재처리 전략

타임아웃이 발생하면 즉시 VAN사의 거래 조회 API를 호출합니다. 여기서 나올 수 있는 결과는 크게 네 가지입니다:

SUCCESS (팬텀 성공): 우리는 타임아웃으로 실패 처리했지만 실제로는 성공한 경우입니다. 이 경우 즉시 내부 상태를 성공으로 변경하고 클라이언트에게 알림을 보내야 합니다. 동시에 정산 데이터의 정합성도 체크해야 합니다.

PROCESSING: 아직 VAN이나 은행에서 처리 중인 경우입니다. 이때는 지수 백오프(exponential backoff) 전략으로 재조회 간격을 늘려가며 확인합니다.

NOT_FOUND: VAN사에서도 해당 거래를 찾을 수 없는 경우입니다. 네트워크 단에서 요청이 유실되었을 가능성이 높으므로, 재시도 한계 내에서는 새로운 요청을 시도합니다.

FAILED: 명확한 실패입니다. 이 경우 실패 사유를 분석하여 재시도 가능 여부를 판단합니다.

3. 보상 트랜잭션 패턴

재시도 한계에 도달했거나 특정 VAN에서 계속 문제가 발생한다면, 보상 트랜잭션을 생성합니다. 이는 원거래와는 별개의 새로운 거래로, 다음과 같은 특징을 가집니다:

  • 원거래와의 연결 관계를 명확히 유지
  • 대체 VAN 경로를 통한 우회 처리
  • 더 보수적인 타임아웃 설정 (평소의 2배)
  • 실패 시 즉시 수동 개입 알림

4. 데이터 일관성 보장 전략

금융 거래에서 가장 중요한 것은 데이터 일관성입니다. 타임아웃 상황에서도 이를 보장하기 위해:

분산 락 활용: 동일한 거래가 여러 스레드에서 동시에 처리되는 것을 방지합니다. 특히 타임아웃 후 재처리 시 원거래가 아직 진행 중일 수 있으므로 필수적입니다.

멱등성 키 관리: 모든 거래는 고유한 멱등성 키를 가져야 하며, 재시도 시에도 동일한 키를 사용해야 합니다. VAN사도 이 키로 중복 요청을 필터링합니다.

상태 동기화: 타임아웃이 발생해도 락을 일정 시간 유지하여, 비동기로 들어오는 VAN 콜백과의 경합을 방지합니다.

5. 실시간 모니터링과 자가 치유

타임아웃은 단발성 이벤트가 아닌 패턴을 가집니다. 이를 실시간으로 모니터링하고 자동으로 대응하는 시스템이 필요합니다:

패턴 분석: 최근 30분간의 타임아웃을 분석하여 특정 VAN의 성능 저하나 특정 시간대의 스파이크를 감지합니다.

자동 조치: VAN별 트래픽 비율 조정, 타임아웃 임계값 동적 변경, 대체 경로 활성화 등을 자동으로 수행합니다.

장기 미해결 건 처리: 10분 이상 TIMEOUT 상태인 거래는 별도 큐로 이동시켜 집중 관리합니다.

실전 트러블슈팅 사례

Case 1: 오전 9시의 악몽

매일 오전 9시 정각, 대량의 타임아웃이 발생했습니다. 원인은 모든 가맹점이 전일 정산을 동시에 요청했기 때문입니다.

해결책은 의외로 간단했습니다. 가맹점 ID를 해시한 값으로 0-300초 사이의 랜덤 지연을 주어 요청을 분산시켰습니다. 또한 Circuit Breaker 패턴을 적용하여 특정 임계값을 넘으면 자동으로 요청을 지연시키거나 대체 VAN으로 라우팅하도록 했습니다.

Case 2: 유령 거래의 미스터리

Read Timeout이 발생했는데 고객 계좌에는 입금이 완료된 케이스입니다. VAN-Bank 구간에서 지연이 있었지만 결국 처리는 성공한 것이죠.

이런 경우를 대비해 타임아웃 발생 즉시 30초 후 상태 확인을 스케줄링하고, 고객에게는 “처리 중” 안내를 발송합니다. 상태 확인은 30초, 1분, 2분, 5분 간격으로 최대 5회까지 시도하며, 각 시도마다 이전보다 2배의 대기 시간을 둡니다.

Case 3: 네트워크 단절 vs 타임아웃 구분

Connection Timeout과 Read Timeout은 처리 방식이 완전히 달라야 합니다.

Connection Timeout은 요청 자체가 전달되지 않았을 가능성이 높으므로 즉시 재시도가 가능합니다. 반면 Read Timeout은 요청은 전달되었지만 응답이 늦는 것이므로, 먼저 상태를 확인한 후 조치를 취해야 합니다.

특히 Socket Timeout의 경우 패킷 손실을 의미하므로 네트워크 품질 이슈로 분류하여 별도 관리합니다.

모니터링과 알림 체계 구축

타임아웃 대시보드 구성

효과적인 모니터링을 위해 다음 지표들을 실시간으로 추적합니다:

  • 타입별 타임아웃 횟수: Connection, Read, Socket 각각의 발생 빈도
  • VAN별 분포: 어느 VAN에서 주로 발생하는지
  • 시간대별 패턴: 특정 시간대에 집중되는지
  • 팬텀 성공률: 타임아웃 후 실제로는 성공한 비율
  • 평균 복구 시간: 타임아웃에서 최종 상태 확정까지 걸린 시간

실시간 알림 설정

타임아웃 알림은 민감도를 적절히 조절해야 합니다:

  • Connection Timeout: 5분 내 10건 이상 시 운영팀 알림
  • Read Timeout: 10분 내 50건 이상 시 개발팀 알림
  • 팬텀 성공: 1건이라도 발생 시 즉시 확인
  • 복구 실패: 3회 재시도 후에도 실패 시 긴급 알림

교훈과 베스트 프랙티스

1. 타임아웃은 계층적으로 설계하라

상위 계층일수록 타임아웃을 길게 설정해야 합니다. 그래야 하위 계층에서 발생한 지연을 상위에서 적절히 처리할 수 있습니다.

2. 타임아웃 ≠ 실패

특히 금융 거래에서는 타임아웃이 발생해도 실제 거래는 성공할 수 있습니다. 항상 상태 확인을 우선하고, 섣부른 실패 처리는 피해야 합니다.

3. 멱등성은 필수, 선택이 아니다

모든 금융 API는 멱등성 키를 가져야 하며, 타임아웃 후 재시도 시에도 같은 키를 사용해야 합니다. 이는 중복 거래를 방지하는 가장 기본적인 안전장치입니다.

4. 로깅은 디버깅의 시작

타임아웃 발생 시점, 소요 시간, 발생 위치를 정확히 기록해야 합니다. 특히 어느 구간(PG-VAN, VAN-Bank)에서 발생했는지 구분하는 것이 중요합니다.

5. 부하 테스트는 실제 환경처럼

테스트 시나리오에 반드시 타임아웃 케이스를 포함시켜야 합니다. 정상 응답뿐 아니라 지연 응답, 타임아웃, 네트워크 단절 등 다양한 상황을 시뮬레이션해야 실제 운영에서 당황하지 않습니다.

맺는글

TimeoutException은 분산 시스템에서 피할 수 없는 현상입니다. 특히 금융 결제 시스템처럼 여러 기관이 연계된 환경에서는 더욱 복잡해집니다.

중요한 것은:

  1. 타임아웃을 실패로 단정 짓지 말 것: 특히 금융 거래에서는 치명적일 수 있습니다
  2. 계층별 타임아웃 설계: 상위 계층일수록 관대하게
  3. 상태 확인 메커니즘: 타임아웃 후 반드시 거래 상태 확인
  4. 멱등성 보장: 재시도가 중복 거래로 이어지지 않도록
  5. 모니터링과 분석: 패턴을 파악하여 선제적 대응

타임아웃과의 전쟁은 끝나지 않습니다. 하지만 충분한 이해와 준비가 있다면, 새벽 3시 긴급 호출을 받는 횟수는 확실히 줄일 수 있습니다.

그리고 기억하세요. “타임아웃이 났다”와 “거래가 실패했다”는 전혀 다른 이야기입니다.