fos-blog/study
01 / 홈02 / 카테고리03 / 시리즈
01 / 홈02 / 카테고리03 / 시리즈

카테고리

  • AI 페이지로 이동
    • RAG 페이지로 이동
    • agent 페이지로 이동
    • langgraph 페이지로 이동
    • 사람용 CLI와 AI 에이전트용 CLI는 설계가 다르다
    • agents.md
    • Claude Code 메모리: CLAUDE.md와 .claude/rules를 규칙으로 쓰는 법
    • Claude Code의 Skill 시스템 - 개발자를 위한 AI 자동화의 새로운 차원
    • Claude Code를 5주 더 쓴 결과 — 스킬·CLAUDE.md를 키워가는 방식
    • Claude Code를 11일 동안 쓴 결과 — 데이터로 본 나의 사용 패턴
    • Claude Code 멀티 에이전트 — Teams
    • AI 에이전트와 디자인의 새 컨벤션 — DESIGN.md, Google Stitch, Claude Design
    • Docling — IBM Research 의 문서 파싱 toolkit 상세 정리
    • 하네스 엔지니어링 실전 — 4인 에이전트 팀으로 코딩 파이프라인 구축하기
    • 하네스 엔지니어링 — 오래 실행되는 AI 에이전트를 위한 설계
    • 멀티모달 LLM (Multimodal Large Language Model)
    • AI 에이전트와 함께 MVP 만들기 — dooray-cli 사례
    • 온톨로지에서 코딩 에이전트 컨텍스트까지 — 클래스·관계 설계와 그래프 평가
    • OpenClaw는 context와 memory를 어떻게 관리하나 — 나만의 에이전트를 구성하는 법
    • OpenClaw vs Hermes Agent — 갈아탈까 고민하며 정리한 비교
  • ai 페이지로 이동
    • agent 페이지로 이동
    • [초안] AI 제품 백엔드 안정성 — 지연·비용·권한·관측·도구 실패·폴백/재시도/사람 에스컬레이션
    • [초안] LLM 평가 프레임워크: 골든셋, 회귀 테스트, LLM-as-a-judge, 사람 피드백 루프
  • algorithm 페이지로 이동
    • live-coding 페이지로 이동
    • 분산 계산을 위한 알고리즘
  • architecture 페이지로 이동
    • 시니어 백엔드를 위한 API 설계 실전 스터디 팩
    • API 버저닝과 하위 호환성: 모바일·외부 컨슈머까지 안전하게 진화시키기
    • 캐시 설계 전략 총정리
    • [초안] 커머스 Spring 서비스에 Clean/Hexagonal Architecture를 실용적으로 적용하기
    • [초안] 커머스 도메인 모델링: 주문·재고·노출의 세 축을 분리해서 설계하기
    • 커머스 주문 상태와 데이터 정합성 기본기
    • [초안] 쿠폰/프로모션 동시성과 정합성 기본기 — 선착순·중복 사용 방지·발급/사용/복구
    • [초안] DDD와 도메인 모델링: 시니어 백엔드 관점의 전술/전략 패턴 실전 가이드
    • [초안] Decorator & Chain of Responsibility — 행동을 체인으로 조립하는 두 가지 방식
    • 디자인 패턴
    • [초안] 분산 아키텍처 완전 정복: Java 백엔드 시니어 인터뷰 대비 실전 가이드
    • [초안] 분산 트랜잭션과 Outbox 패턴 — 왜 2PC를 피하고 어떻게 대신할 것인가
    • 분산 트랜잭션
    • [초안] e-Commerce 주문·결제 도메인 모델링: 상태머신, 멱등성, Outbox/Saga 실전 정리
    • [초안] Event Sourcing과 CQRS — 상태가 아니라 변화를 저장한다는 발상
    • [학습중] 금융 거래 취소·정정·대사·일마감 운영
    • [학습중] 금융 거래 상태와 원장 설계
    • [초안] F&B 쿠폰·프로모션·멤버십·포인트 설계
    • [초안] F&B · e-Commerce 디지털 채널 도메인 한 장 정리
    • [초안] F&B 주문/매장/픽업 상태머신 설계
    • [초안] F&B 이커머스 결제·환불·정산 운영 가이드
    • [초안] Hexagonal / Clean Architecture를 Spring 백엔드에 적용하기
    • [초안] 대규모 커머스 트래픽 처리 패턴 — 대규모 회원과 메가 프로모션을 버티는 설계
    • [초안] 레거시 JSP/jQuery 화면과 신규 API가 공존하는 백엔드 운영 전략
    • [학습중] 모듈러 모놀리스에서 MSA로 점진 전환하는 실습
    • [초안] MSA 서비스 간 통신: Redis [Cache-Aside](../database/redis/cache-aside.md) × Kafka 이벤트 하이브리드 설계
    • [초안] Observability 입문: 시니어 백엔드가 장애를 탐지하고 대응하는 방식
    • [초안] Outbox / Inbox Pattern 심화 — 분산 메시징의 정합성 문제를 DB 트랜잭션으로 풀어내기
    • [초안] 결제 도메인 멱등성과 트랜잭션 재시도 기본기
    • [초안] 시니어 백엔드를 위한 Resilience 패턴 실전 가이드 — Timeout, Retry, Circuit Breaker, Bulkhead, Backpressure
    • [초안] Spring Batch vs Event-Driven — 같은 비동기처럼 보이지만 전혀 다른 두 패러다임
    • [초안] Strategy Pattern — 분기문을 없애는 설계, 시니어 백엔드 인터뷰 핵심 패턴
    • [초안] 시니어 백엔드를 위한 시스템 설계 입문 스터디 팩
    • [초안] 템플릿 메서드 패턴 - 백엔드 처리 골격을 강제하는 가장 오래되고 가장 위험한 패턴
    • [초안] 대규모 트래픽 중 무중단 마이그레이션 — Feature Flag + Shadow Mode 실전
  • database 페이지로 이동
    • milvus 페이지로 이동
    • mysql 페이지로 이동
    • opensearch 페이지로 이동
    • qdrant 페이지로 이동
    • redis 페이지로 이동
    • vespa 페이지로 이동
    • 김영한의-실전-데이터베이스-설계 페이지로 이동
    • [초안] DB Connection Pool Saturation과 Thread Pool 격리
    • 커넥션 풀 크기는 얼마나 조정해야 할까?
    • 인덱스 - DB 성능 최적화의 핵심
    • [초안] JPA N+1과 커머스 조회 모델: 주문/메뉴/쿠폰 도메인에서 살아남기
    • [초안] MyBatis 기본기 — XML Mapper, resultMap, 동적 SQL, 운영 패턴 정리
    • [초안] MyBatis와 JPA/Hibernate 트레이드오프 — 레거시 백엔드를 다루는 시니어 관점
    • 한국어 형태소 분석기 Nori vs Lindera — OpenSearch에서 Milvus로 갈 때 어휘 검색은 유지되나
    • 벡터 DB 5종, 아키텍처는 어떻게 다른가
    • 벡터 DB 5종을 실제로 벤치마크했다 — 같은 recall에서 QPS는 얼마나 갈리나
    • 벡터 DB 어떻게 고를까 — OpenSearch · Milvus · Qdrant · Vespa · pgvector 비교
    • 벡터 DB를 실제로 도입한 사례 — 빅테크 프로덕션
    • 역정규화 (Denormalization)
    • 데이터 베이스 정규화
  • devops 페이지로 이동
    • docker 페이지로 이동
    • k8s 페이지로 이동
    • k8s-in-action 페이지로 이동
    • observability 페이지로 이동
    • [초안] 커머스/F&B 채널 장애 첫 5분과 관측성 기본기
    • [초안] 운영 데이터 정합성 장애 대응 — 결제 취소 누락과 중복 적재 런북
    • Envoy Proxy
    • [초안] F&B / e-Commerce 운영 장애 대응과 모니터링 — 백엔드 관점 정리
    • Graceful Shutdown
    • [초안] 시니어 백엔드를 위한 SLO와 Error Budget 기반 장애 대응
  • http 페이지로 이동
    • HTTP Connection Pool
    • HTTPS는 어떻게 안전한가 — TLS, 인증서, 그리고 termination
  • interview 페이지로 이동
    • [초안] AI 서비스 팀 경험 기반 시니어 백엔드 면접 질문 뱅크 — Spring Batch RAG / gRPC graceful shutdown / 캐시 정합성 / 12일 AI 웹툰 MVP
    • Observability — 면접 답변 프레임
    • [초안] 시니어 Java 백엔드 면접 마스터 플레이북 — 김병태
    • [초안] NSC 슬롯팀 경험 기반 질문 은행 — 도메인 모델링·동시성·성능·AI 협업
  • java 페이지로 이동
    • concurrency 페이지로 이동
    • jdbc 페이지로 이동
    • opentelemetry 페이지로 이동
    • spring 페이지로 이동
    • spring-batch 페이지로 이동
    • testing 페이지로 이동
    • 더_자바_코드를_조작하는_다양한_방법 페이지로 이동
    • [초안] Java 동시성 락 정리 — 커머스 메뉴/프로모션 정책 캐시 갱신 관점
    • [초안] JVM 튜닝 실전: 메모리 구조부터 Virtual Threads, GC 튜닝, 프로파일링까지
    • Java의 로깅 환경
    • MDC (Mapped Diagnostic Context)
    • Java StampedLock — 읽기 폭주에도 쓰기가 밀리지 않는 락
    • Virtual Thread와 Project Loom
  • javascript 페이지로 이동
    • typescript 페이지로 이동
    • AbortController
    • Async Iterator와 제너레이터
    • CommonJS와 ECMAScript Modules
    • 제너레이터(Generator)
    • Http Client
    • Node 백엔드 운영 패턴 — Streams 백프레셔, pipe/pipeline, 멱등성 vs 분산 락
    • Node.js
    • npm vs pnpm — 어떤 기준으로 선택했나
    • `setImmediate()`
  • kafka 페이지로 이동
    • [초안] Kafka 기본 개념 — 토픽, 파티션, 오프셋, 복제
    • Kafka를 사용하여 **데이터 정합성**은 어떻게 유지해야 할까?
    • [초안] Kafka 실전 설계: 파티션 전략, 컨슈머 그룹, 전달 보장, 재시도, 순서 보장 트레이드오프
    • [학습중] Kafka 파티션·리밸런스·컨슈머 지연 운영
    • 메시지 전송 신뢰성
    • [초안] Spring Kafka 컨슈머 오프셋 커밋과 트랜잭션 정렬: AckMode, manual ack, 멱등 처리
  • linux 페이지로 이동
    • fsync — 리눅스 파일 동기화 시스템 콜
    • tmux — Terminal Multiplexer
  • mlops 페이지로 이동
    • llm-serving 페이지로 이동
    • serving-frameworks 페이지로 이동
    • Python CUDA 버전 생태계 — nvidia-smi, nvcc, pip, conda가 다 다른 버전을 말하는 이유
    • GPU 컨테이너의 CUDA 버전 호환성 — nvidia-smi부터 이미지 다이어트까지
    • Kubernetes GPU 노드에서 /run tmpfs가 꽉 차서 Pod가 안 뜰 때
    • GPU·CUDA·MPS 기초 — 자바 백엔드 개발자가 처음 만나는 그림
    • Multi-process GPU 워크로드 — 자바 ThreadPool 사용자가 만나는 모델 차이
    • ML 서비스 성능 분석 워크플로 — 자바 백엔드 트러블슈팅과 다른 점
    • 한 GPU 를 여러 프로세스가 나눠 쓰기 — Time-Slicing 과 MPS
  • network 페이지로 이동
    • Connection reset by peer는 누가 보낸 걸까 — 리버스 프록시 홉마다 TCP 연결은 따로 논다
    • L2(스위치)와 L3(라우터)의 역할 차이
    • L4와 VIP(Virtual IP Address)
    • IP Subnet
  • python 페이지로 이동
    • Python async/await — CompletableFuture·Reactor 와 다른 점, 그리고 blocking I/O 함정
    • Python 의존성 관리 — Java Maven/Gradle 사용자가 만나는 첫 충격
    • FastAPI 기초 — Spring Boot 사용자가 빠르게 익히는 법
    • Java 개발자를 위한 Python 심화 — OOP·데코레이터·컨텍스트 매니저
    • PyTorch 기초 — 텐서, 디바이스, 그리고 모델 로딩이 무거운 이유
    • Java 개발자를 위한 Python 문법 핵심
    • ThreadLocal 에서 contextvars 로 — Python 의 요청 컨텍스트 전파
    • OCR 동작 원리 — Layout · Text · Post-process 3단계
    • Python 서버의 RSS 가 안 줄어드는 이유 — gc.collect 의 한계와 malloc_trim
  • rabbitmq 페이지로 이동
    • [초안] RabbitMQ Basics — 실전 백엔드 관점에서 정리하는 메시지 브로커 기본기
    • [초안] RabbitMQ vs Kafka — 백엔드 메시징 선택 기준과 실전 운영 관점
  • resume 페이지로 이동
    • [초안] 김병태 경력기술서
    • [초안] 김병태 포트폴리오
  • security 페이지로 이동
    • [초안] 시니어 백엔드를 위한 보안 / 인증 스터디 팩 — Spring Security, JWT, OAuth2, OWASP Top 10
    • [초안] Spring Security 6.x OAuth2 + JWT 상용 인증 설계 — Grant 선택, Resource Server, Refresh Rotation, 로그아웃
  • task 페이지로 이동
    • ai-service-team 페이지로 이동
    • nsc-slot 페이지로 이동
    • sb-dev-team 페이지로 이동
    • the-future-company 페이지로 이동
  • testing 페이지로 이동
    • [초안] 시니어 Java 백엔드를 위한 테스트 전략 완전 정리 — 피라미드부터 TestContainers, 마이크로벤치, Contract까지
  • thinking 페이지로 이동
    • 좋은 일을 넘어 중요한 일을 하는 법
FOS-BLOG · FOOTERall systems normal·v0.1 · 2026.04.27·seoul, kr
Ffos-blog/study

개발 학습 기록을 정리하는 블로그입니다. 공부하면서 기록하고, 기록하면서 다시 배웁니다.

visitors
01site
  • Home↗
  • Posts↗
  • Categories↗
  • Glossary↗
  • About↗
02policy
  • 소개/about
  • 개인정보처리방침/privacy
  • 연락처/contact
03categories
  • AI↗
  • Algorithm↗
  • DB↗
  • DevOps↗
  • Java/Spring↗
  • JS/TS↗
  • React↗
  • Next.js↗
  • System↗
04connect
  • GitHub@jon890↗
  • Source repositoryjon890/fos-study↗
  • RSS feed/rss.xml↗
  • Newsletter매주 1 회 · 한 편의 글→
© 2026 FOS Study. All posts MIT-licensed.
built with·Next.js·Tailwind v4·Geist·Pretendard·oklch
fos-blog/architecture/[학습중] 금융 거래 취소·정정·대사·일마감…
architecturedb

[학습중] 금융 거래 취소·정정·대사·일마감 운영

이 문서는 정상 거래보다 더 자주 운영자의 판단이 필요한 취소·정정·대사·일마감을 공부하기 위해 만들었다. 학습 목표는 확정 거래를 안전하게 되돌리는 방법, 내부 기록과 외부 결과의 차이를 찾는 방법, 영업일 단위로 장부를 닫는 방법을 하나의 운영 흐름으로 연결하는 것이다. 완료 기준은 불일치 데이터를 의도적으로 만들고, 대사 배치가 이를 분류하며, 정정...

2026.07.21·7 min read·30 views

이 문서는 정상 거래보다 더 자주 운영자의 판단이 필요한 취소·정정·대사·일마감을 공부하기 위해 만들었다. 학습 목표는 확정 거래를 안전하게 되돌리는 방법, 내부 기록과 외부 결과의 차이를 찾는 방법, 영업일 단위로 장부를 닫는 방법을 하나의 운영 흐름으로 연결하는 것이다. 완료 기준은 불일치 데이터를 의도적으로 만들고, 대사 배치가 이를 분류하며, 정정 거래와 재실행으로 잔액을 복구하는 실습을 끝내는 것이다.

정상 처리보다 복구 경로가 더 중요하다

금융 거래는 요청과 응답이 한 번에 끝나는 것처럼 보여도 여러 시스템을 지난다. 내부 DB는 성공했지만 외부 기관 응답이 유실될 수 있다. 외부 기관은 성공했지만 내부 결과 저장이 실패할 수도 있다.

이때 단순 재시도는 같은 금액을 두 번 움직일 위험이 있다. 운영 가능한 시스템은 정상 경로뿐 아니라 다음 경로를 명시적으로 가진다.

  • 아직 결과를 모르는 거래를 조회하고 확정한다.
  • 잘못된 거래를 반대 거래로 상쇄한다.
  • 두 시스템의 기록을 주기적으로 비교한다.
  • 영업일 종료 시 미결 항목을 다음 날로 넘기거나 예외로 격리한다.
  • 사람이 개입한 모든 결정을 감사 기록으로 남긴다.

취소와 정정은 같은 말이 아니다

취소는 아직 최종 확정되지 않은 거래를 무효화하는 동작이다. 예약된 금액을 해제하거나 승인 전 거래를 종료하는 흐름이 여기에 가깝다.

정정은 이미 확정된 결과가 잘못됐을 때 반대 효과를 가진 새 거래를 추가하는 동작이다. 확정 원장 항목을 UPDATE나 DELETE로 지우지 않는다.

환불은 고객에게 금액을 돌려주는 별도의 업무 거래다. 원거래와 연결되지만 처리 시점, 수수료, 한도, 외부 기관 상태가 다를 수 있다.

text
원거래  T100: 고객 계정 -10,000 / 정산 계정 +10,000
정정거래 T101: 고객 계정 +10,000 / 정산 계정 -10,000
T101.original_transaction_id = T100

이 구조는 원거래가 존재했다는 사실과 이후 상쇄됐다는 사실을 모두 보존한다.

상태 기계에 불명확 상태를 넣는다

외부 호출 타임아웃을 곧바로 FAILED로 바꾸면 실제 외부 성공을 놓칠 수 있다. 결과를 모르는 상태를 별도로 두고 조회나 대사로 해소해야 한다.

text
REQUESTED -> PROCESSING -> SUCCEEDED
                  |            |
                  v            v
               UNKNOWN      REVERSED
                  |
                  +-> SUCCEEDED
                  +-> FAILED

UNKNOWN은 기술적 실패가 아니라 판단 보류 상태다. 이 상태의 거래는 동일 명령을 다시 보내기 전에 외부 거래 식별자로 상태 조회를 시도한다.

대사는 무엇과 무엇을 비교하는가

대사(reconciliation)는 서로 독립적으로 기록된 두 데이터 집합이 같은 경제적 사실을 가리키는지 비교하는 작업이다.

대표 비교 축은 다음과 같다.

  • 업무 거래 테이블과 내부 원장
  • 내부 원장과 잔액 스냅샷
  • 내부 거래와 외부 기관 거래 명세
  • 원장과 회계 시스템 전표
  • 전일 마감 잔액과 당일 기초 잔액

단순히 총액만 같다고 끝내면 안 된다. 한 건이 누락되고 다른 한 건이 중복돼도 총액은 우연히 같을 수 있다. 건별 일치와 집계 일치를 함께 확인해야 한다.

대사 키를 먼저 설계한다

대사는 비교 키가 없으면 시작할 수 없다. 내부 거래 식별자와 외부 기관 식별자를 양쪽에 보존해야 한다.

sql
CREATE TABLE reconciliation_item (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    business_date DATE NOT NULL,
    internal_transaction_id VARCHAR(80),
    external_transaction_id VARCHAR(80),
    internal_amount DECIMAL(19, 2),
    external_amount DECIMAL(19, 2),
    result_type VARCHAR(30) NOT NULL,
    resolution_status VARCHAR(20) NOT NULL,
    detected_at TIMESTAMP(6) NOT NULL,
    resolved_at TIMESTAMP(6) NULL,
    UNIQUE KEY uk_reconciliation_pair (
        business_date,
        internal_transaction_id,
        external_transaction_id
    )
);

result_type은 최소한 다음 유형을 구분해야 한다.

  • MATCHED: 양쪽에 있고 핵심 값이 같다.
  • INTERNAL_ONLY: 내부에는 있지만 외부에는 없다.
  • EXTERNAL_ONLY: 외부에는 있지만 내부에는 없다.
  • AMOUNT_MISMATCH: 거래는 같지만 금액이 다르다.
  • STATUS_MISMATCH: 금액은 같지만 성공·취소 상태가 다르다.
  • DUPLICATED: 한쪽에 동일 거래가 여러 건 존재한다.

일마감은 날짜가 바뀌는 이벤트가 아니다

일마감(end-of-day close)은 특정 영업일의 거래를 더 이상 자유롭게 변경하지 못하도록 경계를 확정하는 운영 절차다. 달력 날짜와 영업일은 항상 같지 않다. 자정 이후 처리된 거래가 전 영업일에 속할 수도 있고 휴일 정책이 개입할 수도 있다.

마감에는 다음 입력이 필요하다.

  • 영업일과 컷오프 시각
  • 해당 영업일에 포함할 거래 상태
  • UNKNOWN과 미결 거래의 이월 정책
  • 외부 명세 도착 지연 허용 범위
  • 마감 결과를 다시 열 수 있는 권한과 절차

마감 상태를 별도 엔티티로 관리하면 재실행과 운영 판단이 명확해진다.

sql
CREATE TABLE business_day_close (
    business_date DATE PRIMARY KEY,
    status VARCHAR(20) NOT NULL,
    expected_count BIGINT NOT NULL,
    matched_count BIGINT NOT NULL,
    exception_count BIGINT NOT NULL,
    started_at TIMESTAMP(6),
    closed_at TIMESTAMP(6),
    version BIGINT NOT NULL
);
text
OPEN -> CLOSING -> CLOSED
          |
          +-> BLOCKED
CLOSED -> REOPENED -> CLOSING

재마감이 필요하면 기존 결과를 덮어쓰기보다 실행 이력과 사유를 남긴다.

대사 배치의 처리 순서

대사 배치는 입력 파일을 읽어 테이블에 넣는 것만으로 끝나지 않는다. 재실행과 부분 실패를 견디는 단계 분리가 필요하다.

  • 외부 명세의 파일 식별자와 체크섬을 검증한다.
  • 원본을 변경하지 않는 staging 영역에 적재한다.
  • 형식 오류와 중복 행을 격리한다.
  • 거래 키로 내부 기록과 외부 기록을 매칭한다.
  • 건별 차이와 집계 차이를 계산한다.
  • 자동 복구 가능한 항목과 사람 판단이 필요한 항목을 분리한다.
  • 마감 게이트를 평가하고 결과를 고정한다.
java
public record ReconciliationDecision(
    ReconciliationType type,
    ResolutionAction action,
    String reason
) {}
 
public ReconciliationDecision compare(
    InternalTransaction internal,
    ExternalTransaction external
) {
    if (internal == null) {
        return new ReconciliationDecision(
            ReconciliationType.EXTERNAL_ONLY,
            ResolutionAction.INVESTIGATE,
            "외부 성공에 대응하는 내부 거래가 없음"
        );
    }
    if (external == null) {
        return new ReconciliationDecision(
            ReconciliationType.INTERNAL_ONLY,
            ResolutionAction.QUERY_EXTERNAL,
            "외부 명세에서 거래를 찾지 못함"
        );
    }
    if (internal.amount().compareTo(external.amount()) != 0) {
        return new ReconciliationDecision(
            ReconciliationType.AMOUNT_MISMATCH,
            ResolutionAction.MANUAL_REVIEW,
            "거래 금액 불일치"
        );
    }
    return new ReconciliationDecision(
        ReconciliationType.MATCHED,
        ResolutionAction.NONE,
        "일치"
    );
}

자동 정정의 경계를 좁힌다

모든 불일치를 자동 정정하면 잘못된 규칙이 대량 금액 이동으로 이어질 수 있다. 자동 처리 범위는 결정적이고 되돌릴 수 있는 경우로 제한한다.

자동화하기 좋은 사례는 이미 취소 확정된 예약 금액을 해제하는 작업이다. 사람 검토가 필요한 사례는 외부 성공 거래가 내부에 전혀 없거나 금액이 다른 경우다.

각 조치에는 다음 정보가 남아야 한다.

  • 어떤 차이를 근거로 판단했는가.
  • 어떤 규칙 버전이 결정을 내렸는가.
  • 자동 처리인지 운영자 처리인지 구분된다.
  • 원거래와 정정 거래가 서로 연결된다.
  • 재실행해도 같은 조치가 중복 적용되지 않는다.

나쁜 설계와 개선된 설계

타임아웃을 실패로 확정한다

타임아웃은 결과를 받지 못했다는 뜻이지 외부 처리가 실패했다는 뜻이 아니다. UNKNOWN으로 두고 거래 조회와 대사 경로로 넘긴다.

원거래 행을 수정한다

운영자가 원거래 금액이나 상태를 직접 바꾸면 당시의 사실과 수정 과정을 구분할 수 없다. 정정 명령과 반대 원장 항목을 추가한다.

총액만 비교한다

총액 일치는 필요한 조건이지만 충분하지 않다. 거래 식별자, 금액, 통화, 상태를 건별로 비교한 뒤 집계 검증을 추가한다.

배치 성공 여부만 본다

프로세스 exit code가 0이어도 예외 항목이 남아 마감을 막을 수 있다. 처리 건수, 매칭률, 미결 금액, 가장 오래된 미결 건의 나이를 함께 관찰한다.

로컬 실습

앞선 금융 거래 상태와 원장 설계의 테이블을 재사용한다.

다음 데이터를 의도적으로 만든다.

  • 내부와 외부가 완전히 일치하는 거래
  • 내부에만 존재하는 거래
  • 외부에만 존재하는 거래
  • 금액이 다른 거래
  • 외부에는 취소됐지만 내부에는 성공으로 남은 거래
  • 같은 외부 거래 식별자가 두 번 등장한 거래

대사 프로그램을 두 번 실행해 결과 행이 중복되지 않는지 확인한다. 일부 항목을 정정한 뒤 다시 실행해 resolution_status가 안정적으로 전이되는지 확인한다.

sql
SELECT result_type,
       COUNT(*) AS item_count,
       SUM(ABS(COALESCE(internal_amount, 0) - COALESCE(external_amount, 0))) AS difference_amount
FROM reconciliation_item
WHERE business_date = DATE '2026-07-20'
GROUP BY result_type;

마감은 exception_count = 0만으로 결정하지 않는다. 허용된 이월 유형과 금액 한도를 정책으로 분리하고, 정책 버전을 마감 이력에 남긴다.

운영 대시보드에서 볼 지표

  • 영업일별 거래 건수와 금액
  • 대사 일치율과 불일치 금액
  • UNKNOWN 상태 건수와 최고 체류 시간
  • 자동 정정과 수동 정정 건수
  • 마감 시작부터 완료까지 걸린 시간
  • 재실행 횟수와 같은 항목의 반복 실패 횟수

개별 고객 거래 식별자나 계좌 정보는 일반 메트릭 라벨에 넣지 않는다. 고카디널리티와 개인정보 노출을 피하고, 상세 추적은 권한이 통제된 조회 도구로 분리한다.

설명할 때의 답변 구조

확정 전 취소와 확정 후 정정을 구분합니다. 외부 호출 타임아웃은 실패로 단정하지 않고 UNKNOWN 상태로 두며, 외부 거래 조회와 대사로 해소합니다. 확정 원장은 수정하지 않고 원거래를 참조하는 반대 원장 항목으로 상쇄합니다. 대사는 건별 키·금액·상태 비교와 집계 비교를 함께 수행하고, 일마감은 미결 항목과 외부 명세 도착 여부를 확인하는 영업일 게이트로 관리합니다. 배치는 멱등하게 재실행할 수 있어야 하며 자동 정정 범위는 결정적인 경우로 제한합니다.

이 답변 뒤에는 직접 만든 불일치 데이터와 재실행 테스트 결과를 설명해야 한다. 직접 운영 경험이 없다면 경험처럼 말하지 않고 어떤 실패를 재현했고 어떤 불변 조건을 검증했는지 구분한다.

학습 완료 체크리스트

  • 취소, 환불, 정정의 차이를 설명할 수 있다.
  • 외부 타임아웃에 UNKNOWN 상태가 필요한 이유를 설명할 수 있다.
  • 건별 대사와 집계 대사를 함께 구현했다.
  • 불일치 유형별 조치 정책을 분리했다.
  • 확정 원장을 UPDATE하지 않고 정정 거래를 생성했다.
  • 영업일과 달력 날짜의 차이를 모델에 반영했다.
  • 대사 배치를 두 번 실행해도 결과가 중복되지 않는다.
  • 마감 차단 조건과 이월 정책을 설명할 수 있다.
  • 운영자 개입 기록과 규칙 버전을 보존한다.

참고 자료

  • SAP Help Portal — Universal Journal
  • MySQL 8.4 Reference Manual — InnoDB Transaction Model
  • Spring Batch Reference Documentation
  • AWS Prescriptive Guidance — Transactional Outbox Pattern
on this page
  • 01정상 처리보다 복구 경로가 더 중요하다
  • 02취소와 정정은 같은 말이 아니다
  • 03상태 기계에 불명확 상태를 넣는다
  • 04대사는 무엇과 무엇을 비교하는가
  • 05대사 키를 먼저 설계한다
  • 06일마감은 날짜가 바뀌는 이벤트가 아니다
  • 07대사 배치의 처리 순서
  • 08자동 정정의 경계를 좁힌다
  • 09나쁜 설계와 개선된 설계
  • 타임아웃을 실패로 확정한다
  • 원거래 행을 수정한다
  • 총액만 비교한다
  • 배치 성공 여부만 본다
  • 10로컬 실습
  • 11운영 대시보드에서 볼 지표
  • 12설명할 때의 답변 구조
  • 13학습 완료 체크리스트
  • 14참고 자료
tags
#학습중#카카오뱅크#금융도메인#취소#정정#대사#일마감#배치

이런 글도

  • [학습중] 모듈러 모놀리스에서 MSA로 점진 전환하는 실습
    이 문서는 대규모 모놀리스를 한 번에 재작성하지 않고 경계를 세운 뒤 작은 서비스부터 분리하는 방법을 공부하기 위해 만들었다. 학습 목표는 코드 모듈화, 데이터 소유권, 호출 전환, 관찰과 롤백을 하나의 마이그레이션 흐름으로 설명하는 것이다. 완료 기준은 작은 Spring Boot 모놀리스에서 한 모듈을 독립 서비스로 추출하고, 기존 경로와 신규 경로의 결...
    🏗️ architecture
    architecture
    2026.07.21
  • [학습중] 금융 거래 상태와 원장 설계
    이 문서는 수신·여신·지급결제 서비스에서 돈의 상태가 어떻게 변하고, 그 변화를 원장에 어떻게 남기는지 공부하기 위해 만들었다. 학습 목표는 거래 상태와 잔액을 구분하고, 이중 기록과 불변 조건으로 오류를 탐지하며, 재시도에도 같은 거래를 한 번만 반영하는 설계를 설명하는 것이다. 완료 기준은 간단한 계좌이체를 상태 기계와 복식 원장으로 구현하고, 중복 요...
    🏗️ architecture
    architecture
    2026.07.21
  • [초안] Event Sourcing과 CQRS — 상태가 아니라 변화를 저장한다는 발상
    이 문서의 목표는 두 가지다. 하나, "현재 상태를 덮어쓰는" 일반적인 CRUD 모델과 "일어난 사건을 append-only로 쌓는" Event Sourcing이 어떻게 다른지 감을 잡는 것. 둘, Event Sourcing과 자주 한 묶음으로 거론되는 CQRS가 사실은 독립된 패턴이며, 언제 함께 쓰고 언제 따로 떼어야 하는지 판단 기준을 세우는 것. 결...
    🏗️ architecture
    architecture
    2026.06.16
  • [초안] Spring Batch vs Event-Driven — 같은 비동기처럼 보이지만 전혀 다른 두 패러다임
    > 관련 문서: Outbox / Inbox Pattern 심화, 분산 트랜잭션과 Outbox 패턴. 본 문서는 두 처리 패러다임의 선택 기준과 trade-off에 집중하고, 위 두 문서는 이벤트 발행의 정합성 메커니즘에 집중한다. 백엔드를 4\5년차 이상 다루다 보면 어느 시점에서 "이건 동기로 처리하기 어렵다"는 결론에 도달한다. 사용자가 결제 버튼을 누...
    🏗️ architecture
    architecture
    2026.05.16

댓글 (0)