AI 에이전트를 만들어보겠다고 검색하면 이 두 키워드를 만난다.
LangChain, 그리고 LangGraph.
- 둘 중에 뭘 배워야 해요?
- LangGraph가 LangChain 다음 버전이에요?
- 그냥 OpenAI API를 쓰면 안 되나요?
유난히 어려워 보이는 기술 이름들, 이름은 둘째 치고, 두 도구가 서로 어떻게 맞물리는지도 잘 그려지지 않는다.
이 글에서는 에이전트 개념부터 LangChain과 LangGraph가 각각 어떤 문제를 푸는지, 실제 개발에서 언제 쓰면 좋은지 정리해본다.
에이전트
LangChain과 LangGraph를 이해하려면 에이전트(Agent)라는 개념부터 알아야 한다.
지금 나오고 있는 OpenAI와 Anthropic의 새로운 모델(LLM)은 똑똑하지만 혼자서는 크게 할 수 있는 일이 없다. 질문에 답은 해도, 지금 날씨를 조회하거나 이메일을 보내려면 따로 기능을 붙여야 한다.
그래서 LLM에 도구(Tool)를 연결한다. 날씨 조회 API나 이메일 발송 함수 같은 것들이다.
사용자가 이렇게 물었다고 해보자.
서울 날씨 어때?
에이전트는 대략 이런 과정을 거친다.
- 사용자의 질문을 분석한다.
- 현재 날씨를 알려면 날씨 조회 도구가 필요하다고 판단한다.
- 시스템이 해당 도구를 실행한다.
- 조회 결과를 LLM에 전달한다.
- LLM이 결과를 바탕으로 답변한다.
필요하면 다른 도구를 더 호출하기도 한다.
LLM이 도구를 쓰고, 그 결과를 보고 다음 행동까지 정한다. 에이전트는 여기서 출발한다.
LLM을 쓰는 시스템 중에는 정해진 순서대로 모델과 도구를 호출하는 워크플로도 있다. Anthropic은 「Building effective agents」에서 이 둘을 이렇게 나눈다.
- 워크플로(Workflow): 개발자가 미리 짜둔 코드 순서대로 LLM과 도구가 실행된다.
- 에이전트(Agent): 다음에 무엇을 할지 LLM이 상황을 보고 정한다.
고객 문의가 들어오면 무조건 분류 → 검색 → 답변 순서로 처리하는 건 워크플로다.
검색 결과가 부족하면 다시 검색하고, 정보가 충분하다고 판단했을 때 답변을 쓰게 하면 에이전트 쪽이다.
실제 시스템에서는 둘이 섞이기도 한다. 개발자가 큰 흐름을 정하고, 각 단계 안에서는 LLM이 판단하는 식이다.
이 구분을 기억해 두면 LangChain과 LangGraph가 왜 따로 있는지 이해가 쉬워진다.
LangChain, 에이전트를 빠르게 만드는 도구
LangChain은 2022년 해리슨 체이스(Harrison Chase)가 공개한 오픈소스 프로젝트에서 시작했다. 처음에는 프롬프트를 만들고 LLM을 호출하고 결과를 처리하는 작업을 엮는 데 초점을 맞췄고, 이름에 Chain(사슬)이 들어간 것도 그래서다.
사용자 입력
↓
프롬프트 구성
↓
LLM 호출
↓
결과 파싱
↓
최종 응답
이 정도는 직접 짜도 된다. 그런데 모델을 바꾸고 도구를 추가하다 보면 코드가 금방 복잡해진다.
LangChain은 이런 작업에 필요한 구성 요소를 제공한다.
- 여러 LLM 제공자의 모델 연결
- 프롬프트 구성과 관리
- 도구 정의 및 연결
- 구조화된 출력 처리
- 검색 시스템과 LLM을 결합하는 RAG 구현
- 에이전트 구성 및 실행
물론 이 기능들은 LangChain 없이도 만들 수 있다. OpenAI API를 직접 호출하고 평범한 함수를 짜도 충분하고, LangChain은 반복해서 쓰는 부품을 미리 만들어 둬서 그 과정을 덜 번거롭게 해줄 뿐이다.
LangChain 1.0부터 달라진 방향
LangChain은 2025년 10월 1.0을 출시하면서 에이전트를 쉽게 만드는 쪽으로 방향을 확실히 잡았다. 대표적으로 create_agent 함수가 있다.
날씨 조회 에이전트를 만든다고 해보자.
from langchain.agents import create_agent
def get_weather(city: str) -> str:
"""도시의 현재 날씨를 조회한다."""
return f"{city}는 오늘 맑아요!"
agent = create_agent(
model="anthropic:claude-sonnet-4-6",
tools=[get_weather],
system_prompt="너는 친절한 날씨 도우미야.",
)
result = agent.invoke({
"messages": [
{"role": "user", "content": "서울 날씨 어때?"}
]
})
print(result["messages"][-1].content)
예제의 날씨 함수는 실제 API 대신 고정된 문자열을 돌려준다. 실제 서비스라면 이 자리에 외부 날씨 API를 붙인다.
여기서 볼 건 create_agent다. 도구를 쓰는 에이전트를 직접 만들면 보통 이런 로직을 짜야 한다.
- LLM에 사용자 입력을 전달한다.
- LLM이 도구 호출을 요청했는지 확인한다.
- 요청한 도구를 실행한다.
- 실행 결과를 다시 LLM에 전달한다.
- 최종 답변이 나올 때까지 반복한다.
create_agent를 쓰면 이 실행 루프를 직접 구현할 필요가 없다. 개발자는 모델과 도구만 정하고, 실행은 프레임워크에 맡긴다.
함수에 적은 독스트링(docstring)도 중요하다.
def get_weather(city: str) -> str:
"""도시의 현재 날씨를 조회한다."""
모델은 이 설명을 읽고 도구의 용도를 파악해서, 사용자 질문에 이 도구가 필요한지 판단한다. 그러니 도구를 만들 때는 LLM이 언제 이 도구를 꺼내야 하는지 알아들을 수 있게 설명까지 써줘야 한다.
에이전트 실행 과정에 끼어드는 미들웨어
LangChain 1.0에서 하나 더 알아두면 좋은 기능이 미들웨어(Middleware)다. 에이전트가 실행되는 중간에 원하는 로직을 끼워 넣는 기능이다.
이런 요구사항이 있다고 해보자.
- 이메일을 발송하기 전에 사용자 승인을 받는다.
- 대화가 길어지면 이전 메시지를 요약한다.
- 모델에 전달하기 전에 개인정보를 가린다.
- 도구 호출 전후에 로그를 기록한다.
미들웨어로 이런 기능을 에이전트 실행 과정에 연결 할 수 있다. 위 네 가지를 코드로 옮기면 이렇다.
from langchain.agents import create_agent
from langchain.agents.middleware import (
HumanInTheLoopMiddleware,
PIIMiddleware,
SummarizationMiddleware,
wrap_tool_call,
)
from langgraph.checkpoint.memory import InMemorySaver
def send_email(to: str, body: str) -> str:
"""이메일을 보낸다."""
return f"{to}에게 메일을 보냈어요."
# 도구 호출 전후에 로그를 남기는 미들웨어
@wrap_tool_call
def log_tool_call(request, handler):
print(f"[도구 시작] {request.tool_call['name']}")
result = handler(request)
print(f"[도구 끝] {request.tool_call['name']}")
return result
agent = create_agent(
model="anthropic:claude-sonnet-4-6",
tools=[send_email],
middleware=[
# 모델에 전달하기 전에 카드 번호를 가린다
PIIMiddleware("credit_card", strategy="mask"),
# 대화가 4,000토큰을 넘으면 이전 메시지를 요약한다
SummarizationMiddleware(
model="anthropic:claude-sonnet-4-6",
trigger=("tokens", 4000),
),
# send_email을 실행하기 전에 사람의 승인을 기다린다
HumanInTheLoopMiddleware(interrupt_on={"send_email": True}),
log_tool_call,
],
# 승인을 기다리는 동안 대화 상태를 저장해 둘 곳
checkpointer=InMemorySaver(),
)
에이전트 본체는 그대로 두고 middleware 목록에 필요한 것만 끼워 넣는다. 모델이 send_email을 부르려고 하면 실행이 멈추고, 사람이 승인하면 그 자리부터 다시 이어서 돈다. 이렇게 멈췄다 이어가려면 상태를 어딘가 저장해 둬야 해서 checkpointer를 함께 넘기는데, 이게 LangGraph에서 가져온 부품이다.
그럼 LangChain만으로 모든 에이전트를 만들 수 있을까?
실행 흐름이 복잡해지면서 더 나은 구조가 필요해졌는데, 그래서 생긴 게 바로 LangGraph다.
LangGraph, 실행 흐름을 직접 제어하기
도구를 호출하고 답하는 수준이라면 LangChain으로 충분하다. 그런데 이런 요구사항이 생기면 어떨까?
- 작업 결과가 부족하면 특정 단계로 돌아가 다시 실행한다.
- 고객 문의 유형에 따라 서로 다른 처리 과정을 거친다.
- 중요한 작업을 실행하기 전에 사람이 승인해야 한다.
- 실행 도중 서버가 중단되더라도 작업을 이어서 처리해야 한다.
- 여러 단계에서 만들어진 데이터를 공유하고 관리해야 한다.
이걸 풀려면 에이전트가 어떤 순서로 돌고, 어떤 조건에서 다음 단계로 넘어가며, 지금 어떤 데이터를 들고 있는지 명확하게 관리해야 한다. LangGraph는 이런 상태 기반 실행 흐름을 짜고 제어하는 프레임워크다.
실행 과정을 노드(Node)와 엣지(Edge)로 표현하는데, 플로우차트를 코드로 옮긴다고 생각하면 쉽다. 이름에 Graph가 들어간 이유도 여기 있다.
알아야 할 개념은 다음의 세 가지
상태(State)
그래프 전체가 공유하는 데이터다. 지금 처리 중인 질문, 분류 결과, 검색 결과, 최종 답변 같은 걸 담는다.
노드(Node)
실제 일을 하는 함수다. LLM 호출이든 데이터베이스 조회든 결과 검증이든, 무엇이든 노드가 된다.
엣지(Edge)
노드와 노드를 잇는 연결이다. 어떤 노드 다음에 무엇을 실행할지 정하고, 조건부 엣지를 쓰면 실행 결과에 따라 다른 길로 보낼 수 있다.
예를 들어 고객 문의를 환불 문의와 일반 문의로 나누는 시스템을 만들면 다음처럼 된다.
사용자 질문
↓
[질문 분류]
↓
분류 결과 확인
↙ ↘
[환불 처리] [일반 처리]
↘ ↙
[종료]
LangGraph로 옮기면
from typing import Literal
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
question: str
category: str
answer: str
def classify(state: State):
# 예제를 단순화하기 위해 문자열로 분류한다.
# 실제 서비스에서는 LLM을 이용해 분류할 수 있다.
category = (
"refund"
if "환불" in state["question"]
else "general"
)
return {"category": category}
def refund(state: State):
return {"answer": "환불 담당자에게 연결해 드릴게요."}
def general(state: State):
return {"answer": "무엇이든 물어보세요!"}
def route(
state: State,
) -> Literal["refund", "general"]:
return state["category"]
graph = StateGraph(State)
graph.add_node("classify", classify)
graph.add_node("refund", refund)
graph.add_node("general", general)
graph.add_edge(START, "classify")
graph.add_conditional_edges(
"classify",
route,
{
"refund": "refund",
"general": "general",
},
)
graph.add_edge("refund", END)
graph.add_edge("general", END)
app = graph.compile()
result = app.invoke({
"question": "환불하고 싶어요",
"category": "",
"answer": "",
})
print(result["answer"])
# 환불 담당자에게 연결해 드릴게요.
create_agent보다 코드가 길다. 대신 실행 과정이 전부 드러난다.
질문을 분류하고, 결과에 따라 다른 노드를 실행한다. 노드마다 어떤 데이터를 읽고 무엇을 돌려주는지도 코드에 다 보인다.
예제의 분류는 단순한 문자열 비교지만, 실제로는 더 복잡한 코드가 들어갈 수 있다.
중요한 건 흐름을 개발자가 직접 정한다는 사실이다. 단계마다 상태가 어떻게 바뀌는지, 어디서 갈라지는지가 전부 코드를 통해 확인 할 수 있다.
LangGraph의 장점
플로우차트를 코드로 옮기는 것 말고도 LangGraph가 유용한 점은 많다. 공식 문서에서 강조하는 기능 중 실제 서비스에서 중요한 것만 골라보면
1. 지속 실행(Durable Execution)
몇 분, 몇 시간씩 걸리는 작업을 생각해 보자. 중간에 서버가 재시작되거나 일시적인 오류가 날 때마다 처음부터 다시 돌리는 건 낭비다.
LangGraph는 체크포인트로 그래프의 상태를 저장해 두고, 실행 환경을 제대로 구성했다면 멈춘 지점부터 이어서 돌린다.
하지만 결제나 이메일 발송처럼 두 번 실행되면 곤란한 작업은 멱등성 처리와 오류 복구를 따로 설계해야 한다.
2. 휴먼 인 더 루프(Human-in-the-Loop)
모든 작업을 LLM에게 맡기면 위험할 때 가 있다. 결제, 환불, 이메일 발송, 데이터 삭제처럼 중요한 작업은 실행 전에 사람이 승인하는 편이 안전하다.
LangGraph에서는 실행을 멈추고, 사람이 상태를 확인하거나 승인한 뒤 다시 이어가는 흐름을 짤 수 있다.
환불 요청
↓
환불 가능 여부 확인
↓
환불 금액 계산
↓
사람의 승인 대기
↓
승인 완료
↓
환불 실행
3. 상태와 메모리 관리
에이전트가 여러 단계를 거치면 단계마다 나온 결과를 보관해서 다음 단계로 넘겨야 한다. LangGraph는 체크포인트와 상태 관리 기능으로 실행 중 데이터를 보존하고, 한 대화가 이어지는 동안의 기억(단기 메모리)도 이걸로 구현한다.
장기 메모리는 따로 설계해야 한다. 여러 대화에 걸쳐 무엇을 기억하고 언제 꺼낼지는 애플리케이션이 정할 몫이다.
이런 기능은 실행 시간이 길거나 실패 후 복구가 필요한 시스템에서 특히 쓸모 있다. API 한 번 호출하고 끝나는 기능이라면 LangGraph까지 들일 필요는 없을 수 있다.
LangChain과 LangGraph는 무슨 관계일까?
둘은 서로 다른 층의 문제를 풀고, 함께 쓰는 도구이다.
LangChain 1.0의 create_agent는 LangGraph 런타임 위에서 돌아간다.
LangGraph는 실행 엔진이고, LangChain은 그 엔진 위에서 에이전트를 쉽게 만들도록 부품과 추상화를 얹어준다.
LangGraph가 상태 관리와 실행 흐름 제어라는 바닥을 깔고, LangChain은 모델과 도구를 잇고 흔한 에이전트 패턴을 적은 코드로 짜게 도와준다.
단순한 도구 호출 에이전트라면 LangChain으로 빠르게 만들고, 사람 승인이나 복잡한 분기가 필요해지면 LangGraph로 흐름을 세밀하게 짠다. 둘을 섞어 쓸 수도 있다. LangChain으로 만든 에이전트를 LangGraph 워크플로의 노드 하나로 넣는 식이다.
하나 더 알아두면 좋은 게 Deep Agents다. LangChain 생태계에서 나온 더 고수준의 에이전트 프레임워크로, 작업 계획과 하위 에이전트, 파일 시스템과 메모리를 조합해서 오래 걸리는 작업을 처리하게 돕는다.
Claude Code 같은 코딩 에이전트 구조를 떠올리면 이해가 쉽다(구현이 같다는 얘기는 아니다).
셋의 위치를 표로 보면 이렇다.
| 도구 | 핵심 역할 | 주로 고민하는 문제 |
|---|---|---|
| LangChain | LLM 애플리케이션과 에이전트를 쉽게 구성 | 모델과 도구를 어떻게 연결할까? |
| LangGraph | 상태 기반 실행 흐름과 제어 | 어떤 조건에서 다음 단계로 이동할까? |
| Deep Agents | 복잡하고 장시간 실행되는 에이전트 구성 | 계획, 하위 에이전트, 파일 작업을 어떻게 조합할까? |
셋은 고민하는 문제부터 다르다.
그래서 어떤 걸 써야 할까?
실제 개발 상황에 맞춰 보면 이렇다.
| 상황 | 추천하는 선택 |
|---|---|
| LLM을 한 번 호출하고 결과를 반환한다 | OpenAI 등 모델 API 직접 사용 |
| 여러 모델을 연결하거나 도 구를 사용하는 에이전트를 빠르게 만든다 | LangChain의 create_agent |
| 정해진 단계와 LLM의 판단이 섞여 있다 | LangGraph |
| 사람의 승인, 복잡한 분기, 중단 후 재개가 필요하다 | LangGraph |
| 계획 수립, 하위 에이전트, 파일 작업 등을 포함하는 복잡한 에이전트를 구성한다 | Deep Agents 검토 |
기준은 프로젝트가 요구하는 복잡도다.
사용자 질문을 번역하는 기능을 예로 들어보자. 입력을 받아 LLM API에 넘기고 번역 결과를 돌려주면 끝이다. 여기에 LangChain이나 LangGraph를 굳이 들일 이유가 없다.
여러 도구로 사용자 요청을 처리하는 에이전트라면 LangChain이 편하다. 거기서 더 나아가 검색 결과를 검증하고, 부족하면 다시 검색하고, 중요한 작업은 사람이 승인하게 만들어야 한다면 LangGraph가 쓸모 있어진다.
LangChain으로도 복잡한 시스템을 만들 수 있고, 복잡한 시스템이라고 꼭 LangGraph가 필요하지도 않다. 직접 짠 코드로 충분히 관리되는 경우도 있다.
결국 따져볼 건 이 프로젝트에서 실행 흐름을 얼마나 세밀하게 제어해야 하느냐다.
꼭 써야 할까?
여기까지 읽으면 에이전트를 만드는데 LangChain은 필수겠구나 라고 생각 할 수도 있다.
하지만 LangChain은 오랫동안 추상화가 복잡하고 API가 장황하다는 비판을 받아왔다. 번역 결과 하나 돌려주는 기능도 모델 API를 직접 부르면 몇 줄이면 끝나는데, 프레임워크의 추상화와 사용법을 익히느라 코드가 오히려 늘어나기도 한다.
테스트 자동화 기업 Octomind는 프로덕션에서 LangChain을 쓰다가 걷어낸 이유를 공개했다. 추상화 계층 때문에 코드를 이해하고 디버깅하기가 어려워졌다는 게 주요 이유 중 하나였다.
ThoughtWorks의 Technology Radar도 LangChain 도입에 신중하라는 평가를 낸 적이 있다.
Anthropic의 「Building effective agents」도 방향이 비슷하다. 프레임워크를 들이기 전에 LLM API를 직접 호출하면서 필요한 패턴부터 이해하라고 조언한다.
이 비판들이 문제 삼는 건 비용이다. 프레임워크는 개발을 편하게 하려고 쓰는 도구인데, 이해하고 쓰는 비용이 직접 짜는 비용보다 커지면 들일 이유가 줄어든다.
물론 1.0 이후로 API와 에이전트 구성 방식이 정리되면서 달라진 부분이 많으니, 예전 경험만으로 지금 버전을 평가하는 건 조심해야 한다. 최신 버전이 모든 문제를 해결한 것도 아니다. 실제로 들일 때는 디버깅 편의성과 운영 환경에서의 안정성을 같이 따져보는 게 좋다.
LLM API를 직접 호출하는 것부터 시작하자.
모델 호출부터 도구 사용, 결과 처리까지 직접 짜보면 에이전트가 어떻게 도는지 감이 온다. 그러다 반복 코드가 쌓이거나 모델과 도구를 관리하기 버거워지면 LangChain을 검토하고, 실행 흐름이 복잡해져서 상태 관리나 사람 승인, 중단 후 재개 같은 요구사항이 생기면 그때 LangGraph를 검토한다.
문제를 먼저 이해하고, 거기에 맞는 도구를 고르는 순서가 낫다.
정리하자면
LangChain은 LLM 애플리케이션과 에이전트를 더 쉽게 만들게 도와주는 도구다.
LangGraph는 에이전트의 상태와 실행 흐름을 세밀하게 제어하게 도와주는 도구다.
둘은 서로를 대체하지 않는다. LangChain의 에이전트는 LangGraph 런타임 위에서 돌아가고, 필요하면 둘을 함께 쓴다.
API 직접 호출로 충분한 프로젝트도 있고, LangGraph의 실행 제어까지 필요한 프로젝트도 있다.
LLM이 동작하는 원리를 이해하고 '이런 게 있으면 좋겠는데?', '이런게 있으면 편할거같은데?'라는 생각이 들때 LangChain과 LangGraph라는 도구가 있음을 기억하고 있다면 좋은 에이전트를 만들 수 있을 것이다.
참고 자료
- LangChain and LangGraph Agent Frameworks Reach v1.0 Milestones – LangChain 공식 블로그, 2025.10.22
- Harrison's In the Loop: 3 Years of LangChain – Harrison Chase, 2025.10.20
- LangChain 공식 문서 – Quickstart
- LangGraph 공식 문서 – Overview
- Deep Agents 공식 문서 – Overview
- Building Effective Agents – Anthropic, 2024.12.19
- Why We No Longer Use LangChain for Building Our AI Agents – Octomind, 2024
- Technology Radar – LangChain – ThoughtWorks