Excel 영수증 자동화 개발기: gpt 기반 실용적 OCR 솔루션

hbjs97·2025년 4월 23일
post-thumbnail

시작: 단순한 관찰에서 출발한 프로젝트

어느정도 규모가 있는 기업들은 이미 ERP 시스템과 연동된 자동화 솔루션을 쓰고 있겠지만, 많은 중소기업과 스타트업은 여전히 영수증을 수동으로 처리하고 있을 것이다.
우리 회사도 그랬다. 영수증을 하나하나 Excel에 입력하는 모습을 자주 봤다. 날짜, 상호명, 금액, 품목... 영수증 한 장에 1-2분씩, 월말이면 수백 장이 쌓인다.

시중의 경비처리 솔루션들도 있지만 도입 비용이나 학습 곡선 때문에 결국 익숙한 Excel로 돌아온다. "Excel 안에서 바로 자동화할 수 없을까?"라는 생각이 들었다.

간단한 사이드 프로젝트로 시작했다. 목표는 명확했다:

  • Excel에서 직접 작동할 것
  • 한국어 영수증을 정확히 인식할 것
  • 설치와 사용이 간단할 것

첫 번째 시도: 오픈소스 OCR의 현실

오픈소스 OCR 시도

Tesseract, EasyOCR, PaddleOCR 등 여러 오픈소스 OCR을 테스트했다.

공통적인 문제:

  • 한국어 영수증의 낮은 인식률
  • 영수증 특유의 레이아웃(구분선, 테이블) 처리 미흡
  • 흐릿하거나 기울어진 텍스트 인식 실패

실제로 사용하기에는 정확도가 너무 낮았다. 수동으로 수정해야 할 부분이 많아 자동화의 의미가 없었다.

두 번째 시도: 상용 API의 벽

상용 OCR API 검토

네이버 클로바 OCR와 업스테이지 Document AI를 검토했다. 둘 다 한국어 영수증을 잘 인식한다는 리뷰를 확인할 수 있었다.

하지만 비용이 문제였다:

CLOVA OCR

CLOVA OCR

UPSTAGE OCR

UPSTAGE OCR

세 번째 시도: GPT-4o의 발견

혹시나 하는 마음에 OpenAI의 GPT-4o 멀티모달을 테스트해봤는데 결과가 예상보다 좋았다.

실제 테스트에서 확인한 것:

  • 구조화된 JSON으로 바로 반환 가능
  • 영수증의 맥락을 이해하고 처리
  • 흐릿한 텍스트도 문맥으로 추론 가능
  • 처리 시간은 이미지당 2-4초 정도

Temperature 0에서도 100% 일관성은 없었지만, 기존 OCR 모델보다 정확하며 상용 OCR API 보다 비용이 저렴했다.

구현: Excel Add-in으로

왜 Excel Add-in인가?

처음엔 독립 프로그램을 생각했다. 하지만:

  • 사용자가 Excel 켜고, 프로그램 켜고, 데이터 복사하고... 번거롭다
  • Excel 안에서 모든 게 끝나면 좋겠다
  • Office Add-in은 설치도 간단하다

핵심 로직: Excel API 통합

가장 어려웠던 부분은 Excel API와의 통합이었다.

// Excel 테이블 메타데이터 가져오기
const getTableMeta = async (): Promise<TableMeta> => {
  return Excel.run(async (context) => {
    const selectedRange = context.workbook.getSelectedRange();
    selectedRange.load("rowIndex,columnIndex,columnCount,values");
    await context.sync();

    // 사용자가 선택한 헤더 추출
    const headers = selectedRange.values[0].map((cell) => (cell === null ? "" : String(cell)));

    return {
      headers,
      startRow: selectedRange.rowIndex,
      startCol: selectedRange.columnIndex,
      colCount: selectedRange.columnCount,
    };
  });
};

문제: Excel API는 비동기인데, 에러 처리가 까다롭다.

// 데이터 삽입 로직 (실제는 더 복잡)
const insertBillData = async (data: any[]) => {
  try {
    await Excel.run(async (context) => {
      const sheet = context.workbook.worksheets.getActiveWorksheet();
      const usedRange = sheet.getUsedRange();
      usedRange.load("rowCount");
      await context.sync();

      // 빈 행 찾기, 데이터 삽입
      // 각 단계마다 context.sync() 필요
      // 에러 발생 시 롤백이 어려움
    });
  } catch (error) {
    // Excel API 에러는 매우 불친절하다
    // "GeneralException" 같은 모호한 메시지
    console.error("데이터 삽입 실패:", error);
  }
};

서버 구현: FastAPI + GPT-4o

# 서버 핵심 로직 (Python/FastAPI)
@app.post("/scan")
async def scan_receipts(
    files: List[UploadFile],
    columns: str = Form(...)
):
    results = []

    for file in files:
        # 이미지를 base64로 인코딩
        image_base64 = base64.b64encode(await file.read()).decode()

        # GPT-4o에 요청
        response = await openai.chat.completions.create(
            model="gpt-4o",
            messages=[{
                "role": "system",
                "content": f"영수증에서 다음 정보를 추출: {columns}"
            }, {
                "role": "user",
                "content": [{
                    "type": "image_url",
                    "image_url": {"url": f"data:image/jpeg;base64,{image_base64}"}
                }]
            }],
            temperature=0,
            response_format={"type": "json_object"}
        )

        results.append(json.loads(response.choices[0].message.content))

    return {"results": results}

요청 제한: 현실적 고려

# 요청 제한 구현
from slowapi import Limiter
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)

@app.post("/scan")
@limiter.limit("10/minute")  # 분당 10개 요청
async def scan_receipts(...):
    # GPT-4o API도 제한이 있고
    # 비용도 고려해야 한다
    pass

실제 사용 결과

실제로 사용해본 결과:

  • 한국어 영수증도 대부분 잘 인식
  • 처리 시간은 영수증당 3-5초
  • GPT-4o API 비용은 토큰 사용량에 따라 변동

한계와 개선점

현재 한계

  1. 일관성: Temperature 0에서도 출력 형식이 조금씩 다름
  2. 속도: 대량 처리에는 시간이 걸림
  3. 비용: API 사용료가 발생
  4. 정확도: 100%가 아니므로 검수 필요

마무리: 완벽하지 않아도 충분히 유용한

이 프로젝트는 "완벽한 OCR 솔루션"이 아니다. 하지만:

  • 수동 입력 시간을 크게 줄여준다
  • Excel 안에서 모든 작업이 끝난다
  • 한국어 영수증도 처리 가능하다

무엇보다, 실제로 사용 가능한 수준이다.

물론 한계는 있다. 하지만 영수증 자동화라는 실용적 목표에는 충분했다.

코드는 GitHub에 공개했다. 개선 아이디어가 있다면 PR은 언제나 환영이다.

0개의 댓글