-
00:19 토스뱅크 외화통장 기능
- 양방향 환전수수료 무료 :) → 살 때 환율 / 팔 때 환율 같은 개념이 없어짐
-
기술적으로도 매우 큰 도전이었음
-
00:56 분산 환경에서 환전을 구현하기
-
01:24 거대한 단일 서버가 단일 데이터베이스를 바라보고 있는 Monolithic 구조
- 시스템과 개발팀의 규모가 커질수록 배포 및 개발 경험 / SPOF 등 이슈가 많이 생겨 MSA로의 전환 진행 중
-
[?] 외화 계좌는 왜 DB까지 분리했고, 다른 DB를 사용했을까?
-
02:28 환전이란… 원화 → 외화 받기 / 외화 → 원화 받기
- 모바일 앱 내에서 이 과정이 모두 이루어져야 한다.
- 만약 환전이 같은 DB를 바라보고 있는 단일 서버 위에 구현된다면 매우 간단히 구현될 수 있다
- 단일 서버에서 트랜잭션으로 처리하면 된다 :)
-
03:09 원화/외화 계좌 서버가 분리되어 있고, 서로 다른 DB를 보고 있다면…?

-
분산 트랜잭션이 구현되어야 함
-
03:36 Two Phase Commits vs. SAGA
- 단 하나의 서비스라도 트랜잭션 커밋 불가능 응답이 오면? 롤백 요청을 보내서 데이터 변경을 돌린다.
- 가용성과 확장성이 낮다. 일관성은 높다.
-
04:28 SAGA 각 서비스들이 작은 로컬 트랜잭션을 진행하다가 특정 단계에서 실패하는 경우 이전에 커밋된 트랜잭션들에게 보상 트랜잭션을 보내 롤백
- 가용성과 확장성이 높지만, 중간 상태가 노출된다. 그리고 보상 트랜잭션도 직접 구현해야 한다.
-
05:09 환전 서비스는 높은 트래픽을 견뎌야 해서 SAGA 패턴을 선택함
- 다양한 트랜잭션 참여자가 추가될 수 있기 때문이기도 함
-
05:21 Choreography SAGA vs. Orchestration SAGA
- 전자는 중앙제어가 없어서 SPoF가 없고 느슨한 결합성을 가짐. 추적이나 디버깅은 어려움
- 단일 장애점이 되지만, 현재 진행중인 상태를 추적하기 쉽다.
- 후자를 선택하였음. 클라이언트 요청을 받아 시작하는 환전 서버가 있어야 하고, 현재 진행중인 환전의 상태를 관리했어야 했음
- 환전 한도를 구현하려면 현재 진행중인 환전의 상태도 추적이 필요…!
-
08:25 출금 먼저 해야하는 이유 :)
-
09:06 입출금 요청의 통신방식
- HTTP
- Messaging (Kafka) 에러 처리를 알아서 Broker가 해줌
- 하지만, HTTP를 사용했다. 왜!?
- 입출금 결과를 알고 넘어가야 함 (필연적으로 동기방식으로 진행되어야 함)
- 유저가 환전이 즉시 완료되기를 기대하기 때문에, Timeout을 명시적으로 구현하는 것이 기능으로서 필요하다.
-
10:29 출금 취소는 메시징 방식
-
11:01 에러 핸들링
- 정말 실패했는지 입금/출금 결과를 확인해서 맞추어주는 방식을 사용하고 있음
- 입출금 결과를 확인하는 것 자체가 실패한다면?
- Kafka Message Scheduler를 이용해 메시지를 지연발행한다
- 별도의 지연 토픽으로 발행
- 지연시간만큼 기다린 후, 원래의 토픽에 메시지를 대신 발행하여 컨슈머가 지연시간 뒤에 메시지를 가져가도록 한다.
- 이 때, 프로듀서와 컨슈머 모두 환전서버가 된다면 특정 작업을 뒤로 미루는 효과가 있다 (오…)
- 입출금 계좌 서버에 회복할 시간을 줄 수 있다.
- 즉시 재확인을 시도하는 것이 아니라, 30초 지연 후 재확인
- 또 실패하면? Backoff를 더 줘서 같은 방식으로 진행
- 정해진 횟수를 모두 추가하는 경우 개발자가 수동으로 메시지 재발행 가능
- 환전 지연 이벤트를 발행하지도 못하고 서버가 죽는 경우?
- 장비 결함 / Container OOM
- 배치를 통해 재처리한다.
- 출금이 성공하고 멈춰버린 환전의 경우, 배치가 출금취소 메시지를 발행한 후 환전을 실패처리한다.
-
14:15 Eventual Consistency
- 원화계좌가 출금취소 메시지를 처리하다가 에러가 발생하면?
- Consumer Dead Letter를 이용해 카프카 메시지의 결과적 정합성을 보장하고 있음
- 원화계좌 서버가 출금취소를 처리하다가 에러가 발생하면 > CDL 브로커를 통해 Dead Letter Server로 전달
- 이 DL은 서비스 메시지 브로커로 메시지를 다시 전달하여 원화 계좌에 출금취소를 재시도한다.
- 계속 재시도에 실패하면 → 수동으로 발행
- 원화계좌 서버가 출금취소를 처리하다가 에러가 발생하면 > CDL 브로커를 통해 Dead Letter Server로 전달
-
14:57 Transactional Messaging
- 입금 실패로 인한 환전 실패 처리와 출금 취소 메시지 발행은 항상 같이 이루어져야 한다.
- 서비스 메시지 브로커 장애로 메시지 발행 자체가 안되면…?
- Outbox 패턴 등..
- 토스뱅크에서는 Producer Dead Letter를 이용
- 환전 서버가 메시지 브로커 장애로 메시지 발행에 실패하면 PDL로 발행 → DL
- DL은 일정 시간 후 다시 원래, 서비스 메시지 브로커로 이벤트 전달
-
15:49 모니터링?
- Orchestration SAGA: State Machine
- 각 트랜잭션 상태별 명령이 정해져 있기 때문에 State Machine으로 나타낸다.
- Database에는 환율 / 환전 금액 / 환전 요청 등 Exchange Request라는 테이블에 스냅샷으로 저장된다.
- 이 환전이 거쳐간 모든 상태를 확인하는 것이 가능하다.
- 일정 시간이 흐른 후에도 끝나지 않은 환전을 탐지할 수 있다.
- 계좌 서버들은 입출금의 쌍이 맞는지를 확인할 수 있다.
- 원화/외화 계좌 데이터베이스에서 ETL을 통해 정보계로 전달 → 쌍이 맞는지 지속적으로 탐지
- Orchestration SAGA: State Machine
-
17:32 성과
- 또 다른 SAGA?: 회계 서버가 환전 트랜잭션의 참여자로 추가된다…! 2PC로 구현했다면 회계서버의 투표까지 기다렸어야 했을 것…!
- 회계서버에 장애가 났을 때 의존성도 존재…!
- 부족한 돈 자동환전 결제
- TradeOFF?: 트랜잭션 구현의 복잡도 증가
- 또 다른 SAGA?: 회계 서버가 환전 트랜잭션의 참여자로 추가된다…! 2PC로 구현했다면 회계서버의 투표까지 기다렸어야 했을 것…!
Note
References