본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 22. 19:59

1단계: 문서 인제스션(Document Ingestion) - 임베딩(Embeddings) 전의 숨겨진 복잡성

요약

성공적인 RAG 시스템 구축을 위해 임베딩 전 단계인 문서 인제스션(Document Ingestion)의 중요성을 설명합니다. 단순 업로드를 넘어 중복 방지를 위한 파일 해싱 등 필수적인 전처리 과정을 다룹니다.

핵심 포인트

  • RAG 시스템은 임베딩 전 단계인 인제스션 과정이 매우 복잡함
  • 중복 처리를 방지하여 비용과 리소스를 절약해야 함
  • 파일 이름이 아닌 실제 바이너리 데이터를 해싱해야 정확함
  • 철저한 전처리가 시스템의 답변 정확도를 결정함

전체 이야기: 대부분의 RAG 시스템이 시작하기도 전에 실패하는 이유

이야기의 시작: 왜 당신의 업로드 버튼은 시작일 뿐인가

👦 조카: 삼촌! 드디어 제 RAG 시스템을 만들었어요. 사용자가 PDF를 업로드하면 시스템이 답을 찾아내요. 간단하죠?

👨‍🦳 삼촌: (알겠다는 듯 미소 지으며) PDF를 업로드해서 답을 얻었다고?

👦 조카: 네! 잘 작동해요!

👨‍🦳 삼촌: 정확한 답을 얻었니?

👦 조카: 음... 가끔요. 왜 그러시죠?

👨‍🦳 삼촌: 왜냐하면 "사용자가 PDF를 업로드"하는 것과 "시스템이 임베딩(embeddings)을 생성"하는 것 사이에는 15개의 중요한 단계가 있기 때문이야. 단 하나라도 건너뛰면 시스템은 소리 없이 실패하게 돼. 틀린 답을 얻게 되는데도 왜 그런지조차 모르게 되지.

👦 조카: 15단계라고요? 전 그냥 텍스트를 임베딩(embedded)했을 뿐인데요!

👨‍🦳 삼촌: 바로 그거야. 그게 문제지. 이리 와보렴, 프로덕션 엔지니어들이 실제로 무엇을 하는지 보여줄게.

1단계의 15단계: 문서 인제스션 (Document Ingestion)

👨‍🦳 삼촌: 비리야니(biryani) 요리를 하는 것과 같다고 생각해봐. 그냥 쌀과 고기를 한꺼번에 쏟아붓지는 않지, 그렇지?

👦 조카: 아니죠, 먼저 모든 것을 준비해야 하잖아요!

👨‍🦳 삼촌: 정확해. 요리하기 전에 너는 다음과 같은 일을 하지:

  1. 쌀 씻기
  2. 쌀 불리기
  3. 고기 준비하기
  4. 고기 양념하기
  5. 양파 다지기
  6. ... 그리고 훨씬 더 많은 단계들

그다음에야 비로소 요리를 하는 거야.

문서도 마찬가지야. 임베딩(embeddings)을 하기 전에 반드시 다음 과정을 거쳐야 해:

1. 문서 업로드 (Document Upload, 사용자 작업)
2. 파일 해싱 (File Hashing, 이 파일을 전에 본 적이 있는가?)
3. PDF 파싱 (PDF Parsing, 텍스트 추출)
...

👦 조카: 정말 많네요! 어디서부터 시작해야 하죠?

👨‍🦳 삼촌: 1단계부터 시작하자. 천천히 가보자고. 기초가 탄탄해야 해.

1단계, 1-2단계: 문서 업로드(Document Upload) 및 파일 해싱(File Hashing)

파일 해싱(File Hashing)이 왜 중요한가?

👦 조카: 왜 파일을 해싱(hash)해야 하나요? 그냥 업로드하면 안 되나요?

👨‍🦳 삼촌: 인간은 게으르기 때문이야. 인사 담당자가 똑같은 PDF를 세 번 업로드할 수도 있어. 그러면 네 시스템은 그걸 세 번 처리하게 되지. 임베딩(embeddings)도 세 번 생성되고, 비용도 세 배로 들 거야.

👦 조카: 그럼 해싱(hash)이 중복을 방지한다는 말씀이시죠?

👨‍🦳 삼촌: 맞아. 하지만 여기서 비결이 있어. 파일 이름(filename)을 해싱하지 마라.

👦 조카: 왜 안 되나요?

👨‍🦳 삼촌: 누군가 내용은 바꾸면서 이름은 그대로 유지할 수 있기 때문이야. 봐봐:

File: HR_Policy.pdf (Version 1)
Content: "30 days notice required"
Filename Hash: HR_Policy.pdf
...

이건 재앙이야.

👦 조카: 그럼 콘텐츠를 해싱(Hash)하나요?

👨‍🦳 삼촌: 맞아. 실제 바이너리 데이터 (Binary data)를 말이야.

HR_Policy.pdf (Version 1)
↓
[PDF binary bytes: 0xAA 0xBB 0xCC ...]
...

단계 1-2: Node.js 구현

// src/ingestion/fileHasher.ts

import crypto from 'crypto';
...

👦 조카: 그럼 누군가 같은 PDF를 두 번 업로드하면, 이를 감지하고 건너뛰게 되나요?

👨‍🦳 삼촌: 정확해. 그리고 임베딩 (Embedding) 비용을 절약할 수 있지. 임베딩은 가장 비용이 많이 드는 단계거든.

1단계, 3단계: PDF 파싱 (Parsing) - 적절한 도구 선택

👦 조카: 이제 PDF가 준비되었네요. 텍스트는 어떻게 추출하나요?

👨‍🦳 삼촌: 여기서 진짜 결정이 내려져. 다섯 가지 주요 도구가 있는데, 각각 트레이드오프 (Tradeoffs)가 있어.

👦 조카: 다섯 가지나 요?! 어떤 걸 사용해야 하죠?

👨‍🦳 삼촌: 문서의 종류에 따라 달라. 보여줄게.

PDF 파싱 환경 (The PDF Parsing Landscape)

단순 텍스트 PDF          →  pdf-parse (저렴함, 단순함)
                          ↓
혼합 콘텐츠 (텍스트 + 표) →  PDFPlumber (더 나음)
...

도구 비교

도구최적 용도비용속도표 지원OCR메타데이터프로덕션 준비 완료
pdf-parse단순 텍스트₹0 (무료)⚡⚡⚡ 빠름✗ 아니오⚠️ 취미용
...

👨‍🦳 삼촌: 각각 설명해 줄게.

도구 1: pdf-parse (무료, 단순함)

// 단순한 접근 방식 - 학습용으로는 좋지만, 프로덕션용으로는 부적합함

const pdf = require('pdf-parse');
...

👨‍🦳 삼촌: 우리가 무엇을 놓쳤는지 보이니?

원본 PDF:
═══════════════════════════════════════
COMPANY POLICY
...

👦 조카: 그럼 그냥 텍스트 덩어리 (Text soup)만 얻게 되는 건가요?

👨‍🦳 삼청: 맞아. 그리고 '텍스트 덩어리'를 임베딩하면, '텍스트 덩어리' 같은 답변을 얻게 될 거야.

도구 2: PDFPlumber (더 나음, 여전히 Python 기반)

# PDFPlumber - 표를 더 잘 추출함

import pdfplumber
...

👨‍🦳 삼청: 더 낫긴 하지만, 여전히 구조를 놓쳐. 그리고 Node.js가 아니라 Python이지.

도구 3: Unstructured (프로덕션 선택지)

👨‍🦳 삼촌: 이건 대부분의 기업들이 사용하는 방식이야. 구조를 보존해주거든.

// API를 통한 Unstructured 사용 (Node.js 친화적)

import axios from 'axios';
...

👦 조카: 그럼 구조를 보존한다는 거죠?

👨‍🦳 삼촌: 응. 차이점을 봐봐:

Unstructured 출력 결과:

[
...

도구 4: LlamaParse (최첨단 기술, State of the Art)

👨‍🦳 삼촌: 정말 복잡한 문서라면 LlamaParse를 사용해.

// LlamaParse - 복잡한 PDF에 최적화

import axios from 'axios';
...

👦 조카: Unstructured와 LlamaParse 중 언제 무엇을 사용해야 하나요?

👨‍🦳 삼촌: 간단한 규칙은 이래:

문서 유형?

  ├─ 단순 텍스트 정책 (Simple text policies)
...

도구 5: Azure Document Intelligence (엔터프라이즈급)

// Azure Document Intelligence - 엔터프라이즈 문서를 위한 도구

import { DocumentAnalysisClient, AzureKeyCredential } from "@azure/ai-form-recognizer";
...

👦 조카: 이것들 전부 유료인가요?

👨‍🦳 삼촌: 응, pdf-parse를 제외하고는 말이야. 그리고 여기서 비밀이 하나 있어. 단순한 문서에는 여전히 pdf-parse를 사용해야 한다는 거야!

👦 조카: 왜요?

👨‍🦳 삼촌: 비용 때문이지. 만약 문서의 80%가 단순한 정책 문서라면 pdf-parse를 사용해. 비싼 파서(Parser)는 꼭 필요한 나머지 20%에만 사용하라고.

1단계, 4-5단계: 텍스트 추출 및 정제 (Text Extraction & Cleaning)

쓰레기 문제 (The Garbage Problem)

👨‍🦳 삼촌: 파싱(Parsing)을 하고 나면, 쓰레기 같은 데이터가 나와.

추출된 원문 텍스트:

═════════════════════════════════════════════════════════════
...

👦 조카: 이게 뭐가 문제인가요?

👨‍🦳 삼촌: "[CONFIDENTIAL]"(기밀) 같은 내용을 임베딩(Embedding)하면, 시스템은 모든 정책이 기밀이라고 학습하게 돼. 누군가 "이 정책은 기밀인가요?"라고 물으면, 대답은 항상 YES가 되어버리지!

// 4-5단계: 텍스트 정제

import logger from '../utils/logger';
...

1단계, 6단계: 메타데이터 추출 (Metadata Extraction)

👨‍🦳 삼촌: 메타데이터는 청크(Chunk) '안'에 있는 내용이 아니라, 청크에 '관한' 정보야.

👦 조카: 예를 들면 어떤 거요?

👨‍🦳 삼촌: 예를 들면 이런 거지:

문서: HR_Policy.pdf
부서: 인사부 (Human Resources)
섹션: 휴가 정책 (Leave Policy)
...

이 메타데이터는 검색(Retrieval)을 도와줘.

👦 조카: 어떻게 도와주는데요?

👨‍🦳 삼촌: 누군가 이렇게 질문한다고 가정해 보자. "휴가에 관한 인사(HR) 정책이 뭐야?"

메타데이터가 없다면:

  • 시스템이 모든 문서를 검색함
  • 모든 부서의 휴가 정책을 반환함

메타데이터가 있다면:

  • 시스템이 HR 문서만 검색해야 한다는 것을 인지함
  • HR 휴가 정책만 반환함

훨씬 낫지!

// 6단계: 메타데이터 추출

import logger from '../utils/logger';

interface DocumentMetadata {
  source: string;
  department?: string;
  section?: string;
  version?: string;
  effectiveDate?: string;
  author?: string;
}

/**
 * 문서 구조에서 메타데이터를 추출합니다
 * 
 * 출처:
 * 1. 폴더 구조: /HR/Pol

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0