핀테크 엔지니어링 핸드북

4 days ago 4
  • 돈을 핵심 상태로 다루는 시스템은 데이터를 만들지 않고, 잃지 않으며, 아무 것도 신뢰하지 않는다는 원칙 위에서 설계되어야 함
  • 금액 표현은 float를 피하고 BigDecimal, 최소 단위 정수, 유리수 등을 책임에 맞게 조합해야 하며, JSON 숫자 직렬화도 IEEE-754 double 문제를 다시 만들 수 있음
  • 장부는 복식부기, 불변 감사 추적, value time·booking time·settlement time 분리, 정정·취소 기록을 통해 잔액과 보고서를 재구성 가능하게 유지함
  • 실제 돈의 흐름은 예약, 멱등성, 재시작 가능한 상태 머신, 외부 API·웹훅 검증, outbox·CDC, 대사(reconciliation) 로 중복 지출과 누락을 막아야 함
  • 접근 제어, four-eyes 승인, SDLC 변경 추적, 속성 기반 테스트와 장애 주입은 내부 운영자와 코드 변경까지 신뢰 경계로 취급하게 만듦

핀테크 시스템의 기본 원칙

  • 돈이 시스템의 주된 관심사인 소프트웨어 엔지니어링에서는 일반적인 CRUD보다 추적성, 불변성, 검증 가능성이 훨씬 중요함
  • 대상 독자는 핀테크에 새로 합류한 사람, 이미 핀테크에서 일하는 사람, 핀테크 밖에서 돈 시스템이 일반 시스템과 어떻게 다른지 이해하려는 사람임
  • 모든 패턴은 세 가지 원칙을 지키기 위한 수단임
    • No invented data: 돈은 없던 곳에서 만들어질 수 없으므로 중복 처리와 임의 잔액 변경을 허용하지 않음
    • No lost data: 돈에 일어난 모든 일은 추적되고 영속화되어야 함
    • No trust: 외부 제공자, 내부 컴포넌트, 현실 세계를 신뢰하지 않고 검증함

돈을 표현하는 방식

  • 금액 표현은 금융 시스템의 가장 기본적인 결정이며, 잘못 고르면 상위 계층 전체가 오류를 물려받음
  • float/double은 예측하기 어려운 정밀도 손실을 만들 수 있어 거의 항상 좋은 선택이 아님
    • 빠르고 메모리 효율적이며 추가 라이브러리나 자료구조가 필요 없다는 장점은 있음
  • BigDecimal 같은 임의 정밀도 타입은 계산 정밀도와 반올림 위치를 명시적으로 제어할 수 있음
    • FX나 가격 계산처럼 여러 연산이 이어지는 중간 계산에 적합함
  • 최소 단위 정수 저장은 대부분의 법정화폐에서 중앙은행 시스템과 같은 고정 정밀도를 쓰는 방식임
    • €12.34는 1234로 저장됨
    • ISO 4217의 자릿수를 따라야 하며 항상 2자리라고 가정하면 안 됨
    • 암호화폐도 satoshi, wei 같은 최소 단위 정수를 쓰지만, 정밀도는 자산별로 다르고 ERC-20의 decimals처럼 토큰이 정의함
    • 암호화폐 금액은 64비트 정수를 넘을 수 있어 임의 폭 정수가 필요할 수 있음
  • 유리수는 정밀도 손실이 허용되지 않을 때 가장 강력하지만 느리고, 다른 형식으로 변환할 때 정밀도 손실 없이 바꾸기 어려우며, 보통 커스텀 타입이나 라이브러리가 필요함
  • 저장 방식과 계산 방식은 별도 결정이며, 한 시스템이 정수 저장과 BigDecimal 중간 계산을 함께 쓸 수 있음
  • 금액 직렬화에서도 경계 처리가 중요함
    • 일반 JSON 숫자는 대부분의 파서에서 IEEE-754 double이므로 내부 표현을 조심해도 경계에서 float 문제가 다시 생김
    • 돈은 "12.34" 같은 문자열이나 최소 단위 정수로 보내야 함

반올림과 통화 처리

  • 반올림은 나눗셈, 통화 변환, 수수료, 이자, 비율 적용, 정밀도 이동에서 불가피하므로 암묵적으로 두면 안 됨
  • 반올림 전략은 비즈니스 결정임
    • 보수적으로 내려야 하는 경우가 있고, 통계적 효과를 위해 half-even을 쓸 수도 있음
    • 누가 잔여 소수분을 가져가는지는 법률·세무 영향을 가질 수 있음
  • 가능한 한 오래 전체 정밀도를 유지하고, 보통 저장 전이나 사용자 표시 전 같은 경계에서만 반올림해야 함
  • 값을 여러 부분으로 나눈 뒤 반올림하면 부분 합이 원래 값과 달라질 수 있음
    • 상황에 따라 명시적 rounding account가 필요함
  • 돈은 숫자만으로 표현할 수 없고 항상 통화와 함께 다뤄야 함
    • Money 같은 newtype, struct, class, record로 금액과 통화를 함께 묶으면 오류 가능성이 줄어듦
    • 서로 다른 통화의 덧셈은 금지하고, 변환은 엄격히 통제된 환율로 명시적으로 수행해야 함
    • 임의 통화 코드를 받아들이지 말고 시스템 경계에서 통제된 통화 집합으로 검증해야 함
    • 법정화폐 코드는 식별자로 쓸 수 있지만, 암호화폐는 (network, contract address) 같은 더 복잡한 식별이 필요함
    • pegged, bridged, wrapped 암호화폐는 기초 자산과 동등하지 않음

FX 환율

  • FX rate는 항상 방향성을 가짐
    • EUR/USD 환율은 USD/EUR의 단순 역수가 아님
    • 거래소에서 매수와 매도는 bid/ask spread 때문에 서로 다른 가격의 주문임
  • 환율의 시점도 결과를 바꿈
    • 현재 시점 환율은 현재 보유분이나 지금 발생한 것처럼 가정한 거래 가치를 계산하는 데 쓰임
    • value-date 환율은 가치 변화나 세액 계산에 쓰임
  • 변환에서는 두 종류의 환율이 중요함
    • Transactional rate는 실제 변환이 일어난 환율이며, 원래 금액과 결과 금액에서 도출됨
    • Reference rate는 보유분 가치나 세무 기준 같은 평가와 동등성 판단에 쓰이며 실제 거래 가격은 아님
  • 표준적인 단일 환율은 없음
    • 환율은 시장에서 나오며 거래 장소나 계산 방식에 따라 달라짐
    • 중앙은행 환율은 표준에 가장 가깝지만 reference rate로만 쓸 수 있고, 대안 소스도 유효할 수 있음
  • 금액과 reference rate의 출처를 함께 보관해야 나중에 검증할 수 있음

장부와 복식부기

  • 돈의 이동은 감사 가능하고 몇 년 뒤에도 재구성 가능한 방식으로 기록되어야 함
  • 복식부기는 금융 거래를 (credit account, debit account, amount) 형태의 entry 목록으로 저장하는 널리 쓰이는 방식임
    • 고전적 표현은 이동마다 debit row와 credit row를 따로 둠
    • 모든 entry가 같은 금액을 한 계정에서 다른 계정으로 이동하므로 장부는 항상 균형을 이룸
  • 돈에는 항상 출처와 목적지가 있음
    • 외부 제공자도 전용 계정을 가져야 시스템에 들어오고 나가는 돈을 추적할 수 있음
  • 잔액은 저장하지 않고 돈의 이동에서 파생함
  • 계정에는 asset, liability, equity 같은 타입이 있음
    • accounting equation assets = liabilities + equity가 유지됨
    • 실제로는 수수료 수익이나 write-off 손실을 기록하기 위해 revenue와 expense 계정도 필요함
    • 확장 식은 assets = liabilities + equity + revenue - expenses임
  • 하나의 거래는 보통 여러 이동을 만듦
    • 순금액 이동과 수수료 이동이 별도로 생길 수 있음
  • posted entry는 관례상 불변이며, 정정은 원본을 상쇄하는 새 entry를 추가해 처리함

시간 모델: value, booking, settlement

  • 거래에는 보통 두 개 이상, 때로는 세 개의 타임스탬프가 붙음
    • Value time: 거래가 실제로 발생한 시점
    • Booking time: 시스템에 기록된 시점
    • Settlement time: 돈이 실제 이전되거나 실체화된 시점
  • Settlement time은 모든 거래에 존재하지 않으며 보통 T+X로 표현됨
    • T+2는 value date로부터 2일 뒤 settlement가 일어난다는 뜻임
  • value time과 booking time은 거의 항상 어긋남
    • booking > value이면 backdated이며, 보고 기간이 달라질 때 특히 중요함
    • booking < value이면 forward-dated이며, 예약 결제나 미래 날짜 결제에서 발생함
  • 카드 결제 예시는 T1에 결제가 발생하고, T2에 시스템이 기록하며, T3에 결제 제공자가 계좌로 돈을 이전하는 흐름임
  • 비즈니스 보고서는 value time이나 settlement time을 주로 보고, booking time은 추적성에 유용함
  • 여러 시점을 created_at 하나로 합치면 나중에 재구성할 수 없는 정보를 잃음

감사 추적, 이벤트 소싱, 불변성

  • 금융 시스템은 규제 감사 대상이며, 사용자 자금과 회사 자금의 혼합 여부, 수익의 설명 가능성, 외부에 제공한 정보와 현실의 일치 여부, 자금 보호 상태 등을 증명해야 할 수 있음
  • audit trail은 현재 상태뿐 아니라 그 상태가 어떻게 만들어졌는지의 전체 이력임
    • 무엇이 일어났는지
    • 언제 일어났는지
    • 누가 또는 무엇이 트리거했는지
    • 왜 일어났는지
  • 돈의 이동뿐 아니라 수동 개입, fee schedule, rate source, limit 같은 설정 변경, 권한 변경도 감사 추적이 필요함
  • compliance check나 risk score 같은 결정은 결과만 저장하면 부족할 수 있음
    • DMN, Drools, Decisions4s 같은 decision table이나 rules engine에 있으면 어떤 규칙이 어떤 입력에서 실행되어 어떤 결과가 나왔는지 재생 가능한 구조가 됨
  • Event sourcing은 audit trail을 만들기 위한 체계적인 접근임
    • 현재 상태와 로그를 따로 저장하지 않고 이벤트만 저장한 뒤 상태를 파생함
    • 복식부기 ledger는 balance를 저장하지 않고 entry에서 계산한다는 점에서 이 패턴의 예임
  • Event sourcing에는 실무 제약도 큼
    • 모든 곳에 필요하지 않으며, ledger가 돈을 이미 커버하면 주변 도메인은 일반 모델과 신뢰 가능한 change log로 충분할 수 있음
    • 성능을 위해 balance와 projection을 캐시하거나 snapshot할 수 있음
    • 이벤트 원본은 효율적으로 질의하기 어려워 projection 작업이 많아질 수 있음
    • 이벤트는 수년간 남으므로 오늘의 코드가 오래전 이벤트도 읽어야 함
  • 감사 추적은 수정 가능하면 증거가 되지 않으므로 append-only여야 함
    • append-only table, DB 권한에서 UPDATE·DELETE 제거, 애플리케이션 계층의 mutating operation 차단, checksum이나 hash chain을 통한 tamper evidence가 도구가 됨
  • 실제 시스템에서는 버그 때문에 event log나 audit trail을 고쳐야 할 때가 있음
    • 보통 데이터가 외부에 보고된 뒤에는 고정되어야 하며, 보고 전이라면 문제를 찾아 시스템 밖으로 나가기 전에 제자리 수정이 가능할 수 있음

취소와 정정

  • 잘못된 금액 posting이나 잘못된 계정 posting 같은 실수는 발생함
  • 불변성은 앞으로 고치는 방식을 요구함
    • 새 compensating entry를 posting하고 원본 record와 양방향으로 연결함
  • Reversal은 원본을 경제적으로 없었던 것처럼 완전히 상쇄하지만, 원본과 reversal은 모두 이력에 남음
  • Correction 또는 adjustment는 실제 기록과 올바른 값의 차이를 booking하거나, 되돌린 뒤 올바른 값으로 다시 posting함
  • 정정은 원본과 다른 보고 기간에 들어갈 수 있음
    • 연결 정보가 있어야 보고서가 정정을 올바르게 귀속하고 실제 활동과 cleanup을 구분할 수 있음
  • 이미 닫힌 보고 기간에 backdate가 허용되지 않는 경우가 보통이므로, 정정 시 과거 value time을 지정할지는 보고 일정에 좌우됨

돈 흐름 실행: 불변 조건과 자금 예약

  • Invariant는 시스템에서 항상 참이어야 하는 특성임
    • accounting equation이 한 예이며, 비즈니스 이해관계자가 여러 조건을 정의할 수 있음
  • invariant를 강제하는 방법은 상호 보완적임
    • 생성 단계에서 유효한 객체만 만들도록 설계함
    • 런타임에서 assertion, 테스트, property-based testing으로 확인함
    • 저장된 데이터를 reconciliation job이나 nightly check로 사후 분석함
  • 외부 세계와 상호작용하는 거래는 race condition을 피해야 함
    • 외부 호출 후에야 잔액 부족을 발견하거나 같은 돈을 두 번 쓰는 상황을 막아야 함
  • Funds reservation 또는 hold-and-release는 외부 상호작용 전에 특정 거래를 위해 자금을 예약하는 패턴임
    • 성공하면 reservation을 settle하고 거래를 진행함
    • 실패하면 release해 available balance로 돌림
  • 이 패턴은 total balance와 available balance를 구분함
    • available = total - reserved
    • 잔액 확인과 새 reservation은 available balance 기준으로 수행됨
  • 최종 금액은 사전 추정과 다를 수 있음
    • 수수료나 환율이 달라지면 예상 금액을 예약하고 실제 금액을 settle한 뒤 나머지를 release함
  • reservation은 반드시 settle 또는 release되어야 함
    • 고아 reservation은 사용자 자금을 잠그지만 돈을 잃거나 만들지는 않음
    • expiry나 timeout은 안전망이 될 수 있지만 필수는 아님
  • 잔액 확인과 reservation 기록은 linearizable해야 함
    • stale read에서는 두 거래가 모두 확인을 통과해 같은 자금을 뒷받침할 수 있음

Overdraft와 멱등성

  • Overdraft는 계정 잔액이 음수가 되는 상황임
  • 의도적 overdraft는 한도와 이자가 있는 신용 상품이며, 보통 별도 overdraft 계정으로 모델링됨
  • 의도하지 않은 overdraft는 정책상 금지되어도 발생할 수 있음
    • settlement가 예약 추정보다 크게 들어오거나, reversal이 자금이 이미 빠져나간 뒤 들어올 수 있음
    • funds reservation은 window를 줄이지만 제거하지 못함
  • “금지”와 “표현 불가능”은 다름
    • unsigned integer나 CHECK (balance >= 0)로 음수 잔액을 표현하지 못하게 만들면, 현실적으로 음수 잔액을 받아야 할 때 crash, zero clamp, 잘못된 처리로 이어질 수 있음
    • 음수 잔액을 0으로 clamp하면 돈을 만들어냄
  • overdraft가 감지되면 조사 신호로 보고, future deposit과 netting, repayment 요청, expense/loss 계정으로 write-off 같은 방식으로 명시적으로 회수 또는 처리해야 함
  • 분산 시스템에서는 exactly-once delivery를 보장할 수 없으므로 재시도가 필요하고, 재시도는 중복 전달을 만들 수 있음
  • Idempotency는 같은 메시지가 두 번 전달되어도 처리가 한 번만 효과를 내게 하는 성질임
    • 명시적 idempotency key가 payload 기반 deduplication보다 보통 단순하고 안전함
    • key는 특정 operation과 client 범위로 제한해야 함
    • 오류를 재생할지 재처리할지 결정해야 하며, 영구 오류는 그대로 재생하는 편이 보통 단순함
    • 중복 호출 payload가 원본과 같은지 검증하는 것은 좋은 관행이지만 구현 복잡도와 유연성 비용이 있음
    • 대규모에서는 수십억 요청 deduplication과 동시 접근에서 atomic barrier가 필요함
    • 24시간 같은 idempotency window는 구현을 단순하게 하지만 correctness 비용이 큼
    • retry 테스트와 out-of-order retry 처리도 필요함

재시작 가능한 흐름

  • 돈 흐름은 여러 단계에 걸치며 단계 사이 어디서든 죽을 수 있다고 가정해야 함
  • Full resumability는 반쯤 끝난 흐름이 항상 복구 가능한 상태에 놓이도록 만드는 설계임
  • 진행 상태는 메모리가 아니라 영속 저장소에 저장해야 함
    • 흐름을 명시적 state machine으로 모델링하고 각 단계 완료를 다음 단계 시작 전에 commit해야 함
  • 중단된 흐름을 다시 밀어주는 독립 driver가 필요함
    • scheduler, worker, poller가 orchestrator crash 뒤에도 incomplete flow를 처리해야 함
  • 재개 시 이미 부분적으로 일어난 단계를 다시 실행할 수 있으므로 각 단계는 멱등적이어야 함
  • 외부 효과는 rollback할 수 없음
    • 외부 세계를 호출한 뒤에는 호출하지 않은 상태로 되돌릴 수 없음
    • 완료될 때까지 roll forward하거나, 이후 단계가 영구 실패하면 saga pattern처럼 compensating action을 posting해야 함
  • Temporal, Camunda, Workflows4s, AWS Step Functions 같은 durable-execution engine을 쓰거나 직접 persistent state machine을 만들 수 있음

외부 API 소비

  • 결제 제공자, custodian, blockchain node, KYC vendor 같은 외부 API는 코드, 품질, 가동 시간을 통제할 수 없으므로 방어적으로 다뤄야 함
  • 스키마를 신뢰하면 안 됨
    • 필드 누락, 타입 변경, 예상 밖 null이 생길 수 있음
    • 중요한 부분은 경계에서 검증하고 예상 밖 데이터는 크게 실패해야 함
    • 필요 없는 부분까지 검증하면 제3자의 계약 위반 때문에 불필요한 장애가 생길 수 있음
  • 외부 API에서는 URL에 token 전달, 정밀도 손실, 의미와 다른 HTTP code, 200 안의 error body, 일관성 없는 pagination, custom date format 같은 일이 충분히 발생함
  • 모든 호출은 실패할 수 있으므로 timeout과 retry가 필요함
  • Circuit breaker는 주로 과부하 서버에 대한 courtesy이며 클라이언트 복잡도를 늘림
    • 다만 latency와 thread, connection 같은 유한 자원을 보호할 때 필요할 수 있음
  • rate limit과 quota는 사전 계산으로 예상 호출량과 provider limit을 맞춰봐야 함
  • 모든 request와 response를 구조화되고 질의 가능한 형태로 저장하면 조사, audit trail, provider 행동 분쟁의 증거, 버그 뒤 재처리 자료가 됨
  • 핵심 영역에서는 provider redundancy를 고려할 수 있음
    • 두 blockchain node로 데이터 검증, backup bank partner, crypto custodian, KYC vendor 같은 방식이 있음
    • 개발, 수수료, 복잡도 비용이 매우 큼
  • sandbox는 production과 크게 다를 수 있으므로 canary release나 영향이 작은 controlled usage로 production test를 준비해야 함

웹훅 처리

  • 웹훅은 외부 시스템 신호를 받는 흔한 방식이지만 안전한 처리가 쉽지 않음
  • 순서를 가정하면 안 됨
    • 메시지는 out-of-order로 오거나 stale data를 담을 수 있음
    • 방금 받은 웹훅이 최신 진실이라고 보고 상태를 덮어쓰면 안 됨
  • 유효성을 가정하면 안 됨
    • 웹훅은 issuer의 다른 하위 시스템에서 오며 stale 또는 잘못 변환된 데이터를 담을 수 있음
    • 웹훅 본문은 trigger로만 쓰고 API를 조회해 authoritative state를 확인하는 방식이 좋음
    • API도 eventually consistent일 수 있어 바로 조회하면 이전 상태를 반환할 수 있으므로 retry가 필요함
  • 전달을 가정하면 안 됨
    • issuer가 강한 redelivery policy를 약속해도 웹훅은 언젠가 유실됨
    • reconciliation 같은 독립 프로세스가 데이터 완전성을 보완해야 함
  • 단일 전달도 가정하면 안 됨
    • 같은 웹훅은 여러 번 전달되며 처리는 멱등적이어야 함
  • 빠르게 acknowledge하고 비동기로 처리해야 함
    • raw event를 durable store에 저장한 뒤 즉시 2xx를 반환하고 실제 작업은 비동기로 수행함
  • raw payload는 그대로 저장해야 함
    • 안정적 처리, audit trail, 버그 뒤 재처리에 필요함
  • 호출자는 검증해야 함
    • 일반적으로 issuer가 payload signature를 붙이고, 수신자는 shared secret의 HMAC이나 공개키 기반 비대칭 서명으로 검증함
    • 서명 검증은 재직렬화한 payload가 아니라 받은 raw bytes 위에서 해야 함
  • 웹훅은 무엇이 일어났는지의 진실이 아니라 뭔가 일어났다는 hint로 취급해야 함

신뢰 가능한 알림: Outbox와 CDC

  • 시스템 변경을 Kafka event, webhook call 등 외부 채널로 안정적으로 알려야 할 때 transactionality가 문제가 됨
  • publish가 성공했는데 네트워크 문제로 응답을 받지 못해 시스템 상태를 rollback하거나, state change는 commit됐지만 publish가 실패하는 상황이 생길 수 있음
  • textbook 답은 2-phase commit 또는 distributed transaction이지만 복잡성과 재사용 표준화 어려움 때문에 드물게 쓰임
  • 실용적 선택지는 여러 가지임
    • Outbox pattern: 상태 변경과 함께 publishing intent를 전용 store에 transactionally 기록하고, 나중에 성공할 때까지 처리함
    • Change Data Capture: database write-ahead 또는 replication log를 읽어 commit된 변경을 event stream으로 바꿈
    • Debezium과 AWS DMS가 CDC를 제공함
    • CDC는 table row 형태의 raw event를 내보내므로 내부 schema 누출을 피하려면 postprocessing이 필요함
    • Listen-to-yourself는 먼저 event를 publish한 뒤 자기 상태를 그 event에서 다시 만듦
    • Event sourcing은 event log가 이미 DB에 있으므로 거기서 publish하면 됨
  • 어떤 메커니즘을 고르든 delivery는 at-least-once임
    • relay나 connector가 publish 후 기록 전에 crash하면 재시작 때 다시 보낼 수 있음
    • consumer는 stable event id로 deduplicate하고 멱등적으로 동작해야 함

Reconciliation

  • 외부 데이터에 의존하는 시스템은 두 시스템의 상태가 어긋나는 data drift에 취약함
    • 웹훅을 놓치거나, ledger에는 transaction이 posting됐지만 external provider 시스템에는 반영되지 않을 수 있음
  • Reconciliation은 두 시스템을 맞추는 프로세스임
    • 실제로는 ledger, payment processor, bank처럼 셋 이상일 수 있음
  • cadence는 맥락과 제약에 따라 hourly, daily, monthly, yearly일 수 있음
  • drift는 missing data일 수도 있고, 같은 transaction의 금액이 다른 경우처럼 더 복잡한 차이일 수도 있음
  • timing도 중요함
    • settlement가 T+3이면 record는 3일 동안 unreconciled 상태일 수 있으므로 이를 process에 반영해야 불필요한 alert를 막음
  • matching algorithm이 핵심 난점임
    • 보통 external provider id를 내부에 저장하면 matching이 단순해짐
    • 없으면 amount와 time 기반 heuristic이 필요할 수 있음
  • one-to-many reconciliation도 필요함
    • 하나의 settlement transfer가 여러 transaction을 포괄할 수 있음
  • discrepancy를 reconciliation이 맞도록 단순 overwrite하면 안 됨
    • correction record, webhook data reprocessing 같은 first-class 지원으로 원인을 이해하고 고쳐야 함

제어와 접근

  • 돈 시스템은 데이터뿐 아니라 누가 어떤 행동을 할 수 있는지도 통제하고 사후에 절차 준수를 증명해야 함
  • Segregation of duties는 한 사람이 전체 프로세스를 소유하지 못하게 하는 통제임
  • Four-eyes, maker-checker, dual control은 특정 action이 적용되기 전에 두 번째 사람이 승인해야 하는 방식임
  • 적용 대상은 대규모 또는 수동 withdrawal, manual ledger correction, treasury와 cold-wallet 이동, fee schedule이나 limit 변경처럼 자금을 이동시키거나 잘못 표시할 수 있는 행동임
  • 엔지니어링에도 같은 통제가 적용됨
    • code merge, production deploy, infrastructure change는 돈 시스템에서 민감한 action이므로 review와 approval이 필요함
  • approval 자체도 trail의 일부임
    • 누가 요청했고 누가 승인했으며 두 사람이 달랐는지 기록해야 control을 증명할 수 있음
  • emergency를 위해 명시적이고 강하게 감사되는 break-glass 경로가 필요함
  • Access control은 시스템 상태의 일부이며 시간이 지나며 변함
    • human과 service 모두 최소 권한을 부여해야 함
    • per-person grant보다 RBAC를 선호하면 review가 쉬움
    • capability grant와 revoke도 민감 이벤트이므로 무엇이, 누가, 왜 바뀌었는지 기록해야 함
    • scheduled access review로 오래되거나 부정확해진 permission drift를 잡아야 함

SDLC 변경 추적

  • 규제 환경에서는 코드가 production에 도달하는 과정을 감사할 수 있어야 함
  • source control은 변경 기록임
    • commit history는 모든 변경을 author에 귀속하고, review와 linked ticket을 통해 이유와 연결함
    • signed commit, protected branch, shared history force-push 금지로 보호해야 함
  • review와 pipeline은 강제되어야 함
    • required review, status check, main branch direct push 금지가 중요함
  • deployment는 추적 가능해야 함
    • 어떤 version이 실행 중인지, 누가 언제 release했는지 재구성 가능해야 incident를 원인 변경과 연결할 수 있음

테스트 전략

  • 돈 시스템에서는 operation sequence 공간이 크고 흥미로운 실패가 조합에서 나오므로 테스트가 특히 중요함
  • Property-based testing은 특정 출력보다 어떤 입력에서도 성립해야 하는 property를 검증함
    • invariant와 money math에 잘 맞음
  • operation sequence를 생성할 때 마지막뿐 아니라 매 단계 뒤 invariant를 확인해야 함
    • 수동으로 대규모 수행하기 어려우므로 assertion을 자동 주입하는 testing harness가 필요함
  • Generative idempotency testing은 외부 세계를 만지는 모든 operation이 두 번째 호출에서 영향이 없는지 검증함
  • Crash and resume injection은 long flow가 어떤 단계 사이에서 죽어도 살아나는지 검증함
  • Round-trip testing은 encode/decode, serialize/deserialize, convert/convert back 뒤 시작점으로 돌아오는지 또는 알려진 tolerance 안에 있는지 확인함
    • money와 currency type의 boundary 정밀도 손실과 serialization bug를 잡는 빠른 방법임
  • Golden testing은 fee breakdown, statement, report 같은 계산 결과를 저장된 기대 결과와 비교해 의도치 않은 diff를 드러냄
  • Backward-compatibility testing은 오래된 real-format payload corpus를 유지하고 현재 코드가 여전히 deserialize와 project를 올바르게 하는지 검증함
  • Production testing은 sandbox가 production과 크게 다를 때 필요할 수 있음
    • canary release, 작은 blast radius의 controlled rollout, 작은 실제 금액을 지속적으로 흐르게 하는 synthetic transaction이 예임
    • production test는 실제 돈을 움직이므로 ledger, reconciliation, audit trail을 똑같이 거쳐야 하며, 정상 correction/reversal 경로로 정리해야 함

도메인 용어와 참고 자료

  • 핀테크 입문에서는 코드보다 vocabulary와 concept이 더 어려울 수 있어 핵심 용어를 별도로 정리함
  • 회계와 ledger 영역에는 ledger, general ledger와 sub-ledger, debit/credit, posting, chart of accounts, receivable/payable, IOU, accrual vs cash basis, trial balance, suspense/clearing account, write-off, commingling, reconciliation break가 포함됨
  • Money와 FX 영역에는 Money type, minor units, basis point, notional, fiat vs crypto, stablecoin, pegged/wrapped/bridged, bid/ask/spread, mid-market rate, reference rate, mark-to-market가 포함됨
  • 거래와 settlement 영역에는 value date, booking date, settlement date, T+X, clearing vs settlement, cut-off time, float, netting, backdating, reversal/correction이 포함됨
  • 결제, 카드, 시장, crypto, compliance 용어도 별도로 정리됨
    • PSP, omnibus account, FBO account, chargeback, issuer/acquirer, authorization vs capture
    • order book, market vs limit order, maker/taker, slippage, liquidity, derivative, futures, perpetual, liquidation
    • custody, hot/cold wallet, private key, multisig, MPC, gas, confirmation/finality, reorg, UTXO vs account model
    • KYC, AML/CFT, sanctions screening, PEP, SoF/SoW, Travel Rule, VASP, MiCA, least privilege, RBAC, audit trail
  • 참고 자료는 회계와 ledger, payments와 cards, markets와 trading, crypto, engineering, KYC와 AML로 나뉨

세 가지 end-to-end 예시

  • Crypto withdrawal

    • 사용자가 0.5 ETH를 외부 주소로 출금하는 흐름임
    • 요청은 idempotency key를 포함해 중복 제출이 하나의 withdrawal만 만들게 함
    • 0.5 ETH와 예상 network fee를 available balance에서 예약함
    • compliance gate는 sanctions, AML, destination address를 확인하며 외부 호출과 수동 review 때문에 며칠 동안 sleep할 수 있음
    • on-chain broadcast는 idempotent해야 하며, crash 뒤에는 두 번째 broadcast가 아니라 chain을 다시 확인해야 함
    • 충분한 confirmation 뒤 ledger에 user account debit, external on-chain account credit, network fee expense, service fee revenue를 posting함
    • nightly job이 ledger와 chain reality를 reconcile함
  • Card deposit

    • PSP를 통한 카드 충전 흐름임
    • 사용자는 금액과 카드 정보를 제출하고 PSP에 idempotency key로 deposit transaction을 엶
    • authorization은 hold만 만들며 아직 돈이 회사 것이 아니므로 user balance를 credit하지 않음
    • captured 웹훅은 raw bytes signature 검증, raw payload 저장, 빠른 2xx 응답 뒤 비동기로 처리함
    • 웹훅은 trigger일 뿐이므로 PSP API에서 authoritative state를 조회함
    • captured but not settled 상태는 clearing account를 통해 posting하며, settlement는 T+X 뒤 batch로 도착할 수 있음
    • chargeback은 원본을 수정하지 않고 linked compensating entry로 처리함
  • In-app conversion with cashback

    • 1,000 EUR를 USDC로 바꾸고 promotional cashback을 주는 흐름임
    • EUR→USDC quote는 USDC→EUR의 역수가 아니며 directional rate임
    • EUR와 USDC는 서로 더하지 않고, USDC는 (network, contract address)로 식별되며 pegged fiat와 같지 않음
    • 계산은 전체 정밀도를 유지하고 경계에서 한 번만 명시 전략으로 반올림함
    • spread는 revenue account에 명시적으로 booking해야 하며 rounding residual로 사라지면 안 됨
    • cashback은 무료 balance bump가 아니라 company promotional/expense account에서 user balance로 이동하는 실제 돈임
    • 결과 publish는 outbox, CDC, event log 같은 메커니즘으로 reliable delivery를 보장하고 downstream consumer는 stable event id로 dedupe함
Read Entire Article