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