Skip to content

AI-powered invoice parser that turns PDF chaos into subscription insights

Notifications You must be signed in to change notification settings

gimseonjin/invoice-ocr

Repository files navigation

Invoice OCR

PDF 인보이스를 AI로 자동 분석하고, 구독 비용을 한눈에 관리하는 시스템

SaaS 구독이 늘어나면서 매달 쌓이는 인보이스 PDF를 일일이 열어보는 게 번거로웠습니다. "PDF만 넣으면 알아서 정리해주는 시스템"을 만들고 싶었고, 그 결과물입니다.

CI


스크린샷

대시보드

전체 인보이스 현황과 주요 지표를 한눈에 확인할 수 있습니다.

대시보드

인보이스 목록

파싱된 모든 인보이스를 조회하고 검색할 수 있습니다.

인보이스 목록

인보이스 상세

개별 인보이스의 상세 정보와 항목별 내역을 확인합니다.

인보이스 상세

통계 분석

월별 지출 추이와 벤더별 비용 분석 차트를 제공합니다.

통계 분석

배치 동기화

디렉토리 단위로 인보이스 PDF를 일괄 처리하고 진행 상황을 확인합니다.

배치 동기화

API 문서

Swagger UI를 통해 REST API를 테스트하고 문서를 확인할 수 있습니다.

API 문서


주요 기능

1. AI 기반 인보이스 OCR

  • PDF 업로드 → 자동으로 인보이스 정보 추출
  • 인보이스 번호, 발행사, 금액, 항목별 내역까지 구조화
  • 구독 서비스의 경우 청구 주기(월간/연간) 자동 감지

2. 배치 처리 시스템

  • 디렉토리 단위 일괄 처리 지원
  • 중복 요청 방지 및 실시간 진행률 확인
  • 실패한 파일만 따로 재처리 가능

3. 분석 대시보드

  • 월별 지출 추이 차트
  • 벤더별 비용 분석
  • 갱신 예정 구독 알림

기술적 의사결정

AI OCR 파이프라인

왜 Zerox + GPT-4o-mini인가?

처음엔 Tesseract OCR을 고려했지만, 인보이스마다 레이아웃이 달라서 정규식 기반 파싱이 한계가 있었습니다. LLM 기반 추출로 방향을 틀었고, 몇 가지 옵션 중 Zerox를 선택했습니다:

  • 비용: GPT-4o-mini는 GPT-4 대비 약 1/10 비용
  • 정확도: JSON Schema를 지정하면 구조화된 응답을 안정적으로 받음
  • 개발 편의성: Zerox가 PDF → 이미지 변환, 멀티페이지 처리를 추상화
// apps/batch/src/parser/parser.service.ts
const result = await zerox({
  filePath,
  model: 'gpt-4o-mini',
  extractOnly: true,
  schema: invoiceSchema,  // JSON Schema로 출력 형식 고정
  concurrency: 5,
  maxRetries: 2,
});

날짜 정규화 문제

해외 SaaS 인보이스는 25.12.2024 (유럽식), 12/25/2024 (미국식) 등 포맷이 제각각입니다. 모든 날짜를 ISO 형식(YYYY-MM-DD)으로 통일하는 정규화 레이어를 추가했습니다.


배치 처리 아키텍처

왜 Bull Queue인가?

단순히 for문으로 처리할 수도 있지만, 다음 이유로 메시지 큐를 도입했습니다:

  1. 장애 격리: API 서버와 배치 워커 분리 → 한쪽이 죽어도 다른 쪽 영향 없음
  2. 재시도 정책: 지수 백오프로 일시적 실패 자동 복구
  3. 수평 확장: 워커만 늘리면 처리량 증가
┌─────────────┐     ┌─────────┐     ┌─────────────┐
│   API App   │────▶│  Redis  │────▶│ Batch Worker│
│  (트리거)   │     │ (Queue) │     │  (처리)     │
└─────────────┘     └─────────┘     └─────────────┘

3계층 중복 방지

같은 인보이스가 여러 번 등록되는 걸 막기 위해 3단계 검증을 구현했습니다:

계층 체크 포인트 구현
1. 작업 레벨 동일 디렉토리/파일에 대해 이미 진행 중인 작업이 있는가? BatchJobRepository.findActiveJob()
2. 큐 레벨 Bull Queue에 고유한 jobId 지정 parse-dir-${directory}-${id}
3. 비즈니스 레벨 이미 등록된 인보이스 번호인가? InvoiceRepository.existsByInvoiceNumber()

재시도 정책

네트워크 오류나 OpenAI API 일시 장애에 대응하기 위해 지수 백오프를 적용했습니다:

{
  attempts: 3,
  backoff: {
    type: 'exponential',
    delay: 1000,  // 1초 → 2초 → 4초
  },
}

아키텍처

flowchart TB
    subgraph Client
        Browser[웹 브라우저]
    end

    subgraph API["API Server :3000"]
        REST[REST API]
        SSR[SSR Views]
        Swagger[Swagger Docs]
    end

    subgraph Queue["Message Queue"]
        Redis[(Redis)]
        Bull[Bull Queue]
    end

    subgraph Worker["Batch Worker :3001"]
        Processor[Job Processor]
        Parser[PDF Parser]
        Zerox[Zerox + GPT-4o-mini]
    end

    subgraph Storage
        PG[(PostgreSQL)]
        Files[/Invoice PDFs/]
    end

    Browser --> REST
    Browser --> SSR
    REST --> Bull
    Bull --> Redis
    Redis --> Processor
    Processor --> Parser
    Parser --> Zerox
    Parser --> PG
    REST --> PG
    SSR --> PG
    Processor --> Files
Loading

기술 스택

분류 기술
Runtime Node.js, NestJS 11
Database PostgreSQL, TypeORM
Queue Bull, Redis
AI/OCR Zerox, OpenAI GPT-4o-mini
Frontend Handlebars (SSR), Tailwind CSS, Chart.js
DevOps Docker, GitHub Actions
Documentation Swagger/OpenAPI

빠른 시작

Docker Compose (권장)

# 1. 저장소 클론
git clone https://github.com/YOUR_USERNAME/invoice.git
cd invoice

# 2. 환경 변수 설정
cp .env.example .env
# .env 파일을 열어 OPENAI_API_KEY 설정

# 3. 실행
docker-compose up -d

# 4. 접속
# - 웹 UI: http://localhost:3000
# - API 문서: http://localhost:3000/api/docs

로컬 개발 환경

# 사전 요구사항: Node.js 18+, PostgreSQL, Redis

# 1. 의존성 설치
npm install

# 2. 환경 변수 설정
cp .env.example .env
# DATABASE_*, REDIS_*, OPENAI_API_KEY 설정

# 3. DB 마이그레이션
npm run migration:run

# 4. 개발 서버 실행 (터미널 2개)
npm run start:api:dev    # API 서버
npm run start:batch:dev  # Batch 워커

# 5. 접속
# - 웹 UI: http://localhost:3000
# - API 문서: http://localhost:3000/api/docs

환경 변수

변수명 설명 기본값
PORT API 서버 포트 3000
BATCH_PORT Batch 워커 포트 3001
DATABASE_HOST PostgreSQL 호스트 localhost
DATABASE_PORT PostgreSQL 포트 5432
DATABASE_USER PostgreSQL 사용자 postgres
DATABASE_PASSWORD PostgreSQL 비밀번호 -
DATABASE_NAME 데이터베이스 이름 invoice
REDIS_HOST Redis 호스트 localhost
REDIS_PORT Redis 포트 6379
OPENAI_API_KEY OpenAI API 키 (필수) -
INVOICES_DIR 인보이스 PDF 디렉토리 ./invoices

테스트

# 단위 테스트
npm run test

# 특정 파일만 테스트
npm run test -- --testPathPattern="invoices"

# 테스트 커버리지
npm run test:cov

# E2E 테스트
npm run test:e2e

API 엔드포인트

Invoices

Method Endpoint 설명
GET /api/invoices 인보이스 목록 조회
GET /api/invoices/:id 인보이스 상세 조회
DELETE /api/invoices/:id 인보이스 삭제

Statistics

Method Endpoint 설명
GET /api/statistics/summary 전체 요약 통계
GET /api/statistics/monthly 월별 통계
GET /api/statistics/by-vendor 벤더별 통계
GET /api/statistics/subscriptions 활성 구독 목록

Batch

Method Endpoint 설명
POST /api/batch/trigger 디렉토리 동기화 트리거
POST /api/batch/trigger/file 단일 파일 파싱
GET /api/batch/job/:jobId 작업 상태 조회

전체 API 문서는 /api/docs에서 Swagger UI로 확인할 수 있습니다.


프로젝트 구조

invoice/
├── apps/
│   ├── api/                 # REST API + SSR 서버
│   │   └── src/
│   │       ├── invoices/    # 인보이스 CRUD
│   │       ├── statistics/  # 통계 분석
│   │       ├── batch/       # 배치 트리거
│   │       └── views/       # Handlebars 템플릿
│   │
│   └── batch/               # 배치 처리 워커
│       └── src/
│           ├── parser/      # PDF 파싱 (Zerox)
│           ├── batch/       # 큐 프로세서
│           └── commands/    # CLI 명령어
│
├── libs/
│   └── common/              # 공유 라이브러리
│       └── src/
│           ├── database/    # TypeORM 엔티티, 레포지토리
│           ├── dto/         # 공통 DTO
│           ├── interfaces/  # 인터페이스 정의
│           └── filters/     # 글로벌 예외 필터
│
└── test/                    # E2E 테스트

향후 개선 계획

현재 버전에서 아쉬운 부분과 개선 방향입니다:

  • 인증/인가: 현재 인증 없음 → JWT 기반 인증 추가 예정
  • 알림 기능: 구독 갱신 임박 시 이메일/슬랙 알림
  • 다중 통화 지원: 현재 USD 기준 → 환율 적용 통합 대시보드
  • PDF 미리보기: 원본 PDF를 웹에서 바로 확인
  • 벤더 자동 분류: 인보이스 패턴 학습으로 카테고리 자동 태깅

라이선스

이 프로젝트는 MIT 라이선스를 따릅니다.

About

AI-powered invoice parser that turns PDF chaos into subscription insights

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published