데이터를 수집해서 CSV로 저장하는 일은 어렵지 않습니다. 문제는 서로 다른 두 시스템에서 추출한 CSV가 실제로 같은 데이터를 의미하는지 확인하는 것입니다. 예를 들어 원본 시스템에는 주문이 100건 있는데 정산 파일에는 99건만 있거나, 같은 주문번호가 두 번 들어가거나, 주문금액이 서로 다른 경우를 생각해 봅시다. 파일을 눈으로 비교해서는 원인과 영향 범위를 파악하기 어렵습니다.
이전에 작성한 대용량 데이터 로드와 처리 방법에서 CSV와 Parquet 등 파일 형식의 처리 효율을 비교했습니다. 이번에는 속도보다 중요한 데이터 정합성(Data Reconciliation)을 살펴보고자 합니다. 파일을 빠르게 읽더라도 잘못된 값이 조용히 통과하면 자동화는 오히려 오류를 확산시킵니다.
무엇을 기준으로 비교해야 할까?
처음부터 두 파일의 행 수나 총금액만 비교하면 안 됩니다. 예를 들어 한 파일에서 10원이 누락되고 다른 행에서 10원이 중복된다면 총액은 같아 보일 수 있습니다. 먼저 두 파일에서 같은 사건을 가리키는 고유 식별자(예: 주문번호, 거래 ID, 설비 이벤트 ID)를 정해야 합니다.
| 상태 | 의미 | 다음 조치 |
|---|---|---|
| MATCH | 키와 비교 대상 값이 동일 | 정상으로 분류 |
| SOURCE_ONLY | 원본에만 존재 | 누락된 적재·전송 단계 점검 |
| TARGET_ONLY | 대상에만 존재 | 범위·기간 차이 또는 과거 잔존 데이터 확인 |
| DUPLICATE_ID | 동일 파일에 같은 키가 여러 번 있음 | 자동 합산 금지, 중복 원인 검토 |
| AMOUNT_MISMATCH | 같은 키지만 금액이 다름 | 단위·반올림·환율·갱신 시점 확인 |
여기서 SOURCE_ONLY는 값이 비어 있다는 뜻이 아닙니다. 비교 대상 파일에 해당 키 자체가 없다는 뜻입니다. 결측값의 의미와 처리 정책은 별개의 문제입니다.
예전에 결측치 처리 방법과 실무 전략에서 다뤘었으니 참고 하셔도 좋습니다.
작은 데이터로 먼저 재현하기
아래 코드는 외부 시스템에 접근하지 않는 합성 데이터 기반 Python 예제입니다.
통화 값을 부동소수점 float으로 비교하지 않고 Decimal로 처리하며, 같은 키가 여러 번 나오면 임의의 한 행을 선택하지 않고 중복으로 표시합니다.
from collections import defaultdict
from decimal import Decimal, InvalidOperation
def index_rows(rows):
grouped = defaultdict(list)
for row in rows:
key = str(row['id']).strip()
if not key:
raise ValueError('empty id')
try:
amount = Decimal(str(row['amount']).strip())
except InvalidOperation as exc:
raise ValueError(f'invalid amount for {key}') from exc
if not amount.is_finite():
raise ValueError(f'non-finite amount for {key}')
grouped[key].append(amount)
return grouped
def reconcile(source_rows, target_rows):
left, right = index_rows(source_rows), index_rows(target_rows)
result = []
for key in sorted(left.keys() | right.keys()):
a, b = left.get(key, []), right.get(key, [])
if len(a) > 1 or len(b) > 1:
status = 'DUPLICATE_ID'
elif not a:
status = 'TARGET_ONLY'
elif not b:
status = 'SOURCE_ONLY'
elif a[0] != b[0]:
status = 'AMOUNT_MISMATCH'
else:
status = 'MATCH'
result.append((key, status))
return result
원본 데이터가 A001=120.00, A002=80.00, A003=45.00 두 행이고, 대상 데이터가 A001=120.00, A002=82.00, A004=20.00 이라면 결과는 다음과 같습니다.
A001: MATCH
A002: AMOUNT_MISMATCH
A003: DUPLICATE_ID
A004: TARGET_ONLY
이 작은 결과만으로도 “총액이 왜 다른가?”라는 질문을 각 거래 단위의 검증 가능한 예외 목록으로 바꿀 수 있습니다.
실무 자동화에서는 무엇을 추가해야 할까?
위 코드를 바로 운영 시스템에 붙이는 것은 권장하지 않습니다.
실제 업무에서는 비교 기준을 문서화하고 예외 경로를 테스트해야 합니다.
- 비교 범위: 시스템별 시간대, 정산 기준일, 취소·재처리 건의 포함 여부를 고정한다.
- 키 정책: 중복 키를 오류로 둘지, 버전이 다른 이벤트로 볼지 사전에 합의한다.
- 단위와 정밀도: 통화·세율·환율·반올림 단위를 명시한다.
Decimal만 사용한다고 모든 정산 규칙이 해결되지는 않는다. - 입력 검증: 필수 열, 인코딩, 잘못된 숫자, 공백 식별자, NaN을 확인한다.
- 출력: 원본을 덮어쓰지 말고
status, 식별자, 비교값, 오류 사유가 들어간 별도 예외 리포트를 만든다. - 재현성: 동일한 입력과 규칙으로 다시 실행했을 때 같은 결과가 나와야 한다.
- 검수: 정상·중복·누락·금액 불일치·비정상 입력 사례에 대한 테스트를 준비한다.
CSV 비교와 데이터 동기화는 다르다
정합성 검사(Reconciliation)는 차이를 발견하는 작업이고, 동기화(Synchronization)는 한쪽 시스템에 변경을 적용하는 작업입니다. 후자는 삭제·수정 권한과 되돌리기 정책이 필요하며, 두 작업을 하나의 버튼으로 묶으면 오류를 발견하는 순간 잘못된 수정까지 실행할 수 있습니다.
직접 확인할 수 있는 예제와 적용 범위
DeepLinK에는 합성 데이터를 이용해 정합성 검사를 체험하는 CSV Reconciliation Lab과 Python Automation & Data Validation 서비스 안내가 있습니다. 브라우저 체험판은 실제 고객 시스템에 연결된 운영 배포 사례가 아니라 개념을 시험하는 데모입니다.
실무에 테스트해 볼 때는 먼저 두 파일의 샘플(필요하면 익명화), 식별자, 비교 규칙, 예상 예외를 정리하고, 입력과 결과를 검증할 수 있는 범위를 합의한 뒤 자동화로 발전시키는 것이 안전합니다.
관련 서비스: Python Workflow Automation & Data Validation
기술 범위 문의: Technical scope request
