← 모든 글

fail-open vs fail-closed — 우리의 결제 시스템 선택

결제 시스템 장애 상황에서 fail-open과 fail-closed 중 무엇을 선택했는지, 그 이유와 트레이드오프를 정리합니다.

선택지

결제 시스템에 장애가 생기면 두 가지 방향 중 하나를 선택해야 한다. 시스템이 응답하지 않을 때 거래를 허용할 것인가(fail-open), 아니면 차단할 것인가(fail-closed). 둘 다 비용이 있다. fail-open은 잘못된 거래가 통과될 위험을 감수하고, fail-closed는 정상적인 사용자가 차단될 위험을 감수한다.

이론적으로는 단순한 선택처럼 보인다. 돈이 오가는 시스템이니까 당연히 fail-closed가 맞다고 생각하기 쉽다. 실제로는 그렇게 단순하지 않았다.

우리가 부딪힌 상황

우리 시스템에서 결제 흐름은 여러 내부 서비스가 연결된 체인이다. 외부 PG사 연동, 잔액 조회, 한도 검증, 거래 기록이 순서대로 이어진다. 이 중 어느 하나가 느려지거나 타임아웃이 나면 전체 흐름이 영향을 받는다.

처음에는 각 단계를 fail-closed로 구현했다. 잔액 조회 서비스에서 타임아웃이 나면 거래를 막았다. 그런데 잔액 조회 서비스는 쓰기 경합이 많은 서비스라 가끔 느려지는 경우가 있었다. 결과적으로 실제 잔액은 충분한데 거래가 막히는 상황이 발생했다.

단계별로 다른 선택

해결책은 “모든 단계를 동일하게 처리하지 않는다”는 것이었다.

한도 검증과 사기 방지 로직은 fail-closed를 유지했다. 이 단계에서 오판이 나면 피해가 크고, 속도보다 정확성이 우선이다. 하지만 잔액 조회처럼 최신 상태를 반드시 조회하지 않아도 되는 단계는 캐시된 값으로 fail-open 처리하고 이후 비동기로 정산을 맞추는 방식을 택했다.

이 구조를 만들면서 중요하게 생각한 건 “어느 단계에서 잘못된 결과가 나왔을 때 되돌릴 수 있는가”였다. 되돌릴 수 있는 단계는 fail-open의 여지가 생기고, 되돌리기 어려운 단계는 fail-closed가 맞다.

모니터링이 필수다

fail-open 구간을 두면 반드시 그 구간에서 발생한 불일치를 모니터링해야 한다. 우리는 비동기 정산 과정에서 불일치가 발생하면 알림이 오는 구조를 만들었다. 운영 중 실제로 알림이 온 경우가 있었고, 대부분은 잠깐의 네트워크 지연 때문이었지만 몇 건은 로직 버그를 발견하는 계기가 됐다.

fail-open을 선택하면 더 많은 운영 부담을 감수해야 한다. 단순히 기술적인 결정이 아니라 팀이 감당할 수 있는 운영 역량에 대한 결정이기도 하다. 모니터링 없이 fail-open을 쓰는 건 선택이 아니라 무관심이다.

— by slecs