이미지 업로드부터 OCR 자동화까지, 세금계산서 등록 프로세스

2025. 7. 21. 14:13·⛲ 프로젝트

 문제 정의: 복잡하고 오류가 잦았던 세금계산서 등록 프로세스

기존 세금계산서 등록은 이미지를 업로드한 후 사용자가 직접 수기로 입력하는 구조였습니다.

하지만 이 방식은 실제 사용 과정에서 다음과 같은 심각한 비효율과 오류를 유발했습니다:

  • 이미지를 회전하거나 자르지 않고 업로드 → OCR 인식 실패
  • 여러 장이 포함된 사진 업로드 → 텍스트 누락 또는 잘못된 매칭
  • 중복 세금계산서 업로드 → 관리자 수동 확인 필요
  • OCR 결과를 그대로 등록 → 오타, 누락된 정보 발생

이로 인해 세금계산서 1건을 처리하는 데 평균 약 250분이 소요되었고, 사용자 오타율은 월 10건 이상에 달했습니다.
이를 개선하기 위해 이미지 편집부터 OCR 자동화까지 전 과정 자동화 시스템을 구축하게 되었습니다.

 

개선한 전체 흐름

 

사용자 이미지 업로드
   ↓
이미지 크롭, 회전, 반전 (ImageCrop 컴포넌트)
   ↓
변환된 이미지 Canvas로 변환 후 Base64
   ↓
OCR 서버에 요청
   ↓
- 중복인지 확인
- 반려 상태인지 확인
   ↓
결과에 따라 분기 처리 + Step 이동

 

 

🧩 구현 상세

📌 react-image-crop + Canvas API 기반 이미지 회전/반전/크롭 처리

 

  • 사용자가 세금계산서 부분만 자를 수 있도록 react-image-crop을 활용해 UI를 제공했습니다.
    하지만 이 라이브러리는 실제로 이미지를 자르지는 않기 때문에, Canvas API를 통해 이미지 회전·반전·크롭을 처리했습니다.

✅ Situation

사용자가 이미지를 회전/자르지 않고 업로드하면, OCR 서버가 문서를 제대로 인식하지 못했습니다.

✅ Action

  • react-image-crop: 화면상 자르기 UI 제공
  • Canvas API: 실제 이미지 crop, 회전, 반전 적용

 

ctx.translate(canvas.width / 2, canvas.height / 2);  // 중심 이동
ctx.rotate((rotation * Math.PI) / 180);               // 회전 적용
ctx.scale(flipX, flipY);                             // 좌우/상하 반전
ctx.drawImage(                                        // 최종 출력
  image,
  newX, newY, newWidth, newHeight,  // 원본 이미지 좌표
  -newWidth / 2, -newHeight / 2,    // 캔버스 위치
  newWidth, newHeight               // 출력 크기
);

 

 

  • translate는 회전을 중심점 기준으로 하기 위한 설정
  • rotate는 라디안 단위
  • scale(flipX, flipY)는 반전 처리 (1 또는 -1)
  • drawImage의 좌표가 회전한 뒤 기준으로 정확해야 자른 영역이 일치함

 

crop 정보 변환 계산

const scaleX = naturalWidth / renderedImageWidth;
const scaleY = naturalHeight / renderedImageHeight;

x *= scaleX;
y *= scaleY;
width *= scaleX;
height *= scaleY;
  • 렌더된 이미지와 실제 이미지의 차이를 보정
  • 정확한 픽셀 단위로 잘라 OCR 정확도를 높였습니다

✅ Result

  • 이미지가 항상 정렬된 상태로 OCR 서버에 전달
  • 사용자가 이미지를 손쉽게 편집 가능
  • OCR 인식률 상승

 

📌 OCR 결과 보정 UI

OCR 결과가 도착하면 다음과 같은 정보를 자동으로 입력 필드에 반영합니다:

  • 승인번호 (approvalNo)
  • 공급자 등록번호 (supplier)
  • 공급 받는 자 등록번호 (recipient)
  • 작성일자 (date)
  • 공급가액 (amount)

이 필드는 모두 수정 가능하며, 이후 "업로드" 버튼을 누르면 백엔드에 유효성 검사를 요청합니다.

 데이터 흐름 관리

  • location.state + query-string → Step 간 데이터 전달
  • useState로 폼 상태 로컬 관리

 

 

📌 서버 캐싱 이슈: 파일명이 동일할 경우 OCR 결과가 재사용되는 문제

✅ Situation

Base64 → Blob으로 변환된 이미지를 FormData로 전송할 때, **파일명을 고정값(image.jpg 등)**으로 설정해 서버에서 동일 파일로 인식됨. 그 결과 OCR 응답이 이전 결과와 동일하게 나오는 문제 발생.

✅ Task

업로드할 파일을 매번 고유하게 식별하고, 서버가 새 파일로 처리되도록 보장해야 했습니다.

✅ Action

  • uuidv4()를 사용하여 고유한 파일명을 생성
  • Blob → File 변환 시 "image.jpg" → "crop_<uuid>.jpg"로 파일명 지정
  • 동일한 이미지를 여러 번 올려도 매번 다른 파일처럼 인식되도록 처리
const uniqueFile = new File([blob], `crop_${uuidv4()}.jpg`, { type: 'image/jpeg' });
formData.append('file', uniqueFile);

 

✅ Result

  • OCR 서버에서 캐시된 이미지를 재사용하지 않게 되었고
  • 반복 업로드, 여러 사용자 동시 업로드 상황에서도 정확한 OCR 결과 반환

 

📌OCR 실패 시 사용자 UX 단절 문제

✅ Situation

OCR 실패(isSuccess: false) 응답이 오면 사용자는 왜 실패했는지 알 수 없었고, 다시 업로드해야 했지만 어떤 문제가 있었는지 힌트가 없어 불편하다는 피드백이 있었습니다.

✅ Task

OCR 실패 시, 사용자가 상황을 인지하고 즉시 대처할 수 있도록 UI/UX 측면에서 개선이 필요했습니다.

✅ Action

  • 실패 원인 메시지를 기반으로 커스텀 모달 출력
  • "재업로드" 버튼을 모달 내에 배치하고, 클릭 시 fileInput.click() 자동 실행
  • 이전 이미지 제거 + 업로드 초기 상태로 리셋되도록 처리
setErrorMessage('파일 업로드 중 오류가 발생했어요');
setIsErrorModalOpen(true);

✅ Result

  • 사용자는 더 이상 업로드 실패 후 브라우저를 새로고침하지 않음
  • 평균 재업로드 시도 수가 감소, 업로드 완료율이 20% 이상 향상

🧪 예외 대응

1.  OCR 실패

  • isSuccess가 false일 경우 사용자에게 오류 모달 제공
  • 파일 업로드 중 오류가 발생했습니다 메시지 출력 후 재업로드 유도

2. 중복 문서

  • 이미 등록된 세금계산서입니다. 메시지 시, 상태값 따라 모달 처리
  • 승인된 문서는 링크 안내
  • 반려된 문서는 자동 삭제 후 재업로드

 

 

📈 최종 성과 정리


⏱ 평균 등록 시간 250분 2분
🧠 OCR 오류율 37% 8%
✏️ 오타율 월 10건 0건
🧑 사용자 만족도 불편함, 수동 중심 자동화, 편집 UI, 예외 대응 포함

 

 

'⛲ 프로젝트' 카테고리의 다른 글

이벤트 페이지 성능최적화 (LCP 37-> 2.5, CLS 0.219→0.006)  (4) 2025.08.18
10,000건 테스트, MSW 기반 대용량 가상+무한 스크롤 적용  (6) 2025.08.12
카카오톡에서 앱 설치 유도하기: PWA 외부 브라우저 리다이렉션 전략  (5) 2025.08.06
Home 진입 40초 → 2.5초, LCP를 93% 줄인 Next.js 최적화  (3) 2025.08.05
(husky) Husky와 Lint-staged를 이용한 Lint 자동화  (1) 2024.11.23
'⛲ 프로젝트' 카테고리의 다른 글
  • 10,000건 테스트, MSW 기반 대용량 가상+무한 스크롤 적용
  • 카카오톡에서 앱 설치 유도하기: PWA 외부 브라우저 리다이렉션 전략
  • Home 진입 40초 → 2.5초, LCP를 93% 줄인 Next.js 최적화
  • (husky) Husky와 Lint-staged를 이용한 Lint 자동화
gprorogpfus
gprorogpfus
:- >
  • gprorogpfus
    gpfusdldks
    gprorogpfus
  • 전체
    오늘
    어제
    • 분류 전체보기 (55)
      • 🎭JavaScript (2)
      • 👚 CSS (1)
      • ⚛️ React (13)
      • 🌃 Next.js (5)
        • 🔜 next.js-study (3)
      • 🥏TypeScript (10)
      • 🏴알고리즘 (2)
      • 🌴트러블슈팅 (3)
      • ⛲ 프로젝트 (6)
        • 👖gproro-shop-app (8)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글쓰기
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    react
    Redux
    TypeScript
    GIT
    JavaScript
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
gprorogpfus
이미지 업로드부터 OCR 자동화까지, 세금계산서 등록 프로세스
상단으로

티스토리툴바