Apache Tika 리뷰: 파일명이 아니라 바이트를 읽는다 — 마크다운을 만나기 전까지는

최종 업데이트: August 14, 2026
Apache Tika 리뷰: 파일명이 아니라 바이트를 읽는다 — 마크다운을 만나기 전까지는
AI 요약
Apache Tika는 Apache Software Foundation의 문서 파싱 도구로, 거의 어떤 형식의 파일이든 넣으면 일반 텍스트와 정규화된 메타데이터 딕셔너리를 돌려줍니다. 프로젝트 README에는 1,000개가 넘는 파일 형식을 지원한다고 적혀 있으며, Tika는 이를 위해 PDF용 PDFBox, Office 문서용 Apache POI, HTML용 jsoup, ODT용 ODF 리더 같은 전문 라이브러리를 직접 묶어 단일 fat jar로 제공합니다. 데이터 파이프라인에서는 검색 인덱스, e-discovery 검토 세트, LLM 코퍼스 앞단에서 이질적인 파일 묶음을 균일한 형태로 바꿔 주는 첫 단계 역할을 합니다.

Apache Tika는 Apache Software Foundation의 문서 파싱 도구입니다. 거의 어떤 형식의 파일을 넣어도 일반 텍스트와 정규화된 메타데이터 딕셔너리를 돌려줍니다. 프로젝트 README에는 1,000개가 넘는 파일 형식을 지원한다고 소개되어 있으며, Tika는 이를 위해 전용 라이브러리들을 자체적으로 묶어 제공합니다. PDF는 PDFBox, Office 문서는 Apache POI, HTML은 jsoup, ODT는 ODF 리더를 사용하므로, 파싱 시점에 추가로 가져올 것이 없는 단일 fat jar로 배포됩니다. 데이터 파이프라인에서 Tika는 화려하진 않지만 꼭 필요한 첫 단계입니다. 검색 인덱스, e-discovery 검토 세트, LLM 코퍼스 앞단에서 서로 제각각인 파일들을 같은 형태로 바꿔 주는 역할을 하죠. 사실 해야 할 일은 두 가지입니다. 바이트 스트림이 무엇인지 판별한 뒤, 그 안에서 텍스트와 메타데이터를 꺼내는 것입니다.

오랜만에 설치가 가장 쉬운 도구였습니다. jar 파일 하나만 있으면 됩니다. java -jar tika-app-3.3.2.jar --text file.pdf처럼 실행하면 끝이고, 설정 파일도, 모델 가중치도, 설치 후 추가 단계도 없습니다. 더 놀라운 점은, 같은 날 같은 호스트에서 다른 Java 도구들을 멈춰 세운 최신 JDK 환경에서도 Tika는 문제없이 돌아갔다는 겁니다. 다만 이번에 제가 확인하고 싶었던 건 카탈로그에 적힌 지원 형식 수가 아니었습니다. 더 좁고 실제적인 질문이었죠. 입력이 거짓말을 할 때, Tika는 실제로 무엇을 하느냐는 것입니다. 그래서 각 콘텐츠 블록마다 고유한 마커 토큰을 심은 뒤, 같은 논리 문서를 아홉 가지 캐리어 형식으로 렌더링하고, 잘못된 확장자, 확장자 없음, 파일명 없음, 0바이트 파일, 일부만 작성된 바이너리로 전부 시험해 봤습니다.

가장 흥미로운 동작은 탐지 단계에서 나옵니다. PDF를 .txt로 바꿔 Tika에 물었더니 application/pdf라고 답했습니다. 이어서 파일명 자체를 지우고 원시 바이트를 stdin으로 넣어도 같은 답이 나왔습니다. 제 테스트 세트의 다섯 개 콘텐츠 탐지 가능 형식 전반에서 이런 결과가 유지되었고, 20개의 고유 논리 조건 모두에서 같았습니다. 형식마다 파일명 조건 세 가지와 파일명 없는 스트림 조건 하나를 테스트했기 때문입니다. 하네스는 스트림 케이스를 서로 다른 라벨로 세 번 실행해 총 30회의 성공한 raw 실행을 남겼지만, 이 반복은 독립적인 증거는 아닙니다. PDF와 RTF는 알아볼 수 있는 바이트 시그니처를 드러내고, DOCX는 컨테이너 자체로 판별되며, HTML과 XML은 마크업이나 루트 콘텐츠로 식별됩니다. 방식은 달라도 이번 테스트 세트에서는 같은 유용한 결과가 나왔습니다. 확장자가 내용을 이기지 못했습니다. 반면 텍스트 계열에서는 이야기가 달라집니다. Markdown은 파일명이 틀리거나 사라지는 순간 text/plain으로 내려갑니다. 여기서는 .md가 정체성을 사실상 전부 담당했습니다.

이후의 모든 수치에는 두 가지 전제가 있습니다. 저는 Apache Tika 3.3.2를 테스트했으며 — 2026년 7월 27일 기준 최신 안정판이 맞는지도 확인했습니다. 4.0.0 계열은 Maven Central에서 alpha와 beta 빌드만 존재합니다. 프로젝트는 2026년 7월 27일 기준 GitHub 스타가 약 3.9k였고, 라이선스는 Apache-2.0입니다. 상업적으로도 거의 부담이 없는 편이죠. 그리고 OCR은 아예 테스트하지 않았습니다. 스캔된 페이지도, 이미지 전용 PDF도 하나도 다루지 않았습니다. 제가 실행한 머신에는 Tesseract와 poppler가 설치되어 있지 않았기 때문에 OCR 경로는 시작도 못 한 채 막혔습니다. 여기에는 OCR 수치가 없습니다. 왜냐하면 OCR 수치가 없기 때문입니다.

겉포장을 걷어낸 뒤 보이는 Tika의 정체

보통 Apache Tika를 문서 변환기라고 생각합니다. DOCX를 넣으면 제목과 표가 살아 있는 깔끔한 Markdown이 나온다고 기대하죠. 그런데 Tika는 그런 도구가 아닙니다. 이 점을 빨리 이해할수록, 오히려 도구의 가치가 더 잘 보입니다.

이번에 테스트한 경로는 세 단계로 이뤄집니다. 콘텐츠 타입 탐지기, 바이트를 적절한 파서에 넘기는 디스패처, 그리고 CLI의 --text 출력 핸들러입니다. 이 출력은 평평한 텍스트와 별도로 조회 가능한 메타데이터를 내보냅니다. 이 계약 안에는 Title 객체도 없고, ListItem도 없고, 다시 조립된 표 그리드도 없습니다. Tika는 XHTML/SAX 계열 출력 등 다른 핸들러와 API도 제공합니다. 다만 저는 그 부분은 테스트하지 않았습니다. 따라서 아래의 구조 관련 결론은 전부 tika-app --text에 대한 것이며, 툴킷 어디에도 구조화된 이벤트 스트림이 없다는 뜻은 아닙니다.

제한처럼 들릴 수 있지만, 한편으로는 그게 장점이기도 합니다. Tika는 잘못 분류할 구조 자체가 없으니까요. 오히려 그게, 더 소란스러운 도구들이 반대 방향으로 치르는 대가이기도 합니다.

탐지는 문서화된 순서대로 진행됩니다. 먼저 시그니처 바이트, 그다음 XML 루트 검사, 그다음 파일명 glob, 마지막으로 사용자가 직접 지정한 타입 순입니다(Tika의 탐지 문서에 정리되어 있습니다). 타입이 정해져야만 디스패처가 해당 번들 파서 — PDFBox, POI, jsoup, 텍스트 계열의 TextAndCSVParser — 에 바이트를 넘깁니다.

이 “탐지 후 파싱” 분리는 내부 잡설이 아닙니다. 파싱할 수 없을 정도로 깨진 파일도 타입은 맞게 판별할 수 있기 때문이고, 실제로 Tika가 망가졌을 때 가장 유용하게 보여 주는 능력이 바로 이것입니다.

환경 구성: jar 하나, 명령 하나, 그리고 까다롭지 않은 JVM

설치는 다운로드로 끝납니다. Maven Central의 tika-app-3.3.2.jar는 모든 파서를 묶은 fat jar로 약 67MB이며, 이후에는 java -jar tika-app-3.3.2.jar --text file.pdf만 실행하면 됩니다. 설정 파일도, 모델 가중치도, 설치 후 처리도, brew install을 연달아 돌릴 일도 없습니다.

JDK 호환성은 예상 밖이었습니다. 저는 전체 테스트를 OpenJDK 26.0.1이라는 최신 비-LTS 빌드에서 실행했고, --version, --text, --metadata, --detect 모두 호환성 경고 없이 종료 코드 0을 반환했습니다. 이 점을 굳이 언급하는 이유는, 같은 호스트에서 같은 날 Apache Nutch도 돌려 봤는데 그 크롤링 사이클은 JDK 26에서 전혀 실행되지 않았기 때문입니다. 최신 JDK에서 제거된 SecurityManager 때문에 LTS 21 이하가 필요했죠. Tika는 그런 문제를 전혀 신경 쓰지 않았습니다. JVM 도구를 특정 고장 패턴 때문에 피하고 있었다면, Tika는 그 문제를 일으키는 쪽이 아닙니다.

설치 측면에서의 정직한 단점도 있습니다. CLI는 호출할 때마다 새 JVM을 띄우기 때문에 콜드 스타트가 현실적입니다. 제 하네스에서 131번 호출을 돌리는 데 약 1분이 걸렸고, 대부분은 JVM 워밍업 시간이었습니다. 많은 파일을 처리한다면 jar를 쉘 루프로 돌리기보다 라이브러리 모드나 서버 모드를 쓰는 편이 낫습니다. 또 외부 의존성이 없다는 말에도 분명한 한계가 있습니다. PDF의 텍스트 레이어 추출에는 외부 도구가 필요 없지만, OCR에는 tesseract와 poppler가 필요합니다. 텍스트 레이어 PDF, DOCX, ODT, RTF, HTML, XML, TXT, Markdown, CSV는 둘 다 없는 환경에서도 잘 파싱됐습니다. 하지만 스캔 문서는 그랬을 리 없고, 저도 그렇게 주장하지 않았습니다.

같은 날 함께 테스트한 unstructured와 비교하면 대비가 더 선명합니다. 그쪽은 전자 PDF 경로가 아예 막혔는데, PDF 모듈을 import하는 순간 추론 스택(torch 등)이 로드되기 때문입니다. 전략 분기보다 앞단에서 막혀서, “fast” 전략도 import 자체가 안 됩니다. Tika는 동일한 PDF의 텍스트 레이어를 java -jar 한 줄로 처리했습니다.

거짓 확장자 테스트: 파일명보다 내용을 믿는 MIME 타입 탐지

Measured results chart: Type detection across filename conditions

여덟 가지 형식을 각각 올바른 확장자, 의도적으로 틀린 확장자, 또는 확장자 없이 제공하고, 파일명 없는 바이트 스트림도 stdin으로 넣었습니다. 이렇게 해서 32개의 고유 논리 조건이 됩니다. 원래 하네스는 동일한 스트림 바이트를 각 파일명 라벨 아래 한 번씩도 실행해 총 48번의 raw 실행을 만들었지만, stdin에는 파일명이 없으므로 그 세 스트림 행은 하나의 조건으로 합쳐집니다.

Fixture실제 타입바꿔 붙인 확장자올바른 확장자거짓 확장자확장자 없음파일명 없는 raw 스트림
PDFapplication/pdf.txt
DOCXOOXML wordprocessingml.jpg
RTFapplication/rtf.html
HTMLtext/html.csv
XMLapplication/xml.txt
일반 텍스트text/plain.pdf
Markdowntext/markdown.pdftext/plaintext/plaintext/plain
CSVtext/csv.txttext/plaintext/plaintext/plain

(스트림 열은 세 가지 확장자 조건을 하나로 합친 것입니다. 파일명이 없으면 glob가 읽을 것도 없기 때문입니다.)

콘텐츠 탐지가 가능한 다섯 형식 — PDF, DOCX, RTF, HTML, XML — 은 20개 고유 조건 모두에서 실제 타입으로 판별됐습니다. (중복 스트림 실행까지 포함한 raw 하네스 기준으로는 30/30 성공입니다.) report.txt로 바뀐 PDF는 여전히 PDF였습니다. photo.jpg로 바뀐 DOCX도 여전히 DOCX였습니다. 파일명이 없어도 결과는 같았습니다. 다만 이게 다섯 형식 모두가 고정된 바이트 시그니처만 쓴다는 뜻은 아닙니다. PDF와 RTF는 알아볼 수 있는 헤더가 있고, DOCX는 ZIP 기반 컨테이너이며, HTML/XML은 마크업이나 루트 콘텐츠로 판별됩니다. 이번 피xture에서는 거짓 확장자가 이기지 못했습니다.

이제 텍스트 계열로 가면 상황이 바뀝니다. Markdown은 .md 확장자가 보이고 읽을 수 있을 때만 text/markdown으로 식별됐습니다. 이름을 바꾸거나, 확장자를 지우거나, 스트림으로 보내면 이번 테스트에서는 text/plain으로 내려갔습니다. CSV도 의도적으로 작은 그리드에서는 비슷했습니다. text/csv.csv glob에서만 나왔습니다. 고유 조건 기준으로 보면 Markdown과 CSV는 각각 네 조건 중 한 번만 특정 타입으로 판별됐고, 일반 텍스트는 이미 text/plain이므로 따로 “붕괴”할 것도 없었습니다. 48회 raw 실행 로그는 재현성 기록으로는 유용하지만, 분모를 더 크게 만들어 주지는 않습니다.

여기서 Tika에 유리한 점도 하나 있습니다. 거짓 확장자가 이기지도 못합니다. .pdf로 바꾼 Markdown fixture는 application/pdf가 아니라 text/plain으로 돌아왔습니다. Tika는 거짓을 믿지 않았습니다. 다만 진실을 확인하지 못했을 뿐입니다. 상위 타입으로 떨어지는 건 잘못된 타입을 자신 있게 선언하는 것보다 훨씬 낫고, text/markdown이 문서화된 text/plain의 하위 타입이라는 점에서 이 fallback은 임의적이지도 않습니다.

CSV에는 한 가지 주의점이 있습니다. Tika에는 통계 기반 CSV 탐지기가 있고, 파싱 시점에는 TextAndCSVParserX-TIKA:Parsed-By 체인에 나타나는 것을 확인했습니다. 그런데 제 2열 x 3행의 작은 그리드에서는 text/csv가 아니라 text/plain으로 나왔습니다. 이건 의도적으로 작은 fixture 하나에서 관찰한 결과일 뿐입니다. 더 크거나 따옴표가 많은 CSV라면 탐지기가 작동할 수도 있습니다. 저는 CSV 콘텐츠 탐지가 깨졌다고 말하는 게 아닙니다. 이번 그리드에서는 확장자가 text/csv를 만들어 냈다고 말하는 것입니다.

실제 업로드 파이프라인에서 왜 중요한가

가장 현실적인 사례는 업로드 라우터입니다. 사용자가 파일을 업로드하면 타입별로 라우팅한다고 합시다. PDF는 송장 파서로, 스프레드시트는 원장 임포터로, 나머지는 텍스트 인덱스로 보내는 식이죠. 확장자만 믿으면 notes.txt라는 이름의 PDF가 잘못된 분기로 들어갑니다. 이건 그나마 순한 사례고, 악의적인 경우는 친절한 확장자를 단 폴리글롯 파일입니다.

이번에 테스트한 바이너리와 마크업 fixture에서는 파일명이 사라져도 Tika가 내용을 보고 라우팅했기 때문에, blob 저장소나 HTTP body 핸들러가 파일명을 버려 버리는 상황에서 특히 유용합니다. 다만 이 결과가 Tika의 긴 꼬리 사례, 애매한 파일, 폴리글롯 전체를 커버하는 것은 아닙니다. 반면 테스트한 텍스트 계열 fixture는 달랐습니다. 파이프라인이 파일명을 제거하면 Markdown과 CSV는 text/plain으로 들어왔고, 그들의 특정 미디어 타입에 묶인 규칙은 더 이상 발동하지 않았습니다. 원본 파일명은 부가 메타데이터로 보존하고, 콘텐츠 탐지가 그것까지 복원해 줄 것이라 기대하지 않는 편이 좋습니다.

심어 둔 콘텐츠는 살아남았다. --text는 구조를 평평하게 만들었다.

정확도는 두 번째 축이고, 이쪽은 깔끔하게 둘로 나뉩니다. 하나의 정식 문서(제목, 본문 두 단락, 글머리표 목록, 번호 목록, 마무리 문단)를 HTML, Markdown, 일반 텍스트, DOCX, PDF, RTF, ODT, XML로 렌더링했고, 또 다른 표 문서를 HTML, Markdown, 텍스트, DOCX, CSV, XML로 만들었습니다. 총 14개의 캐리어 렌더링입니다. 각 블록에는 zztitle1, zzitem3, zztblcell_beta 같은 고유 토큰이 들어 있어, “살아남음”과 “사라짐”은 추정이 아니라 정확한 부분 문자열 검사로 판별됩니다.

마커 토큰 회수율은 14개 렌더링 전부에서 1.000이었습니다. 심어 둔 토큰이 하나도 사라지지 않았습니다. 태그가 붙은 표 셀, 목록 항목, 제목이 모두 존재했습니다. 캐리어별로 워밍업 후 세 번씩 반복한 결과는 바이트 수준에서 동일한 --text 출력을 돌려주었습니다. 다만 이 지표는 태그가 없는 문자, 순서, 공백, 유니코드 정규화, 반복 콘텐츠, 링크, 헤더, 각주, 임베디드 객체에 대해서는 아무 말도 하지 않습니다. 블록 존재 여부를 확인한 것이지, 문서 전체가 완벽히 보존되었음을 증명한 것은 아닙니다.

하지만 평면 텍스트 출력은 원본 구조의 대부분을 포기합니다.

아래는 HTML 표 문서가 --text로 나온 결과입니다.

	Tool	Throughput

	zztblcell_alpha	120

	zztblcell_beta	95

탭으로 이어진 줄만 남습니다. 헤더 행이 헤더로 표시되지 않습니다. 그리드도 없고, 탭 외에는 셀 경계도 없고, 이것이 원래 <table>이었다는 단서도 없습니다. DOCX 표도 같은 방식으로 평평해집니다.

목록은 조금 더 미묘하고, 소스에 실제로 무엇이 들어 있었는지에 따라 갈립니다.

소스에서 글머리표가 무엇이었는가캐리어--text가 돌려주는 것
문자 그대로의 기호 — 이 렌더링들은 모두 - 를 실제 텍스트로 썼음일반 텍스트, Markdown, RTF, ODT, PDFTika가 문자를 그대로 통과시키므로 - 가 살아남음
진짜 구조 — HTML <li>, DOCX의 List Bullet 스타일HTML, DOCX마커는 완전히 사라지고 항목 텍스트만 남음: HTML에서는 탭 들여쓰기된 줄, DOCX에서는 아무 장식 없는 일반 줄

Tika는 텍스트로 받지 않은 마커를 다시 만들어 주지 않습니다. 내용은 같아도 출력 모양은 달라집니다.

Markdown 사례가 이 점을 가장 분명하게 보여 줍니다. pipe table이 들어 있는 .md 파일을 Tika에 주면 파이프 기호가 그대로 돌아오는데, 언뜻 보면 구조가 보존된 것처럼 느껴집니다. 하지만 아닙니다. Tika는 이것을 텍스트로 파싱한 뒤 바이트를 그대로 돌려줬을 뿐입니다. 그 표를 이해한 주체는 없었습니다.

그래서 실제 측정 계약은 더 좁습니다. 심어 둔 마커는 모두 살아남았지만, --text는 typed element나 재구성 가능한 표 그리드를 보존하지 못했습니다. 이것을 파서 결함이라고 부르면 핵심을 놓칩니다. 평면 추출은 의도적으로 요소 분류 문제를 피합니다. 대신 해당 요소 타입이 필요한 하위 소비자 요구는 충족할 수 없습니다. typed block이나 재구성된 표가 필요하다면, --text는 스택의 일부일 뿐 스택 전체가 아닙니다. 다른 Tika 핸들러는 더 많은 구조를 보여 줄 수 있지만, 이번 실행 범위 밖이었습니다.

여기서도 늘 적용되는 주의사항이 있습니다. 이 모든 수치는 통제된 합성 fixture, 단일 머신, 단일 버전, 단일 JDK에서 나온 것입니다. 태그가 붙은 블록이 출력에 존재했음을 보여 줄 뿐, 실제의 지저분한 문서 집합에서 문자 단위 보존이나 정확성을 증명하지는 않습니다.

메타데이터: 정규화되며, 놀랄 만큼 억지 추측을 하지 않음

Measured results chart: Metadata recovery by carrier

메타데이터 레이어가 있는 모든 캐리어에 알려진 author, title, creation-date 값을 심어 넣고, 무엇이 돌아오는지 확인했습니다.

캐리어author → dc:creatortitle → dc:titlecreated → dcterms:created
HTML (<meta name=author>, <title>)내장되지 않음
DOCX (core properties)✅ 정확히 2021-03-15T09:30:00Z
PDF (info dict)존재함, 하지만 생성기가 넣은 자체 타임스탬프였음 — 점수화하지 않음
ODT (meta.xml)✅ 정확히 2021-03-15T09:30:00Z
TXT / MD / CSV / RTF / XML메타데이터 레이어 없음

author와 title은 메타데이터를 담을 수 있는 4개 캐리어 모두에서 복원됐고, 더 중요한 점은 정규화된다는 것입니다. HTML의 <meta name="author">, DOCX의 core property, PDF의 /Author 항목, ODT의 dc:creator 요소가 모두 같은 dc:creator 키 아래로 들어옵니다. 소비자를 하나만 만들면 됩니다. 네 개를 만들 필요가 없습니다.

created는 솔직히 흔들리는 부분입니다. DOCX와 ODT는 제가 심은 2021년 타임스탬프를 정확히 돌려줬습니다. PDF도 creation date를 돌려주긴 했지만, 제가 넣고자 한 값이 아니라 빌드 시 생성기 라이브러리가 찍은 날짜였기 때문에 존재는 했어도 복원됐다고 보지는 않았습니다. 그리고 메타데이터 레이어가 없는 포맷들은 아무것도 보여 주지 않았습니다. 그게 맞는 답입니다. Tika는 본문 텍스트만 보고 author를 추측하지 않습니다.

일부러 부숴 보기: 거기서 드러나는 트리아지 요령

네 가지 적대적 입력을 던져 봤습니다. 0바이트 파일. 본문이 잘린 유효한 PDF 헤더. 일부만 남은 DOCX ZIP. 그리고 BOM도 없고 인코딩 선언도 없는 UTF-8 파일, 멀티바이트 문자를 포함한 상태로요. 이건 Tika의 임계점이 아니라 제 로컬 fixture의 형태입니다.

생성된 하네스, fixture, raw JSON, jar 체크섬, 환경 매니페스트는 이 초안에 링크되어 있지 않습니다. 따라서 외부 독자는 아직 정확한 분모를 독립적으로 재현할 수 없습니다. 표들은 보고된 관찰치로 받아들이고, 공개용으로 쓰려면 먼저 안정적인 번들로 묶어 둬야 합니다.

입력--text / --json발생한 오류--detect
0바이트 파일exit 1, stdout 비어 있음ZeroByteFileException: InputStream must have > 0 bytesexit 0 → 파일명이 있으면 text/plain, 스트림에서는 application/octet-stream
일부만 남은 PDFexit 1, stdout 비어 있음TikaException: TIKA-198: Illegal IOException from PDFParserexit 0 → application/pdf
일부만 남은 DOCXexit 1, stdout 비어 있음POI 치명적 오류: "XML document structures must start and end within the same entity"exit 0 → OOXML 타입
UTF-8, BOM 없음, 선언 없음exit 0없음exit 0 → text/plain, charset UTF-8

추출은 큰 소리를 내며 실패하고, 이 실패들은 외형이 같습니다. 0바이트 파일, 일부만 남은 PDF, 일부만 남은 DOCX는 모두 예외, 종료 코드 1, 비어 있는 stdout을 남겼습니다. CLI가 실패를 보기 좋은 빈 결과로 삼켜 버리지는 않습니다. 이 경우 프로세스는 안전합니다. 멈추지도 않고, segfault도 없지만, 호출자는 빈 문자열만 볼 것이 아니라 종료 상태와 stderr를 반드시 확인해야 합니다.

탐지는 파싱과 분리되어 있습니다. 두 개의 잘린 바이너리 모두에서 --detect는 온전한 앞부분 콘텐츠를 바탕으로 기대한 타입을 반환하며 exit 0으로 끝났습니다. 그 다음 파서가 깨진 본문에서 실패한 것입니다. 따라서 파이프라인은 실패한 파싱 앞뒤로 탐지를 별도의 트리아지 신호로 사용할 수 있습니다. detect-first가 기본값으로 좋은지는 배포 방식에 따라 다릅니다. 이번 테스트는 detect-first와 parse-only의 성능 비교를 하지 않았고, 새 CLI JVM을 매번 띄우는 방식이 대량 처리에서 더 나쁜 선택일 수도 있습니다.

문자셋 탐지는 제대로 동작합니다. BOM도 없고 선언도 없는 UTF-8 파일은 UTF-8로 디코딩되었고, 日本語テスト도 깨지지 않고 그대로 나왔습니다. 메타데이터 딕셔너리를 읽는 분들을 위한 작은 주의점 하나: 제 순수 ASCII fixture들은 charset=ISO-8859-1로 보고되는데, ASCII 바이트에서는 UTF-8과 구분되지 않습니다. 틀린 게 아니라 동률입니다.

Tika와 unstructured: 같은 파일 형식, 다른 역할

두 도구 모두 같은 연구 세션에서 다뤘지만, 여기서는 대칭 벤치마크가 아니라 출력 계약의 분류입니다. 두 도구는 서로 다른 결과로 점수를 받았습니다.

관련 리뷰: Unstructured 리뷰.

Apache Tikaunstructured
내가 측정한 항목콘텐츠 보존성: 무엇이든 빠지지 않았는가?요소 분류 정확도: 각 블록이 올바른 타입으로 잡혔는가?
결과14개 렌더링 전부에서 심어 둔 마커가 모두 존재함별도의 분류 테스트에서 일반 텍스트 표는 Table 재현율 0.000이었고, 동사를 포함한 제목 하나는 narrative text로 분류됨
반환된 typed element없음 — 구조가 아예 돌아오지 않음Title, NarrativeText, ListItem, Table — Tika가 하지 않는 바로 그 일
OCR내 호스트에서 차단됨, tesseract 없음내 호스트에서 차단됨, tesseract 없음

마커를 보존하는 평면 출력과, 분류 오류가 관찰되는 typed element 출력. 하위 소비자가 무엇을 원하는지에 따라 고르면 됩니다. 검색 인덱스나 LLM 컨텍스트 윈도우가 목적이라면 평면 텍스트로 충분할 수 있습니다. 요소 타입을 키로 삼는다면 Tika의 --text 경로는 그 계약을 제공하지 못합니다.

둘 다 스캔 문서 수치는 없습니다.

장점과 단점

장점

  • 다섯 개 콘텐츠 탐지 가능 fixture에서 거짓 파일명을 무시하고 20/20 고유 조건에서 올바른 콘텐츠 타입을 판별함. 중복 스트림 실행도 동일하게 일치함.
  • 14개 캐리어 렌더링 모두에서 심어 둔 모든 마커가 살아남음. 태그가 붙은 표 셀과 목록 항목도 포함.
  • 로컬 재실행 3회에서도 반복 가능함. 이 환경에서는 각 캐리어가 바이트 동일한 텍스트를 반환함.
  • 형식 간 메타데이터 정규화가 잘 됨. 소스 형식과 무관하게 dc:creator / dc:title / dcterms:created로 통일되며, 메타데이터 보유 캐리어 4/4에서 복원됨.
  • 내가 테스트한 형식들에 대해서는 정말로 외부 의존성이 없음. PDF 텍스트 레이어, DOCX, ODT, RTF, HTML을 하나의 jar로 외부 바이너리 없이 파싱 가능.
  • OpenJDK 26에서도 깔끔하게 실행됨. LTS 전용 제약이 없음.
  • 잘린 바이너리에서도 탐지가 맞게(exit 0) 유지되어, 파싱 실패 시 유용한 트리아지 신호를 제공함.
  • Apache-2.0, 성숙함, 적극적으로 유지보수됨.

단점

  • Markdown과 CSV의 정체성은 전적으로 파일명에 의존함. 시그니처가 없는 18개 셀 중 10개는 파일명이 없거나 틀리면 text/plain으로 내려감.
  • --text는 요소 타입을 반환하지 않음. 표 그리드는 탭으로 이어진 줄로 평평해지고, 구조적 목록 마커도 사라짐.
  • 빈 파일과 손상된 입력에서는 처리 중 예외가 발생함. 추출 호출만 보면 두 경우가 똑같아 보임.
  • CLI 모드에서 67MB jar에 호출마다 새 JVM 콜드 스타트가 붙음.
  • OCR과 스캔 이미지 PDF는 여기서 전혀 테스트하지 않음 — tesseract와 poppler가 없어서, 그 경로에 대해서는 어떤 주장도 하지 않음.
  • 여기의 모든 수치는 한 대의 머신, 한 버전의 합성 ground truth입니다. 실제 코퍼스 정확도, 암호화 파일, 임베디드/재귀 문서, 대규모 처리량은 측정하지 않았습니다.

누가 쓰면 좋고, 누가 쓰지 말아야 하나

Tika는 이미 가지고 있는 파일기계가 색인할 수 있는 텍스트와 메타데이터로 바꿔야 할 때 잘 맞습니다. 검색 인덱싱, e-discovery, 아카이브 처리, LLM용 코퍼스 구축, 업로드 파이프라인의 콘텐츠 타입 검증 계층 등이 그렇습니다. 더 똑똑한 무언가 앞에 두는 1차 트리아지 및 정규화 단계로 유용합니다. 테스트된 형식을 판별하고, 평평한 텍스트를 추출한 뒤, 파이프라인이 놓치면 안 되는 콘텐츠에 대해서는 명시적인 검사와 함께 넘기면 됩니다.

typed element, 재구성된 표, 문서 레이아웃이 필요하다면 사용하지 마세요. 또는 적어도 --text에서 멈추지 마세요. 문서가 스캔본이라면, 먼저 tesseract를 설치하고 직접 수치를 낸 뒤에 판단하세요. 저는 그 데이터가 없습니다. 대량 처리라면 CLI가 아니라 라이브러리 모드나 서버 모드를 대표 문서로 벤치마크해야 합니다. 이 소규모 하네스에서도 프로세스 시작 비용은 분명히 보였지만, 처리량과 자원 비용은 측정하지 않았습니다.

많은 사람들이 놓치는 포인트 하나: 저장 계층이 파일명을 제거하고 Markdown이나 CSV를 다룬다면, Tika가 그것들을 일반 텍스트와 구분해 줄 것이라 기대하지 마세요. 원본 이름을 보존하세요.

대안과 Thunderbit의 위치

먼저 공정하게 구분하겠습니다. 이 비교는 품질이 아니라 입력의 종류에 관한 것입니다. Tika는 파일 파싱용 무료 Apache-2.0 셀프호스티드 도구입니다. 디스크나 버킷에 이미 있는 파일을 다룹니다. 페이지를 가져오지도 않고, JavaScript를 실행하지도 않으며, 안티봇 문제를 상대하지도 않습니다. 그렇게 하겠다고 주장하지도 않습니다.

그 경계에서, Thunderbit를 포함한 관리형 웹 추출 서비스가 아키텍처에 들어올 수 있습니다. 이 서비스는 살아 있는 웹페이지를 가져오고, Tika는 이미 보유한 파일을 파싱합니다. 이 글에서는 그런 서비스들을 Tika와 직접 벤치마크하지 않았고, 같은 입력에 대한 대체재라고 보지도 않습니다.

구분은 간단합니다. 이미 보유한 문서는 Tika, 가져와야 하는 웹페이지는 관리형 추출 API. 실제 파이프라인에서는 둘 다 함께 쓰는 경우가 많습니다. 웹 쪽에서는 크롤링과 추출을 하고, PDF와 DOCX 첨부파일은 돌아온 뒤 Tika로 처리하는 식입니다.

오픈소스 전반을 비교하고 싶다면, 제가 쓴 오픈소스 스크래퍼 종합 비교, GitHub에서 가장 유용한 웹 스크래핑 프로젝트, 브라우저 기반 Markdown 방식을 다룬 실전 Crawl4AI 리뷰, 더 넓은 범위의 스크래핑 도구 모음도 참고하실 수 있습니다. 노코드 경로가 필요하다면 AI로 사이트를 스크래핑하는 방법도 안내해 두었습니다.

웹 데이터 추출을 위해 Thunderbit 사용해 보기

최종 판단

Apache Tika를 써야 할까요? 네, 입력으로 들어온 이질적인 파일들을 평평한 텍스트와 정규화된 메타데이터로 바꾸는 일이 필요하고, 파이프라인에서 절대 잃어버리면 안 되는 필드나 마커를 검증할 수 있다면 그렇습니다.

이번 실행에서 가장 강력했던 부분은 탐지기였습니다. 다섯 개 콘텐츠 탐지 가능 fixture의 20개 고유 조건 모두에서 기대한 타입을 반환했고, 파일명 없는 스트림도 포함했습니다. 심어 둔 마커는 14개 렌더링 전부에서 살아남았고, 출력은 세 번의 로컬 재실행에서도 바이트 단위로 반복되었습니다. 유용한 증거입니다. 그래도 합성 증거라는 점은 변하지 않습니다. 이 JDK에서 하나의 jar만으로, 그리고 이번에 테스트한 비-OCR 경로에 외부 바이너리 없이 실행했다는 점은 배포를 상당히 심심하게 만들어 줬습니다.

하지만 그 범위를 정확히 이해해야 합니다. Tika에 넣은 표는 모두 탭으로 이어진 줄로 돌아옵니다. 구조적 목록 마커는 모두 사라집니다. Markdown과 CSV는 파일명이 사라지는 순간 정체성을 잃습니다. 빈 파일과 손상 파일은 같은 형태의 실패를 일으키므로, 둘을 구분하려면 별도의 detect 호출이 필요합니다. 그리고 많은 Tika 사용자들이 가장 궁금해하는 OCR은, 저는 답이 없습니다. 실행할 수 없었고, 추정도 하지 않겠습니다.

그 범위 안에서는 Tika가 꽤 놀라울 정도로 안정적으로 일합니다. 라벨이 아니라 바이트를 읽는 도구입니다. 다만 바이트의 모양까지 물어보지는 마세요.

웹 데이터 추출을 위해 Thunderbit 사용해 보기 Get Started Free

자주 묻는 질문

Apache Tika는 파일 확장자가 틀려도 파일 형식을 제대로 감지하나요? 이번에 테스트한 다섯 개 콘텐츠 탐지 가능 fixture에서는 그렇습니다. PDF, DOCX, RTF, HTML, XML은 오해를 부르는 확장자, 확장자 없음, 파일명 없는 스트림을 포함한 20개의 고유 논리 조건 모두에서 기대한 미디어 타입으로 판별됐습니다(중복 스트림 실행을 포함하면 raw 기준 30회). .txt로 이름 붙인 PDF도 여전히 application/pdf로 감지됐습니다. 반면 Markdown과 작은 CSV fixture는 파일명 정보에 의존했고, 그 정보가 없거나 틀리면 text/plain으로 내려갔습니다.

Tika는 표와 문서 구조를 보존하나요? 이번에 테스트한 --text 모드에서는 아닙니다. 표 그리드는 셀이나 헤더 의미 없이 탭으로 이어진 줄로 돌아왔고, 구조적 목록 마커(HTML <li>, DOCX의 List Bullet 스타일)는 사라졌습니다. 심어 둔 마커는 14개 캐리어 렌더링 전부에서 살아남았지만, 그것만으로 완전한 콘텐츠 보존이 증명되지는 않습니다. 또한 --text는 요소 타입을 제공하지 않습니다. typed element나 재구성된 표가 필요하다면, 다른 Tika 출력 핸들러를 시험하거나 다른 도구를 함께 사용하세요.

Apache Tika는 스캔된 PDF에서 OCR을 할 수 있나요? Tika는 Tesseract를 통해 OCR을 지원하지만, 저는 테스트하지 않았고 이 결과들 가운데 그 경로에 대한 주장도 없습니다. 제 테스트 호스트에는 Tesseract와 poppler가 없어서 OCR 및 스캔 이미지 경로는 실행되기 전부터 막혔습니다. 이번 테스트에는 OCR 수치가 전혀 없습니다. OCR이 사용 사례라면 tesseract를 설치하고 직접 벤치마크하세요. 이 부분은 여기서 검증되지 않았다고 보는 것이 맞습니다.

Tika는 비어 있거나 손상된 파일을 어떻게 처리하나요? 조용히 넘어가지 않고 명확하게 실패합니다. 0바이트 파일은 ZeroByteFileException을 던지고, 일부만 남은 PDF는 PDFParser에서 TikaException을 던지며, 일부만 남은 DOCX는 POI XML 오류를 냅니다. 세 경우 모두 종료 코드 1과 비어 있는 stdout을 남기므로, 추출 호출만으로는 빈 파일과 손상 파일을 구분할 수 없습니다. 다만 탐지는 여전히 견고합니다. --detect는 두 잘린 바이너리 모두에서 올바른 타입으로 exit 0을 반환했기 때문에, 파싱에 시간을 쓰기 전에 신뢰할 수 있는 트리아지 단계가 됩니다.

이번 Tika 테스트에서 다루지 않은 것은 무엇인가요? 명시적으로 네 가지입니다. OCR과 스캔 이미지(차단되어 미테스트). 실제 코퍼스 정확도 — 모든 결과는 심어 둔 마커 토큰이 있는 통제된 합성 fixture로, 지저분한 실제 문서에서의 정확도가 아니라 알려진 라벨 대비 보존성을 측정한 것입니다. 자원 비용, 처리량, 최대 메모리 사용량은 측정하지 않았습니다. 그리고 “1,000개 파일 형식”이라는 주장 중 긴 꼬리 전체도 다루지 않았습니다. 저는 외부 의존성이 없는 대표 포맷 9개만 테스트했지 전체 카탈로그를 다 본 것은 아닙니다. 여기의 모든 내용은 Tika 3.3.2, OpenJDK 26.0.1, macOS arm64, 단일 머신 기준입니다.

Ke
Ke
Thunderbit CTO | 시니어 데이터 사이언티스트 & ML 전문가 머신러닝과 데이터 과학 분야에서 약 10년에 가까운 경험을 쌓아온 Ke Shen은 컬럼비아 대학교 출신이며, 전 Walmart Labs의 시니어 데이터 사이언티스트였습니다. Python, R, Java, 통계 분야에서 동료들에게도 인정받는 깊은 전문성을 바탕으로, 복잡한 AI 알고리즘을 이론에서 실제 운영 수준의 아키텍처로 전환하는 데 필요한 실전 인사이트를 공유합니다.
목차
Thunderbit · AI 웹 데이터 에이전트

1회 클릭 안에 어떤 페이지든 데이터 추출

250,000명+ 사용자가 신뢰
무료 플랜 제공
웹페이지에서 스프레드시트까지
필요한 내용을 설명하세요 — Thunderbit의 AI Agent가 수집하고 Excel, Google Sheets, Airtable, Notion으로 내보냅니다. 시작은 무료입니다.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week