에이전트 아키텍처

LangGraph 기본 Message Channel과 내장 체크포인트 테이블을 그대로 쓰지 않는 이유

2026.08.29 · 4분 읽기 · 1,074 단어
📊 슬라이드

개요

요약

LangGraph로 멀티에이전트 시스템을 만들 때 그래프 실행 상태는 LangGraph에 맡긴다. 다만 서버 재기동이나 여러 인스턴스 조율처럼 서비스 운영에 필요한 부분은 왜 별도 테이블로 나눠 관리하는지 정리한다. 대화만 담당하는 Message channel과 실행 상태를 저장하는 Checkpointer가 어떻게 다른 개념인지도 함께 짚는다.

목차

LangGraph의 체크포인트 기능은 그대로 쓴다. 그래프 실행 상태는 LangGraph의 네이티브 AgentStateMemorySaver가 관리하고 재기동과 다중 인스턴스 재개는 우리 테이블인 ag_agent_checkpoint가 맡는다. 대화 이력과 질의 재실행은 채팅·실행 로그 테이블의 몫이다.

정리하면 그래프 실행은 LangGraph에, 서비스 운영과 데이터 관리는 우리 테이블에 맡긴다.

현재 역할 분리

  • 그래프 실행 상태: LangGraph AgentState
  • 같은 프로세스 안에서 중단 후 재개: LangGraph MemorySaver
  • 재기동·다중 인스턴스 재개: ag_agent_checkpoint
  • 세션과 질문 순서: ag_chat_session_meta, ag_chat_turn_meta
  • 질의 재실행과 실행 이력: ag_chat_turn_meta, op_* 로그

LangGraph가 제공하는 내장 기능을 그대로 쓸지 판단하려면 먼저 Message channel과 Checkpointer가 어떤 관계인지부터 알아야 한다.

Message channel과 Checkpointer의 관계

둘은 연관돼 있지만 서로 다른 개념이다.

  • Message channel: State 안에 어떤 대화 내용을 담을지 정의한다.
  • Checkpointer: Message를 포함한 전체 State를 언제, 어디에 저장하고 복원할지 담당한다.

일반적인 관계는 다음과 같다.

AgentState
├─ messages          ← Message channel
├─ current_step
├─ next_agent
├─ retry_count
└─ approval_status

   Checkpointer

 MemorySaver 또는 DB

Message channel은 Checkpointer가 저장하는 State의 일부일 수 있다. Message channel만 있고 Checkpointer가 없으면 프로세스가 종료된 뒤 대화와 실행 상태를 복원할 수 없다.

반대로 Checkpointer가 있다고 Message channel을 반드시 써야 하는 것은 아니다. Checkpointer는 Message가 없는 구조화된 AgentState도 저장할 수 있다.

지금 시스템은 Message channel 없이 AgentState를 중심으로 실행하고 MemorySaver가 State와 실행 위치를 저장한다. 시스템 재기동과 다중 인스턴스 재개는 ag_agent_checkpoint가 담당한다. Message channel만으로 처리하기에는 몇 가지 한계가 있기 때문이다.

Message channel만으로는 부족한 이유

Message channel은 LangGraph의 State 안에 사용자 질문, AI 답변, 도구 호출 결과를 순서대로 쌓는 대화용 공간이다. Message channel 자체가 별도 DB나 체크포인터인 것은 아니다.

단순한 챗봇이라면 Message channel 중심으로도 충분히 구성할 수 있다.

  • 사용자 질문과 AI 답변 저장
  • 이전 대화를 참고한 후속 답변
  • 간단한 도구 호출과 결과 전달
  • 단순한 대화 중단과 재개

하지만 대형 멀티에이전트는 대화 내용 말고도 다음과 같은 실행 상태가 더 필요하다.

  • 지금 실행 중인 에이전트와 현재 작업 단계, 다음에 실행할 에이전트
  • Gate(안전성 검사) 결과
  • 사용자 승인 종류와 재개 위치, 즉 HIL(Human-in-the-loop) 지점
  • 재시도·취소·중복 실행 제어
  • SQL과 대용량 구조화 결과
  • 채팅 질문 순서와 특정 질문 재실행
  • 다중 인스턴스 간 실행 상태 공유

Message는 대화 내용일 뿐 실행 상태 자체는 아니다. 예를 들어 사용자가 "승인"이라고만 입력해도, 그게 계획 승인인지 보고서 선택인지 NL2SQL 추가 질문에 대한 답인지는 Message만 보고는 알 수 없다. 현재 단계와 승인 대기 위치가 함께 있어야 정확히 재개할 수 있다.

Message 중심으로 실행 상태까지 관리하면 다음 문제가 생긴다.

  • Message를 다시 해석해야 하니, 모델이나 프롬프트가 바뀌면 다음 실행 경로도 달라질 수 있다.
  • SQL 결과와 워커 결과를 Message에서 다시 꺼내면 구조와 타입이 깨질 수 있다.
  • 도구 호출 여부를 다시 판단하는 과정에서 SQL이나 외부 API가 중복 실행될 수 있다.
  • 대화가 길어질수록 체크포인트 저장량과 모델 입력 토큰이 함께 늘어난다.
  • 오래된 Message를 요약하거나 지우면 실행 재개에 필요한 정보까지 함께 사라질 수 있다.
  • 대화 원문, 내부 오류, 도구 결과가 뒤섞여서 보안 등급과 보존 기간을 나누기 어렵다.

그래서 Message는 대화 문맥으로, AgentState실행 제어로 역할을 나눠 쓴다.

한마디로 Message channel은 단순 챗봇에는 적합하지만, 대형 멀티에이전트의 실행 상태·재개·분산 제어까지 혼자 맡기에는 한계가 있다. 여기에 LangGraph의 DB checkpointer까지 그대로 얹으면 문제가 하나 더 생긴다.

내장 DB 테이블을 그대로 쓰지 않는 이유

LangGraph의 DB checkpointer를 쓰면 프레임워크가 다음 용도의 전용 테이블을 대신 관리해 준다.

  • 체크포인트 스키마 버전 관리
  • 그래프 단계별 state와 부가 정보 저장
  • state 안의 큰 데이터를 따로 분리 저장
  • 노드 실행 중간 결과 저장

이 테이블은 어디까지나 그래프를 중단한 위치에서 다시 실행하기 위한 내부 저장소다. 우리 서비스에 필요한 아래와 같은 업무 기능까지 제공하지는 않는다.

  • 사용자와 세션의 소유권 확인
  • 채팅 화면에 보여줄 질문 순서
  • 특정 질문부터 다시 실행
  • 이후 질문과 관련 로그 정리
  • 사용자 승인 응답을 먼저 처리한 쪽만 인정하는 선점 처리
  • 취소, 중복 실행 방지

그래프 순서와 채팅 순서는 다르다

한 번의 사용자 질문 안에서도 에이전트와 Gate를 여러 번 거치기 때문에, LangGraph 체크포인트는 그때마다 여러 개 생긴다. 체크포인트 순서는 그래프가 실행된 순서이고 채팅의 질문 순서는 사용자가 실제로 질문한 순서다. 이 둘은 같지 않다.

예를 들어 사용자가 2번 질문을 다시 실행하면 기존 2·3·4번 질문과 관련된 실행 로그를 먼저 정리한 뒤 새 2번 질문부터 이어야 한다. LangGraph 내장 테이블만 봐서는 어떤 체크포인트 묶음이 채팅의 2번 질문에 해당하는지 바로 알 수 없다. 그래서 ag_chat_turn_meta.turn_index가 따로 필요하다.

내장 테이블을 써도 업무 테이블은 남는다

내장 테이블만으로 지금 기능을 구현하려면 다음 작업이 추가로 필요하다.

  • 사용자·세션·워크플로우·채팅 순서를 서로 연결하는 정보 추가
  • 사용자·세션 조회와 채팅 순서 조회를 위한 인덱스 추가
  • 사용자 승인 선점과 취소를 위한 조건부 처리 추가
  • 질의를 다시 실행할 때 체크포인트·채팅 턴·실행 로그를 함께 정리하는 처리 추가
  • Java·Python, PostgreSQL·Oracle 사이의 저장 방식 차이 대응

결국 내장 테이블을 도입해도 ag_chat_turn_metaop_* 로그는 그대로 유지해야 한다. 소유권 확인, 취소, 사용자 승인 선점, 중복 실행 방지도 여전히 별도 업무 로직으로 남는다.

내장 테이블을 직접 손대면 부담이 커진다

내장 테이블에 우리 컬럼과 인덱스를 직접 추가하면 업무 기능이 LangGraph 내부 구조에 그대로 묶여 버린다. 이후 LangGraph나 checkpointer 버전이 바뀔 때마다 다음 항목을 다시 확인해야 한다.

  • 테이블과 컬럼 구조가 바뀌었는지
  • 인덱스와 저장 방식이 바뀌었는지
  • 기존 체크포인트 데이터와 호환되는지
  • 대기 중인 사용자 승인을 그대로 재개할 수 있는지
  • 질의 재실행과 데이터 삭제 범위는 그대로인지

이렇게 되면 버전 업그레이드가 단순한 패키지 교체로 끝나지 않고 DB 전환과 회귀 테스트까지 딸려온다.

대형 멀티에이전트 시스템에서는 체크포인트 하나에 모든 역할을 몰아넣지 않는다. 대화 이력, 검색, 권한, 감사, 재실행처럼 업무 성격이 다른 데이터는 각각 별도 테이블로 관리한다. 실제로 내장 테이블 중심으로 옮기면 여러 곳이 함께 바뀐다.

내장 테이블 중심으로 바꾸면 생기는 영향

  • MemorySaver를 DB용 LangGraph checkpointer로 바꾸고 전용 패키지와 테이블을 새로 추가해야 한다.
  • AgentState에 Message channel과 메시지 병합·삭제 규칙을 추가해야 한다.
  • HIL 재개와 상태 복원 로직을 LangGraph checkpoint 조회 방식에 맞춰 다시 짜야 한다.
  • ag_agent_checkpoint의 저장·복원 기능은 줄일 수 있지만, 사용자 소유권·취소·HIL 선점·중복 실행 방지는 여전히 따로 구현해야 한다.
  • ag_chat_turn_metaop_* 로그는 채팅 순서와 질의 재실행에 필요해서 그대로 유지해야 한다.
  • 질의를 다시 실행할 때 LangGraph checkpoint와 채팅 턴·실행 로그를 함께 정리하는 로직을 새로 맞춰야 한다.
  • 기존에 대기 중이던 HIL과 체크포인트 데이터를 새 구조로 옮기는 전환 작업이 필요하다.
  • Java와 Python이 같은 체크포인트 테이블을 함께 쓰기 어려워서 공통 복구 규약을 다시 정해야 한다.
  • PostgreSQL과 Oracle마다 DB checkpointer를 쓸지, 별도로 구현할지 다시 정해야 한다.
  • LangGraph 버전이 바뀔 때마다 테이블 구조, 데이터 호환성, HIL·재실행 회귀 테스트를 새로 돌려야 한다.

결과적으로 체크포인트 저장 방식 하나만 바뀌는 게 아니라, 상태 모델·재개·재실행·분산 제어·데이터 전환까지 함께 바뀐다. 반면 채팅과 운영에 쓰는 업무 테이블은 대부분 그대로 남는다.

한 줄 답변

LangGraph의 네이티브 state와 체크포인트 기능은 그대로 쓴다. 다만 Message와 내장 테이블만으로는 채팅 순서 질의 재실행, 사용자 승인, 취소, 다중 인스턴스 제어까지 처리할 수 없어서 ag_agent_checkpoint, ag_chat_turn_meta, op_* 테이블로 역할을 나눴다.