원인: 완료 처리를 PG 웹훅 수신에만 의존했는데, 0원 결제는 PortOne에 요청 자체가 가지 않아 웹훅이 영영 오지 않음. 게다가 마일리지를 prepare 시점에 선차감해 결제 미완료에도 차감은 이미 발생 — 두 결함의 결합으로 결제 불일치
해결: 0원 결제는 PG 조회·웹훅 대기를 건너뛰고 즉시 완료 처리하는 분기를 추가하고, 마일리지 차감 시점을 prepare→complete(@Transactional)로 이동해 실패 시 롤백으로 자동 복원. 완료는 기업 단위 Redisson 락으로 동시성을 보호하고, 누락 위험이 있던 수동 보상 로직을 제거(약 -255줄)
배운 점: 완료 처리를 외부 콜백(웹훅) 단일 경로에만 의존하면 콜백이 안 오는 엣지(0원 등)에서 통째로 멈춘다 — 동기 검증 경로에도 완료 트리거를 두고, 차감 같은 부수효과는 확정 시점에 두거나 트랜잭션 롤백으로 복원되게 설계한다
원인: Tesseract의 인식 성능 자체가 사업자등록증에서 부족(필드 누락·오인식). 전처리·파라미터·후처리 규칙을 계속 손봐야 했다
해결: 튜닝 비용과 교체 비용을 비교해 Gemini Vision으로 교체. PDFBox로 PDF를 이미지화하고, 출력 형식을 JSON 스키마로 고정해 필드를 직접 구조화 수신 — 정확도를 끌어올린 핵심은 이 JSON 형식 지정이었다. LLM 응답의 코드펜스·잡텍스트는 정제 후 파싱(신뢰 경계)
배운 점: 표·레이아웃이 있는 정형 서류는 텍스트 OCR 결과를 후처리로 필드에 매핑하는 방식이 문서 서식이 바뀔 때마다 깨진다 — 추출 단계에서 바로 구조화(JSON)하면 후처리 자체가 사라진다
Metrics
PENDING 고착 해소
0원 결제 완료 분기 + 마일리지 차감 시점 이동
수동 보상 제거
트랜잭션 롤백으로 마일리지 정합성 보장
후처리 제거
OCR 결과를 Gemini JSON 구조화 추출로 대체
Infra & Deploy
Spring Boot 3.5(Java 21) · Spring Security + JWT · MySQL · Redis(토큰). WebFlux WebClient로 PortOne 결제 API 호출, PDFBox+Gemini Vision 사업자등록증 OCR, 메일 발송, SSE 기반 실시간 알림. 기업·사업자 서류와 인증서 문서 흐름 관리.