RAG 챗봇 만들기 3 - 문서 준비와 텍스트 추출
처음에 PDF 텍스트 추출하려고 PyPDF2 직접 썼다가 고생 좀 했어요. 페이지별로 읽어서 합치고, 인코딩 문제 처리하고… LangChain Document Loader 쓰면 이런 거 다 알아서 해줘서 훨씬 편하더라고요.
오늘은 RAG 파이프라인의 첫 번째 단계인 문서 로딩을 배울 거예요. PDF, TXT, Word 파일에서 텍스트를 추출하는 방법이에요.
Document Loader란?
LangChain에서는 다양한 형식의 문서를 읽어오는 걸 “Document Loader”라고 불러요. 파일 형식마다 전용 로더가 있어요:
PyPDFLoader- PDF 파일TextLoader- TXT 파일Docx2txtLoader- Word 파일CSVLoader- CSV 파일UnstructuredURLLoader- 웹페이지
PDF 파일 로딩하기
가장 많이 쓰는 PDF부터 해볼게요.
샘플 PDF 준비
테스트용으로 아무 PDF나 준비해요. 없으면 구글에서 “sample pdf”로 검색해서 다운받으세요.
프로젝트 폴더에 docs 폴더 만들고 PDF를 넣어요:
rag-chatbot/
├── docs/
│ └── sample.pdf
└── ...
PDF 로딩 코드
from langchain_community.document_loaders import PyPDFLoader
# PDF 파일 로드
loader = PyPDFLoader("docs/sample.pdf")
documents = loader.load()
# 결과 확인
print(f"총 {len(documents)}개 페이지 로드됨")
print(f"\n첫 번째 페이지 내용:")
print(documents[0].page_content[:500])
print(f"\n메타데이터: {documents[0].metadata}")
실행 결과:
Document 객체 구조
로드된 문서는 Document 객체로 되어있어요:
from langchain.schema import Document
# Document 구조
doc = documents[0]
print(doc.page_content) # 텍스트 내용
print(doc.metadata) # 메타데이터 (출처, 페이지 번호 등)
메타데이터에 출처 정보가 있어서 나중에 “이 정보 어디서 왔어?”에 답할 수 있어요.
TXT 파일 로딩하기
텍스트 파일은 더 간단해요.
TXT 로딩 코드
from langchain_community.document_loaders import TextLoader
# TXT 파일 로드
loader = TextLoader("docs/manual.txt", encoding="utf-8")
documents = loader.load()
print(f"문서 개수: {len(documents)}")
print(f"내용 미리보기: {documents[0].page_content[:200]}")
실행 결과:
TXT는 페이지 개념이 없어서 통째로 하나의 Document로 로드돼요.
여러 파일 한 번에 로딩
폴더 안의 여러 파일을 한 번에 로드하고 싶을 때:
DirectoryLoader 사용
from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader
# docs 폴더의 모든 PDF 파일 로드
loader = DirectoryLoader(
"docs/",
glob="**/*.pdf",
loader_cls=PyPDFLoader
)
documents = loader.load()
print(f"총 {len(documents)}개 문서 로드됨")
for doc in documents[:3]:
print(f"- {doc.metadata['source']}: {doc.page_content[:50]}...")
실행 결과:
Word 파일 로딩하기
Word 파일은 추가 패키지가 필요해요:
pip install docx2txt
Word 로딩 코드
from langchain_community.document_loaders import Docx2txtLoader
loader = Docx2txtLoader("docs/report.docx")
documents = loader.load()
print(f"문서 내용: {documents[0].page_content[:300]}")
웹페이지 로딩하기
웹 문서도 로드할 수 있어요:
from langchain_community.document_loaders import WebBaseLoader
loader = WebBaseLoader("https://example.com/docs")
documents = loader.load()
print(documents[0].page_content[:500])
실전 예제: 여러 형식 통합 로딩
실제로는 여러 형식의 파일을 한 번에 처리하는 경우가 많아요.
통합 로더 만들기
from langchain_community.document_loaders import (
PyPDFLoader,
TextLoader,
Docx2txtLoader,
DirectoryLoader
)
import os
def load_documents(docs_path: str) -> list:
"""여러 형식의 문서를 한 번에 로드"""
all_documents = []
# PDF 파일 로드
pdf_loader = DirectoryLoader(
docs_path,
glob="**/*.pdf",
loader_cls=PyPDFLoader
)
all_documents.extend(pdf_loader.load())
print(f"PDF: {len(pdf_loader.load())}개 로드")
# TXT 파일 로드
for file in os.listdir(docs_path):
if file.endswith(".txt"):
loader = TextLoader(
os.path.join(docs_path, file),
encoding="utf-8"
)
all_documents.extend(loader.load())
# Word 파일 로드
for file in os.listdir(docs_path):
if file.endswith(".docx"):
loader = Docx2txtLoader(os.path.join(docs_path, file))
all_documents.extend(loader.load())
return all_documents
# 사용
documents = load_documents("docs/")
print(f"총 {len(documents)}개 문서 로드 완료!")
실행 결과:
로딩 시 주의사항
인코딩 문제
한글 문서는 인코딩 지정 안 하면 깨지는 경우 있어요:
# 한글 TXT 파일
loader = TextLoader("docs/korean.txt", encoding="utf-8")
# 또는
loader = TextLoader("docs/korean.txt", encoding="cp949")
스캔 PDF
스캔해서 만든 PDF는 텍스트가 안 추출돼요. 이미지로 되어있거든요. OCR이 필요한데, 이건 나중에 따로 다룰게요.
대용량 파일
파일이 너무 크면 메모리 문제가 생길 수 있어요. 나중에 청킹할 때 작게 나눠서 처리하면 돼요.
운영자 실전 노트
실제 프로젝트 진행하며 겪은 문제
- PDF 한글 인코딩 깨짐 → UTF-8/CP949 인코딩 옵션 명시적으로 지정 필요
- 스캔 PDF 텍스트 추출 안 됨 → OCR(pytesseract) 추가 처리 필요. PDF 종류 사전 확인 필수
- 대용량 파일(100MB+) 메모리 에러 → 페이지 단위 스트리밍 로드로 해결
- 표와 이미지가 많은 PDF는 텍스트 순서 뒤섞임 → 전처리 단계에서 구조화된 데이터로 변환
이 경험을 통해 알게 된 점
- 문서 품질 검증이 선행되어야 함. 스캔 PDF인지 텍스트 PDF인지 먼저 확인
- PyPDF2 직접 쓰는 것보다 LangChain Loader가 에지 케이스 처리 잘 함
- 표/이미지가 많은 복잡한 문서는 Unstructured.io 같은 전문 라이브러리 고려
마무리
Document Loader는 바퀴를 다시 발명하지 않게 해준다. 직접 파싱 코드 짜는 것보다 검증된 라이브러리 쓰는 게 시간 절약이다.
다음 편에서는 로드한 문서를 적당한 크기로 쪼개는 “청킹”을 배운다. 왜 쪼개야 하는지, 어떻게 쪼개야 효과적인지 알아본다.
RAG 챗봇 만들기 시리즈:
← 블로그 목록으로