아키텍처
(왼쪽 아키텍처) 기존 금융권의 경우 대부분의 비즈니스 로직이 계정계라고 불리는 모놀리틱 코어뱅킹 시스템내에 구현된다. 전통적인 금융권 아키텍처에서 채널계라고 불리는 서비스 서버는 사용자와 연계해 제품을 사용하는데 필요한 요청과 응답을 코어 뱅킹 시스템의 전문 규격에 맞게 변환하고 전달하는데에 중점을 두고 있었다.
(오른쪽 아키텍처) 토스뱅크는 마이크로 서비스로 구성된 서비스 서버와 모놀리틱으로 구성된 코어뱅킹 서비스를 각각 독립된 API서비스로 정의하고 전문방식이 아닌 HTTP API통신을 통해 필요할 경우 코어뱅킹 시스템과 통신하며 독립된 마이크로 서비스가 비즈니스 로직을 직접 해결할 수 있도록 구성했다.
비즈니스 로직을 처리하는 시스템 구조뿐만 아니라 KCB, NICE, 신용정보원같은 대외기관과의 통신구조도 새롭게 디자인했다. 대출이 실행되기 위해서는 고객의 신용정보가 필요하며 신용정보는 허가받은 기관과의 통신을 통해 사용할 수 있다. 또한 현재 고객이 가지고 있는 대출 정보와 안전한 대출 실행을 위해 필요한 부가 정보등을 대외기관과의 연동을 통해 가져올 수 있다.
대출 심사를 고도화하는 과정에서 관계된 기관들이 늘어났고 이에 따라 심사 응답 속도도 함께 늘어나는 문제가 발생했다. 기존 금융권에서는 여러 기관을 동시에 호출하는 비동기 처리로 이 문제를 해결했다. 하지만 사용자 수가 갑자기 증가하게 되면 대외기관이 처리할 수 있는 트래픽양보다 많은 요청을 동시에 보내게 되어 결국 상대 대외기관의 시스템이 마비되는 현상이 자주 발생했다. 토스뱅크는 대외기관이 견딜 수 있을 만큼의 트래픽을 처리하고 유입되는 사용자 수에 맞게 동적으로 요청을 처리할 수 있는 유량 제어 시스템을 도입했다.
비즈니스 로직을 처리하기 위한 관계형 데이터베이스로는 MySQL, 이벤트 처리를 위한 메시지 서비스로는 카프카, 유량제어와 캐시 처리를 위해서는 레디스를 활용했다.
마이크로 서비스 아키텍처 기반으로 구성된 대출 시스템은 고전적인 금융 시스템과는 다른 데이터 흐름을 가지고 있다. 코어뱅킹 시스템 역시 독립된 시스템으로써 API요청으로 데이터를 주고받을 수 있다. 대출이 실행되기까지는 다양한 정보가 필요하고 각 비즈니스 로직에서 구축된 데이터베이스 정보와 더불어 코어뱅킹에서 제공하는 정보를 적절하게 활용할 수 있다.
대출이 실행되기까지 필요한 서비스를 간략하게 살펴보더라도 사용자/약관/상품/대출 서버마다 독립된 스키마를 가지게 되었기 때문에 관리의 문제가 발생할 수 있다. 각 서비스마다 담당하는 개발자가 있지만 협업없이 이렇게 큰 시스템을 유지할 수는 없다. 협업을 위해서는 현재 서버에 구성된 스키마를 언제든지 복제하여 생성하고 Version Control System과 연동되어 DDL쿼리의 버전이 관리되어야 하며 비즈니스 로직과 스키마의 동기화를 구현해야한다.
마이그레이션 전략
토스뱅크는 시스템 초기부터 Flyway와 Hibernate의 DDL Validation을 적용했다. 위 사진을 보면 재슬 PC는 버전 16의 마이그레이션 파일을 가지고 있고 DEV는 15, TEST는 14를 가지고 있다. 이 때 재슬 PC 브랜치가 DEV에 머지되면 DEV의 마이그레이션 파일의 버전이 16으로 올라가고 DEV가 TEST에 머지되면 TEST도 16버전으로 올라간다.
토크뱅크는 데이터베이스 형상과 서비스 로직 차이로 인한 오류를 막기 위해 Hibernate DDL기능 중 validate 옵션을 사용했다. 이 옵션을 사용하면 현재 정의된 서비스 로직에서의 엔티티와 스키마의 정의와 동일한지 확인할 수 있다.
대출 시스템 메시지 서비스
대출 시스템은 고객의 신용평가와 소득산출을 위해서 여러 대외기관과의 협업이 필요하다. 과거 새로운 은행이 오픈하면 이런 대외기관들이 신규 은행의 트래픽을 감당하지 못해 많은 트래픽이 대외기관에 유입되어 한동안 대출신청이 동작하지 않는 등 문제가 생기는 일이 많았다. 토스뱅크는 구축 초기부터 대외기관들이 견딜 수 있을 만큼의 트래픽을 전달하는 파이프라인과 유량 제어 시스템을 적용했다.
대규모 트래픽이 발생할 때 대외기관의 성능과 토스뱅크의 신청 시스템을 분리하기 위해서는 파이프라인 도입이 필수다. 그리고 고객의 대외기관 요청을 파이프라인화 하기 위해 카프카를 도입했다. 카프카와 세팅된 파티션을 통해 고객의 요청은 파이프라인에 배정된다. 각 파티션에 1:1로 대응되는 컨슈머를 배정했고 각 컨슈머에는 메시지를 처리할 수 있는 독립된 스레드 풀을 배정했다.
이 스레드 풀 사이즈는 대외기관의 상태에 따라 다이나믹하게 조절되며 기본적으로는 대외기관이 토스뱅크를 위해 알려준 기본 성능으로 세팅되어있다. 많은 트래픽이 한번에 들어와도 파이프라인과 정해진 스레드 풀을 통해 요청이 나가게 된다면 대외기관이 견딜 수 있을 정도의 TPS만 전달된다.
컨슈머와 연동된 스레드 풀은 현재 서버의 상태에 맞게 동적으로 사이즈를 조절할 수 있는 풀이다. 대외기관의 TPS는 한정적이고 카프카로 구성된 파이프라인과 동적으로 구성 가능한 스레드 풀로도 충분히 컨트롤할 수 있는 IO구간이다. 정확한 TPS측정을 위해서 API호출은 동기 호출로 구성했다.
스레드 풀 세팅은 코루틴에서 제공하는 Fixed Thread Pool을 활용해서 각기 다른 사이즈를 가진 스레드 풀을 array로 선언하여 필요에 따라서 사이즈가 크거나 작은 스레드 풀을 동적으로 선택할 수 있도록 했다. 사용자의 유입이 증가할 경우 고객의 신청 대기 시간을 최소화하기 위해 동적으로 사이즈를 조정하여 대외기관이 받을 수 있는 최대의 요청 수를 활용했다. 그리고 각 기관마다 최적화된 요청 수가 다르기 때문에 대외기관마다 모두 다른 세팅값을 가진다.
대출 시스템의 유량 제어
하지만 대외기관과의 통신이 파이프라인화 되었다고 할지라도 사용자가 급격하게 증가하게 되면 사용자는 여전히 오랜 시간 동안 심사가 완료되기까지 기다려야한다. 또한 대외기관이 장애를 일으킬 수 있기 때문에 고객이 대출을 실행하기까지 문제에 직면하지 않도록 해당 기관의 대체 기관을 유동적으로 활용할 수 있어야 한다. 그래서 토스뱅크는 오픈 초기부터 유량 제어 시스템을 적용했다.
일반적인 서킷의 동작 방식은 API의 실패율과 요청 수에 기반하여 요청을 전달하지 않도록 서킷을 OPEN하거나 일부만 요청을 흘려서 서킷의 상태를 확인할 수 있도록 HALF OPEN처리하면 된다. 하지만 대출 신청 시스템은 대출이 실행되기까지 여러단계를 거쳐야하기 때문에 중간 단계에서의 서킷의 동작으로부터 앞단계의 서킷이 서로 연동하면서 동작하여 고객이 대출을 실행하기까지 불필요한 과정을 거치지 않도록 처리했다.
유량 제어에서의 지표 수집
토스뱅크에서는 코틀린, 코루틴, WebClient, Redis를 활용하여 논 블록킹으로 지표 수집 시스템을 구축했다. 지표 수집 자체가 서비스에 영향을 주면 안되고 IO기반의 작업이기 때문에 IO시간동안 요청 스레드를 점유하지 않는 논 블록킹 아키텍처를 적용하게 되면 스레드의 불필요한 점유없이 시스템을 처리할 수 있다.
레디스역시 인 메모리 기반으로 빠른 응답성을 보장하고 1분 단위의 지표를 적재해서 처리하기에 최적의 데이터 저장소이다. 대외기관의 처리시간을 측정하기 위해 서버는 대외기관의 파이프라인 적재 직전 이벤트의 시작 시간을 레디스에 기록하고 대외기관과 연동이 끝난 뒤 응답 완료 시각과 성공 실패 여부를 레디스에 기록한다. 분 단위로 처리된 레디스의 키 값에는 누적된 요청 수와 누적된 응답시간, 그리고 성공 여부가 기록되고 이러한 데이터는 레디스에서 MySQL에 분 단위 통계적 지표로 쌓이게 된다. 이러한 데이터를 통해 대외기관의 심사 시간, TPS 증가 여부, 실패율을 판단할 수 있게 된다.
대출 시스템 유량 제어 동작 방식
만약 KCB, NICE와 같은 대외기관이 약속된 TPS보다 성능이 떨어지게 되고 실패율이 점차 증가하게 된다면 대외기관 파이프라인에 있는 다이나믹 풀의 사이즈를 조정하여 대외기관이 견딜 수 있는 수준의 요청 수를 조정한다.
요청 수를 조정했음에도 신용정보원에서 제공하는 마이데이터 API의 실패율이 일정 비율로 증가하게 된다면 계속해서 마이데이터 API를 활용하는것은 좋지않은 고객 경험으로 이어지게 된다. 따라서 만약 고객이 앱 스크래핑 가능한 인증서를 보유했다면 상호 보완이 가능한 대체 기관에서 정보를 가져올 수 있도록 마이데이터의 서킷을 HALF OPEN 처리하여 앱 스크래핑으로 전환한다.
마이데이터가 온전히 제 기능을 하지 못한다면 마이데이터의 서킷을 OPEN하고 모든 고객이 인증서를 통한 앱 스크래핑으로 강제 전환하게 된다. 상호보완 가능한 기능과는 다르게 KCB와 NICE처럼 신용평가에 반드시 필요한 기관에서 문제가 발생한다면 어떻게 해야할까? 해당 정보 없이는 대출을 진행할 수 없고 보완 가능한 대외기관이 존재하지 않기 때문에 대외기관의 서킷을 OPEN하고 고객의 대출 상품 전체의 진입 서킷도 연동해서 같이 OPEN해서 고객의 대출 상품 진입을 차단하게 된다.
대외기관의 응답속도뿐만 아니라 응답에 대한 실패율 등을 종합적으로 판단하여 고객의 경험이 저하되는 순간에 각 대외기관의 서킷이 OPEN, HALF OPEN, CLOSE 여부를 판단하게 되고 대출상품 전체의 유량제어 서킷과 연동할 수 있다. 적절한 TPS로 대외기관을 향해서 요청을 보내고 있기 때문에 토스뱅크로 인해서 대외기관에 문제가 발생할일은 거의 발생하지 않는다.
대출 시스템 유량 제어 대기열
사용자가 많이 진입할 경우 고객의 경험이 저하되기 때문에 이를 보완하기 위한 유량 제어 시스템과 함께 대기열 시스템 또한 도입했다. 대기열 시스템은 대출 신청 시스템에 진입하기 위해서 반드시 입장 티켓을 발급 받아야만 진입할 수 있는 시스템이다. 크게 대기열과 입장 열 2개의 큐로 구성할 수 있다.
대기열 시스템이 동작하게 되면 모든 고객은 대기열에 입장하게 되며 일정한 주기에 따라 정해진 수의 티켓만 발급하여 고객을 입장 열로 이동시키고 입장열에 들어온 고객만이 대출 신청 시스템에 진입할 수 있다. 일정한 주기에 따라 정해진 수만 입장열로 이동되기 때문에 시스템에 진입할 수 있는 사용자 수의 속도를 제한할 수 있다. 대외기관에 처리되어있는 파이프라인의 LAG의 임계치가 증가하게되거나 고객이 심사 결과를 받게 되는 시간이 임계시간을 넘어가게 되었을 때 대기열 시스템이 동작하게 된다.
대기열 시스템은 레디스에서 제공하는 ZSet을 활용하여 문제를 해결했다. 대기열에 입장하는 수는 현재 진행중인 서킷의 지표에 따라 배압으로 다이나믹하게 관리된다. 대기열에 입장된 고객은 현재 배압으로 정해진 수에 따라 N명씩 입장열로 이동하게 되고 한번 입장열에 진입한 고객은 지속해서 심사 시스템을 사용할 수 있다.
실제 사례
- 2022년 1월 1일 토스뱅크 대출 재개에 따라 대규모의 유저가 유입됨
- 대외기관 파이프라인을 통해 많은 유저의 진입을 정해진 TPS에 맞게 대외기관으로 전달함
- 하지만 이미 진입해있던 많은 유저로 인해 신규 진입하는 유저들은 대출 심사 결과를 받기까지 오랜 시간을 기다려야 했음
- 이를 위해 앱 스크래핑을 통해 결과를 받아볼 수 있는 유저는 곧바로 해당 방식으로 전환함
- 유저수는 계속해서 늘어났고 최종적으로 대기열 시스템까지 동작함
- 대기열 시스템이 동작하기 전 진입한 경우 인증서 미보유 유저들도 최대한 앱 스크래핑으로 전환하여 많은 유저가 일정 시간 내에 대출 실행 결과를 받아볼 수 있도록 함
댓글 질문 정리
Q-1. 타 은행에 의해 대외기관 트래픽이 몰리면서 토스뱅크에게 할당된 할당량이 줄어드는 경우 추가 대처가 필요하거나 가능한지?
대외기관에 문제가 발생했을 때는 가변적인 스레드 풀을 이용해 줄어든 할당량에 맞게 조정을 진행한다. 그런데 해당 대외기관에 허용가능한 TPS를 줄이기만 하면 고객의 심사 대기는 지속해서 증가하므로 대체할 수 있는 대외기관으로 확률적으로 고객을 우회시킨다. (HALF OPEN) 만약 대체할 수 있는 대외기관이 아니라면 심사 대기열에 따라(심사 결과를 받기까지 N분이상 지연 및 실패율 N% 이상 증가) 전체 대기열이 동작하게 된다.
Q-2. 대기열을 만드는 것과 같은 대응을 넘어 근본적으로 몰려드는 트래픽을 분산시킬 방법도 고려해봤을 것 같은데 어떠한 비즈니스/기술적 옵션들이 있었는지?
기술적으로는 대기열과 동일하지만 실제로 고객이 느끼는 경험은 다른 온라인 쇼핑몰이나 게임 사이트의 대기열과는 조금 다르다. 대기열에 걸리게 되면 바로 대기열에 진입하고 실시간으로 심사를 받을 수 있는 순번이 되었을 때 고객에게 푸시를 보내 입장권을 발급하는 방식으로 진행된다. 그래서 고객은 입장되기까지 화면을 보면서 기다리는것이 아니라 푸시를 받은 순간부터 심사와 실행이 가능하고 트래픽 분산에 효율적이라고 생각했다.
Q-3. 결국 대외기관이 토스 외에도 타 은행들이 함께 이용하는 경우 타 은행의 작업 요청량에 따라 토스의 가용량이 달라질 수 있는지, 그 부분은 어떻게 대응하고 있는지 궁금하다. 예를 들어 경쟁사가 트래픽을 모두 점유하는 경우
대외기관이 연동된 회사에 QOS를 제공하기 위해 노력하고 있는 것으로 알고 있다. 영상에 나오는 주요 대외기관과는 전용선으로 연동해서 사용하고 있다. 경쟁사가 트래픽을 100% 점유하는 상황을 확정할 수는 없지만 대외기관이 일정량의 트래픽을 보장할 수 있도록 요청하고 있으며 만약의 사태에 대비해 영상에서 설명한 방식으로 대응하고 있다.
Q-4. 대기의 경험을 개선하는 것 보다 대기 자체를 없앨 수 있는 보다 앞단에서의 문제 해결 시도가 있었는지?
상품을 오픈할 때 대기 자체를 줄이기 위해 세그먼트 단위로 고객군을 나눠서 오픈하거나 사전 신청을 받아 오픈하게 된다. 대기열이 생기는 경험은 최대한 지양하려 노력한다. 다만 대출 상품 같은 경우 예측하기 어려운 외부 이벤트 요소들이 많아 내부적으로는 영상에 설명한 방식으로 기술적으로 준비해두고 있다.
Reference
'Coding > 시스템 디자인' 카테고리의 다른 글
티켓 예매 서버 시스템 디자인 (25) | 2024.10.13 |
---|---|
토스 SLASH 23 - 실시간 시세 데이터 안전하고 빠르게 처리하기 정리 (0) | 2024.08.13 |
Rate Limiter 시스템 디자인 (0) | 2024.08.07 |
토스 SLASH 22 - 애플 한 주가 고객에게 전달 되기까지 정리 (21) | 2024.07.04 |
틴더(Tinder) 시스템 디자인 (0) | 2024.05.16 |