개발 slecs

오피스 문서도 텍스트 추출 가능하도록 변환 파이프라인 연결

목차

Word / PPT / Excel 같은 오피스 문서도 처리할 수 있도록, PDF 변환 → 텍스트 추출 파이프라인을 연결했다.


배경: "PDF만 되나요?"

pdf2api라는 이름이 붙어 있다 보니 처음부터 PDF만 받도록 설계됐었다. 그런데 실제로 써보는 사람들 입장에선 "Word 파일도 같은 방식으로 넣으면 안 되나?"가 자연스러운 질문이다. 문서에서 텍스트를 뽑아내고 싶다는 니즈는 포맷에 상관없이 동일하니까.

문제는 이 요청을 어떻게 처리하느냐인데, 선택지가 크게 두 갈래였다.

접근 방식 장점 단점
포맷별 파서 직접 구현 변환 서버 불필요 포맷마다 파서 유지보수 부담
오피스 → PDF 변환 후 기존 파이프라인 재사용 추출 로직 변경 없음 변환 레이어 추가 필요

두 번째가 훨씬 합리적이었다. 이미 PDF 추출 로직이 안정적으로 돌고 있는 상황에서, 포맷별 파서를 새로 짜는 건 유지보수 비용이 눈덩이처럼 불어난다. "일단 PDF로 바꾼 다음 기존 로직을 태운다"는 접근이 트레이드오프 상 명확히 우위였다.


작업 내용

변경된 파일은 네 개다.

  • src/lib/convert.ts — 핵심. Word / PPT / Excel 파일을 PDF로 변환하는 로직이 여기 들어갔다. 변환 실패 케이스, 지원하지 않는 포맷 분기 처리도 이 파일에서 담당.
  • src/app/api/extract/route.ts — API 라우트 진입점. 업로드된 파일의 MIME 타입을 보고 PDF가 아니면 convert.ts를 먼저 거치도록 흐름을 수정했다.
  • src/app/home-client.tsx — 파일 선택 UI에서 허용 확장자를 확장하고, 어떤 포맷이 지원되는지 사용자에게 보여주는 부분을 손봤다.
  • src/lib/i18n.ts — 새로 추가된 포맷 지원 안내 문구, 변환 중 상태 메시지 등 i18n 키 추가.

파이프라인 구조로 보면 이렇다.

업로드된 파일
    │
    ├─ PDF → 기존 추출 로직
    │
    └─ Word / PPT / Excel 등
           │
           ▼
       convert.ts (→ PDF 변환)
           │
           ▼
       기존 추출 로직 (변경 없음)

route.ts에서 분기는 대략 이런 패턴이다.

const isPdf = file.type === 'application/pdf';
const fileBuffer = isPdf
  ? await file.arrayBuffer()
  : await convertToPdf(file); // convert.ts

const extracted = await extractFromPdf(fileBuffer);

추출 로직 자체는 전혀 건드리지 않았다. 이 부분이 이번 작업에서 의도적으로 지킨 원칙이었다. 파이프라인의 "앞단"만 확장하고, 검증된 "뒷단"은 그대로 재사용. 변경 범위를 최소화하면 QA 범위도 줄어들고, 회귀 리스크도 낮아진다.


회고

이런 류의 작업에서 팀이 종종 빠지는 함정이 있다. 기능 추가를 하면서 기존 로직을 "조금만 수정하면 되겠지" 하고 건드리기 시작하는 것. 한 번 시작하면 연쇄적으로 영향 범위가 퍼지고, 결국 "단순한 포맷 지원 추가"가 대규모 리팩터링이 된다.

이번엔 그 유혹을 의식적으로 차단했다. 변환 레이어를 완전히 별도 파일(convert.ts)로 분리한 것도 그 이유다. 추출 로직에 손대지 않겠다는 경계선을 코드 구조 자체로 명시한 셈이다.

i18n 파일을 같이 수정한 것도 놓치기 쉬운 포인트인데, UI 메시지가 업데이트되지 않으면 변환 중 상태나 오류 안내가 이상하게 나온다. 기능 구현하고 i18n 깜빡하는 일이 생각보다 자주 생기는데, 이번엔 같은 커밋에 묶어서 처리했다. 리뷰어 입장에서도 "이 기능이 UI에서 어떻게 보이는지"를 한 번에 확인할 수 있어서 낫다.

지원 포맷이 늘어날수록 변환 실패 케이스도 다양해질 거다. convert.ts가 앞으로 얼마나 두꺼워질지가 관건인데, 변환 로직이 복잡해지면 그 시점에 별도 서비스로 분리하는 것도 고려해야 한다. 지금은 인라인으로 처리해도 충분하지만, 파일 크기나 변환 시간이 문제가 되는 순간 비동기 큐나 외부 변환 서비스로 넘기는 구조를 열어뒀다.

끝.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.