본문 바로가기

AI Agent

Mastra AI 완전 가이드 — TypeScript로 AI 에이전트 만드는 LangChain 대안

반응형

LangChain 쓰려면 파이썬 배워야 하고, LangChain.js는 파이썬 포팅이라 DX가 어색합니다. Mastra는 Gatsby 팀이 만든 TypeScript 네이티브 에이전트 프레임워크입니다. Next.js 하던 방식 그대로 에이전트를 짭니다.

[핵심 요약]
→ Mastra: TypeScript 네이티브 AI 에이전트 프레임워크 (2026년 1월 v1.0 출시)
→ 만든 팀: Gatsby.js 창업팀 (YC W25, $13M 펀딩)
→ 현황: GitHub 22,000+ 스타 / npm 주간 다운로드 300,000+
→ 모델: Vercel AI SDK 기반 — Claude, GPT, Gemini, Ollama 3,300+ 모델 지원
→ 6가지 핵심 프리미티브: Agent, Workflow, Tool, Memory, RAG, Evals
→ Mastra Studio: 에이전트 테스트·트레이싱 내장 비주얼 IDE
→ 배포: Next.js, Express, Hono, Vercel, Cloudflare Workers 전부 지원
→ MCP 네이티브 지원: 툴을 MCP 서버로 노출 가능

 


Mastra vs LangChain vs LangGraph

LangChain.js:
→ 파이썬 포팅 — TypeScript 경험이 어색함
→ 타입 안전성 부족 (any 타입 남발)
→ 보일러플레이트 많음
→ Mastra Studio 같은 비주얼 IDE 없음

LangGraph (Python):
→ 상태 머신 기반 — 복잡한 에이전트 플로우에 강점
→ 파이썬 전용 → TypeScript 팀은 FastAPI 서비스 별도 필요
→ 학습 곡선 가파름

Mastra:
→ TypeScript 네이티브 — Zod 스키마가 툴 정의·출력 검증·API 응답 타입 전부 공유
→ 에이전트(자율) + 워크플로우(결정론적) 두 프리미티브 명확히 분리
→ Mastra Studio 내장 — 로컬에서 바로 트레이싱·테스트
→ Next.js 앱에 그대로 번들 가능

실전 1 — 설치 및 첫 번째 에이전트

# Node.js 18+ 필요
npm create mastra@latest my-agent-app
# 또는
pnpm create mastra

# 프롬프트에서 선택
# Provider: anthropic (claude) / openai / google / groq
# 프로젝트 구조 자동 생성

생성된 구조:

src/
└── mastra/
    ├── agents/      # 에이전트 정의
    ├── tools/       # 툴 정의
    ├── workflows/   # 워크플로우 정의
    └── index.ts     # Mastra 런타임 등록
// src/mastra/agents/weather-agent.ts
import { Agent } from '@mastra/core/agent';
import { anthropic } from '@ai-sdk/anthropic';
import { weatherTool } from '../tools/weather';

export const weatherAgent = new Agent({
  id: 'weather-agent',
  name: 'Weather Assistant',
  instructions: `
    당신은 날씨 정보를 제공하는 어시스턴트입니다.
    사용자가 날씨를 물어보면 weatherTool을 사용해서 정확한 정보를 제공하세요.
    항상 한국어로 답변하세요.
  `,
  model: anthropic('claude-sonnet-4-5'),
  tools: { weatherTool },
});
// src/mastra/index.ts — Mastra 런타임에 등록
import { Mastra } from '@mastra/core';
import { weatherAgent } from './agents/weather-agent';

export const mastra = new Mastra({
  agents: { weatherAgent },
});
# Mastra Studio 실행 (로컬 비주얼 IDE)
npx bgproc start -n my-agent-app -w -- npm run dev
# → http://localhost:4111 에서 에이전트 테스트 가능
[Mastra Studio 제공 기능]
→ 에이전트 채팅 인터페이스 (실시간 테스트)
→ 툴 호출 트레이싱 (입력/출력/소요시간)
→ 토큰 사용량·비용 추적
→ 프롬프트 편집 및 즉시 재실행
→ 워크플로우 실행 시각화

실전 2 — Tool 정의 (Zod 스키마 타입 공유)

Mastra의 툴은 Zod 스키마로 입출력을 정의합니다. 이 스키마가 모델 프롬프트 설명 + 런타임 검증 + TypeScript 타입을 동시에 담당합니다.

// src/mastra/tools/weather.ts
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';

export const weatherTool = createTool({
  id: 'get-weather',
  description: '특정 도시의 현재 날씨를 가져옵니다',
  inputSchema: z.object({
    city: z.string().describe('날씨를 조회할 도시명 (예: Seoul, Tokyo)'),
    unit: z.enum(['celsius', 'fahrenheit']).default('celsius'),
  }),
  outputSchema: z.object({
    temperature: z.number(),
    condition: z.string(),
    humidity: z.number(),
    city: z.string(),
  }),
  execute: async ({ context }) => {
    const { city, unit } = context;

    // 실제 날씨 API 호출
    const res = await fetch(
      `https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_API_KEY}&q=${city}`
    );
    const data = await res.json();

    return {
      temperature: unit === 'celsius'
        ? data.current.temp_c
        : data.current.temp_f,
      condition: data.current.condition.text,
      humidity: data.current.humidity,
      city: data.location.name,
    };
  },
});
// src/mastra/tools/github.ts — GitHub MCP 서버 툴 연결
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';

export const createIssueTool = createTool({
  id: 'create-github-issue',
  description: 'GitHub 저장소에 이슈를 생성합니다',
  inputSchema: z.object({
    repo: z.string().describe('저장소 (owner/repo 형식)'),
    title: z.string(),
    body: z.string(),
    labels: z.array(z.string()).optional(),
  }),
  outputSchema: z.object({
    issueNumber: z.number(),
    url: z.string(),
  }),
  execute: async ({ context }) => {
    const res = await fetch(
      `https://api.github.com/repos/${context.repo}/issues`,
      {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          title: context.title,
          body: context.body,
          labels: context.labels,
        }),
      }
    );
    const data = await res.json();
    return { issueNumber: data.number, url: data.html_url };
  },
});
[Mastra Tool 핵심 개념]
→ inputSchema: 모델이 보는 툴 설명 + 런타임 입력 검증 + TS 타입 동시 처리
→ outputSchema: 모델 출력 검증 + 다음 스텝 타입 자동 추론
→ execute: context로 타입 안전하게 입력 접근
→ MCP 서버 툴도 동일한 인터페이스로 등록 가능
→ 여러 에이전트에서 같은 툴 공유 가능

실전 3 — Memory (대화 기억 + 관찰 메모리)

Mastra는 메모리를 두 가지로 분리합니다. 대화 메모리(메시지 히스토리)와 관찰 메모리(사용자에 대해 에이전트가 학습한 것)입니다.

// src/mastra/agents/support-agent.ts
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { LibSQLStore } from '@mastra/memory/storage/libsql';
import { anthropic } from '@ai-sdk/anthropic';

// 메모리 스토어 설정 (LibSQL — 로컬 SQLite 호환)
const memory = new Memory({
  storage: new LibSQLStore({
    url: 'file:./agent-memory.db',  // 로컬 개발
    // url: process.env.TURSO_URL,  // 프로덕션 (Turso)
  }),
  options: {
    lastMessages: 20,          // 최근 20개 메시지 유지
    semanticRecall: true,      // 시맨틱 검색으로 관련 과거 대화 검색
    workingMemory: {
      enabled: true,           // 관찰 메모리 활성화
    },
  },
});

export const supportAgent = new Agent({
  id: 'support-agent',
  name: '고객 지원 에이전트',
  instructions: `
    당신은 친절한 고객 지원 에이전트입니다.
    사용자의 과거 대화와 관찰된 정보를 참고해서 개인화된 답변을 제공하세요.
  `,
  model: anthropic('claude-sonnet-4-5'),
  memory,
});
// Next.js API Route에서 사용
// app/api/chat/route.ts
import { mastra } from '@/src/mastra';

export async function POST(req: Request) {
  const { message, userId } = await req.json();

  const agent = mastra.getAgent('supportAgent');

  // threadId로 대화 세션 구분 — 같은 threadId면 메모리 이어받음
  const response = await agent.stream(message, {
    threadId: `user-${userId}`,
    resourceId: userId,  // 관찰 메모리 사용자 식별자
  });

  return response.toDataStreamResponse();
}
[Mastra Memory 구조]
→ 대화 메모리: lastMessages 개수만큼 최근 메시지 유지
→ 시맨틱 리콜: 관련 과거 대화 벡터 검색으로 자동 주입
→ 관찰 메모리: 에이전트가 사용자에 대해 학습한 것 자동 축적
→ threadId: 대화 세션 구분 (사용자별·채널별)
→ 스토리지: LibSQL(로컬), PostgreSQL, Upstash 등 선택 가능
→ 프롬프트 캐싱: 안정적인 컨텍스트 윈도우로 캐시 히트율 향상

실전 4 — Workflow (결정론적 파이프라인)

에이전트는 LLM이 툴 호출 순서를 결정합니다. 워크플로우는 개발자가 실행 순서를 직접 정의합니다. 비용이 크거나 순서가 중요한 작업에 사용합니다.

// src/mastra/workflows/pr-review.ts
import { createWorkflow, createStep } from '@mastra/core/workflows';
import { z } from 'zod';

// Step 1: PR 코드 가져오기
const fetchPRStep = createStep({
  id: 'fetch-pr',
  inputSchema: z.object({ prUrl: z.string() }),
  outputSchema: z.object({
    files: z.array(z.object({ filename: z.string(), patch: z.string() })),
    description: z.string(),
  }),
  execute: async ({ inputData }) => {
    // GitHub API로 PR diff 가져오기
    const prData = await fetchPRFromGitHub(inputData.prUrl);
    return prData;
  },
});

// Step 2: 코드 리뷰 (LLM)
const reviewStep = createStep({
  id: 'review-code',
  inputSchema: fetchPRStep.outputSchema,  // 이전 스텝 출력 타입 자동 연결
  outputSchema: z.object({
    issues: z.array(z.object({
      severity: z.enum(['critical', 'warning', 'suggestion']),
      file: z.string(),
      comment: z.string(),
    })),
    summary: z.string(),
  }),
  execute: async ({ inputData, mastra }) => {
    const agent = mastra?.getAgent('codeReviewAgent');
    const result = await agent?.generate(
      `다음 PR을 리뷰해줘:\n${JSON.stringify(inputData.files)}`
    );
    return JSON.parse(result?.text || '{}');
  },
});

// Step 3: GitHub에 리뷰 코멘트 등록
const postReviewStep = createStep({
  id: 'post-review',
  inputSchema: reviewStep.outputSchema,
  outputSchema: z.object({ reviewUrl: z.string() }),
  execute: async ({ inputData }) => {
    const url = await postGitHubReview(inputData);
    return { reviewUrl: url };
  },
});

// 워크플로우 조합
export const prReviewWorkflow = createWorkflow({
  id: 'pr-review-workflow',
  inputSchema: z.object({ prUrl: z.string() }),
  outputSchema: z.object({ reviewUrl: z.string() }),
})
  .then(fetchPRStep)
  .then(reviewStep)
  .then(postReviewStep)
  .commit();
// 워크플로우 실행
const { runId, start } = prReviewWorkflow.createRun();

const result = await start({
  inputData: { prUrl: 'https://github.com/owner/repo/pull/123' },
});

console.log(result.result.reviewUrl);
[Workflow vs Agent 선택 기준]
→ Workflow: 순서 중요 / 비용 큰 작업 / 중간 승인 필요 / 병렬 처리
→ Agent: 순서 불확실 / 툴 조합 자율 판단 / 열린 목표
→ 함께 쓰기: Workflow 스텝 안에서 Agent 호출 가능
→ Human-in-the-Loop: 워크플로우 중간 suspend → 승인 후 resume
→ 타임트래블: 특정 스텝으로 롤백해서 재실행 가능

마무리

✅ Mastra 써야 하는 경우
→ TypeScript/Next.js 메인 스택인 풀스택·프론트엔드 팀
→ 파이썬 서비스 별도 없이 에이전트 붙이고 싶을 때
→ Vercel, Cloudflare Workers 배포 환경
→ 에이전트 + 결정론적 워크플로우 둘 다 필요한 경우
→ 로컬 비주얼 IDE로 에이전트 빠르게 테스트하고 싶을 때

❌ Mastra가 안 맞는 경우
→ Python 팀 (Mastra는 TS 전용 — Python SDK 없음)
→ 복잡한 멀티에이전트 토론·협업 시뮬레이션 (AutoGen이 더 적합)
→ 연구용 실험적 에이전트 구조 (LangGraph가 더 유연)
→ 이미 LangChain 파이썬으로 구축된 레거시 스택

관련 글

반응형