AI 에이전트 DB 설계 — Mastra와 LangGraph.js의 영속화 패턴

Maru

@maru

AI 에이전트 DB 설계 — Mastra와 LangGraph.js의 영속화 패턴

AI 에이전트 DB 설계 — Mastra와 LangGraph.js의 영속화 패턴

TypeScript 기반 AI 에이전트를 프로덕션에 배포할 때 가장 먼저 마주하는 장벽은 상태 영속화입니다. 단순히 대화 기록을 서버 메모리에 임시 보관하던 단계를 넘어, 이제는 복잡한 워크플로우 스냅샷과 트랜잭션까지 안정적으로 보존하는 다중 계층 데이터베이스 설계가 필수적입니다. Mastra와 LangGraph.js, Vercel AI SDK 같은 도구들이 분산 환경에서의 상태 오염과 커넥션 병목을 해결하기 위해 채택하고 있는 프로덕션 수준의 DB 설계 패턴과 실전 구현 전략을 살펴봅니다.

Mastra의 분할 설계: MastraCompositeStore

Mastra 1.0은 단일 데이터베이스 설계에서 벗어나 데이터 도메인별로 저장소를 다르게 결합하는 MastraCompositeStore 아키텍처를 공식 도입했습니다. 이 구조를 활용하면 서비스 특성에 맞춰 개별 상태 도메인별로 최적의 데이터베이스를 격리하여 관리할 수 있습니다.

가장 대표적인 형태는 대화 세션과 워크플로우 엔진의 격리입니다. 가볍고 빠른 반응 속도가 중요한 단기 대화 세션과 작동 메모리 영역인 memory 도메인은 엣지 친화적인 MemoryLibSQL로 전달하여 처리 지연 시간을 최소화합니다. 반대로 실행 노드의 추적, 중단 후 재개, 재시도 제어 등이 중요하며 실패 위험이 적어야 하는 오케스트레이션 상태인 workflows 도메인은 트랜잭션 신뢰성이 높은 PostgreSQL 백엔드인 WorkflowsPG로 전송하여 영속화합니다.

Mastra 1.0+ 기반의 대표적인 이중 계층 영속화 설정 예시입니다.

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

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

이러한 분할 영속화 방식은 단일 데이터베이스 공유 시 발생하는 성능 병목을 예방합니다. 엣지 등에서 가볍게 구동되는 에이전트의 대화 루프는 로컬이나 인접 노드에서 실시간에 가깝게 기록하면서도, 안전한 상태 트랜잭션이 요구되는 비즈니스 워크플로우 상태는 메인 데이터베이스에 확실히 보존하는 탄력적인 아키텍처 구현이 가능해집니다.

LangGraph.js의 2단계 메모리 격리와 DB 커넥션 튜닝

LangGraph.js는 에이전트의 상태 영속화를 단기와 장기로 명확히 분리하여 영속성 부하를 제어합니다. 단일 스레드 내의 상세한 작업 흐름과 상태 스냅샷을 촘촘히 기록하는 체크포인터(Checkpointer)와 여러 스레드가 대화 맥락을 공유하는 전역 스토어(Store)가 이중 구조를 이룹니다. 이 패턴을 PostgreSQL 환경에서 프로덕션 수준으로 구현할 때는 @langchain/langgraph-checkpoint-postgres 패키지의 PostgresSaver를 사용합니다.

하지만 이 패턴을 도입할 때 커넥션 누수와 트랜잭션 실패라는 복병을 쉽게 마주치게 됩니다. 서버가 재시작되거나 핫 리로드(Hot Reload)가 발생할 때마다 불필요한 커넥션이 중복으로 쌓이지 않도록 커넥션 풀을 반드시 글로벌 싱글톤 인스턴스로 관리해야 합니다. 또한 드라이버 및 세이버 설정 시 autocommit: true 옵션을 활성화해야 합니다. 이 옵션이 누락되면 데이터베이스 스키마 생성이나 스냅샷 데이터 커밋이 대기 상태에 빠지거나 영구적으로 트랜잭션이 유실될 수 있습니다.

싱글톤 커넥션 풀과 autocommit 옵션을 적용해 영속성 레이어를 안정적으로 초기화하는 구현 예시입니다.

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

// 글로벌 싱글톤 패턴으로 커넥션 풀을 관리해 핫 리로드 시 누수를 방지합니다.
const pool = globalThis.dbPool || new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  max: 10,
});

if (process.env.NODE_ENV !== "production") {
  globalThis.dbPool = pool;
}

// autocommit 설정을 활성화하여 체크포인트 스냅샷 커밋 실패를 예방합니다.
const checkpointer = new PostgresSaver(pool, {
  autocommit: true,
});

보안 관점에서도 다중 테넌트 환경에서 신뢰되지 않은 페이로드의 MsgPack 역직렬화 공격을 미연에 차단할 수 있도록 엄격한 격리 및 검증 레이어를 함께 구축하는 것이 안전합니다. 체크포인터와 글로벌 스토어 간에 하나의 싱글톤 커넥션 풀을 긴밀하게 공유하면서도, 드라이버 세부 설정을 정확히 튜닝해야 실서비스 환경에서 메모리 정합성을 확보할 수 있습니다.

Vercel AI SDK의 스트림 중단 방어와 DB 동기화

LLM과 도구를 연동한 대화형 에이전트를 서비스할 때 가장 흔히 발생하는 장애는 스트림이 전송되는 도중 사용자가 브라우저 탭을 닫거나 네트워크가 끊기는 상황입니다. 이 경우 클라이언트 측에서 연결을 끊으면 백엔드의 스트림 전송도 즉시 중단됩니다. 문제는 이 과정에서 에이전트의 도구 실행 루프가 중간에 끊기거나, 영속화 작업을 수행하는 onFinish 콜백이 호출되지 않아 데이터베이스 상태가 비정상적으로 남는다는 점입니다.

Vercel AI SDK는 이러한 스트림 중단으로 인한 상태 오염을 방지하기 위해 consumeStream() 메서드를 제공합니다. 클라이언트와의 연결이 끊기더라도 서버 내부에서 남은 스트림 데이터를 끝까지 강제로 소비하여 에이전트의 모든 도구 호출 루프와 영속화 작업을 안전하게 마칠 수 있도록 보장합니다.

typescript
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

const result = streamText({
  model: openai('gpt-4o'),
  prompt: '...',
  onFinish: async ({ text }) => {
    // 클라이언트 중단 여부와 무관하게 반드시 실행되어야 하는 DB 저장 로직
    await saveToDatabase(text);
  }
});

// 클라이언트 연결이 유실되어도 서버에서 스트림을 완수하여 onFinish를 실행합니다.
result.consumeStream();

이와 더불어 에이전트의 작동 메모리와 대화 내역을 더욱 유연하게 관리하기 위해 @ai-sdk-tools/memory 패키지를 함께 결합하는 패턴이 권장됩니다. 이 패키지가 제공하는 DrizzleProviderUpstashProvider를 활용하면 복잡한 데이터베이스 업데이트 훅을 수동으로 작성할 필요 없이, 에이전트의 상태 변화를 Drizzle ORM이나 Redis에 자동으로 동기화할 수 있어 아키텍처의 트랜잭션 안전성이 한층 더 강화됩니다.

프로덕션을 위한 에이전트 DB 선택 기준

프로덕션 환경의 AI 에이전트 DB 설계는 단순히 대화 기록을 저장하는 수준을 넘어, 시스템 완결성과 비동기 흐름을 안전하게 보장하는 단계로 진화해야 합니다. 프레임워크 선택과 영속화 아키텍처는 에이전트가 처리하는 작업의 특성에 맞춰 신중하게 결정해야 합니다.

단기 대화의 실시간 반응성과 대규모 실행 그래프의 탄력성이 동시에 필요하다면 Mastra가 제공하는 분할 설계가 효과적입니다. 세션 맥락과 단기 메모리는 엣지 친화적이고 가벼운 libSQL로 전달하고, 복잡한 워크플로우 상태는 트랜잭션 처리가 확실한 PostgreSQL에 분리해 저장함으로써 인프라 부하를 적절히 나눌 수 있습니다.

정교한 상태 제어와 그래프 추적 기능이 핵심이라면 LangGraph.js 모델이 유리합니다. 이 방식을 채택할 때는 커넥션 고갈을 방지하기 위한 전역 ConnectionPool 싱글톤 패턴과 autocommit: true 옵션을 인프라 계층에 반드시 사전 반영해야 합니다. 반면 사용자 경험을 극대화하는 스트리밍 연동이 최우선 과제라면 Vercel AI SDK를 검토하는 것이 좋습니다. 사용자가 브라우저 창을 닫더라도 에이전트의 잔여 작업과 최종 상태가 안전하게 커밋될 수 있도록 consumeStream을 연동한 백엔드 동기화 설계를 구현해 두어야 장애 없는 프로덕션 서비스를 구축할 수 있습니다.


참고 링크