체크포인팅과 멱등성
루프가 중단될 때 발생하는 비용
섹션 제목: “루프가 중단될 때 발생하는 비용”장기 실행 에이전트 루프는 네트워크 오류, 컨테이너 재시작, 요금 한도 초과, 인간 개입 등 예측 불가한 이유로 언제든 중단될 수 있다. 체크포인팅(checkpointing) 없이 중단되면 선택지는 하나뿐이다. 처음부터 다시 시작. 수십 분, 수백 이터레이션의 작업이 사라진다. 비용과 시간이 두 배가 된다. 더 나쁘게는, 절반만 완료된 외부 상태(파일 수정, API 호출, 데이터베이스 변경)가 남아서 재시작 시 충돌을 일으킬 수 있다.
체크포인팅은 이 문제를 해결한다. 작업 상태를 주기적으로 저장하여 실패 지점 이후부터 재개할 수 있게 한다.
체크포인팅 없는 경우 vs 있는 경우
체크포인팅 없음: [A ─── B ─── C ─── D ─── (실패)] ──▶ 처음부터: [A ─── B ─── ...]
체크포인팅 있음: [A ─── B ─▣─ C ─── D ─── (실패)] ──▶ 체크포인트 B부터: [C ─── D ─── ...] ↑ 저장된 체크포인트무엇을 체크포인트에 저장할까
섹션 제목: “무엇을 체크포인트에 저장할까”에이전틱 루프의 체크포인트는 최소한 다음 세 가지를 포함해야 한다.
┌──────────────────────────────────────────────────────────────┐│ 체크포인트에 저장할 항목 │├──────────────────────────────────────────────────────────────┤│ 1. 메시지 히스토리 전체 대화 컨텍스트 (직렬화 가능 형태) ││ 2. 루프 상태 이터레이션 번호, 현재 단계, 완료된 서브태스크││ 3. 외부 상태 참조 수정된 파일 목록, 생성된 리소스 ID │└──────────────────────────────────────────────────────────────┘외부 상태 참조가 특히 중요하다. 루프가 파일을 생성하거나 외부 API를 호출했다면, 재개 시 그 작업을 중복 실행하지 않도록 추적해야 한다. 이것이 멱등성과 연결되는 지점이다.
LangGraph의 체크포인터
섹션 제목: “LangGraph의 체크포인터”LangGraph는 그래프 노드 사이의 상태를 자동으로 영속화하는 체크포인터(checkpointer) 를 내장한다. 각 노드 실행 후 상태 스냅샷을 저장하고, 실패 시 마지막 성공 노드부터 재개한다.
# 개념 이해용 의사 코드이며 실제 API와 다를 수 있습니다from langgraph.checkpoint.memory import MemorySaverfrom langgraph.graph import StateGraph
# 인메모리 체크포인터 (프로덕션에서는 PostgreSQL/Redis 사용)checkpointer = MemorySaver()
builder = StateGraph(AgentState)builder.add_node("reason", reason_node)builder.add_node("act", act_node)builder.add_node("observe", observe_node)builder.add_edge("reason", "act")builder.add_edge("act", "observe")builder.add_conditional_edges("observe", should_continue)
graph = builder.compile(checkpointer=checkpointer)
# 스레드 ID로 실행 — 같은 ID로 재실행하면 마지막 체크포인트부터 재개config = {"configurable": {"thread_id": "task-abc-001"}}
result = graph.invoke(initial_state, config)thread_id는 재개를 위한 키다. 같은 thread_id로 다시 invoke하면 처음부터 실행하는 것이 아니라 마지막 저장된 체크포인트에서 이어진다.
git commit per session 패턴
섹션 제목: “git commit per session 패턴”코드 작업 에이전트에서 자주 사용하는 또 다른 체크포인팅 방식은 세션 단위 git commit이다. 각 의미 있는 작업 완료 후 commit을 생성하면 git 히스토리 자체가 체크포인트가 된다.
# 개념 이해용 의사 코드이며 실제 API와 다를 수 있습니다import subprocess
def commit_checkpoint(message: str, iteration: int): """현재 작업 상태를 git commit으로 체크포인트화.""" subprocess.run(["git", "add", "-A"], check=True) subprocess.run([ "git", "commit", "-m", f"[checkpoint] iter={iteration}: {message}" ], check=True)
# 루프 내에서 주요 단계 완료 후 호출for iteration in range(MAX_ITER): ... if milestone_reached(response): commit_checkpoint( f"마일스톤 완료: {describe_milestone(response)}", iteration )실패 시 git log로 마지막 체크포인트를 확인하고, 해당 commit으로 git reset하여 깨끗한 상태에서 재개할 수 있다. Anthropic의 컨텍스트 엔지니어링 가이드에서도 Claude Code가 주요 변경 후 커밋을 만드는 이 패턴을 권장한다.
멱등성: 두 번 실행해도 결과가 같아야 한다
섹션 제목: “멱등성: 두 번 실행해도 결과가 같아야 한다”체크포인팅과 함께 반드시 설계해야 하는 속성이 멱등성(idempotency) 이다. 멱등성이란 같은 작업을 두 번 이상 실행해도 결과가 동일한 성질이다. 수학적으로 f(f(x)) = f(x)인 함수다.
에이전틱 루프에서 멱등성이 왜 중요한가? 체크포인트에서 재개할 때, 이미 완료된 단계가 다시 실행될 수 있다. 이 재실행이 부작용을 발생시키면 안 된다.
멱등성의 예시
멱등적 (안전): 파일을 특정 내용으로 덮어쓰기 두 번 실행해도 파일 내용은 동일
비멱등적 (위험): 파일에 줄을 추가(append) 두 번 실행하면 줄이 중복됨
멱등적 (안전): API 호출에 idempotency key 포함 서버가 중복 처리를 방지함
비멱등적 (위험): 이메일 발송 두 번 실행하면 이메일이 두 번 전송됨Idempotency Key 패턴
섹션 제목: “Idempotency Key 패턴”외부 API 호출처럼 서버 측에서 멱등성을 지원해야 하는 경우, idempotency key를 사용한다.
# 개념 이해용 의사 코드이며 실제 API와 다를 수 있습니다import hashlibimport json
def make_idempotency_key(task_id: str, action: dict) -> str: """ 태스크 ID와 액션 내용으로 결정론적 키 생성. 같은 태스크의 같은 액션은 항상 같은 키를 가짐. """ content = json.dumps({"task_id": task_id, "action": action}, sort_keys=True) return hashlib.sha256(content.encode()).hexdigest()
def call_external_api(task_id: str, action: dict): key = make_idempotency_key(task_id, action) return api_client.post( "/operations", json=action, headers={"Idempotency-Key": key} # 서버가 중복 처리 방지 )이미 완료된 작업의 키를 서버가 기억하고 있으면, 재시도 시 실제 작업을 수행하지 않고 이전 결과를 반환한다. 결제, 메시지 발송, 데이터 삽입 등 비멱등적 작업에서 필수적인 패턴이다.
체크포인팅과 멱등성의 관계
섹션 제목: “체크포인팅과 멱등성의 관계”두 개념은 상호 보완적이다.
┌────────────────────────────────────────────────────────┐│ 체크포인팅 루프 상태를 저장하여 재개 지점 제공 ││ 멱등성 재개 시 이미 완료된 작업의 중복 실행 방지 ││ ││ 체크포인팅만 있고 멱등성이 없으면: ││ 재개 시 이전에 성공한 API 호출이 다시 실행됨 → 부작용 ││ ││ 멱등성만 있고 체크포인팅이 없으면: ││ 실패 시 처음부터 시작하지만 부작용은 없음 (비효율) ││ ││ 둘 다 있으면: ││ 효율적으로 재개 + 부작용 없음 │└────────────────────────────────────────────────────────┘다음 챕터에서는 체크포인팅과 멱등성을 기반으로 재시도 전략을 어떻게 구성하는지, 그리고 극단적인 경우 재시작을 유일한 복구 수단으로 삼는 크래시-온리 설계를 살펴본다.
참고 자료
- LangGraph 공식 문서 — 접속 2026-06-30
- Anthropic — Effective harnesses for long-running agents — 접속 2026-06-30
- Anthropic — Building Effective AI Agents — 접속 2026-06-30