
300페이지 분량의 스캔된 교과서를 마라티어로 번역하려 했던 시도: 'OCR + 번역'만으로는 부족한 이유
요약
스캔된 교과서의 레이아웃을 유지하며 마라티어로 번역하기 위한 OCR 및 번역 파이프라인 구축 과정을 다룹니다. 단순 OCR의 한계를 극복하기 위해 마크다운 형식을 활용한 구조 보존 방식과 다양한 LLM 접근법을 비교 분석합니다.
핵심 포인트
- 단순 OCR은 문서의 구조적 정보(표, 헤딩 등)를 파괴함
- 레이아웃 보존을 위해 마크다운(Markdown) 출력이 가능한 도구 활용 권장
- 멀티모달 LLM과 텍스트 기반 번역 방식의 장단점 비교
- 5달러 미만의 저비용으로 효율적인 번역 파이프라인 구축 가능
저는 원본 레이아웃(headings, tables 등)을 그대로 유지하면서 300페이지 분량의 스캔된 영어 교과서를 마라티어(Marathi)로 변환해야 했습니다. "당연해 보이는" 접근 방식(페이지를 OCR하고 텍스트를 번역하는 방식)은 작동하는 듯 보이다가 결국 실패합니다. OCR은 구조를 파괴하며, 일반적인 기계 번역(Machine Translation)은 입력값에 노이즈가 조금이라도 섞이는 순간 의미를 놓쳐버리기 때문입니다. 이 포스트에서는 제가 평가한 네 가지 아키텍처(Architectures), 저의 첫 번째 실제 접근 방식을 망가뜨린 단 하나의 버그, 그리고 최종적으로 5달러 미만으로 구축하여 배포한 파이프라인(Pipeline)에 대해 설명합니다.
Repo: github.com/adityanile/BookTranslationPipeline
문제 (The problem)
스캔된 영어 교과서. 300페이지. 오래된 인쇄물, 불완전한 스캔, 밀집된 단락, 표(Tables), 섹션 헤더(Section headers). 목표: 원본과 "똑같이" 보이는 마라티어 번역 — 소스(Source)가 가졌던 모든 구조를 잃어버린 번역 텍스트의 벽이 아닌 결과물.
이 작업이 끝났을 때 다음 세 가지가 반드시 충족되어야 했습니다:
- 레이아웃 보존 (Layout preserved). 헤딩(Headings)은 헤딩으로 남아야 합니다. 표(Tables)는 표로 남아야 합니다. 읽기 순서(Reading order)가 유지되어야 합니다.
- 의미 보존 (Meaning preserved). 이것은 문학적/교육적 콘텐츠입니다. 의미를 망가뜨리는 직역은 무용지물보다 못합니다.
- 비례적인 노력 (Proportionate effort). 이것은 일회성 작업이지 제품이 아닙니다. 플랫폼이 필요한 것이 아니라, 제대로 작동하는 스크립트(Script)가 필요합니다.
첫 번째 시도: "당연한" 파이프라인
첫 번째 단계는 Azure Document Intelligence의 Read API를 사용하는 것이었습니다. 견고한 OCR을 통해 텍스트를 깔끔하게 추출합니다. 하지만 일반 텍스트(Plain text)로 추출하는 순간, 모든 구조적 정보(Structural information)를 잃게 됩니다. 표는 형체를 알 수 없는 덩어리가 되고, 헤딩은 본문 텍스트와 구분이 되지 않습니다. 레이아웃 보존이 필수 요구 사항인 상황에서 이는 시작조차 할 수 없는 방식입니다.
그래서 저는 Azure AI Content Understanding으로 넘어갔습니다. 이는 평면 텍스트(Flat text) 대신 마크다운(Markdown)을 출력합니다. 즉, 헤딩, 표, 읽기 순서가 평면화되지 않고 실제 마크다운 문법(#, |, 리스트 마커 등)으로 추출 과정에서 살아남는다는 의미입니다. 이로써 레이아웃 문제는 해결되었습니다. 하지만 번역 문제는 해결되지 않았습니다.
추출된 텍스트를 번역하는 네 가지 방법
이 시점에서 저는 모든 페이지에 대해 깨끗하고 구조화된 영어 Markdown (마크다운)을 확보한 상태였습니다. 이제 문제는 '무엇이 실제로 번역을 잘 해내는가?'였습니다. 저는 네 가지 옵션을 평가했습니다.
| # | 접근 방식 (Approach) | 문제점 (The catch) |
|---|---|---|
| 1 | 페이지 이미지에 직접 멀티모달 LLM (Multimodal LLM) 적용 | 간단하지만, 독립적으로 프롬프트가 입력되는 300페이지 전체에 걸쳐 서식 일관성을 보장할 수 없음 |
| ... |
비용 측면만 놓고 보면 300페이지 기준 모든 옵션이 총 $5에서 $25 사이였기에 큰 차이가 없었습니다. 진짜 차별화 요소는 제가 테스트할 계획에 없었던 것이었습니다.
아키텍처를 결정지은 버그
오래된 스캔 인쇄물은 깨끗하지 않습니다. Content Understanding (콘텐츠 이해) 단계에서 오래된 페이지에 OCR (광학 문자 인식)을 실행하면, 요약본을 눈으로 훑어보는 것만으로는 절대 잡아낼 수 없는 작은 아티팩트(artifacts)가 가끔 발생했습니다. 한 페이지에서는 **"oral"**이라는 단어가 **"or al"**로 추출되었습니다.
여기서 흥미로운 지점이 발생했습니다. 저는 이 손상된 파편을 두 가지 서로 다른 번역 경로를 통해 실행해 보았습니다.
- Azure Translator의 클래식 NMT 엔진 (기본값인 통계적 모델): 이를 직역(literally) 했습니다. 즉, "or"와 "al"을 별개의 파편으로 처리하여 문장의 의미를 조용히 바꿔버렸습니다. 오류도, 경고도 없었습니다. 그저 틀렸을 뿐입니다.
- Azure Translator의 GPT-5.1 기반 모드 (Azure는 이제 클래식 엔진뿐만 아니라 파운데이션 모델(foundation models)을 통한 "유연한 번역(flexible translation)"을 제공합니다): 문맥으로부터 의도된 단어를 정확하게 추론하여 문장을 제대로 번역했습니다.
이 단 하나의 테스트 케이스가 기본 번역 엔진을 완전히 배제하게 만들었습니다. 이것은 유창함(fluency)의 선호도 문제가 아니라, 정확성(correctness)의 요구 사항이었습니다. 소스 자료에 현실적인 노이즈가 조금이라도 포함되어 있다면 (스캔된 역사적 인쇄물은 항상 그렇습니다), 파운데이션 모델 기반의 번역기는 있으면 좋은 기능(nice-to-have)이 아닙니다. 그것은 의미를 조용히 오염시키는 파이프라인과 그렇지 않은 파이프라인 사이의 차이입니다.
제가 구축한 아키텍처
Scanned Pages
│
▼
...
하나의 Python 스크립트가 전체 과정을 오케스트레이션(orchestrate)합니다. Durable Functions, Logic Apps, 또는 관리형 워크플로 엔진(managed workflow engine)은 사용하지 않았습니다. 이는 의도적인 선택이었습니다. 이 작업은 일회성 작업이며, 추출됨(extracted) → 번역됨(translated) → 완료(done) 단계를 통해 각 페이지를 추적하는 매니페스트 기반(manifest-driven) 스크립트는 배포 오버헤드 없이도 호스팅된 오케스트레이터와 동일한 재개 가능성(resumability)을 제공하기 때문입니다. 만약 300페이지 중 220페이지에서 실행이 중단되더라도, 동일한 명령어를 다시 실행하면 중단된 지점부터 다시 시작합니다.
Source, Markdown, Target이라는 세 개의 스토리지 컨테이너(storage containers)가 각 단계 사이에서 영구적인 체크포인트(durable checkpoints) 역할을 합니다. Markdown 체크포인트는 보기보다 훨씬 중요합니다. 이는 번역 호출(translation call) 비용을 단 1원이라도 쓰기 전에, 추출 오류를 확인(spot-check)할 수 있는 검사 가능하고 사람이 읽을 수 있는 영어 결과물(artifact)이기 때문입니다. 만약 이 체크포인트를 건너뛴다면(Option 4처럼 스캔된 PDF에서 바로 번역하는 경우), 추출 오류가 최종 출력물에 영구적으로 박혀버리기 전에 이를 잡아낼 수 있는 유일한 기회를 놓치게 됩니다.
Managed Identity가 모든 서비스 간 인증(service-to-service auth)을 처리하므로, 설정 파일에 키(key)를 저장해 둘 필요가 없습니다.
실제 비용
| 단계 | 비용 |
|---|---|
| Content Understanding 추출 | ~$1.50 |
| ... |
전체 페이지 번역을 위해 파운데이션 모델(foundation model)을 직접 호출하는 대신, 관리형 Translator 서비스(managed Translator service)를 통해 라우팅(routing)하여 번역한 것이 비용을 무료로 만들었습니다. 원시 모델 호출(raw model calls)에는 무료 티어 글자 허용량이 존재하지 않지만, Translator 서비스 자체에는 존재하기 때문입니다.
과거의 나에게 해주고 싶은 말
- 평균적인 입력이 아니라, 최악의 입력을 테스트하세요. 전체 아키텍처가 하나의 저하된 OCR 사례를 중심으로 전환되었습니다. 만약 깨끗한 페이지들만을 대상으로 벤치마킹했다면, 저는 전통적인 NMT (Neural Machine Translation, 신경망 기계 번역) 경로를 출시했을 것이고, 노이즈가 있는 섹션들이 조용히 오역되고 있다는 사실을 결코 알지 못했을 것입니다.
- 비용이 발생하는 모든 단계 사이에 검사 가능한 체크포인트 (checkpoint)를 유지하세요. 스캔에서 번역된 출력물로 바로 넘어가는 방식은 구축하기는 더 간단하지만, 세 단계 뒤에서 문제가 발생했을 때 디버깅하기는 훨씬 더 어렵습니다.
- 습관적으로 관리형 오케스트레이터 (managed orchestrator)를 찾지 마세요. Durable Functions는 반복되는 파이프라인 (pipeline)에는 적합한 도구입니다. 일회성 작업의 경우, 매니페스트 파일 (manifest file)과 일반 Python 스크립트만으로도 훨씬 적은 설정 비용으로 동일한 신뢰성 보장을 얻을 수 있습니다.
- "GPT 기반" 번역은 단순히 품질을 업그레이드하는 것이 아니라, 때로는 당신과 조용히 잘못된 출력물 사이를 가로막고 있는 유일한 방어선입니다.
전체 구현 코드, 오케스트레이터 코드, 그리고 아키텍처 다이어그램은 리포지토리(repo)에 있습니다: github.com/adityanile/BookTranslationPipeline
만약 스캔된 문서, 레이아웃 보존, 저자원 언어 쌍 (low-resource language pairs)과 관련된 유사한 프로젝트를 구축하고 있다면, 무엇이 작동했고 무엇이 작동하지 않았는지 기꺼이 이야기 나누고 싶습니다. 댓글을 남기거나 리포지토리에 이슈 (issue)를 생성해 주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기