대부분의 에이전트 코드는 간단한 스크립트로 시작하지만, 재시도·분기·도구 호출이 추가되는 순간 유지보수가 어려워집니다. LLM을 연결하고 도구를 제공한 뒤 워크플로우가 루프를 돌거나, 조건에 따라 분기하거나, 사람의 승인을 기다려야 한다면 직선형 파이프라인만으로는 부족합니다. LangGraph는 에이전트를 공유 상태를 가진 그래프로 모델링해 이러한 제어 흐름을 명시적으로 다루는 프레임워크입니다. 이 글에서는 LangGraph의 핵심 개념, 최소 구현 예시, 영속성 설정, 그리고 에이전트가 실제 서비스를 호출할 때 API 테스트를 어디에 적용해야 하는지를 실무 관점에서 정리합니다.
LangGraph란 무엇인가
LangGraph는 장기 실행되는 상태 저장 에이전트를 만들기 위한 저수준 오케스트레이션 프레임워크이자 런타임입니다. LangChain 팀인 LangChain Inc에서 만들었지만, LangChain과는 별도의 라이브러리로 사용할 수 있습니다.
설치는 단순합니다.
pip install -U langgraph
LangGraph의 핵심은 에이전트를 그래프로 표현하는 것입니다.
- 노드: 모델 호출, 도구 실행, 데이터 변환 같은 작업 단위
- 엣지: 다음에 실행할 노드를 결정하는 연결
- 상태: 모든 노드가 읽고 업데이트하는 공유 객체
- 루프: 도구 실행 후 다시 모델로 돌아가는 순환 흐름
기존 체인 방식은 보통 1단계 → 2단계 → 3단계 → 종료 형태입니다. 반면 LangGraph는 현재 상태를 보고 다음 경로를 선택할 수 있습니다. 예를 들어 모델이 도구 호출을 요청하면 도구 노드로 이동하고, 더 이상 호출할 도구가 없으면 종료할 수 있습니다.
LangGraph가 해결하는 문제
에이전트 워크플로우는 대부분 선형이 아닙니다. 일반적인 흐름은 다음과 같습니다.
- 사용자의 요청을 받습니다.
- 모델이 다음 행동을 결정합니다.
- 필요한 경우 도구 API를 호출합니다.
- 결과를 다시 모델에 전달합니다.
- 완료 여부를 판단합니다.
- 완료되지 않았다면 다시 반복합니다.
이 흐름을 중첩된 if 문과 while 문으로 직접 구현하면 빠르게 복잡해집니다. LangGraph는 다음 기능을 구조적으로 제공합니다.
- 정지 조건이 있는 루프: 무한 루프를 피하면서 작업 완료 전까지 반복
- 상태 기반 분기: 모델 응답, 도구 결과, 사용자 입력에 따라 라우팅
- 영속성: 충돌, 타임아웃, 재시작 후에도 마지막 체크포인트에서 재개
- Human-in-the-loop: 실행 중간에 사람이 상태를 확인하거나 수정한 뒤 계속 진행
- 스트리밍: 토큰과 중간 실행 결과를 실시간으로 UI에 전달
즉, LangGraph는 에이전트를 단순 함수 호출 묶음이 아니라 상태 머신으로 다룰 수 있게 해줍니다.
핵심 개념: 그래프, 상태, 노드, 엣지
LangGraph를 구현할 때는 네 가지 개념만 먼저 잡으면 됩니다.
1. 상태(State)
상태는 전체 실행 동안 공유되는 데이터 구조입니다. 보통 TypedDict 또는 LangGraph가 제공하는 MessagesState를 사용합니다.
MessagesState는 채팅 메시지 목록을 관리하는 기본 상태 스키마입니다.
2. 노드(Node)
노드는 일반 Python 함수입니다. 현재 상태를 입력으로 받고, 상태에 병합할 업데이트를 반환합니다.
def call_model(state):
response = model.invoke(state["messages"])
return {"messages": [response]}
3. 엣지(Edge)
엣지는 노드 사이의 이동 경로입니다.
-
add_edge()는 항상 같은 다음 노드로 이동합니다. -
add_conditional_edges()는 상태를 보고 다음 노드를 동적으로 결정합니다.
4. START와 END
START는 그래프 실행 시작점이고, END는 종료 지점입니다.
최소 LangGraph 예제
아래 예제는 모델이 도구 호출을 요청하면 tools 노드로 이동하고, 도구 실행 후 다시 모델로 돌아가는 기본 루프입니다.
from langgraph.graph import StateGraph, START, END, MessagesState
def call_model(state: MessagesState):
response = model.invoke(state["messages"])
return {"messages": [response]}
def should_continue(state: MessagesState) -> str:
last = state["messages"][-1]
return "tools" if last.tool_calls else END
builder = StateGraph(MessagesState)
builder.add_node("model", call_model)
builder.add_node("tools", tool_node)
builder.add_edge(START, "model")
builder.add_conditional_edges("model", should_continue)
builder.add_edge("tools", "model") # 도구 실행 후 다시 모델로 루프
graph = builder.compile()
중요한 부분은 이 줄입니다.
builder.add_edge("tools", "model")
도구 실행이 끝난 뒤 모델로 돌아가기 때문에 에이전트는 다음을 계속 판단할 수 있습니다.
- 추가 도구 호출이 필요한가?
- 사용자에게 답변할 수 있는가?
- 오류가 발생했는가?
- 종료해야 하는가?
이 순환 구조가 LangGraph를 사용하는 핵심 이유입니다.
영속성 및 human-in-the-loop 설정
LangGraph에서 체크포인터를 사용하면 각 단계 후 상태 스냅샷을 저장할 수 있습니다. 이후 같은 thread_id로 호출하면 마지막 상태를 복원합니다.
from langgraph.checkpoint.memory import InMemorySaver
graph = builder.compile(checkpointer=InMemorySaver())
config = {
"configurable": {
"thread_id": "user-42"
}
}
graph.invoke(
{"messages": [user_message]},
config
)
InMemorySaver는 개발 환경에서 테스트하기에 적합합니다. 재시작 후에도 상태를 유지해야 한다면 데이터베이스 기반 저장소를 사용해야 합니다. LangGraph는 단일 서버용 SQLite와 다중 인스턴스 환경용 Postgres 기반 저장 옵션을 제공합니다.
이 구조를 사용하면 human-in-the-loop 흐름도 쉽게 설계할 수 있습니다.
예를 들어 다음과 같은 승인 게이트를 만들 수 있습니다.
- 모델이 외부 API 호출 계획을 생성합니다.
- 그래프를 특정 노드에서 일시 중지합니다.
- 사람이 요청 본문, 대상 API, 비용 등을 확인합니다.
- 승인 또는 수정 후 같은 체크포인트에서 실행을 재개합니다.
노드 로직에 별도의 저장·복구 코드를 넣지 않아도 런타임이 상태를 관리합니다.
스트리밍을 UI에 연결하기
LangGraph는 실행 중 모델 토큰과 노드 업데이트를 스트리밍할 수 있습니다. UI에서는 단순 로딩 스피너 대신 다음 정보를 표시할 수 있습니다.
- 현재 실행 중인 노드
- 모델의 부분 응답
- 도구 호출 요청
- 도구 실행 결과
- 최종 응답
에이전트 디버깅에서는 최종 답변만 보는 것보다 중간 상태를 보는 것이 훨씬 중요합니다. 특히 조건부 엣지가 예상과 다른 경로를 선택하는 경우, 스트리밍 로그가 문제 원인을 찾는 데 도움이 됩니다.
LangGraph와 LangChain의 관계
LangChain은 모델 래퍼, 프롬프트 템플릿, 검색기, 문서 로더, 다양한 제공업체 통합을 포함하는 더 넓은 도구 키트입니다. LangGraph는 그중 에이전트 실행 흐름을 담당하는 오케스트레이션 레이어에 가깝습니다.
LangGraph를 사용하기 위해 반드시 LangChain을 알아야 하는 것은 아닙니다. 노드 내부에서 원하는 모델 클라이언트를 직접 호출할 수 있습니다. 다만 LangChain의 모델 및 도구 추상화가 편리하기 때문에 많은 팀이 두 라이브러리를 함께 사용합니다.
| LangChain | LangGraph | |
|---|---|---|
| 역할 | 구성 요소 및 통합 | 오케스트레이션 및 런타임 |
| 제어 흐름 | 선형 체인 | 순환 및 분기가 있는 그래프 |
| 내장 상태 | 제한적 | 공유, 유형 지정, 내구성 |
| 영속성 / 재개 | 주요 초점 아님 | 체크포인터 + 스레드 ID |
| 가장 적합한 용도 | 모델 호출 및 도구 구성 | 상태 저장, 다단계 에이전트 |
현재 LangGraph v1.0 라인에서는 사전 빌드된 에이전트 헬퍼의 위치가 변경되고 있으므로, 예전 코드를 복사하기 전에 설치된 버전과 공식 참조 문서에서 정확한 임포트 경로를 확인해야 합니다.
더 넓은 관점에서 에이전트 구조를 설계하려면 사용자 정의 AI 에이전트 구축 가이드도 참고할 수 있습니다.
LangGraph Platform 및 Studio
오픈 소스 라이브러리만으로도 로컬 개발은 가능합니다. 하지만 디버깅과 배포 단계에서는 LangGraph Studio와 LangGraph Platform이 도움이 됩니다.
LangGraph Studio
LangGraph Studio는 시각적 에이전트 IDE입니다. 그래프 구조를 렌더링하고, 실행 경로와 각 노드의 상태를 확인할 수 있습니다.
조건부 라우팅이나 루프가 있는 에이전트에서는 로그만으로 흐름을 추적하기 어렵습니다. Studio를 사용하면 실제로 어떤 노드를 거쳤는지 시각적으로 확인할 수 있습니다.
LangGraph Platform
LangGraph Platform은 에이전트를 API 엔드포인트로 배포하고, 장기 실행을 위한 영속성 및 호스팅 옵션을 제공하는 관리형 배포 레이어입니다. 자체 호스팅부터 클라우드 관리형 옵션까지 선택할 수 있습니다.
라이브러리 사용에 필수는 아니지만, 프로덕션에서 에이전트 실행 인프라를 직접 운영하고 싶지 않을 때 유용합니다.
LangGraph를 언제 사용해야 하는가
LangGraph는 모든 LLM 작업에 필요한 도구가 아닙니다. 다음 조건 중 하나라도 해당하면 사용을 검토할 만합니다.
- 도구 호출 후 결과를 보고 다시 판단해야 한다.
- 모델 출력에 따라 다음 단계가 달라진다.
- 실행 시간이 길고, 중간에 실패해도 재개해야 한다.
- 실행 도중 사람의 승인이나 수정이 필요하다.
- 여러 하위 에이전트 또는 액터가 같은 상태를 공유한다.
- 에이전트의 실행 경로를 추적하고 디버깅해야 한다.
반대로 다음 작업에는 과할 수 있습니다.
- 단일 프롬프트로 텍스트를 요약하는 작업
- 한 번의 모델 호출로 끝나는 분류 작업
- 짧고 고정된 선형 파이프라인
분기와 루프가 없다면 LangGraph의 구조는 오히려 오버헤드가 될 수 있습니다.
API 테스트 및 목업(Mocking)의 적용 지점
LangGraph는 에이전트의 실행 흐름을 오케스트레이션하지만, 에이전트가 호출하는 API 자체를 검증하지는 않습니다. 실제 에이전트는 보통 다음 API에 의존합니다.
- LLM API
- 검색 API
- CRM 또는 내부 백엔드 API
- 결제, 주문, 사용자 관리 API
- 벡터 데이터베이스 API
- 사내 도구 API
이 지점에서 Apidog를 사용할 수 있습니다.
1. 실제 API 대신 목업 API로 그래프 로직 테스트
모든 테스트 실행에서 실제 API를 호출하면 비용과 속도 문제가 생깁니다. 특히 LLM API는 토큰 비용과 속도 제한의 영향을 받습니다.
개발 중에는 에이전트가 의존하는 API를 목업(mock)해 그래프 로직을 빠르게 반복할 수 있습니다.
예를 들어 도구 노드가 다음 API를 호출한다고 가정합니다.
GET /users/{id}/orders
목업 응답을 고정해두면 LangGraph 테스트에서 항상 같은 결과를 받을 수 있습니다.
{
"userId": "user-42",
"orders": [
{
"id": "order-1001",
"status": "shipped"
}
]
}
이렇게 하면 모델 라우팅과 조건부 엣지를 검증할 때 외부 시스템 상태에 흔들리지 않습니다.
2. 응답 스키마를 단언(assert)해 노드 오류 방지
LangGraph 노드는 보통 API 응답 형태를 가정합니다.
예를 들어 노드가 status 필드를 읽는다고 가정합니다.
def route_by_order_status(state):
order = state["order"]
if order["status"] == "shipped":
return "notify_user"
return "check_again"
그런데 백엔드 API가 status를 orderStatus로 바꾸면 에이전트는 잘못된 분기로 이동하거나 예외를 낼 수 있습니다.
API 단언(assertions)을 사용하면 다음 조건을 사전에 검증할 수 있습니다.
- 필수 필드가 존재하는가?
- 필드 타입이 예상과 일치하는가?
- 상태 코드가 올바른가?
- 응답 시간이 기준 이하인가?
- 에러 응답 형식이 일관적인가?
API 계약이 깨졌을 때 그래프 실행 중에 발견하는 것이 아니라, API 테스트 단계에서 먼저 감지하는 것이 안전합니다.
3. 환경별 키와 엔드포인트 분리
에이전트는 개발, 스테이징, 프로덕션 환경에서 서로 다른 API 키와 엔드포인트를 사용합니다. 이 값을 노드 코드에 직접 넣으면 보안과 유지보수 문제가 생깁니다.
환경 변수 또는 API 테스트 도구의 환경 관리 기능을 사용해 다음 값을 분리해야 합니다.
BASE_URLAPI_KEYLLM_API_KEYMOCK_SERVER_URLSTAGING_SERVER_URL
완전한 에이전트 중심 워크플로우가 필요하다면 AI 에이전트를 위한 Apidog 테스트 하네스를 참고할 수 있습니다.
중요한 점은 역할 분리입니다. LangGraph는 에이전트를 오케스트레이션하고, Apidog는 에이전트가 호출하는 API를 테스트하고 목업합니다.
자주 묻는 질문
LangGraph가 LangChain을 대체하나요?
아닙니다. LangGraph는 오케스트레이션 런타임이고, LangChain은 모델·도구·검색기·통합을 포함하는 더 넓은 구성 요소 집합입니다. 둘은 같은 팀에서 만든 별도 라이브러리이며, 함께 사용할 수도 있고 LangGraph만 단독으로 사용할 수도 있습니다.
LangGraph를 시작하기 위해 LangChain을 알아야 하나요?
아닙니다. StateGraph를 정의하고, 노드와 엣지를 추가하고, 노드 내부에서 원하는 모델 클라이언트를 호출하면 됩니다. LangChain의 모델 래퍼는 편리하지만 필수는 아닙니다.
LangGraph는 호출 간에 어떻게 내용을 기억하나요?
체크포인터를 사용합니다. 체크포인터로 그래프를 컴파일하고 thread_id를 전달하면 LangGraph가 각 단계 후 상태 스냅샷을 저장합니다. 이후 같은 thread_id로 호출하면 해당 상태를 복원합니다.
에이전트가 호출하는 API는 어떻게 테스트하나요?
그래프와 분리해서 테스트하고 목업해야 합니다. 개발 중에는 LLM 및 도구 엔드포인트를 목업해 비용과 지연을 줄이고, 응답 스키마를 단언해 필드 변경으로 인한 노드 오류를 방지해야 합니다. ChatGPT API 테스트 가이드는 인증, 스트리밍, 도구 호출처럼 에이전트가 의존하는 주요 부분을 다룹니다.
요약
LangGraph는 루프, 분기, 영속성, human-in-the-loop가 필요한 에이전트에 적합한 오케스트레이션 프레임워크입니다. 워크플로우를 공유 상태 기반 그래프로 모델링하고, 체크포인터로 실행 상태를 저장하며, 필요하면 Studio와 Platform으로 디버깅 및 배포를 보완할 수 있습니다.
다만 LangGraph는 API 계약을 검증하지 않습니다. 에이전트가 호출하는 API는 별도로 목업하고 테스트해야 합니다. 개발 비용을 줄이고 도구 호출의 신뢰성을 높이려면 Apidog에서 엔드포인트를 목업하고 응답을 단언하세요. 에이전트가 실제 엔드포인트를 호출하기 전에 테스트 환경을 구성하려면 Apidog를 다운로드하십시오.



Top comments (0)