흩어진 금융 데이터들의 거대한 대통합
세금계산서 따로, 통장 입출금 내역 따로, 법인카드 명세서 따로... 사람이 일일이 눈으로 대조(Reconciliation)하던 끔찍한 시간은 끝났습니다.
Python을 이용해 "세금계산서 1건 + 통장 입금 1건 + 카드 결제 1건 + 거래처 1곳"을 완벽한 '하나의 거래 객체'로 엮어버리는 통합 매칭 시스템을 구축합니다.
Reconciliation (거래 대사) 란?
Reconciliation은 재무제표와 실제 통장/증빙의 숫자가 정확히 맞아떨어지는지(대사) 확인하는 회계의 핵심 과정입니다.
이번 27단계에서는 단순한 1:1 매칭을 넘어, 부분 입금, 분할 결제까지 고려한 Entity Matching(객체 매칭)과 Record Linkage(레코드 연결) 알고리즘을 파이썬으로 구현합니다.
예를 들어, ABC상사와 1,100만원 거래를 했다면, 기존에는 엑셀 창 3개를 띄워놓고 눈으로 비교했습니다. 이제 Python이 이것들을 단 하나의 구조화된 노드(Node)로 묶어줍니다.
4대 핵심 데이터의 수집 및 표준화
최소 4개의 서로 다른 원천 데이터를 다룹니다: transactions.xlsx(장부), tax_invoice.xlsx(증빙), bank.xlsx(통장), card.xlsx(카드).
각 문서마다 "공급자", "적요", "가맹점" 등 부르는 이름이 다릅니다. 파이썬 Pandas를 이용해 컬럼명과 데이터 타입을 강제 표준화 시켜야 매칭이 가능해집니다.
import pandas as pd # 1. 컬럼명 표준화 (모두 merchant_name으로 통일) bank = bank.rename(columns={"적요": "merchant_name"}) cards = cards.rename(columns={"가맹점": "merchant_name"}) # 2. 날짜 데이터 강제 형변환 transactions["date"] = pd.to_datetime(transactions["date"]) # 3. 금액 데이터 정제 (콤마 제거 후 Float 변환 함수) def clean_amount(series): return series.astype(str).str.replace(",", "").astype(float)
ABC상사, ABC 상사, (주)ABC상사, ABC상사(주)... 이름만으로 매칭하면 100% 오류가 납니다.
정규화 함수를 통해 공백과 (주)를 제거하되, 가장 안전한 방법은 '사업자등록번호'라는 고유 식별자를 최우선 매칭 키로 사용하는 것입니다.
계층적 매칭과 Score 산정
모든 데이터가 깔끔하게 1원, 1일 단위로 일치하면 좋겠지만 실무는 그렇지 않습니다. 세금계산서 작성일(3/15)과 실제 입금일(3/18)이 며칠씩 차이 날 수 있습니다.
따라서 여러 항목을 복합적으로 검사하여 매칭 점수(Matching Score)를 도출하는 함수를 작성합니다.
def matching_score(row): score = 0 if row["business_match"]: score += 40 # 사업자번호 일치 (가장 강력) if row["amount_match"]: score += 30 # 금액 일치 if row["date_diff"] <= 3: score += 20 # 날짜가 ±3일 이내 차이남 if row["name_match"]: score += 10 # 정규화된 거래처명 일치 return score # [점수 기반 판단 기준] # 90~100점 : 높은 확률 매칭 (자동 승인) # 70~89점 : 매칭 가능 (회계사 확인 필요) # 50~69점 : 검토 필요 (부분 입금 의심) # 0~49점 : 매칭 실패
가장 큰 난제: 분할입금 (1:N, N:1 매칭)
1천만 원짜리 세금계산서가 발행되었는데, 통장으로는 3백만, 3백만, 4백만씩 세 번에 걸쳐 입금(1:N)될 수 있습니다. 혹은 반대로 5백만 원짜리 계산서 두 장을 묶어서 1천만 원에 한 번에 입금(N:1)할 수도 있습니다.
단순한 1:1 VLOOKUP으로는 이 문제를 풀 수 없습니다. 파이썬의 groupby()를 활용해 특정 기간 내의 동일 거래처 입금 합계(Sum)를 산출하고, 이를 매출액과 대조하여 잔액(receivable = sales - payment)이 0이 되는지 확인하는 다대다 매칭 로직을 짜야 합니다.
단일 거래 객체 (Transaction Object)와 그래프 데이터
위의 모든 과정을 거치면, 파이썬 메모리 상에 다음과 같이 훌륭하게 구조화된 JSON 기반 거래 객체가 탄생합니다. 이는 향후 Graph DB(Neo4j 등) 기반 회계 분석 시스템으로 나아가는 초석이 됩니다.
{ "transaction_id": "T001", "customer": { "name": "ABC상사", "business_number": "1234567890" }, "invoice": { "id": "E001", "amount": 11000000 }, "bank": [ { "id": "B001", "amount": 11000000 } ] }
위험점수 엔진과 RAG 기반 AI 검토 지원
이렇게 모인 자료를 바탕으로 "증빙금액 불일치", "미수금 존재", "증빙 없음" 등의 상태를 판별하여 위험 점수(Risk Score)를 부과합니다.
그리고 위험 점수가 높은 거래는 즉각 RAG (23단계)와 AI Agent에게 전달되어, 단순 오류 표기를 넘어 회계사를 위한 상세한 조언을 생성합니다.
이 화면을 본 회계사는 [정상] [미수금 처리] [거래 수정] 버튼 중 하나를 누르게 되며, 이 행위는 모두 Audit Log로 영구 보존됩니다.
DB 성능 최적화 (Index) 및 SaaS 통제
수십만 건의 거래와 증빙을 매칭하려면 Python의 for문으로는 한계가 옵니다. DB 레벨에서의 SQL Index 설계가 필수적입니다.
CREATE INDEX idx_transaction_business ON transactions (office_id, business_number); CREATE INDEX idx_transaction_amount ON transactions (office_id, amount);
위 SQL 인덱스에 office_id가 가장 먼저 등장하는 것을 확인하셨나요?
24단계에서 배웠듯, 조회, 매칭, 필터링 등 시스템의 모든 쿼리 동작은 무조건 WHERE office_id = ? 를 기본 전제로 깔고 들어가야 보안 사고가 발생하지 않습니다.
28단계 예고: 붕어빵처럼 찍어내는 AI 자동 분개
이제 우리는 완벽하게 깨끗하고, 서로 연결(매칭)되어 있고, 증빙이 첨부되어 있으며, 이상 거래가 걸러진 [무결성 100% 통합 거래 데이터]를 손에 넣었습니다.
다음 28단계에서는 이 완벽한 덩어리를 보고, AI가 "차변: 소모품비 100,000 / 대변: 보통예금 110,000" 형태의 분개(Journal Entry) 후보를 스스로 생성하고 추천하는 마법 같은 시스템을 만들겠습니다.