Mastra — TypeScript 네이티브 AI 에이전트와 워크플로우 설계

Maru

@maru

Mastra — TypeScript 네이티브 AI 에이전트와 워크플로우 설계

Mastra — TypeScript 네이티브 AI 에이전트와 워크플로우 설계

그동안 AI 에이전트 개발은 대부분 파이썬 생태계를 중심으로 이루어져 왔습니다. 이 때문에 타입스크립트 기반 백엔드를 운영하는 팀은 타입 안전성이 보장되지 않는 래퍼 API에 의존하거나, 런타임이 서로 다른 두 시스템을 무리하게 통합하는 비효율을 감수해야 했습니다. Mastra는 이러한 기술적 간극을 해결하기 위해 등장한 타입스크립트 네이티브 에이전트 프레임워크로, 백엔드 아키텍처와 유기적으로 결합되는 영속 상태 제어와 견고한 비동기 작업 엔진을 제공합니다. 타입스크립트 생태계에서 오버헤드 없이 프로덕션 수준의 고성능 에이전트 시스템을 설계하는 방법과 Mastra의 아키텍처적 가치를 짚어봅니다.

Mastra의 핵심 아키텍처: Agent와 Workflow

Mastra의 핵심 설계는 비결정론적 추론을 담당하는 Agent와 결정론적 제어 흐름을 보장하는 Workflow의 명확한 역할 분리에 있습니다. 모든 컴포넌트는 중앙 레지스트리 역할을 하는 단 하나의 Mastra 인스턴스에 바인딩되어 통제됩니다. 개발자는 이 안에서 자율적 루프와 구조화된 파이프라인을 자유롭게 조합하여 프로덕션에 적합한 안정적인 AI 시스템을 설계할 수 있습니다.

먼저 Agent는 자율적인 의사결정이 필요할 때 사용합니다. LLM을 기반으로 시스템 지침(instructions)과 Zod 스키마로 정의된 도구들을 장착하여 작동합니다. 사용자 입력에 따라 어떤 도구를 실행할지, 추론 과정을 어떻게 이어갈지 동적으로 판단하기 때문에 고도로 유연하지만 실행 경로가 고정되지 않는 비결정론적 성격을 가집니다.

반면 Workflow는 흐름이 명확히 통제되어야 하는 비즈니스 로직에 적합한 그래프 기반의 상태 머신입니다. .then(), .branch(), .parallel() 같은 메서드를 사용해 작업 단계를 명시적으로 체이닝하고 마지막에 .commit()으로 최종 확정합니다. 각 단계의 입출력 스키마가 완전하게 고정되므로 데이터 정합성을 보장하고 예외 처리를 안정적으로 제어할 수 있습니다.

typescript
import { Mastra } from "@mastra/core";
import { Agent } from "@mastra/core/agent";
import { createWorkflow } from "@mastra/core/workflows";
import { z } from "zod";

const supportAgent = new Agent({
  id: "support-agent",
  name: "Support Agent",
  model: "openai/gpt-5.5",
  instructions: "고객 문의에 맞춰 필요한 도구를 호출하세요.",
  tools: { searchDatabaseTool },
});

const onboardingWorkflow = createWorkflow({
  id: "user-onboarding",
  inputSchema: z.object({
    userId: z.string(),
  }),
  outputSchema: z.object({
    message: z.string(),
  }),
})
  .then(verifyUserStep)
  .branch([
    [
      async ({ inputData }) => inputData.plan === "premium",
      premiumWelcomeStep,
    ],
    [
      async ({ inputData }) => inputData.plan === "free",
      freeWelcomeStep,
    ],
  ])
  .commit();

export const mastra = new Mastra({
  agents: { supportAgent },
  workflows: { onboardingWorkflow },
});

두 컴포넌트를 하나의 Mastra 인스턴스에 등록하면 공유 저장소, 로깅, 옵저버빌리티 인프라를 유기적으로 활용할 수 있습니다. 또한 Workflow의 특정 단계에서 의사결정이 필요할 때 Agent를 호출하거나, 반대로 Agent가 장시간이 소요되는 복잡한 비즈니스 프로세스를 처리하기 위해 Workflow를 도구처럼 실행하는 유기적인 연계 구조도 손쉽게 구현할 수 있습니다.

영속 실행과 선택 가능한 메모리 전략

서버리스 환경에서 장시간 실행되는 AI 작업은 요청 시간 제한이나 네트워크 연결 종료로 인해 실행 상태와 스트림을 잃을 수 있습니다. Mastra는 이러한 문제를 하나의 자동 저장 기능으로 처리하는 것이 아니라, Workflow snapshot과 Durable Agent라는 서로 다른 기능으로 지원합니다.

Mastra Workflow는 실행 단계에서 suspend()가 호출되면 현재 단계의 상태와 완료된 단계의 출력, 실행 경로 등의 정보를 snapshot으로 저장합니다. 이후 resume()을 호출하면 저장된 상태를 불러와 중단된 지점부터 Workflow를 계속 실행할 수 있습니다. 이 방식은 사용자 승인이나 외부 이벤트를 오랫동안 기다려야 하는 작업에 적합합니다.

에이전트 스트리밍 연결을 복구해야 할 때는 createDurableAgent()를 사용할 수 있습니다. Durable Agent는 생성된 스트림 데이터를 설정된 서버 캐시에 저장하고 이벤트 시스템을 통해 전달합니다. 클라이언트 연결이 끊기거나 브라우저가 새로고침되더라도 동일한 runId로 다시 연결하면 실행 중이거나 완료된 스트림을 처음부터 관찰할 수 있습니다. 이 기능은 일반 Agent에 자동 적용되지 않으며, Mastra 인스턴스에 cache와 pubsub을 명시적으로 구성해야 합니다. 백엔드 프로세스 종료 이후에도 작업 자체를 지속하거나 자동 재시도해야 한다면 Inngest나 Temporal 같은 별도의 durable execution 통합을 검토해야 합니다.

대화 맥락 관리를 위해 Mastra Memory는 다음과 같은 여러 방식을 제공합니다. 이 기능들은 반드시 순서대로 작동하는 고정된 4단계 계층이 아니라, 애플리케이션 요구사항에 따라 선택하고 조합하는 옵션입니다.

  • 대화 기록(Conversation History): 최근 메시지와 도구 호출 결과를 저장하고 필요한 범위만 현재 대화 컨텍스트에 포함합니다.
  • 작업 메모리(Working Memory): 사용자 선호, 프로필과 작업 진행 상태처럼 여러 대화에서 유지해야 하는 정보를 템플릿이나 스키마에 따라 관리합니다.
  • 의미론적 회상(Semantic Recall): 임베딩과 벡터 검색을 이용해 현재 요청과 관련된 과거 메시지를 찾아 컨텍스트에 추가합니다. 최신 설정에서는 기본적으로 비활성화되어 있으며, 사용할 경우 지원되는 vector store를 구성해야 합니다.
  • 관찰 메모리(Observational Memory): 대화와 도구 실행 결과를 핵심 관찰 정보로 압축해 장기 대화에서도 컨텍스트 크기를 안정적으로 관리하도록 돕습니다. 필요에 따라 명시적으로 활성화하며, 비동기 버퍼링을 통해 일부 관찰 작업을 대화가 진행되는 동안 미리 처리할 수 있습니다.

메모리와 Workflow snapshot은 설정된 storage adapter를 통해 저장되지만, PostgreSQL과 LibSQL 사이의 데이터가 자동으로 복제되거나 동기화되는 것은 아닙니다. 하나의 저장소를 공통으로 사용할 수도 있고, MastraCompositeStore를 구성해 memory는 LibSQL, Workflow 상태는 PostgreSQL, observability 데이터는 ClickHouse처럼 도메인별 저장소를 명시적으로 분리할 수도 있습니다. 이 구조를 활용하면 각 데이터의 지연시간, 처리량과 보관 비용에 맞춰 저장소를 선택할 수 있습니다.

@mastra/mcp를 활용한 Model Context Protocol 통합

Mastra는 에이전트의 생태계 확장성을 극대화하기 위해 모델 컨텍스트 프로토콜(MCP)을 핵심 인터페이스로 내장하고 있습니다. @mastra/mcp 패키지를 활용하면 외부 도구를 에이전트에 주입하는 소비자(MCPClient)의 역할과, Mastra로 개발한 결과물을 외부 시스템에 개방하는 공급자(MCPServer)의 역할을 양방향으로 완벽히 지원합니다.

먼저 MCPClient는 로컬 stdio 서버나 Smithery, Composio 같은 원격 HTTP/SSE 엔드포인트에 유연하게 연결됩니다. 클라이언트는 연결된 서버로부터 동적으로 도구 목록을 조회하여 Mastra 에이전트의 tools 배열에 그대로 주입할 수 있습니다. 이 과정에서 도구 실행 전에 사용자 확인을 강제하는 승인 설정(requireToolApproval) 등도 명시적으로 처리할 수 있어 실제 프로덕션 환경에서의 안전성을 높여줍니다.

typescript
import { MCPClient } from "@mastra/mcp";
import { Agent } from "@mastra/core/agent";

const mcpClient = new MCPClient({
  servers: {
    filesystem: {
      command: "npx",
      args: [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/path/to/allowed-directory",
      ],
    },
  },
});

const agent = new Agent({
  id: "filesystem-agent",
  name: "Filesystem Agent",
  model: "openai/gpt-4o-mini",
  instructions: "허용된 디렉터리 안에서 파일을 조회합니다.",
  tools: await mcpClient.getTools(),
});

const result = await agent.generate("파일을 정리해 줘", {
  requireToolApproval: true,
});

반대로 MCPServer를 사용하면 Mastra로 구축한 독립적인 에이전트와 워크플로우를 표준 MCP 도구 형태로 외부에 개방할 수 있습니다. 내가 작성한 에이전트를 ask_<agentId> 형태로 노출하고 복잡한 비즈니스 파이프라인을 run_<workflowId> 형태의 도구로 포장하여 Cursor, Windsurf, Claude Desktop 같은 개발 환경에 즉시 제공하는 식입니다. 이는 프레임워크에 갇히지 않는 유연한 결합도를 보장하는 강력한 설계적 장점입니다.

Mastra, 지금 프로덕션에 도입해도 괜찮을까?

Mastra는 타입스크립트 생태계에서 에이전트의 타입 안전성과 영속성 제어를 동시에 확보할 수 있는 가장 완성도 높은 프레임워크 중 하나입니다. 아직 초기 프레임워크인 만큼 프로덕션 도입 시 정밀한 부하 테스트가 필요하지만, 데이터베이스 기반 스냅샷 관리와 양방향 MCP 지원 등 백엔드 지향적 설계가 매우 매력적입니다.

특히 대규모 분산 환경으로의 확장을 고민하는 팀이라면 현재 제공되는 템포럴(Temporal) 통합 기능인 @mastra/temporal를 눈여겨볼 만합니다. 애플리케이션 수준의 일반 작업 흐름에는 기본 Mastra Workflow가 적합하며, 장기 실행 분산 시스템에서는 Temporal 같은 전용 실행 엔진이 더 적합할 수 있습니다.


참고 링크

(수정됨)