LangGraph — 흐름을 직접 그리는 저수준 에이전트 오케스트레이션
LangGraph는 왜 저수준(low-level) 프레임워크인가. State·Node·Edge라는 원초적 부품으로 흐름을 직접 설계하는 방식과, 체크포인트·human-in-the-loop 같은 운영 안전장치까지 함께 제공하는 이유를 Why→How 관점으로 정리합니다.
지난 글의 ReAct는 생각하고, 행동하고, 관찰하는 루프였습니다. 문제는 그 루프를 실제 시스템에 옮길 때입니다. 어떤 단계는 사람이 경로를 정해 두고, 어떤 단계는 모델이 다음을 고르게 하고, 위험한 순간에는 사람을 끼워 넣어야 합니다. 그 흐름을 직접 그리게 하는 저수준(low-level) 프레임워크가 LangGraph입니다.
에이전트 프레임워크는 대체로 두 부류입니다. 하나는 정해진 협업 틀을 통째로 제공해 빠르게 만들게 해주는 고수준(high-level) 방식, 다른 하나는 원초적인 부품만 주고 흐름은 개발자가 직접 그리게 하는 저수준 방식입니다. LangGraph는 후자에 속합니다.
Why — 정해진 틀은 편하지만, 곧 벽에 부딪힌다
고수준 프레임워크는 매력적입니다. “에이전트 여러 개가 이렇게 협업한다”는 틀을 미리 짜서 주니, 몇 줄로 그럴듯한 에이전트가 만들어집니다. 문제는 현실의 요구가 그 틀을 벗어나는 순간입니다.
실제 에이전트는 이런 걸 요구합니다. 모델을 호출하고, 결과를 검사하고, 재시도할지 사람에게 넘길지 판단하고, 도구 두 개를 병렬로 부르고, 그다음 사람의 승인을 기다린다. 이런 흐름은 단순한 선형 체인으로는 표현하기 어렵습니다. 고수준 프레임워크가 제공하는 정해진 틀에서 조금만 벗어나도, 틀을 우회하고 억지로 끼워 맞추다 결국 감춰둔 내부를 파헤치게 됩니다.
LangGraph의 문제의식이 여기입니다. 복잡하고 커스텀한 제어 흐름이 필요할 때, 틀을 주는 대신 흐름을 직접 그릴 수 있는 부품을 주자는 것입니다. 공식 문서도 이 철학을 분명히 합니다. LangGraph는 프롬프트나 에이전트 아키텍처를 대신 정해 주지 않습니다. 대신 흐름을 설계할 저수준 인프라를 제공합니다.
앞서 Graph / Node / Edge로 오케스트레이션 엔진을 직접 만들었던 경험과도 맞닿아 있습니다. 그때는 경로의 모양을 제가 정하는 철길(workflow)이었습니다. LangGraph는 그 부품을 표준으로 주고, 같은 그래프 위에 사거리(agent)와 신호등(harness)까지 그릴 수 있게 합니다.
How — State, Node, Edge라는 세 가지 부품
LangGraph가 주는 부품은 세 가지입니다.
- State (상태): 그래프 전체를 흐르는 공유 스냅샷입니다. 각 단계가 읽고 쓰는 공용 작업 공간이라고 보면 됩니다.
- Node (노드): 실제 작업의 단위입니다. 모델을 호출하거나, 도구를 실행하거나, 어떤 처리를 하는 함수 하나가 노드가 됩니다.
- Edge (엣지): 다음에 어느 노드를 실행할지 정하는 경로입니다. 항상 같은 곳으로 가는 고정 전이일 수도, 상태를 보고 갈리는 조건부 전이일 수도 있습니다.
개발자는 이 셋으로 그래프를 직접 그립니다. 공식 문서의 가장 단순한 예시가 이 구조를 잘 보여줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from langgraph.graph import START, StateGraph
from typing_extensions import TypedDict
class State(TypedDict):
text: str
def node_a(state: State) -> dict:
return {"text": state["text"] + "a"}
def node_b(state: State) -> dict:
return {"text": state["text"] + "b"}
graph = StateGraph(State)
graph.add_node("node_a", node_a)
graph.add_node("node_b", node_b)
graph.add_edge(START, "node_a")
graph.add_edge("node_a", "node_b")
print(graph.compile().invoke({"text": ""}))
# {'text': 'ab'}
State를 정의하고, 두 개의 노드를 만들고, 엣지로 순서를 이었을 뿐입니다. 단순해 보이지만, 바로 이 단순함이 확장성의 원천입니다. 시리즈의 언어로 옮기면 노드는 작업이고, 고정 엣지는 철길이며, 조건부 엣지에서 모델이 다음을 고르면 사거리가 됩니다.
확장성 — 원하는 제어 흐름을 원하는 대로
부품만 주어지면, 조립은 자유에 가깝습니다.
엣지를 조건부로 만들면 분기가 됩니다. 상태를 보고 이 노드로 갈지 저 노드로 갈지 결정하는 라우터 패턴입니다. 이전 노드로 되돌아가는 엣지를 만들면 반복(루프)이 됩니다. 결과가 만족스럽지 않으면 다시 시도하게 하는 식입니다. ReAct의 Thought → Action → Observation도 이 루프로 그릴 수 있습니다. 여러 노드로 동시에 뻗는 엣지를 만들면 병렬 실행이 됩니다. 특정 조건에서 그래프를 멈추게 하면 중단이 됩니다.
분기, 반복, 조건, 병렬, 중단 — 이 제어 흐름을 개발자가 직접 설계할 수 있습니다. 그래서 LangGraph는 단순한 챗봇에는 과한 경우가 많습니다. 선형 체인으로 충분한 일에 그래프를 그리는 건 불필요한 복잡성이니까요. 대신 다단계 리서치 에이전트, 에스컬레이션이 있는 고객지원 워크플로우, 테스트 실패를 보고 스스로 고치는 코딩 어시스턴트처럼 복잡하고 커스텀한 제어 흐름이 필요할 때 빛납니다.
공식 문서의 선택 기준도 여기에 가깝습니다. 결정적 흐름과 에이전트적 흐름을 한 그래프에서 섞어야 하거나, 커스터마이징이 무겁거나, 지연을 세밀하게 제어해야 하면 LangGraph가 맞습니다. 반대로 사전 제작된 아키텍처로 빠르게 만들고 싶다면 더 높은 수준의 추상화를 쓰는 편이 낫습니다.
한 겹 더 — 흐름 설계를 넘어, 안전하게 운영하는 장치
여기까지는 “복잡한 흐름을 표현하는 확장성”에 관한 이야기입니다. 그런데 LangGraph가 부품만 주는 그래프 라이브러리였다면, 유연함에서 끝났을 겁니다. 실무에서 선택받는 이유는 한 겹 더 있습니다. 복잡한 흐름을 안전하게 운영하는 장치를 함께 내장한다는 점입니다.
상태 지속과 체크포인트. 그래프를 체크포인터와 함께 컴파일하면, 각 슈퍼스텝 경계에서 상태가 저장됩니다. 실패하거나 중간에 멈춰도 마지막 체크포인트에서 이어서 재개할 수 있습니다. 오래 걸리는 작업이나 장시간 도는 에이전트에서는 처음부터 다시 돌릴 필요가 없습니다. 다만 이건 실패 복구이지 실패 예방은 아닙니다. 그리고 재개 시 해당 노드는 함수 처음부터 다시 돌아가므로, 일시정지 전에 일어난 부수 효과는 한 번 더 실행될 수 있습니다.
Human-in-the-loop. 실행 도중 어느 지점에서든 사람이 개입해 상태를 검사하고 수정할 수 있습니다. 민감한 작업 — 프로덕션 DB에 쓰거나 결제를 실행하기 직전 — 에서 사람의 승인을 받도록 중단점을 심을 수 있습니다. 이 중단이 며칠 뒤에도 재개되려면 체크포인트가 필요합니다. 에이전트에게 자율성을 주되, 위험한 순간에는 사람이 손을 얹는 장치입니다. 시리즈에서 말한 하네스의 한 형태입니다.
스트리밍과 관측성. 그래프가 실행되는 과정을 실시간으로 스트리밍하고, 각 노드에서 무슨 일이 일어나는지 추적할 수 있습니다. 에이전트의 동작이 블랙박스가 되지 않게 하는 장치입니다.
정리하면 LangGraph는 두 가지를 함께 줍니다. 흐름을 자유롭게 설계하는 부품과, 그 흐름을 실제로 운영할 때 필요한 안전장치입니다. 전자만 있으면 유연하지만 위태롭고, 후자만 있으면 안전하지만 뻣뻣합니다. 둘을 함께 주기 때문에 복잡한 프로덕션 에이전트를 만들 수 있습니다.
정리
LangGraph를 한 줄로 요약하면 이렇습니다.
- Why: 정해진 틀을 주는 고수준 프레임워크는 편하지만, 커스텀한 제어 흐름이 필요한 순간 벽에 부딪힌다.
- How: State·Node·Edge라는 저수준 부품만 주고, 분기·반복·병렬·중단을 개발자가 직접 그리게 한다.
- 한 겹 더: 흐름 설계에 더해 상태 지속·체크포인트, human-in-the-loop, 스트리밍·관측성이라는 운영 안전장치를 내장한다.
저수준 프레임워크는 진입 장벽이 있습니다. 흐름을 직접 그려야 하니까요. 하지만 그 대가로 얻는 것은 제어권입니다. 에이전트가 복잡해질수록, 남이 짜준 틀보다 내가 그린 그래프가 필요해집니다. ReAct가 루프의 리듬이라면, LangGraph는 그 리듬을 철길과 사거리가 섞인 실제 시스템으로 옮기는 도면입니다.