본문으로 건너뛰기
고급약 40분

원장 정합성과 금액 계산 검증 — 불변식·단수 처리·영업일·결과 불명 거래·마감 배치·대사

금융 시스템의 가장 치명적인 결함은 화면 오류가 아니라 한 원이 어긋난 원장이다. 복식부기와 잔액 불변식을 시험 오라클로 쓰는 법, 부동소수점과 단수 처리·이자 일수 계산의 경계, 영업일과 휴일 이월, 타임아웃 뒤 결과를 모르는 거래와 멱등성, 동시 출금 경합, 마감·결산 배치의 재실행, 내부 원장과 대외기관 정산 파일의 대사를 시험으로 설계한다.

  • #원장정합성
  • #금액계산
  • #단수처리
  • #멱등성
  • #대사
  • #배치테스트
  • #금융IT

개념

금융에서 "대체로 맞다"는 없다

대부분의 소프트웨어에서 작은 계산 오차는 낮은 심각도 결함이다. 금융에서는 한 원이 어긋난 원장 이 치명 결함이다. 한 원은 작아 보이지만, 수백만 계좌에 같은 계산이 적용되면 큰 금액이 되고, 무엇보다 원장을 믿을 수 없게 된다. 고객 민원, 회계 결산 불일치, 감독 당국 보고 오류가 그 뒤를 따른다.

레슨 1에서 금융 전산장애의 가장 큰 원인이 프로그램 오류라고 했다. 그중 발견이 가장 늦고 수습이 가장 비싼 것이 원장과 금액 계산 결함 이다. 화면이 깨지면 바로 보이지만, 이자 계산이 특정 조건에서 틀리면 결산 때나 고객 민원으로 드러난다.

불변식을 시험 오라클로 쓴다

금융 원장은 몇 가지 항상 성립해야 하는 관계(불변식) 를 갖는다. 이 불변식이 가장 강력한 시험 오라클이다. 개별 거래의 기대 결과를 일일이 계산하지 않아도, 불변식이 깨지면 결함이다.

불변식내용확인 시점
복식부기 균형모든 분개에서 차변 합계 = 대변 합계거래마다
잔액 연속성기말 잔액 = 기초 잔액 + 입금 합계 − 출금 합계계좌별, 일마감
거래 내역 합과 잔액계좌 잔액 = 거래 내역의 누적 합 (잔액 테이블과 내역 테이블이 따로 있을 때)일마감, 무작위 표본
계정 합계개별 계좌 잔액 합 = 총계정원장의 해당 계정 잔액일마감
이체 보존내부 이체에서 출금 계좌 감소액 = 입금 계좌 증가액 (+ 수수료 계정)거래마다
음수 잔액 금지한도 없는 계좌의 잔액 ≥ 0거래마다, 동시성 시험

이 불변식은 모든 기능 시험의 후처리 검사 로 붙인다. 이체 기능을 시험하든, 수수료 기능을 시험하든, 시험이 끝나면 영향받은 계좌와 계정에 대해 불변식을 검사한다. 기능 시험 기대 결과가 맞아도 불변식이 깨졌다면 다른 곳에 부수 효과 가 있었다는 뜻이다.

불변식은 속성 기반 테스트(property-based testing) 와 궁합이 좋다. 무작위 거래 시퀀스를 대량 생성해 실행하고 불변식이 깨지는 최소 사례를 찾는다.

from hypothesis import given, strategies as st
 
amounts = st.integers(min_value=1, max_value=10_000_000)   # 원 단위 정수
ops = st.lists(st.tuples(st.sampled_from(["deposit", "withdraw", "transfer"]), amounts), max_size=200)
 
@given(ops)
def test_balance_continuity(sequence):
    ledger = TestLedger(accounts={"A": 1_000_000, "B": 0})
    opening = ledger.total()
    for op, amount in sequence:
        ledger.try_apply(op, amount)          # 잔액 부족 등은 거절되어야 함
    assert ledger.total() == opening + ledger.external_in() - ledger.external_out()
    assert all(bal >= 0 for bal in ledger.balances().values())
    assert ledger.journal_is_balanced()       # 모든 분개 차변 = 대변

금액을 원 단위 정수 로 생성한 점을 보자. 다음 절의 이유 때문이다.

금액 표현과 단수 처리

부동소수점으로 금액을 계산하면 안 된다. 0.1 + 0.2 가 0.30000000000000004 가 되는 이진 부동소수점 오차는 금액에서 한 원 차이를 만든다. 금액은 최소 단위 정수(원, 센트)나 십진 고정소수점 타입으로 다룬다. 코드 리뷰(모듈 9)에서 금액 필드의 타입을 확인하는 것이 가장 싼 시험이다.

더 까다로운 것은 단수 처리(반올림·절사·절상) 다. 금융 계산에는 원 미만이 자주 생기고, 어느 단계에서 어떻게 처리하느냐에 따라 결과가 달라진다.

선택예결과 차이
처리 방식반올림 / 절사 / 절상 / 은행가 반올림(짝수 쪽으로)0.5원 경계에서 달라짐
처리 시점일별 이자마다 처리 후 합산 / 기간 이자 합산 후 한 번 처리30일 합산 시 최대 수십 원 차이
처리 단위원 단위 / 10원 단위세금·수수료 규정에 따라 다름
처리 대상세전 이자에서 세액 계산 후 각각 처리 / 세후 금액 한 번에 처리세액과 세후 이자의 합이 세전과 한 원 어긋날 수 있음

이 선택들은 상품 규정·약관·세법 등 계산 규칙 문서 에서 와야 하고, 시험 기준은 그 문서를 근거로 한다(모듈 38 레슨 1의 근거 칸). 구현의 현재 동작을 기대 결과로 쓰면 잘못된 단수 처리가 영원히 통과한다.

경계값 분석(모듈 3 레슨 1)을 단수 처리에 적용하면 이런 시험이 나온다.

[이자 단수 처리 경계 — 예시 규칙: 일별 이자 원 미만 절사 후 월 합산]
잔액 1,000,000원, 연 3.65%, 일수 기준 365일
일별 이자 = 1,000,000 × 0.0365 / 365 = 100.0원        → 100원 (경계 정확히 정수)
잔액 1,000,001원 → 100.0001원                         → 100원 (절사)
잔액   999,999원 → 99.9999원                          → 99원  (절사 — 반올림이면 100원)
30일 합산: 절사 후 합산 99×30 = 2,970원 vs 합산 후 절사 floor(2,999.997) = 2,999원 → 29원 차이

마지막 줄의 29원 차이가 전형적인 결함 부류다. 명세가 "월 합산 후 절사"인데 구현이 "일별 절사 후 합산"이면, 모든 계좌에서 매달 몇십 원씩 고객에게 덜 지급된다. 금액이 작아 눈에 띄지 않다가 대량 민원이나 감독 검사에서 드러난다.

이자 일수와 기간 계산

이자 계산에서 날짜는 금액만큼 결함을 만든다.

요소시험 포인트
일수 계산 기준실제 일수/365, 실제 일수/실제 일수, 30/360 — 상품별 규칙 확인
윤년2월 29일 포함 기간, 윤년의 연 365일 기준 상품에서 366일째 처리
양 끝 포함 여부가입일 포함·만기일 제외 (한편 넣기) 등 규칙
월말1월 31일 가입 1개월 만기 → 2월 28일(또는 29일)
금리 변경기간 중 금리 변경 시 구간별 계산
중도 해지경과 기간별 중도해지 이율 구간 경계

"1월 31일 가입 월 단위 상품"은 날짜 라이브러리마다 결과가 다르다(2월 28일·3월 3일·예외). 모듈 40 레슨 1에서 SOUP 요구사항으로 날짜 라이브러리를 시험했던 것처럼, 날짜 계산 라이브러리의 동작을 상품 규칙과 대조 하는 시험을 둔다.

영업일과 휴일 이월

금융 거래는 영업일 달력을 따른다. 자동이체일·만기일·결제일이 휴일이면 다음 영업일(또는 이전 영업일)로 옮겨진다. 결함은 달력 데이터와 이월 규칙 두 곳에서 나온다.

[영업일 시험 조건]
- 토·일, 법정공휴일, 대체공휴일, 선거일 등 임시공휴일 (연중 갑자기 지정됨)
- 연휴가 월말·분기말에 걸침 (결산일 이월)
- 이월 규칙이 상품마다 다름 (다음 영업일 / 이전 영업일 / 다음 영업일이 다음 달이면 이전 영업일)
- 휴일 이월로 이자 일수가 늘어나는가 줄어드는가 (규칙 확인)
- 대외기관(금융결제원 등) 영업일과 내부 달력 불일치
- 임시공휴일이 지정된 뒤 이미 예약된 자동이체의 처리

임시공휴일은 연중에 발표되므로 달력 데이터 갱신 절차 자체가 시험 대상이다. 달력 데이터가 늦게 반영되면 그날 대외기관이 쉬는데 이체 요청이 나가 대량 실패가 난다.

결과 불명 거래와 멱등성

모듈 29 레슨 7에서 네트워크 저하 상태의 타임아웃·재시도·중복을 봤다. 금융에서는 이것이 돈의 중복 이동 이 된다.

가장 까다로운 상황은 결과 불명(unknown) 거래 다. 대외기관에 이체를 요청했는데 응답이 타임아웃으로 오지 않았다. 대외기관 쪽에서는 처리됐을 수도, 안 됐을 수도 있다.

잘못된 처리결과
타임아웃 = 실패로 보고 잔액 복원, 고객이 재시도대외기관이 처리했다면 이중 출금
타임아웃 = 성공으로 보고 완료 처리대외기관이 처리 안 했다면 돈이 사라짐

올바른 처리는 결과 불명 상태를 따로 두고 확인 절차(거래 조회·망취소·일괄 대사)로 확정하는 것이다. 시험 기준은 이렇게 쓴다.

D-TRF-UNK-01  대외 응답 타임아웃 시 거래 상태 = 결과 불명, 출금 금액은 보류(홀드)로 유지
D-TRF-UNK-02  결과 불명 거래가 있는 동안 고객 화면에 "처리 확인 중" 표시, 같은 거래의 재요청은 차단 또는 같은 멱등성 키로 흡수
D-TRF-UNK-03  거래 조회 응답 "처리됨" → 완료 확정, "미처리" → 보류 해제, 조회도 실패 → 재조회 정책대로
D-TRF-UNK-04  정해진 시간 안에 확정되지 않으면 대사 대상으로 넘어가고 운영자 알림
D-TRF-UNK-05  같은 멱등성 키로 요청이 두 번 들어오면 원장 반영은 한 번, 두 번째 응답은 첫 결과와 동일

이 기준들은 장애 주입 없이는 시험할 수 없다. 대외기관 모의 서버에서 "처리 후 응답 누락", "처리 전 연결 끊김", "지연 응답(타임아웃 직후 도착)"을 만들어 원장 결과를 확인한다. 모듈 19 레슨 4의 프록시 응답 조작, 모듈 39 레슨 7의 네트워크 장애 주입이 도구다. 세 번째 경우 — 타임아웃 직후에 성공 응답이 도착 — 가 가장 많은 결함을 찾는다.

동시성 — 같은 계좌에 동시에

잔액 확인과 출금 반영 사이에 다른 출금이 끼어들면 잔액 이상 출금이 된다. 동시성 결함은 단일 시험에서 재현되지 않는다.

시험기대
잔액 10만 원 계좌에 7만 원 출금 요청 두 건을 동시에한 건 성공, 한 건 잔액 부족 거절. 잔액 3만 원
같은 계좌에서 이체·카드 승인·자동이체가 동시에모든 조합에서 음수 잔액 없음, 불변식 유지
입금과 출금이 동시에결과 잔액이 순서와 무관하게 일관
한도(일일 이체 한도) 경계에서 동시 요청한도 초과 누적 없음

동시 요청을 수백 번 반복하고 매번 불변식을 검사 한다. 한 번 통과는 아무것도 증명하지 않는다(모듈 18b 레슨 5의 반복 실행과 같은 논리).

마감·결산 배치

금융 시스템에는 일마감, 이자 결산, 수수료 일괄 부과, 대외기관 정산 파일 생성 같은 배치 가 많다. 배치 결함은 대량으로 한 번에 터진다.

시험이유
재실행 멱등성배치가 중간에 실패해 다시 돌렸을 때 이자가 두 번 붙지 않는가
부분 실패100만 계좌 중 1건 오류 시 나머지 처리·오류 건 격리·재처리
실행 중 거래마감 배치 실행 중 들어온 거래가 어느 영업일로 귀속되는가
시간 경계자정 직전·직후 거래, 서버 간 시각 차이
처리 시간운영 규모 데이터에서 마감 시간 창 안에 끝나는가 (모듈 16 성능)
결과 검증배치 후 불변식 전수 검사, 전일 대비 이상치(이자 총액 급변)

재실행 멱등성 이 가장 중요하다. 새벽 배치가 실패하면 운영자는 재실행 버튼부터 누른다. 그때 이미 처리된 계좌에 이자가 한 번 더 붙으면 장애 위에 원장 오류가 겹친다.

대사 — 내 원장과 남의 원장을 맞춘다

금융회사는 대외기관(금융결제원, 카드사·VAN사, 한국은행, 다른 은행)과 돈을 주고받고, 매일 정산 파일 로 결과를 맞춘다. 내부 시스템끼리도 계정계와 정보계, 원장과 회계 시스템이 맞아야 한다. 이 맞추는 일이 대사(reconciliation) 다. 모듈 4 레슨 6에서 마이그레이션 검증 도구로 봤던 리콘실리에이션이 금융에서는 매일 돌아가는 운영 통제 다.

대사 기능 자체가 시험 대상이다.

[대사 기능 시험 — 불일치 유형을 의도적으로 만든다]
유형 1  내부에만 있음        내부 완료, 대외 파일에 없음 (결과 불명 거래가 잘못 확정)
유형 2  대외에만 있음        대외 파일에 있음, 내부에 없음 (응답 유실)
유형 3  금액 불일치          수수료 계산 차이, 단수 처리 차이
유형 4  상태 불일치          내부 취소, 대외 완료
유형 5  날짜 불일치          영업일 귀속 차이
유형 6  중복                같은 거래가 파일에 두 번
 
판정: 각 유형이 정확히 검출·분류되고, 운영자 조치 화면에 나타나며, 조치 기록이 원장 통제(레슨 1 §27)를 따른다
     일치하는 거래 수백만 건 속에서 불일치 1건을 놓치지 않는다

"일치 수백만 건 속 불일치 1건"은 성능과 정확성이 함께 걸린 시험이다. 대사 로직이 키 매칭을 느슨하게 하면(거래일+금액만 비교) 같은 날 같은 금액의 다른 거래끼리 잘못 짝지어져 불일치가 사라진다.

실무에서 왜 필요한가

금융 결함 중 사후 수습 비용이 가장 큰 것은 원장 오류다. 영향받은 계좌를 찾고, 올바른 금액을 재계산하고, 보정하고(레슨 1의 원장 통제 절차), 고객에게 안내하고, 감독 당국에 보고한다. 몇 원짜리 단수 처리 결함 하나가 수백만 계좌에 걸치면 이 비용이 기능 결함 수백 건보다 크다.

불변식 오라클, 단수 처리 경계, 결과 불명 거래, 배치 재실행, 대사 시험은 금융 QA 경력자와 일반 QA를 가르는 도메인 감각 이다. 면접에서 "이체 기능을 어떻게 시험하겠습니까"에 화면과 응답 코드만 말하면 일반 QA이고, "타임아웃 직후 성공 응답이 도착하는 경우의 원장 상태와 대사 결과"를 말하면 금융 QA다.

실습 과제

과제 1 — 불변식 도출 (15분)

신용카드 결제 시스템(승인·매입·취소·부분 취소·할부)에 대해 항상 성립해야 할 불변식을 다섯 개 이상 쓰고, 각각을 언제 검사할지 정한다.

과제 2 — 단수 처리 경계 (20분)

"정기적금 월 납입, 연 4.2%, 월 단위 단리, 이자소득세 원천징수" 상품의 계산 규칙을 가정해 적고(단수 처리 방식·시점·단위를 명시), 규칙이 틀리게 구현됐을 때 결과가 달라지는 경계 시험 케이스를 여덟 개 이상 설계한다.

과제 3 — 결과 불명 거래 시험 (20분)

타행 이체의 대외기관 모의 서버로 만들 장애 시나리오 다섯 개(처리 후 응답 누락·처리 전 연결 끊김·타임아웃 직후 성공 응답 등)를 설계하고, 각 시나리오의 원장·고객 화면·대사 결과 기대값을 쓴다.

과제 4 (선택) — 배치 재실행 (15분)

"월말 이자 결산 배치가 계좌 60만 건 처리 후 실패"한 상황에서 재실행 시험을 설계한다. 이미 처리된 계좌의 판별 방법, 재실행 후 불변식 검사, 전월 대비 이상치 검사를 포함한다.

자가 체크리스트

  • 금융에서 한 원 차이가 치명 결함인 이유를 설명할 수 있다
  • 복식부기 균형·잔액 연속성·계정 합계 등 원장 불변식을 시험 오라클로 쓸 수 있다
  • 속성 기반 테스트로 무작위 거래 시퀀스에서 불변식 위반을 찾는 방법을 안다
  • 금액에 부동소수점을 쓰면 안 되는 이유와 단수 처리의 방식·시점·단위·대상 차이를 설명할 수 있다
  • 이자 일수·윤년·월말·금리 변경·영업일 이월의 경계 시험을 설계할 수 있다
  • 결과 불명 거래의 위험과 올바른 처리, 장애 주입 시험을 설계할 수 있다
  • 멱등성 키와 동시 출금 경합을 반복 시험과 불변식 검사로 확인할 수 있다
  • 마감·결산 배치의 재실행 멱등성·부분 실패·시간 경계를 시험할 수 있다
  • 대사 기능의 불일치 유형을 의도적으로 만들어 검출·분류를 시험할 수 있다

흔한 실수

기능 시험의 기대 결과만 보고 불변식을 검사하지 않는다. 다른 계좌·계정의 부수 효과를 놓친다.

단수 처리 기대값을 현재 구현에서 가져온다. 계산 규칙 문서가 근거여야 한다.

타임아웃을 실패로 시험한다. 결과 불명 상태와 확정 절차가 있어야 한다.

동시성 시험을 한 번 돌리고 통과로 본다. 수백 번 반복하고 매번 불변식을 검사한다.

배치를 처음부터 끝까지 성공하는 경우만 시험한다. 중간 실패 후 재실행이 운영의 현실이다.

대사 매칭 키를 느슨하게 둔 채 불일치 0건을 믿는다. 불일치를 의도적으로 만들어 검출되는지 확인한다.

참고 자료

낯선 말 풀이

이 레슨에 나온 약어입니다. 본문에서도 처음 나올 때 점선으로 표시해 두었습니다 (제목과 링크 글자는 그대로 둡니다).

SOUP — 출처가 불분명한 소프트웨어
우리가 만들지 않아서 어떻게 만들어졌는지 모르는 채 가져다 쓰는 부품 코드.
QA — 품질보증
만든 것이 쓸 만한지 확인하고, 애초에 덜 망가지게 만드는 방법을 정하는 일. "테스트"는 그 일의 일부다.
API — 응용 프로그램 인터페이스
프로그램끼리 주고받는 창구. 사람이 화면 버튼을 누르는 대신, 프로그램이 정해진 주소로 요청을 보내 답을 받는다.

용어집 전체 보기