3,000만이 사용하는 토스 개발자가 알려주는 금융 백엔드 실전: Outbox, 멱등성
아직 오픈 전인 콘텐츠예요. 오픈하면 제가 버튼 추가해둘게요.
금융 백엔드 · 분산 데이터 동기화
Dual Write는
하나의 트랜잭션이 될 수 없습니다
결제와 정산 시스템에서 가장 많이 터지고, 백엔드 면접에서 가장 많이 물어보는 문제입니다. 토스에서 10년째 이 문제만 붙잡고 있는 개발자가, 실제로 나는 사고 여덟 개를 그대로 재현하고 하나씩 막는 과정을 처음부터 끝까지 보여드립니다.
토스 누적 가입자 · 국민 10명 중 6명 · 2030 가입률 91%
2025년 7월 말 기준, 비바리퍼블리카 발표
목차를 사고 순서로 짰습니다
여덟 개의 실패를
하나씩 만들어 봅니다
각각을 먼저 재현해요. 서버를 중간에 죽이고, 응답이 오기 전에 연결을 끊고, 같은 작업을 두 번 실행합니다. 남은 데이터를 눈으로 확인한 다음에야 막는 법으로 넘어갑니다.
여덟 개 모두 실패하는 테스트로 먼저 고정해 두고, 통과시킨 뒤에 다음 실패로 넘어갑니다.
소재 이야기
하필 스테이블코인으로
만드는 이유
실습으로 만드는 건 정산 송금 API 서버예요. 운영 지갑에서 가맹점 지갑으로 테스트용 스테이블코인을 보내는 서버인데, 이걸 고른 이유는 하나입니다. 우리가 다뤄야 할 세 가지가 여기서 한꺼번에 나타나거든요.
되돌릴 수 없다
한 번 나간 전송은 취소가 안 됩니다. 우리 DB 트랜잭션이 롤백돼도 밖으로 나간 건 그대로 남아요.
결과가 늦다
요청을 보내고 결과를 알기까지 초 단위, 때로는 분 단위가 걸립니다. 게다가 중간 결과는 확정이 아니에요.
성공했는지 모른다
응답이 오기 전에 연결이 끊기면, 안 보내진 건지 보내졌는데 응답만 못 받은 건지 그 순간에는 구분할 방법이 없습니다.
스마트 컨트랙트를 짤 필요는 없어요. 이미 배포된 걸 외부 API처럼 부르기만 합니다.
검증된 설계 패턴
AWS가 정리한 설계 패턴을
직접 구현합니다
이 강의의 중심인 Transactional Outbox는 AWS 공식 문서인 Prescriptive Guidance의 클라우드 설계 패턴으로 정리되어 있습니다. 가이드가 짚는 문제와 주의점을 강의에서는 하나씩 실패로 재현하고 직접 막아요.
공식 문서 AWS Prescriptive Guidance · Cloud design patterns, architectures, and implementations Transactional outbox pattern DB 쓰기와 메시지 발행이 한 작업에 함께 있을 때 생기는 이중 쓰기 문제를 해결하는 패턴. 같은 가이드의 Retry with backoff도 강의에서 함께 구현합니다. 원문 보기 →AWS 가이드가 말하는 것
이 강의에서 하는 것
이중 쓰기 문제
DB 쓰기와 메시지 발행 중 하나만 실패하면 시스템이 어긋난 상태로 남는다
DB 다음 전송, 전송 다음 DB 순서로 직접 만들고 중간에 서버를 죽여 어긋남을 재현합니다
같은 트랜잭션에 기록
업무 데이터와 outbox 테이블을 하나의 트랜잭션으로 함께 갱신한다
정산 요청, 상태 이력, Outbox를 하나의 PostgreSQL 트랜잭션으로 커밋합니다
별도 발행자
다른 서비스가 outbox를 읽어 메시지 브로커로 보낸다
Outbox 발행자가 잠금을 잡고 배치로 꺼내 큐에 넣고, 도중에 죽어도 다시 꺼냅니다
중복 메시지
같은 메시지가 두 번 갈 수 있으니 처리한 메시지를 추적해 소비자를 멱등하게 만든다
처리 기록과 자연 키로, 같은 메시지를 열 번 받아도 송금은 한 번만 시작합니다
롤백과 순서
롤백된 트랜잭션의 이벤트는 보내지 않고, DB를 갱신한 순서대로 보낸다
커밋된 Outbox 행만 발행하고, 발행 순서와 재시도 규칙을 테스트로 고정합니다
AWS 서비스를 쓰지는 않아요. 같은 설계를 PostgreSQL과 Redis로 로컬에서 구현하기 때문에, 어느 클라우드나 회사 인프라에도 그대로 옮길 수 있습니다.
만드는 것
화면 없는 서버 하나를
끝까지 만듭니다
확인은 전부 HTTP 요청과 응답, 상태 조회 API, 로그, 테스트로 합니다. 강의가 끝나면 처음부터 끝까지 동작하는 저장소 하나가 손에 남아요.
전체 구조
API 하나, 워커 넷,
테이블 여덟 개
요청은 API가 받아 PostgreSQL에 한 번에 기록합니다. 그다음 일은 전부 워커 몫이에요. Outbox를 꺼내 큐에 넣고, 큐에서 받아 체인에 보내고, 영수증을 다시 확인하고, 마지막에 원장과 체인 기록을 대사합니다. 화살표 하나하나가 강의에서 일부러 끊어 보는 지점이에요.
보라색 화살표가 돈이 실제로 움직이는 경로예요.
109개 파일
소스 75 · 테스트 32 · 컨트랙트와 스크립트 2
74개 테스트
강의 화면에서 직접 작성하는 테스트 케이스
8개 테이블
원장, 상태 이력, Outbox, 처리 기록, 전송, nonce, 대사 결과
4개 워커
발행 · 전송 · 확인 · 대사. API와 별도 프로세스로 띄웁니다
직접 만드는 파일 109개
복사해 오는 파일 없이 전부 강의 화면에서 만듭니다. 테스트가 가장 많은 건 우연이 아니에요. 실패를 먼저 테스트로 고정한 뒤에 코드를 쓰는 순서라서 그렇습니다.
테이블 설계
불변이라는 것은 코드가 아니라
Database를 통해서 확보합니다
중복 송금을 막는 마지막 방어선은 if 문이 아니라 유니크 제약이에요. 정산 하나에 전송 하나, 같은 지갑에서 같은 nonce는 한 번, 같은 메시지는 소비자마다 한 번. 강의에서 만드는 여덟 개 테이블과 그 관계를 그대로 옮겼습니다.
보라색 머리글 세 테이블은 정산 요청을 받는 순간 하나의 트랜잭션으로 함께 커밋됩니다. 나머지는 강의가 진행되면서 하나씩 늘어나요.
상태부터 정합니다
업무 상태와 전송 상태를
섞지 않습니다
외부 호출이 타임아웃됐다고 정산 자체가 실패한 건 아니잖아요. 그런데 두 상태를 하나의 enum에 넣는 순간 복구할 방법이 사라집니다. 그래서 코드를 쓰기 전에 상태와 불변식부터 못 박고 시작해요.
- 하나의 정산에는 최대 하나의 전송만 존재한다
- 하나의 정산에는 하나의 Idempotency-Key만 존재하고, 키는 전역 유일하다
- 서명 이후에는 새 전송을 만들지 않는다. 저장된 해시로 기존 전송을 먼저 찾는다
- SETTLED와 FAILED는 자동으로 다른 상태로 바뀌지 않는다
- 모든 상태 변경은 이력 한 건을 남긴다
지금 시장
은행이 먼저
사람을 찾고 있습니다
원화 스테이블코인을 두고 시중은행이 상표부터 걸어두고 컨소시엄을 짜는 중이에요. 조직이 먼저 생기고 채용은 그 뒤를 따라옵니다.
8개
원화 스테이블코인 발행 컨소시엄에 참여한 시중은행. 출범은 2025년 말에서 2026년 초로 전망
32건
KB국민은행이 특허청에 출원한 원화 스테이블코인 상표. 신한 21건, 하나 16건, BNK 11건이 뒤를 이음
40명
우리은행 신사업제휴플랫폼부 규모. 가상자산팀을 포함해 5개 팀으로 구성
34.1%
2026년 상반기 암호화폐 채용 공고 2,932건 중 엔지니어링 비중. 직군 1위이고 스테이블코인·결제 부문에 집중
여기 붙는 개발자에게 요구되는 건 컨트랙트 작성이 아니라, 원장과 외부 결과를 어긋나지 않게 맞추는 설계예요. 이 강의가 처음부터 끝까지 그것만 다룹니다.
공고는 이미 올라오고 있어요
최근 1년여 동안 실제로 올라온 스테이블코인·디지털자산 공고를 모았습니다. 이미 마감된 공고도 그대로 넣었어요. 지금 열려 있느냐보다, 은행·증권·카드·핀테크가 같은 시기에 같은 사람을 찾기 시작했다는 게 더 중요하니까요.
보도된 토스 블록체인 서버 개발자 공고의 주요 업무는 트랜잭션 처리, 비동기·상태 관리, 잔액·거래내역 정합성 보장, 외부 네트워크 장애 대응이었어요. KB국민카드 공고는 지갑과 내외부 시스템을 잇는 연계 아키텍처 설계를 요구했고요. 이 강의 목차와 거의 같은 문장들입니다.
출처: 이비엔·이포커스·아시아경제(상표 출원 현황), 인베스트조선·99bitcoins(컨소시엄), 바이라인네트워크(우리은행 조직), 타이거리서치·블록미디어(2026년 상반기 채용 시장)
채용 공고 출처: 이데일리·서울경제(카카오페이), 비즈니스피플(카카오페이·카카오뱅크·KB국민카드), 잡코리아(카카오뱅크), 다음뉴스(토스), 금융투자협회 채용공고(신한은행), 디지털애셋(하나은행), 파이낸셜뉴스(NH농협은행), 전자신문(하나금융티아이·미래에셋증권·한화투자증권), 이투데이(현대차증권·IBK투자증권·한국투자증권), 넥스블록(두나무). 2026년 10월 1일 기준이며, 마감 표시가 없는 공고는 진행 여부를 따로 확인하지 못한 것입니다.
끝내고 나면
이력서에 쓸 수 있게 됩니다
Outbox, 멱등성, 대사는 이름만 알아서는 면접에서 한 줄 이상 말하기 어렵습니다. 직접 만들어 보면 그때부터 경험이 되고, 아래 여섯 줄은 강의를 마치면 그대로 쓸 수 있는 문장이에요.
Transactional Outbox 직접 구현
발행자의 잠금·배치·순서까지 만들고, 발행 도중 죽어도 다시 꺼내는 구조를 붙였습니다.
멱등한 결제 API 설계
Idempotency-Key로 중복 요청을 흡수하고, 자연 키로 같은 업무가 한 번만 시작되게 만들었습니다.
장애 시나리오 회귀 테스트
여덟 가지 사고를 실패 주입으로 재현하고 전부 회귀 테스트로 고정했습니다.
대사 배치와 자동 복구
DB 원장과 외부 기록을 비교해 누락·중복·불일치를 분류하고, 안전한 것만 자동 복구합니다.
correlation ID 기반 추적
HTTP 요청 하나가 워커를 거쳐 외부 반영까지 가는 경로를 한 줄로 추적할 수 있게 만들었습니다.
상태 머신과 불변식 설계
업무 상태와 전송 상태를 분리하고, 유니크 제약으로 지킬 불변식을 먼저 못 박고 시작했습니다.
전부 이 강의에서 직접 만드는 것들입니다. 저장소 하나가 그대로 포트폴리오가 됩니다.
추천 대상
이런 분들이
많이 듣습니다
- 백엔드 취업·이직을 준비하면서 남들과 다른 프로젝트 하나가 필요한 분
- 결제·정산 도메인으로 옮기고 싶은데 뭘 준비해야 할지 막막한 분
- DB에 저장하고 외부 API를 호출하는 코드를 쓰면서, 사이에서 죽으면 어떻게 되는지 확신이 없었던 분
- 결제 승인 요청이 타임아웃됐을 때 재시도해야 할지 말아야 할지 판단이 안 섰던 분
- 재시도 로직은 넣어뒀는데 중복 처리가 안 나는 이유를 설명하지 못하는 분
- Outbox 패턴을 글로는 읽었지만 발행자를 직접 만들어본 적은 없는 분
- 데이터가 어긋난 걸 발견하고 손으로 DB를 고쳐본 적 있는 분
- 분산 트랜잭션 면접 질문에 이론으로만 답해본 분
언어는 상관없어요. Spring이든 Node든 백엔드 코드를 짜본 적 있으면 따라올 수 있게 만들었습니다. 큐도, Redis도, Docker도, 체인도 몰라도 됩니다. 처음 나오는 자리에서 필요한 만큼만 그때그때 설명해요.
실습 환경
터미널과 Docker면
충분합니다
macOS 기준으로 진행해요. PostgreSQL과 Redis는 Docker Compose 한 줄로 뜨고, 외부 시스템 역할을 하는 로컬 노드도 명령 하나면 끝납니다. 실제 자금은 어디에도 쓰이지 않으니 마음껏 망가뜨려도 돼요.
누가 만들었나요
이 강의를 함께
준비한 두 사람
토스 · 10년차 서버 개발자
Toss 개발자
10년차 백엔드 서버 개발자로 토스에서 금융 인프라를 운영하고 있습니다. 지방 대학에서 컴퓨터 공학을 전공했고 Java와 Spring을 메인 스택으로 쓰며 저수준 언어에도 관심이 많습니다. Hong과는 컨퍼런스에서 인연이 닿아 공동으로 강의를 촬영하고 있습니다.
돈이 오가는 서버에서 제일 무서운 건 에러가 나는 게 아니라, 에러 없이 데이터가 조용히 어긋나 있는 겁니다.
[現] 토스 서버 개발자 · [前] KT 서버 엔지니어 · [前] 넥슨 플랫폼 서버 개발자
지식공유자 · 판교 플랫폼 서버 개발
Hong
판교에서 플랫폼 서버 개발을 담당하고 있습니다. 직접 공부한 방법과 실무에서 마주치는 문제·해결책을 나누려고 지식공유자 활동을 이어가고 있어요. 이 강의는 커리큘럼을 짜고 촬영을 맡았습니다.
정상 흐름만 보여주는 강의는 이미 많더라고요. 그래서 실패부터 만들어 보는 순서로 커리큘럼을 짰습니다.
[現] 판교 플랫폼 서버 개발자 · [前] 블록체인·메타버스 백엔드 개발 · 인프런 지식공유자
자주 받는 질문
이런 걸 많이 물어보세요
Transactional Outbox 패턴은 무엇인가요?
Transactional Outbox는 DB 밖에서 해야 할 작업을, 업무 데이터와 같은 트랜잭션 안에서 메시지 테이블에 함께 기록해 두는 패턴입니다. 커밋이 성공하면 작업 의도가 반드시 남고, 별도 발행자가 그 메시지를 꺼내 큐로 보냅니다. 이 강의에서는 발행자의 잠금·배치·순서까지 직접 만들어요.
블록체인을 몰라도 들을 수 있나요?
들을 수 있습니다. 스마트 컨트랙트를 짜지 않고, 이미 배포된 걸 외부 API처럼 호출만 해요. 필요한 개념은 처음 나오는 강의에서 최소한만 설명하고, 사전 시청용 영상도 따로 준비했습니다. 이 강의의 주제는 체인이 아니라 데이터 동기화예요.
회사에서 Java와 Spring을 쓰는데 TypeScript로 배워도 되나요?
됩니다. 이 강의가 다루는 건 언어 기능이 아니라 설계예요. Outbox 테이블 구조, 유니크 제약으로 지키는 불변식, 상태 전이 규칙, 재시도 분류 기준은 언어와 무관하게 그대로 옮겨집니다. TypeScript를 고른 이유는 API 서버와 워커를 한 저장소에서 짧게 보여주기 좋아서예요.
정확히 한 번(exactly-once) 처리를 배우는 건가요?
아닙니다. 이 강의는 정확히 한 번을 약속하지 않아요. 메시지는 적어도 한 번 전달될 수 있다는 전제에서 출발하고, 중복이 도착해도 업무가 한 번만 일어나도록 소비자 쪽에서 흡수하는 방법을 만듭니다. 실무에서 실제로 쓰는 접근이에요.
대사(reconciliation)는 왜 필요한가요?
아무리 잘 설계해도 우리 DB와 외부 시스템은 어긋납니다. 대사는 두 기록을 비교해 누락·중복·상태 불일치를 찾아내고 복구하는 작업이에요. 무엇을 자동으로 되돌리고 무엇을 사람 판단으로 넘길지 경계를 긋는 게 핵심이라, 강의에서도 그 기준부터 정한 뒤 코드를 씁니다.
어떤 내용은 다루지 않나요?
이중부기 원장이나 정산 마감 같은 회계 기능은 만들지 않아요. 우리 기록과 외부 결과를 맞추는 데 집중합니다. 서명 키도 실습에서는 개발용 키를 환경 변수로 쓰고, 프로덕션에서 KMS가 필요하다는 경계까지만 짚습니다. 2PC 같은 분산 트랜잭션도 붙이지 않아요.
어디부터 보면 되나요?
앞에서부터 보시길 권해요. 하나의 서버를 이어서 만드는 구조라 앞의 실패를 본 뒤라야 뒤의 해법이 왜 필요한지 보이거든요. 다만 Outbox나 멱등성만 필요하시다면 해당 트랙부터 골라 보셔도 됩니다.
함께 이야기해요
취업했다고,
이직했다고 연락이 올 때가 있어요
커리어, 지금 회사에서 겪고 있는 문제, 새로 나온 기술 이야기를 나누는 공간이에요. 강의를 듣다 막히는 부분이 있으면 여기에 물어보셔도 좋습니다.