AI 에이전트 상태 관리 — Mastra와 LangGraph.js의 DB 설계 패턴

Maru

@maru

AI 에이전트 상태 관리 — Mastra와 LangGraph.js의 DB 설계 패턴

AI 에이전트 상태 관리 — Mastra와 LangGraph.js의 DB 설계 패턴

AI 에이전트가 복잡한 장기 실행 업무를 수행하는 프로덕션 환경에서는 일시적인 메모리 저장만으로 시스템의 신뢰성을 보장하기 어렵습니다. 시스템이 오작동하거나 다운되더라도 마지막 작동 지점부터 안전하게 복구해 실행 흐름을 이어 나가는 영속성 레이어 설계가 핵심 과제입니다. 현대적인 에이전트 프레임워크인 Mastra와 LangGraph.js가 선택한 데이터베이스 영속성 설계 패턴을 살펴보고, 멀티 테넌트 격리와 커넥션 풀 고갈을 방지하는 실전 프로덕션 적용 기준을 제시합니다.

Mastra의 영역 분리 — MastraCompositeStore를 통한 멀티 DB 전략

AI 에이전트 서비스가 커질수록 쓰기 빈도가 높은 대화 메모리와 정교한 일관성이 필요한 워크플로우 상태를 하나의 데이터베이스에 모두 담는 방식은 한계에 부딪힙니다. Mastra는 이를 해결하기 위해 에이전트 영속성을 대화 메모리, 실행 스냅샷, 관찰 가능성의 세 가지 도메인으로 명확하게 분리합니다.

이러한 구조적 분리를 단일 인터페이스로 우아하게 묶어주는 핵심 도구가 바로 MastraCompositeStore입니다. Mastra 공식 문서에 따르면, 빈번하게 호출되며 응답 속도가 중요한 대화 기록은 에지에 최적화된 libSQL에 라우팅하고, 트랜잭션 안전성이 필수가 되는 복잡한 워크플로우 스냅샷은 PostgreSQL에 분리하여 저장할 수 있습니다. 이를 통해 불필요한 네트워크 지연을 줄이고 데이터베이스 리소스와 인프라 운영 비용을 획기적으로 최적화할 수 있습니다.

다음과 같은 코드를 통해 이러한 멀티 데이터베이스 라우팅 환경을 구축할 수 있습니다.

typescript
import { MastraCompositeStore } from "@mastra/core/storage";
import { MemoryLibSQL } from "@mastra/libsql";
import { WorkflowsPG } from "@mastra/pg";

const storage = new MastraCompositeStore({
  id: "composite-storage",
  domains: {
    memory: new MemoryLibSQL({
      url: "file:./local.db",
    }),
    workflows: new WorkflowsPG({
      connectionString: process.env.DATABASE_URL!,
    }),
  },
});

Mastra 인스턴스를 초기화할 때 이 복합 스토어 객체를 넘겨주기만 하면, 프레임워크 수준에서 대화 쓰레드 정보와 워크플로우 단계별 상태를 정의된 저장소로 알아서 라우팅합니다. 인프라의 물리적 복잡성을 추상화 계층 뒤로 완전히 숨겨주기 때문에 개발자는 데이터 레이어를 고민할 필요 없이 비즈니스 로직에만 집중할 수 있습니다.

LangGraph.js의 2단계 아키텍처 — 단기 체크포인트와 장기 의미론적 저장소

LangGraph.js는 상태 관리를 단기 실행 체크포인트와 장기 의미론적 저장소라는 두 가지 레이어로 엄격하게 분리하여 해결합니다. 이를 통해 복잡한 에이전트 그래프의 상태 변화를 실시간으로 기록하면서도, 서비스 전반에서 전역적으로 관리해야 하는 사용자 컨텍스트를 고성능으로 처리하는 이원화된 데이터 설계를 제공합니다.

단기 상태 관리를 전담하는 체크포인터는 에이전트 그래프의 매 실행 단계인 수퍼스텝마다 현재 상태를 데이터베이스에 스냅샷으로 영속화합니다. 이 덕분에 에이전트가 중단되더라도 마지막 실행 단계에서 안전하게 다시 재개할 수 있으며, 개발자는 타임트래블 디버깅을 하거나 사람의 승인이 필요한 '휴먼 인 더 루프' 단계에서 워크플로우를 일시 정지하고 재개하는 고도의 복구 프로세스를 안정적으로 설계할 수 있습니다.

특히 엔터프라이즈 환경에서 테넌트별 데이터 격리를 안전하게 보장하려면 데이터베이스 스키마 분리가 필수적입니다. LangGraph.js 공식 문서에 따르면 아래와 같이 @langchain/langgraph-checkpoint-postgres 패키지의 PostgresSaver를 사용해 커넥션 풀을 공유하면서도 개별 테넌트 전용 스키마를 지정하여 세션을 격리할 수 있습니다.

typescript
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";

const connectionString = "postgresql://user:password@localhost:5432/dbname";

// 멀티테넌트 격리를 위해 독립된 DB 스키마명을 옵션으로 주입
const checkpointer = PostgresSaver.fromConnString(connectionString, {
  schema: "tenant_alpha_schema",
});

// 이 checkpointer 인스턴스를 에이전트 그래프의 컴파일 단계에 주입하여 사용합니다.

이렇게 스키마 분리를 커넥션 생성 단계에서 명시적으로 처리해주면, 세션의 검색 경로가 충돌하여 생기는 멀티테넌트 데이터 혼선 버그를 원천적으로 예방할 수 있습니다.

반면 여러 대화 스레드를 관통하며 유지해야 하는 사용자 프로필 정보나 전역 비즈니스 규칙은 장기 기억 메커니즘인 PostgresStore에 위임합니다. 이 스토어는 pgvector 확장 모듈을 적극 활용하도록 설계되어 단순한 키-값 영속화를 넘어 의미론적 유사도 검색을 원활하게 지원하며, 에이전트가 시간이 지나도 사용자의 문맥과 선호도를 유연하게 인지할 수 있는 기억 장치 역할을 수행합니다.

실전 프로덕션 고려사항 — 커넥션 풀 관리와 보안 대책

로컬에서 잘 작동하던 에이전트 서비스를 Next.js나 NestJS 같은 프레임워크에 올려 실행하면, 개발 단계와 운영 환경에서 각각 예상치 못한 병목과 보안 위협을 마주하게 됩니다.

가장 먼저 겪는 문제는 개발 환경의 핫 모듈 리로드(HMR)로 인한 데이터베이스 커넥션 고갈입니다. 코드가 수정될 때마다 파일이 다시 실행되면서 새로운 커넥션 풀이 중복 생성되기 때문입니다. 이를 방지하기 위해 Mastra 공식 문서에서는 globalThis를 활용한 싱글톤 패턴으로 저장소 인스턴스를 유지할 것을 권장합니다.

typescript
// db-singleton.ts (Mastra / Next.js 환경 예시)
import { PostgresStore } from '@mastra/core';

const globalStore = globalThis as unknown as { mastraStore?: PostgresStore };

export const getMastraStore = () => {
  if (!globalStore.mastraStore) {
    globalStore.mastraStore = new PostgresStore({
      connectionString: process.env.DATABASE_URL!,
      disableInit: process.env.NODE_ENV === 'production', // CI/CD 마이그레이션 분리 시 설정
    });
  }
  return globalStore.mastraStore;
};

또한, 멀티테넌트 환경에서 세션이 혼선되거나 다른 사용자의 데이터가 조회되는 사고를 예방하려면 스키마를 논리적으로 완전히 분리하는 것이 좋습니다. LangGraph.js의 PostgresSaver를 다룰 때 schema 옵션을 지정하면 테넌트별 테이블을 격리하여 보다 안전하게 상태를 관리할 수 있습니다.

typescript
// LangGraph.js의 PostgresSaver 멀티테넌트 스키마 격리 예시
import { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres';

const checkpointer = PostgresSaver.fromConnString(
  process.env.DATABASE_URL!,
  { schema: 'tenant_company_a' } // 테넌트 스키마 지정으로 세션 혼선 차단
);

서버리스 환경이 아닌 고정형 컨테이너나 장기 실행 환경에서 에이전트를 가동한다면, 연결 끊김 현상을 방지하기 위해 생 커넥션 대신 pg 패키지의 ConnectionPool을 활용하는 설계 패턴도 함께 갖추는 것이 안정적입니다.

내 프로젝트에 맞는 영속성 패턴 선택하기

AI 에이전트의 영속성 설계는 단순히 대화 기록을 남기는 수준을 넘어, 시스템의 장애 복구력과 아키텍처 유연성을 좌우하는 핵심 레이어입니다.

도메인별 특성에 맞춰 데이터베이스 엔진을 유연하게 분할하고 가벼운 마이크로 에이전트를 민첩하게 구축하고 싶다면 Mastra의 복합 스토어 모델이 좋은 답안이 됩니다. 반면, 복잡한 상태 머신 기반의 그래프 실행 통제, 엄격한 멀티 테넌트 격리, 그리고 의미론적 검색을 통합한 정교한 장기 기억 장치가 필수적이라면 단기와 장기 저장소를 엄격하게 이원화한 LangGraph.js가 최선의 선택입니다.


참고 링크

(수정됨)