OpenAI SDK만으로 버티다가 결국 LangChain을 쓴 이유
한동안 저는 “LangChain 굳이 필요해? openai 라이브러리로 직접 호출하면 되지”라는 입장이었습니다. 실제로 간단한 챗봇은 그걸로 충분했고, 프레임워크가 한 겹 더 끼는 게 오히려 디버깅을 방해한다고 느꼈거든요. 그런데 사내에서 “PDF 수백 개를 검색해서 답하는 봇”을 만들게 되면서 생각이 바뀌었습니다. 그 전환점을 솔직하게 적어봅니다.
직접 짜보니 무너진 지점
순수 SDK로 RAG를 짜려면 이걸 다 손으로 해야 합니다. 문서를 자르고(chunking), 임베딩을 만들고, 벡터 DB에 넣고, 질문이 오면 유사 문서를 뽑아 프롬프트에 끼우고, 대화 기록도 직접 리스트로 관리하고… 처음엔 “그거 다 for문이지” 했는데, 임베딩 모델을 바꾸거나 벡터 DB를 FAISS에서 다른 걸로 교체하려는 순간 코드 절반을 다시 짜야 했습니다. 각 조각이 서로 강하게 묶여 있었던 거죠.
LangChain은 이 조각들(모델, 프롬프트, 리트리버, 메모리)을 표준 인터페이스로 규격화해 둔 프레임워크입니다. 그래서 벡터 DB를 바꿔도 retriever만 갈아 끼우면 나머지가 그대로 돌아갑니다. 저에게 LangChain의 진짜 가치는 “기능이 많아서”가 아니라 “부품을 교체 가능하게 만들어줘서”였습니다.
가장 작은 예제
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("{q} 를 초등학생도 알게 한 문장으로 설명해줘")
model = ChatOpenAI(model="gpt-4o-mini")
chain = prompt | model
print(chain.invoke({"q": "LangChain"}).content)
여기서 model만 ChatAnthropic 등으로 바꿔도 나머지는 손댈 필요가 없습니다. 이 “갈아 끼우기”가 규모가 커질수록 시간을 아껴줍니다.
솔직히, 안 써도 되는 경우
그렇다고 무조건 도입하라고는 못 하겠습니다. LLM을 한두 번 호출하는 단순 기능이라면 SDK 직접 호출이 더 가볍고 디버깅도 쉽습니다. 저는 요즘 “검색·메모리·도구 호출 중 두 개 이상이 엮이면 LangChain, 아니면 순정 SDK”라는 기준으로 나눕니다. 프레임워크는 문제가 충분히 복잡해졌을 때 비로소 본전을 뽑습니다.
정리하며
LangChain은 “AI를 외부 시스템과 연결하는 중간 계층”이라는 교과서적 정의보다, 저에겐 “부품을 규격화해 교체 비용을 줄여주는 도구”라는 정의가 더 와닿습니다. 다음 글에서는 이 위에서 자주 쓰는 Prompt Template을, 제가 하드코딩 프롬프트로 고생했던 이야기와 함께 다뤄보겠습니다.

댓글 남기기