# RAG와 LLM 2026년판: 데이터 사이언스 면접을 위한 검색 증강 생성 완벽 가이드 > 2026년 데이터 사이언스 면접을 위한 RAG 가이드입니다. 검색 증강 생성 파이프라인, 벡터 데이터베이스, 청킹, 임베딩, 에이전틱 RAG, Graph RAG를 포괄적으로 다룹니다. - Published: 2026-06-06 - Updated: 2026-06-06 - Author: SharpSkill - Tags: RAG, LLM, data-science, vector-database, embeddings - Reading time: 12 min --- 검색 증강 생성(RAG: Retrieval-Augmented Generation)은 LLM의 출력을 사실에 기반한 최신 데이터로 뒷받침하는 표준 아키텍처로 자리 잡았습니다. 2026년 데이터 사이언스 및 AI 엔지니어링 면접에서는 RAG 관련 질문이 기존 ML(머신러닝) 주제와 함께 출제되고 있으며, 시스템 설계 역량과 실제 구현 능력이 동시에 평가됩니다. > **RAG를 한 문장으로 설명하면?** > > 검색 증강 생성(RAG)은 검색 시스템(지식 베이스에 대한 벡터 검색)과 LLM 생성기를 결합하여, 모델이 학습 데이터의 기억에 의존하지 않고 실제 문서를 기반으로 답변을 생성하는 아키텍처입니다. ## RAG 파이프라인의 엔드투엔드 동작 원리 RAG 시스템은 두 가지 단계로 동작합니다. 지식 베이스를 구축하는 **오프라인 인제스천 단계**와, 관련 컨텍스트를 검색하여 답변을 생성하는 **온라인 쿼리 단계**입니다. 인제스천 과정에서는 원본 문서가 클리닝, 청킹, 임베딩 처리를 거쳐 벡터 데이터베이스에 저장됩니다. 추론 시에는 사용자의 쿼리가 동일한 임베딩 경로를 따르며, 최근접 이웃 검색을 통해 가장 관련성이 높은 청크가 검색됩니다. 이러한 청크가 LLM 프롬프트의 일부가 되어, 생성되는 답변이 소스 자료에 근거하게 됩니다. ```python # rag_pipeline.py from langchain_openai import OpenAIEmbeddings, ChatOpenAI from langchain_community.vectorstores import Chroma from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough # Offline: ingest documents into the vector store def build_index(documents: list[str]) -> Chroma: splitter = RecursiveCharacterTextSplitter( chunk_size=512, # tokens per chunk chunk_overlap=64, # overlap preserves context at boundaries separators=["\n\n", "\n", ". ", " "] ) chunks = splitter.create_documents(documents) embeddings = OpenAIEmbeddings(model="text-embedding-3-large") return Chroma.from_documents(chunks, embeddings) # Online: retrieve + generate def query(vectorstore: Chroma, question: str) -> str: retriever = vectorstore.as_retriever( search_type="mmr", # Maximal Marginal Relevance for diversity search_kwargs={"k": 5, "fetch_k": 20} ) prompt = ChatPromptTemplate.from_template( "Answer based on this context only:\n{context}\n\nQuestion: {question}" ) chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | ChatOpenAI(model="gpt-4o", temperature=0) ) return chain.invoke(question).content ``` 이 파이프라인은 RAG의 핵심 루프(청킹, 임베딩, 저장, 검색, 생성)를 다루고 있습니다. `search_type="mmr"` 파라미터는 검색된 청크가 관련성과 다양성을 동시에 갖추도록 보장하여, 컨텍스트 윈도우 내의 중복을 줄입니다. ## 검색 품질을 좌우하는 청킹 전략 청킹은 다른 어떤 컴포넌트보다 검색 품질에 큰 영향을 미칩니다. 부적절한 청킹은 컨텍스트가 부족한 단편이나 무관한 정보로 신호가 희석된 단편을 검색 결과로 반환하는 원인이 됩니다. 2026년 프로덕션 시스템에서는 세 가지 청킹 접근 방식이 주류를 이루고 있습니다. **고정 크기 청킹**은 설정된 토큰 수(보통 256~512 토큰)로 텍스트를 분할하고 오버랩을 둡니다. 구현이 간단하지만 문장이나 아이디어 중간에서 분할되는 단점이 있습니다. **시맨틱 청킹**은 연속된 문장 간의 임베딩 유사도를 측정하여 주제의 경계를 감지합니다. 유사도가 임계값 아래로 떨어지면 새로운 청크가 시작됩니다. 각 청크는 텍스트의 임의 단편이 아닌 일관된 아이디어를 담게 됩니다. **레이트 청킹**은 먼저 트랜스포머 모델을 문서 전체에 적용하여 컨텍스트가 포함된 토큰 임베딩을 생성한 후, 청크로 분할합니다. 이를 통해 기존 청킹이 파괴하는 장거리 의존 관계를 보존할 수 있습니다. ```python # semantic_chunking.py import numpy as np from sentence_transformers import SentenceTransformer def semantic_chunk(text: str, threshold: float = 0.3) -> list[str]: """Split text where semantic similarity drops below threshold.""" model = SentenceTransformer("all-MiniLM-L6-v2") sentences = text.split(". ") embeddings = model.encode(sentences) chunks, current_chunk = [], [sentences[0]] for i in range(1, len(sentences)): # Cosine similarity between consecutive sentences sim = np.dot(embeddings[i-1], embeddings[i]) / ( np.linalg.norm(embeddings[i-1]) * np.linalg.norm(embeddings[i]) ) if sim < threshold: # topic shift detected chunks.append(". ".join(current_chunk)) current_chunk = [sentences[i]] else: current_chunk.append(sentences[i]) chunks.append(". ".join(current_chunk)) # final chunk return chunks ``` 면접에서의 핵심 포인트는 청크 크기가 정밀도와 재현율 간의 트레이드오프라는 점입니다. 작은 청크(100 토큰)는 검색 정밀도를 향상시키지만 컨텍스트가 단편화됩니다. 큰 청크(1000 토큰)는 컨텍스트를 보존하지만 임베딩의 특이성이 희석됩니다. 대부분의 프로덕션 시스템에서는 10~20%의 오버랩을 둔 256~512 토큰으로 설정합니다. ## 프로덕션 환경의 벡터 데이터베이스와 임베딩 모델 벡터 데이터베이스는 임베딩을 저장하고 고속 근사 최근접 이웃(ANN) 검색을 지원합니다. 임베딩 모델과 벡터 데이터베이스의 적절한 조합을 선택하는 것이 검색 레이턴시와 정확도에 직접적인 영향을 미칩니다. 2026년의 [임베딩 모델](https://huggingface.co/spaces/mteb/leaderboard)은 몇 가지 고성능 옵션으로 수렴되었습니다. OpenAI의 `text-embedding-3-large`(3072차원)와 BAAI의 `bge-m3`, Cohere의 `embed-v4` 같은 오픈소스 대안이 강력한 다국어 검색 성능을 제공하고 있습니다. [MTEB 리더보드](https://huggingface.co/spaces/mteb/leaderboard)는 임베딩 품질 비교를 위한 표준 벤치마크로 계속 활용되고 있습니다. [Pinecone](https://www.pinecone.io/), Weaviate, Milvus, Qdrant, pgvector 등의 벡터 데이터베이스는 각각 다른 트레이드오프를 가지고 있습니다. | 데이터베이스 | 인덱싱 | 매니지드 | 강점 | |----------|----------|---------|----------| | Pinecone | 독자 방식 | 지원 | 간편함, 서버리스 스케일링 | | Weaviate | HNSW | 지원/셀프 | 하이브리드 검색(벡터 + BM25) | | Milvus | IVF, HNSW | 지원/셀프 | 수십억 규모 데이터셋 | | Qdrant | HNSW | 지원/셀프 | 필터링 + 페이로드 저장 | | pgvector | IVF, HNSW | 셀프 | PostgreSQL 통합 | 면접에서 중요한 것은 HNSW(Hierarchical Navigable Small World) 알고리즘에 대한 이해입니다. HNSW는 다층 그래프를 구축하여 각 노드가 최근접 이웃에 연결되며, 메모리 사용량 증가를 대가로 O(log n) 검색을 구현합니다. ## 하이브리드 검색: 밀집 벡터와 희소 벡터의 결합 순수 벡터 검색은 정확한 키워드 매칭이나 희귀 용어 검색에 실패합니다. 반면 순수 렉시컬 검색(BM25)은 시맨틱 유사성을 놓칩니다. 하이브리드 검색은 이 둘을 결합하는 접근 방식이며, 2026년 프로덕션 RAG 시스템에서는 기본 설정이 되었습니다. 표준 패턴에서는 [Reciprocal Rank Fusion(RRF)](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf)을 사용하여 두 리트리버의 랭킹 결과를 통합합니다. ```python # hybrid_retrieval.py from rank_bm25 import BM25Okapi import numpy as np def reciprocal_rank_fusion( dense_results: list[str], sparse_results: list[str], k: int = 60 ) -> list[str]: """Merge dense (vector) and sparse (BM25) results using RRF.""" scores: dict[str, float] = {} for rank, doc_id in enumerate(dense_results): scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1) for rank, doc_id in enumerate(sparse_results): scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1) # Sort by combined RRF score, highest first return sorted(scores.keys(), key=lambda d: scores[d], reverse=True) ``` 하이브리드 검색은 '어휘 불일치' 문제를 해결합니다. 예를 들어, 사용자가 '구독 취소'에 대해 질문하고 있는데, 관련 문서에서는 '계정 해지 정책'이라는 표현을 사용하는 경우입니다. BM25가 정확한 용어 일치를 포착하는 반면, 벡터 검색은 시맨틱 관련성을 파악합니다. ## 리랭킹: 2단계 필터 검색은 후보를 반환하고, 리랭킹은 그것들을 실제 관련성에 따라 정렬합니다. [Cohere Rerank](https://cohere.com/rerank)나 `bge-reranker-v2.5-gemma2-lightweight` 같은 크로스 인코더 리랭커는 각 쿼리-문서 쌍을 동시에 스코어링하여, 바이 인코더 유사도보다 훨씬 정확한 관련성 점수를 생성합니다. 2단계 검색 파이프라인 -- 넓은 1단계 리콜(벡터 + BM25로 상위 50~100개 후보), 이어서 정밀 리랭킹(프롬프트용 상위 5~10개) -- 은 프로덕션 환경의 표준입니다. 1단계에서 고속 ANN 검색을 사용하고, 비용이 높은 크로스 인코더는 소수의 후보 세트만 처리하기 때문에 레이턴시를 관리 가능한 수준으로 유지할 수 있습니다. > **리랭킹 관련 면접 포인트** > > 크로스 인코더가 바이 인코더보다 정확한 이유는 쿼리와 문서를 트랜스포머의 모든 레이어에서 동시에 처리하기 때문입니다. 바이 인코더는 각각을 독립적으로 임베딩하므로 세밀한 상호작용 신호가 손실됩니다. 트레이드오프는 속도입니다. 크로스 인코더는 사전 인덱싱이 불가능합니다. ## 에이전틱 RAG: 단발성 검색을 넘어서 나이브 RAG는 한 번 검색하고 생성하는 것이 전부입니다. [에이전틱 RAG](https://arxiv.org/abs/2501.09136)는 LLM을 언제 검색할지, 무엇을 검색할지, 검색된 컨텍스트가 충분한지를 판단하는 추론 에이전트로 취급합니다. 2026년에는 다단계 추론이 필요한 복잡한 쿼리에 대해 에이전틱 RAG가 지배적인 패턴이 되었습니다. 에이전트는 다음과 같은 작업을 수행할 수 있습니다. - **자기 평가**: 검색된 문서가 질문에 답변하고 있는지 평가 - **재쿼리**: 초기 결과가 불충분할 경우 검색 쿼리를 재구성 - **라우팅**: 다양한 지식 소스(벡터 DB, SQL 데이터베이스, API) 중에서 선택 - **검증**: 여러 검색 패시지 간의 사실 관계를 교차 확인 ```python # agentic_rag.py from langgraph.graph import StateGraph, END from typing import TypedDict class RAGState(TypedDict): question: str documents: list[str] generation: str retries: int def retrieve(state: RAGState) -> RAGState: """Retrieve documents from vector store.""" docs = vectorstore.similarity_search(state["question"], k=5) return {"documents": [d.page_content for d in docs]} def grade_documents(state: RAGState) -> str: """Decide if documents are relevant enough to answer.""" prompt = f"Are these documents relevant to: {state['question']}?\n" prompt += "\n".join(state["documents"]) relevance = llm.invoke(prompt) # returns 'relevant' or 'not_relevant' return "generate" if "relevant" in relevance.content else "rewrite" def rewrite_query(state: RAGState) -> RAGState: """Reformulate the query for better retrieval.""" new_query = llm.invoke( f"Rewrite this query for better search results: {state['question']}" ) return {"question": new_query.content, "retries": state["retries"] + 1} # Build the agent graph workflow = StateGraph(RAGState) workflow.add_node("retrieve", retrieve) workflow.add_node("grade", grade_documents) # conditional routing workflow.add_node("rewrite", rewrite_query) workflow.add_node("generate", generate_answer) workflow.set_entry_point("retrieve") workflow.add_edge("retrieve", "grade") workflow.add_conditional_edges("grade", grade_documents, {"generate": "generate", "rewrite": "rewrite"}) workflow.add_edge("rewrite", "retrieve") # retry loop workflow.add_edge("generate", END) ``` 이 패턴 -- 검색, 평가, 필요 시 쿼리 재작성 후 재시도 -- 은 Corrective RAG(CRAG)로 알려져 있습니다. LangGraph 프레임워크는 워크플로를 조건 분기가 있는 유향 순환 그래프로 모델링하여, 검증 단계, 휴먼인더루프 체크포인트, 멀티소스 라우팅의 추가를 용이하게 합니다. ## Graph RAG: 구조화된 지식 검색 [Graph RAG](https://microsoft.github.io/graphrag/)는 문서에서 엔티티와 관계를 추출하여 지식 그래프를 구축하고, 그래프와 벡터 스토어 양쪽에 쿼리를 실행합니다. 이 아키텍처는 비정형 텍스트 유사성이 아닌 명시적인 엔티티 간 관계에 답변을 근거함으로써, 사실 관련 쿼리에서의 할루시네이션을 줄입니다. 인제스천 파이프라인은 각 문서 청크에서 트리플(주어, 술어, 목적어)을 추출합니다. 쿼리 시에는 질문 내의 관련 엔티티를 식별하고, 지식 그래프를 탐색하여 연결된 사실을 검색한 후, 그래프 검색으로 얻은 컨텍스트와 벡터 검색으로 얻은 패시지를 결합합니다. Graph RAG는 '문서 X에서 언급된 프레임워크를 사용하는 프로젝트를 이끄는 팀은 어디인가?'와 같은 멀티홉 추론 쿼리에 탁월합니다. 이는 여러 문서에 걸친 사실을 연결해야 하는 쿼리로, 단일 청크에 완전한 답변이 포함되어 있지 않기 때문에 순수 벡터 검색으로는 대응하기 어렵습니다. > **Graph RAG의 트레이드오프** > > Graph RAG는 사실 정확도를 크게 향상시키지만(엔티티가 많은 쿼리에서 할루시네이션을 최대 40% 감소), 성숙한 엔티티 추출 파이프라인을 필요로 합니다. 노이즈가 많은 추출은 노이즈가 많은 그래프를 생성하여, 나이브 RAG보다 결과가 악화될 수 있습니다. ## RAG 시스템 평가: 중요한 지표 RAG 평가는 검색 지표와 생성 지표로 나뉩니다. 장애 진단을 위해서는 두 가지를 독립적으로 측정해야 합니다. **검색 지표:** - **Recall@k**: 관련 문서가 상위 k개 결과에 포함되었는가? - **MRR(Mean Reciprocal Rank)**: 첫 번째 관련 결과의 순위는 얼마나 높은가? - **NDCG**: 랭킹이 이상적인 관련성 순서와 일치하는가? **생성 지표:** - **충실도(Faithfulness)**: 답변이 검색된 컨텍스트의 정보만 사용하고 있는가? (할루시네이션 측정) - **답변 관련성(Answer relevance)**: 답변이 원래 질문에 대응하고 있는가? - **컨텍스트 정밀도(Context precision)**: 검색된 청크가 실제로 답변에 활용되었는가? [Ragas](https://docs.ragas.io/)와 [DeepEval](https://docs.confident-ai.com/) 같은 프레임워크는 LLM-as-judge 패턴을 사용하여 이러한 평가를 자동화합니다. [데이터 사이언스 면접 빈출 질문](/blog/data-science/top-25-data-science-interview-questions-2026)에서는 RAG 평가 설계가 점점 더 많이 포함되고 있으며, RAG 시스템이 올바르게 작동하는지를 어떻게 측정하는지에 대한 설명이 요구됩니다. ## 프로덕션 환경의 장애 모드와 디버깅 RAG 시스템은 예측 가능한 패턴으로 장애를 일으킵니다. 이러한 패턴을 아는 것은 면접과 실제 배포 모두에 필수적입니다. **컨텍스트 윈도우 오염**은 검색된 청크가 너무 많아 관련 신호가 희석될 때 발생합니다. LLM이 10개의 청크를 받지만 유용한 정보를 포함한 것은 2개뿐인 상황입니다. 해결책: 리랭커를 사용하여 필터링하고 리트리버의 top-k를 줄입니다. **청킹 아티팩트**는 고정 크기 분할이 문장, 테이블, 코드 블록을 중간에서 잘라낼 때 발생합니다. 검색된 청크는 구문적으로 불완전하고 시맨틱적으로 무용합니다. 시맨틱 청킹 또는 문서 구조를 고려한 분할(헤더, 단락, 코드 펜스 존중)로 해결할 수 있습니다. **임베딩 드리프트**는 임베딩 모델이 업데이트되었음에도 벡터 스토어에 이전 모델의 임베딩이 그대로 저장되어 있을 때 발생합니다. 새 모델로 인코딩된 쿼리가 이전 모델로 구축된 벡터 공간을 검색하게 되어 검색 품질이 저하됩니다. 해결책: 모델 변경 후 전체 코퍼스를 재임베딩합니다. **노후화된 인덱스**는 인제스천 파이프라인이 문서 업데이트에 뒤처져 오래된 정보를 제공하는 경우입니다. [머신러닝 시스템](/blog/data-science/machine-learning-algorithms-explained)에서는 이를 학습-서빙 스큐에 비유할 수 있습니다. 검색 시스템이 프로덕션 환경에 존재하는 데이터 분포와 다른 것을 참조하게 되는 것입니다. ## 결론 - RAG는 검색(지식 베이스에 대한 벡터 검색)과 LLM 생성을 결합하여, 모델 재학습 없이 사실에 기반한 답변을 생성합니다 - 청킹 전략이 검색 품질에 가장 큰 영향을 미칩니다. 시맨틱 청킹과 레이트 청킹은 대부분의 사용 사례에서 고정 크기 분할을 능가합니다 - 하이브리드 검색(밀집 벡터 + 희소 BM25)과 Reciprocal Rank Fusion은 프로덕션의 기본 설정이며, 순수 벡터 검색으로는 해결할 수 없는 어휘 불일치 문제를 해결합니다 - 크로스 인코더 리랭커는 광범위한 검색 후 정밀도 계층을 추가하며, 소수의 후보 세트만 처리하여 레이턴시를 허용 범위 내로 유지합니다 - 에이전틱 RAG(검색, 평가, 재작성, 재시도)와 Graph RAG(엔티티-관계 추출)는 2026년의 두 가지 주요 아키텍처 발전이며, 나이브 RAG가 실패하는 복잡한 멀티홉 쿼리를 처리합니다 - 평가는 검색 지표(Recall@k, MRR)와 생성 지표(충실도, 답변 관련성)를 분리하여, 파이프라인에서 장애가 발생하는 위치를 진단해야 합니다 - 가장 흔한 프로덕션 장애 -- 컨텍스트 오염, 청킹 아티팩트, 임베딩 드리프트, 노후화된 인덱스 -- 는 식별되면 모두 간단히 수정할 수 있습니다 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ko/blog/data-science/rag-retrieval-augmented-generation-llm-data-science-interview-2026