은행거래내역 CSV 파서
기반 ADR: 0003 Pack 1 채권 계산 §C4
개요
의뢰인이 가져온 은행 거래내역 CSV·PDF·엑셀 을 붙여넣으면 변제 내역을 자동 채움. 사무원 타이핑 시간 대폭 절감. AI 없이 결정론 파싱 (Phase 0) → AI 적요 분류 보조 (Phase 1).
왜 필요한가
- 의뢰인이 가져오는 자료: 은행 거래내역 PDF/엑셀 + 카톡 스샷 + "2022년 봄쯤" 메모 (P2 사무장)
- 한 칸씩 타이핑하게 두면 엑셀 대비 우위 없음 (P2)
- 이게 없으면 사무장은 엑셀로 돌아감 (P10 회계 사무원)
Phase 별 범위
Phase 0 (결정론, AI 0)
- 국민·카카오뱅크·토스뱅크 3개 은행 CSV 어댑터
- 헤더 자동 감지 + 컬럼 매핑 미리보기
- 적요 규칙 기반 80% 분류 (변제/이자/무관)
- 수기 입력 병행
Phase 1 (AI 적요 분류 weight 1)
- 추가 5개 은행: 신한·하나·우리·농협·IBK
- 커스텀 컬럼 매핑 템플릿 저장 (로펌 법무팀 자체 양식)
- AI 적요 분류
settlementParse(weight 1)- 폴백: 규칙 기반 80% + "미분류" 수동 배치
- Gemini zero-shot structured output
Phase 2 (제외)
- 적요 → 충당 금액 자동 분해: out-of-scope (AI 금지, P5·P10 합의)
사용자 플로우
기본 CSV 업로드
- 변제 섹션 하단
[📋 CSV 붙여넣기]클릭 - 은행 드롭다운 선택 (Phase 0: 3개, Phase 1: 8개 + 커스텀)
- 엑셀·CSV 붙여넣기 or 파일 드래그
- 첫 5행 미리보기 + 컬럼 매핑 드롭다운
- 자동 분류 결과 확인 (예: "12건 중 10건 변제·2건 제외")
- 수동 보정 → [검토 완료] → 변제 테이블에 일괄 삽입
커스텀 템플릿 (Phase 1)
- 은행 외 포맷 (법무팀 엑셀·구두 정리 자료)
- 드롭다운 "기타 포맷 수기" 선택
- 컬럼 매핑 수동 지정 (거래일·금액·적요)
- [ 템플릿 저장 ] → 다음에 재사용
은행별 포맷
국민은행 (Phase 0)
| 컬럼 | 의미 |
|---|---|
| 거래일시 | 2026-03-15 14:32:11 |
| 적요 | 박철수 2월이자 |
| 입금액 | 500,000 |
| 출금액 | - |
| 잔액 | 3,200,000 |
카카오뱅크 (Phase 0)
| 컬럼 | 의미 |
|---|---|
| 거래일시 | 2026.03.15 14:32 |
| 거래구분 | 입금 / 출금 |
| 거래금액 | 500,000 |
| 잔액 | 3,200,000 |
| 메모 | 박철수 2월이자 |
토스뱅크 (Phase 0)
- JSON 스타일 Excel
- 헤더 감지 특수 처리
Phase 1 추가 5개 (신한·하나·우리·농협·IBK)
각 은행별 컬럼명·날짜 포맷·부호 (입금 + / 출금 -) 다름 → 어댑터 분기.
적요 해석 규칙 (Phase 0)
80% 커버 목표.
패턴 1 — 채무자 성명 매칭
- 사건의 채무자 이름 "박철수" · 적요 "박철수" 포함 → 변제
- 사건 의뢰인 이름과 다른 성명 포함 → 무관
패턴 2 — 변제 키워드
- "이자", "원금", "변제", "반환", "차용금" 포함 → 변제
- "이자" 단독 →
appropriationOverride: "interest"지정충당 추정
패턴 3 — 조합
- 성명 + 월 단위 ("박철수 2월이자") → 변제 + 해당 월 추정
미분류 처리
- 20% 미분류 → "미분류" 상태 + 사용자 수동 배치
- 완전 자동화는 Phase 2 이후 검토
엣지 케이스
| 케이스 | 처리 |
|---|---|
| 이체 취소 (음수 행) | 원행과 페어링 (거래일·금액·적요 매칭) |
| 수수료 차감 (1,000,000 → 999,500) | 500원 수수료 보정 플래그 |
| 현금 입금 ("타행입금" vs "전자금융") | 증빙 강도 배지 분리 |
| 같은 날 같은 금액 여러 건 (정상 중복: 일급·용돈) | 경고 배지만, 완전 차단 ✗ |
| 중복 입력 (해시 충돌) | (거래일 + 금액 + 적요) 해시 dedupe |
어댑터 인터페이스
// apps/web/app/(workspace)/cases/[caseId]/_lib/bank-csv-parsers.ts
export interface BankAdapter {
readonly bankId: "kb" | "kakao" | "toss" | "shinhan" | "hana" | "woori" | "nh" | "ibk" | "custom";
readonly displayName: string;
readonly samplePreview: string; // 첫 5행 샘플 (UI 에서 표시)
parse(
content: string | ArrayBuffer,
options?: { columnMapping?: ColumnMapping }
): Promise<ParseResult>;
}
export interface ParseResult {
rows: TransactionRow[];
totalCount: number;
classifiedCount: { repayment: number; unrelated: number; unclassified: number };
warnings: Warning[];
}
export interface TransactionRow {
transactionDate: Timestamp;
amount: number; // 정수 원
direction: "in" | "out" | "cancel";
memo: string;
balance?: number;
classification: "repayment" | "unrelated" | "unclassified";
classificationReason: string; // 규칙 matched
feeAmount?: number;
duplicateWarning?: boolean;
sourceHash: string; // 중복 방지
}
AI 적요 분류 (Phase 1)
settlementParse feature, weight 1:
export async function classifyRepaymentsByAi(
caseId: string,
unclassifiedRows: TransactionRow[]
): Promise<ActionResult<TransactionRow[]>>;
프롬프트 구조:
- 사건 정보 (채무자 이름·대여 건수·청구 금액)
- 미분류 거래 20건 묶음
- Gemini Flash structured JSON:
{ rows: [{ index, classification, reason }] } - Zod 검증 → 실패 시
classifyRefund환불
폴백:
- AI 실패 → "미분류" 상태 + 사용자 수동 배치 (기존 동작)
- 품질 회귀 모니터링: P15
editRatio증가 감지
기술 스택
- CSV 파싱:
papaparse(클라이언트) - XLSX 파싱:
xlsx(Server Action 에서만, 클라 bundle 크기) - PDF 파싱:
pdf-parse(OCR 필요 시 Document AI) - HWP 미지원 (은행 내역 HWP 사용 안 함)
테스트 (Phase 0)
- 국민·카카오뱅크·토스뱅크 실제 CSV 샘플 각 20건 파싱 성공
- 헤더 변형 (컬럼명 대소문자·공백) 자동 감지
- 중복 해시 dedupe 검증
- 취소 행 페어링 검증
- 적요 규칙 분류 80%+ 커버 (50건 수동 라벨링 대비)