13. 체크포인트·메모리·휴먼 인 더 루프
- 체크포인터(checkpointer)로 그래프 상태를 영속화하는 법을 안다
thread_id로 여러 대화 세션을 구분하는 법을 익힌다interrupt로 사람의 승인을 받는 휴먼 인 더 루프를 구현한다- durable execution(실패 복구)의 가치를 이해한다
- (보너스) AutoGen의 현재 위치와 후계 프레임워크를 안다
비유로 시작하기: 게임의 세이브 포인트
긴 게임을 하다 전원이 나가도, 마지막 세이브 포인트부터 다시 시작할 수 있습니다. 체크포인터가 LangGraph의 세이브 포인트입니다. 그래프가 각 단계의 상태를 저장해 두므로, 중간에 실패하거나 멈춰도 정확히 그 지점부터 재개할 수 있습니다.
이것이 LangGraph가 프로덕션 표준이 된 핵심 이유입니다. 실제 에이전트는 오래 돌고, 외부 API가 실패하고, 서버가 재시작됩니다. 매번 처음부터 다시 하면 비용과 시간이 폭발합니다. 체크포인터는 이 문제를 구조적으로 해결합니다.
체크포인터 붙이기
compile()에 체크포인터를 넘기면 됩니다. 그리고 실행할 때 thread_id로 어떤 대화인지 지정합니다.
from typing import Annotated, TypedDict from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages from langgraph.checkpoint.memory import InMemorySaver class State(TypedDict): messages: Annotated[list, add_messages] def chat_node(state: State) -> dict: last = state["messages"][-1]["content"] return {"messages": [{"role": "assistant", "content": f"받음: {last}"}]} builder = StateGraph(State) builder.add_node("chat", chat_node) builder.add_edge(START, "chat") builder.add_edge("chat", END) # 체크포인터 부착 checkpointer = InMemorySaver() graph = builder.compile(checkpointer=checkpointer) # thread_id로 세션 구분 config = {"configurable": {"thread_id": "user-123"}} graph.invoke({"messages": [{"role": "user", "content": "안녕"}]}, config) graph.invoke({"messages": [{"role": "user", "content": "방금 뭐라 했지?"}]}, config) # 같은 thread_id이므로 이전 대화가 상태에 누적되어 있다
thread_id가 같으면 같은 대화로 이어지고, 다르면 별개의 대화가 됩니다. 사용자별·세션별로 thread_id를 부여하면 여러 대화를 한 그래프로 관리할 수 있습니다.
| 체크포인터 | 저장 위치 | 용도 |
|---|---|---|
InMemorySaver | 메모리 | 개발·테스트(재시작 시 사라짐) |
SqliteSaver | SQLite 파일 | 로컬 영속·소규모 |
PostgresSaver | PostgreSQL | 프로덕션·수평 확장 |
⚠️ 흔한 실수: 개발 중 InMemorySaver로 잘 되던 메모리가 프로덕션에서 사라진다고 당황하는 것. InMemorySaver는 프로세스가 끝나면 함께 사라집니다. 영속이 필요하면 SqliteSaver나 PostgresSaver를 쓰세요. (PostgreSQL은 대규모 서비스에 널리 쓰이는 강력한 오픈소스 데이터베이스로, 여러 서버가 동시에 접속하는 프로덕션 환경에 적합합니다.)
단기 메모리 vs 장기 메모리
LangGraph는 두 층의 메모리를 구분합니다. 단기 메모리는 체크포인터가 관리하는 thread_id 단위의 대화 상태입니다(한 대화 안의 맥락). 장기 메모리는 Store가 관리하는, 여러 세션을 가로지르는 영속 정보입니다(사용자 이름·선호·설정 등).
from langgraph.store.memory import InMemoryStore long_term_store = InMemoryStore() graph = builder.compile(checkpointer=checkpointer, store=long_term_store)
📌 핵심: 1부에서 손으로 만든 "대화 기록"이 단기 메모리, "사용자별 영속 정보"가 장기 메모리에 해당합니다. LangGraph는 둘을 분리해 각각 최적의 저장소에 둘 수 있게 합니다.
휴먼 인 더 루프: interrupt
자율 에이전트라도 중요한 결정(결제, 계약, 파일 삭제 등)은 사람의 승인을 받아야 합니다. LangGraph는 interrupt로 그래프를 일시정지시키고 사람의 입력을 기다립니다.
from langgraph.types import interrupt, Command def human_review(state: State) -> dict: # 그래프를 멈추고 사람의 답을 기다림 answer = interrupt("이 작업을 승인하시겠습니까?") return {"messages": [{"role": "user", "content": answer}]}
흐름은 이렇습니다. 그래프가 interrupt 지점에서 멈추고, 체크포인터에 상태를 저장합니다. 사람이 검토 후 Command(resume="yes")로 재개하면, interrupt(...)가 그 값을 반환하며 그래프가 이어집니다.
flowchart TD
classDef node fill:#80DEEA,stroke:#00838F,color:#000
classDef pause fill:#FFE082,stroke:#F9A825,color:#000
classDef human fill:#A5D6A7,stroke:#2E7D32,color:#000
A[에이전트 작업]:::node --> I[interrupt<br/>일시정지·상태 저장]:::pause
I -.대기.-> H[사람 검토·승인]:::human
H -->|Command resume| R[그래프 재개]:::node
이것이 가능한 이유가 바로 체크포인터입니다. 멈춘 상태가 저장되어 있어야 나중에 정확히 그 지점에서 재개할 수 있습니다. 휴먼 인 더 루프는 durable execution 위에 세워집니다.
💡 팁: interrupt는 사람의 승인뿐 아니라, 시간이 오래 걸리는 외부 프로세스를 기다릴 때도 씁니다. 며칠 뒤 외부 시스템의 응답이 와도, 저장된 상태에서 이어갈 수 있습니다.
💡 실습 아이디어(강의의 부킹 에이전트 대응): 항공권 검색·예약 그래프에서 "결제 직전"에 interrupt를 넣어, 사용자가 최종 확인을 해야만 결제 노드로 진행하게 만들어 보세요. 자율성과 통제의 균형을 직접 체험할 수 있습니다.
🔎 보너스: AutoGen은 지금 어디에 있나
이 강좌에는 AutoGen(Microsoft의 대화형 멀티에이전트 프레임워크) 섹션이 있습니다. 그런데 2026년 현재 지형이 바뀌었으니, 학습 전에 맥락을 짚어두면 혼란이 줄어듭니다.
검색으로 확인한 현재 상황은 이렇습니다. AutoGen은 유지보수 모드(maintenance mode)로 전환되었습니다. 즉 버그 수정·보안 패치·문서 개선만 이뤄지고, 적극적인 신기능 개발은 멈췄습니다. Microsoft는 후계 프레임워크로 Microsoft Agent Framework(MAF)를 내놓았고, 2026년 2월 Release Candidate에 도달했습니다. Release Candidate(RC)는 정식 출시 직전의 후보 버전으로, 큰 문제가 없으면 그대로 정식판이 되는 단계입니다. MAF는 AutoGen과 Semantic Kernel 두 팀이 합쳐 만든 것으로, 그래프 기반 워크플로(LangGraph와 유사한 명시적 노드·엣지), 체크포인트, 휴먼 인 더 루프, 멀티 프로바이더(OpenAI·Claude·Azure 등)를 지원합니다.
flowchart LR
classDef old fill:#EF9A9A,stroke:#C62828,color:#000
classDef fork fill:#FFE082,stroke:#F9A825,color:#000
classDef new fill:#A5D6A7,stroke:#2E7D32,color:#000
A[AutoGen v0.2<br/>GroupChat] --> B[AutoGen v0.4~0.7<br/>이벤트 기반<br/>⚠️ 유지보수 모드]:::old
A --> AG2[AG2<br/>커뮤니티 포크]:::fork
B --> MAF[Microsoft Agent Framework<br/>공식 후계자]:::new
정리하면, 이미 AutoGen으로 돌아가는 코드가 있다면 계속 쓸 수 있고 강의 학습도 의미가 있습니다(개념은 다른 프레임워크로 그대로 전이됩니다). 하지만 새 프로젝트를 시작한다면 신규 개발이 활발한 LangGraph나, Microsoft 생태계라면 MAF를 검토하는 것이 합리적입니다. 흥미롭게도 MAF가 채택한 "명시적 그래프 워크플로"는 이 부에서 배운 LangGraph의 철학과 매우 닮았습니다. 즉 이 부의 그래프 개념을 익혀두면 MAF 학습도 수월합니다.
이 장에서 배운 것
- 체크포인터는 그래프의 세이브 포인트다. 실패·중단 후 정확히 그 지점에서 재개(durable execution)할 수 있다.
thread_id로 여러 대화 세션을 구분한다. 개발은InMemorySaver, 프로덕션은Postgres/Sqlite Saver를 쓴다.- 단기 메모리(thread 단위)와 장기 메모리(Store, 세션 횡단)를 구분한다.
interrupt로 휴먼 인 더 루프를 구현하며, 이는 체크포인터 위에서 동작한다.- AutoGen은 유지보수 모드이고, 후계는 그래프 워크플로 기반의 Microsoft Agent Framework다.
✍️ 확인 문제
InMemorySaver로 만든 대화가 서버 재시작 후 사라졌다. 원인과 해결책은?- 같은 그래프로 사용자 A와 B의 대화를 섞이지 않게 관리하려면 무엇을 다르게 줘야 하는가?
interrupt를 이용한 휴먼 인 더 루프가 체크포인터 없이는 제대로 동작할 수 없는 이유는?
이전 장: 12. 조건부 엣지와 도구 호출
다음 부: 5부 · CrewAI — 그래프 대신 "역할을 가진 팀"으로 멀티에이전트를 표현하는 또 다른 철학을 배웁니다.