영속성 (Persistence)

영속성 (Persistence)

영속성(Persistence)은 LangGraph 애플리케이션이 단 한 번의 그래프 실행(graph run)을 넘어서도 유용한 정보를 보관할 수 있게 해줘요. 이 기능이 꼭 필요한 순간이 있는데요, 에이전트가 대화를 이어가야 하거나, 중단(interruption) 이후 다시 재개해야 하거나, 실패에서 복구해야 하거나, 여러 상호작용에 걸쳐 정보를 기억해야 하는 경우가 바로 그런 상황이에요.

LangGraph는 이 문제를 해결하기 위해 서로 보완해 주는 두 가지 영속성 시스템을 제공합니다.

  • 체크포인터 (Checkpointers): 스레드의 그래프 상태를 체크포인트(checkpoint)로 저장해요. 단기적이고 스레드 범위에 한정된 메모리용으로 사용하면 되는데, 대화의 연속성, 인간-인-더-루프(human-in-the-loop) 워크플로, 타임 트래블(time travel), 장애 허용(fault tolerance) 등이 여기에 해당해요.
  • 스토어 (Stores): 그래프 상태 밖에 애플리케이션이 정의한 데이터를 저장해요. 장기적이고 스레드를 넘나드는 메모리용으로 사용하면 되는데, 사용자 선호, 사실, 공유 지식 등이 여기에 해당해요.

대부분의 애플리케이션은 두 시스템을 함께 쓰게 돼요. 체크포인터가 현재 스레드를 추적하고, 스토어가 스레드를 아우르는 지속적인 정보를 추적하니까요.

퀵스타트 (Quickstart)

그래프를 컴파일할 때 체크포인터, 스토어, 또는 둘 다를 함께 전달하면 됩니다.

from langgraph.checkpoint.memory import InMemorySaver
from langgraph.store.memory import InMemoryStore

checkpointer = InMemorySaver()
store = InMemoryStore()

graph = builder.compile(checkpointer=checkpointer, store=store)

result = graph.invoke(
    {"messages": [{"role": "user", "content": "Hi, my name is Bob."}]},
    {"configurable": {"thread_id": "thread-1"}},
)

에이전트 서버(Agent Server)는 영속성을 자동으로 처리해요. 에이전트 서버를 사용한다면 체크포인터나 스토어를 직접 구현하거나 설정할 필요가 없어요. 서버가 뒤에서 영속성 인프라를 대신 처리해 주거든요.

체크포인터 vs. 스토어

체크포인터 (Checkpointer) 스토어 (Store)
저장 내용 그래프 상태의 스냅샷 애플리케이션에서 정의한 키-값 데이터
범위 (Scope) 단일 스레드 스레드 간 (크로스 스레드)
메모리 유형 단기, 스레드 범위 메모리 장기, 크로스 스레드 메모리
용도 대화 연속성, 인간-인-더-루프, 타임 트래블, 장애 허용 사용자 선호, 사실, 공유 지식
접근 방식 그래프 config에 thread_id 전달 노드 또는 애플리케이션 코드에서 항목 읽기/쓰기
전체 가이드 체크포인터 스토어

흔한 문제 해결

PostgresSaver: thread_id가 너무 긴 경우

PostgresSaver(또는 AsyncPostgresSaver)를 사용할 때 thread_id는 길이가 제한된 컬럼에 저장됩니다. thread_id가 컬럼 크기를 넘어서면 데이터베이스 오류가 나게 되는데요. 해결책: thread_id 값을 255자 미만으로 유지하세요. 결정적인(선택 가능한) ID가 필요하다면 UUID나 해시를 사용하면 됩니다.

import uuid

config = {"configurable": {"thread_id": str(uuid.uuid4())[:255]}}

MemorySaver는 재시작 후에도 유지되지 않아요

MemorySaverInMemorySaver는 체크포인트를 RAM에 저장합니다. 프로세스가 재시작되면 모든 체크포인트가 사라지는데요. 해결책: 운영 환경(프로덕션)에서는 지속형 체크포인터를 사용하세요.

  • PostgresSaver: 비동기 지원이 되는 PostgreSQL
  • SqliteSaver: 개발용 로컬 파일 기반 저장소

체크포인트가 끝없이 늘어나는 경우

대화가 길어지면 체크포인트가 계속 쌓입니다. 이렇게 되면 지연 시간(latency)과 저장 비용이 늘어날 수 있는데요. 해결책: 오래된 체크포인트를 주기적으로 정리(prune)하거나 보존 정책(retention policy)을 설정하세요.

from langgraph.checkpoint.postgres import PostgresSaver

checkpointer = PostgresSaver.from_conn_string("postgresql://...")
checkpointer.setup()  # 인덱스가 있는 테이블을 생성합니다
# N일보다 오래된 체크포인트를 삭제하는 cron 작업을 추가하는 것을 고려해 보세요

부모 그래프에서 서브그래프로의 상태 접근

서브그래프가 상태를 업데이트해도 부모 그래프가 즉시 그 변경을 보지 못할 수 있어요. 이유는 각 서브그래프가 **자체 체크포인트 네임스페이스(checkpoint namespace)**를 관리하기 때문입니다. 해결책: 그래프 경계를 넘어야 하는 데이터에는 스토어를 통한 공유 상태를 사용하거나, 서브그래프가 부모 체크포인트에 쓰도록 구성하세요.

다음 단계