
얼마 전 개발자 모임에서 후배 하나가 저한테 그러더라고요. “형, 요즘 Mastra 한번 써보셨어요? 에이전트 만들기 꽤 괜찮던데요.” 사실 그 말을 듣자마자 속으로는 살짝 웃었어요. 요즘 AI 에이전트 프레임워크가 워낙 많이 나오잖아요. LangChain도 있고, AutoGen도 있고, 이름만 들어도 뭔가 해야 할 게 많아 보이는 친구들이 계속 나오니까요.
그래서 처음엔 그냥 흘려들었어요. 그런데 이상하게 집에 와서도 그 말이 좀 남더라고요. 퇴근하고 커피 한 잔 내려놓고 GitHub 저장소를 클론해서 만져봤는데, 어라, 생각보다 느낌이 괜찮았습니다. 뭐랄까. 너무 거창하지 않고, 그렇다고 장난감 같지도 않은 딱 그 중간 지점이 있었어요. 20년 넘게 개발하면서 이런 감각이 오면 일단 더 파보게 되거든요.
이번 글은 제가 Mastra 에이전트 프레임워크를 실제 사이드 프로젝트와 업무성 프로토타입에 붙여보면서 느낀 점을 편하게 정리한 이야기입니다. 거창한 소개 글이라기보다는, 친한 개발자 친구한테 “야, 이거 써보니까 이런 건 좋고 이런 건 조심해야겠더라” 하고 말해주는 느낌으로 봐주시면 좋겠어요.
Mastra를 처음 만졌을 때 느낀 점, 가볍지만 대충 만든 느낌은 아니었어요
저는 원래 LangChain을 꽤 오래 썼습니다. 나쁜 프레임워크라는 얘기는 절대 아니에요. 기능도 많고, 생태계도 크고, 참고할 자료도 많습니다. 그런데 프로젝트가 조금만 커져도 손이 많이 가더라고요. 체인을 만들고, 메모리를 붙이고, 에이전트를 정의하고, 툴을 연결하고, 로깅을 따로 보고... 어느 순간부터는 “내가 지금 고객 문제를 풀고 있는 건가, 프레임워크를 달래고 있는 건가” 싶은 순간이 있었습니다.
AutoGen도 재미있었어요. 특히 멀티 에이전트가 서로 대화하는 구조는 처음 보면 꽤 멋있습니다. 그런데 실제 업무에 넣어보려면 디버깅이 만만치 않더군요. 에이전트가 왜 저런 말을 했는지, 왜 저 툴을 호출했는지, 어느 시점에서 흐름이 꼬였는지 따라가다 보면 은근히 피곤합니다. 나이 들어서 그런가요. 저는 이제 멋진 것보다 고장 났을 때 빨리 고칠 수 있는 것에 더 마음이 갑니다.
그런 면에서 Mastra는 첫인상이 좋았어요. TypeScript 기반이라 VSCode에서 타입 추론이 잘 되고, 함수 구조도 비교적 단순했습니다. 에이전트를 만들고, 툴을 붙이고, 모델을 연결하는 흐름이 머릿속에 금방 들어왔어요. 문서도 아주 방대하진 않지만, 실제로 뭘 만들 때 필요한 부분은 잘 잡혀 있었습니다.
제가 느낀 핵심은 이거였어요. Mastra는 AI 에이전트를 만들 때 개발자가 불필요하게 헤매는 시간을 꽤 줄여준다는 점입니다. 물론 아직 완성형이라고 보긴 어렵지만, 프로토타입 만들 때는 확실히 속도가 납니다.
| 구분 | Mastra | LangChain | AutoGen |
|---|---|---|---|
| 첫 세팅 체감 시간 | 약 10분 정도 | 약 30분 정도 | 약 40분 이상 |
| 디버깅 난이도 | 웹 UI가 있어 꽤 편함 | 직접 로깅을 잘 심어야 함 | 멀티 에이전트 흐름 추적이 어려움 |
| 문서 느낌 | 실전 위주라 빠르게 보기 좋음 | 자료는 많지만 산만하게 느껴질 때가 있음 | 친절하지만 구조가 복잡하게 느껴질 수 있음 |
| 개인적인 인상 | 가볍고 손에 잘 붙음 | 강력하지만 무거움 | 재미있지만 운영은 고민 필요 |
이 표는 어디 공식 벤치마크가 아니라, 제가 실제로 만져보면서 느낀 체감입니다. 개발자마다 다를 수 있어요. 그래도 AI 바이브 코딩처럼 빠르게 만들고 확인하고 고치는 흐름에서는 Mastra 쪽이 꽤 잘 맞았습니다.
제가 만든 건 고객 문의 FAQ 에이전트였어요
이번에 Mastra로 만들어본 건 고객 문의 응대용 FAQ 에이전트였습니다. 회사 내부에서 반복적으로 들어오는 질문들이 있거든요. 환불 규정, 계정 변경, 요금제, 관리자 권한, 데이터 보관 기간 같은 것들요. 사람 입장에서는 단순한데, 매번 답하려면 은근히 시간을 잡아먹습니다.
기존에는 FAQ 데이터가 RDB에 들어가 있었어요. 사용자가 질문하면 DB에서 비슷한 질문을 찾고, 그 답변을 기반으로 자연스럽게 응답하는 구조를 만들고 싶었습니다. 거기에 무턱대고 LLM이 상상해서 답하지 않게 하고 싶었고요. 이 부분은 정말 중요합니다. 고객 응대에서 AI가 자신 있게 틀린 말을 하면, 그때부터는 기술 문제가 아니라 신뢰 문제가 되거든요.
그래서 저는 Mastra의 Tool 구조를 먼저 잡았습니다. FAQ 데이터베이스를 검색하는 툴을 만들고, 에이전트는 필요할 때만 그 툴을 호출하게 했습니다. 코드 느낌은 아래와 비슷합니다.
import { createTool } from '@mastra/core';
import { z } from 'zod';
const searchFaqTool = createTool({
id: 'search-faq',
description: 'FAQ 데이터베이스에서 질문에 대한 답변을 검색합니다.',
inputSchema: z.object({
query: z.string().describe('사용자의 질문 내용'),
}),
outputSchema: z.object({
answer: z.string(),
confidence: z.number(),
}),
execute: async ({ context }) => {
const result = await db.query(
'SELECT answer, confidence FROM faq WHERE question LIKE $1',
[`%${context.query}%`]
);
return result;
},
});
여기서 중요한 건 inputSchema와 outputSchema를 대충 만들면 안 된다는 점이에요. 처음에는 저도 “뭐, 문자열 하나 받으면 되겠지” 하고 가볍게 만들었는데, 나중에 에이전트가 툴을 호출할 때 애매한 값을 넣거나, 응답을 해석하는 과정에서 삐끗하는 일이 있었습니다.
특히 Zod로 스키마를 명확히 잡아두면, 나중에 디버깅할 때 훨씬 편합니다. 이건 여행 갈 때 숙소 위치를 미리 지도에 찍어두는 느낌이에요. 그땐 귀찮아도, 밤늦게 도착했을 때 살려줍니다.
툴을 만들고 나면 에이전트에 연결하는 건 꽤 단순합니다. 모델과 instructions, tools 배열을 넘겨주면 기본 뼈대가 만들어져요.
import { createAgent, openai } from '@mastra/core';
const agent = createAgent({
name: 'FAQ Agent',
model: openai.gpt4o(),
instructions: '당신은 친절한 고객 지원 에이전트입니다. FAQ 툴을 사용하여 사용자의 질문에 답변하세요.',
tools: [searchFaqTool],
});
처음 이 코드를 봤을 때는 솔직히 조금 의심했습니다. “이렇게 간단한데 진짜 잘 돌아간다고?” 싶은 거죠. 그런데 mastra dev로 개발 서버를 띄워보니, 웹 UI에서 에이전트가 어떤 생각을 하고 어떤 툴을 호출하는지 확인할 수 있었습니다. 이게 꽤 좋았어요. 로그 파일만 붙잡고 있는 것보다 훨씬 덜 외롭습니다.
저는 특히 이 디버깅 화면에서 도움을 많이 받았습니다. 사용자가 “요금제 바꾸면 기존 데이터는 어떻게 돼요?”라고 물었을 때, 에이전트가 FAQ 툴을 호출하는지, confidence 값이 어느 정도인지, 답변을 그대로 가져오는지 아니면 살짝 풀어서 말하는지 볼 수 있었거든요.
AI 바이브 코딩으로 붙여보니, instructions가 거의 절반이더라고요
요즘 개발하면서 AI 바이브 코딩이라는 말을 많이 듣습니다. 저는 이 표현이 아주 정확하다고 보진 않지만, 느낌은 알겠어요. 예전처럼 한 줄 한 줄 전부 손으로 짜기보다, AI에게 초안을 만들게 하고 개발자는 방향, 구조, 예외 처리, 품질을 잡는 쪽으로 일이 바뀌고 있잖아요.
Mastra는 이런 흐름하고 잘 맞습니다. 에이전트 구조가 복잡하지 않아서, Cursor나 GitHub Copilot 같은 도구로 빠르게 초안을 만들고 제가 손으로 다듬는 방식이 잘 먹혔어요. 다만 여기서 느낀 게 하나 있습니다. instructions를 대충 쓰면 결과도 대충 나온다는 거예요.
처음에는 instructions를 이렇게 짧게 썼습니다.
당신은 친절한 고객 지원 에이전트입니다. 사용자의 질문에 답변하세요.
당연히 답은 나오죠. 그런데 문제는 너무 자유롭게 답한다는 겁니다. FAQ에 없는 내용도 그럴듯하게 보태고, confidence가 낮은 결과도 그냥 사용하고, 질문이 애매해도 바로 답해버렸습니다. 이건 고객 응대에서는 위험합니다.
그래서 instructions를 조금 더 현실적으로 바꿨습니다.
당신은 회사의 고객 지원 에이전트입니다.
규칙:
- 사용자의 질문을 먼저 분석합니다.
- 질문이 모호하면 바로 답하지 말고 명확화 질문을 합니다.
- FAQ 툴을 사용할 때 confidence가 0.7 이상인 결과만 답변에 사용합니다.
- FAQ에 근거가 없으면 추측하지 말고 상담원 연결을 안내합니다.
- 답변은 친절하되, 정책 내용은 단정적으로 말합니다.
- 내부 시스템 구조나 SQL, 구현 세부사항은 사용자에게 말하지 않습니다.
이렇게 바꾸고 나니까 응답 품질이 확 달라졌습니다. 말투도 안정되고, 없는 내용을 지어내는 빈도도 줄었습니다. 사실 이건 Mastra만의 이야기는 아니에요. LLM 기반 개발을 하다 보면 결국 좋은 instructions는 좋은 API 설계만큼 중요하다는 생각이 듭니다.
제가 지금은 instructions를 쓸 때 아래 항목을 거의 습관처럼 넣습니다.
- 역할: 이 에이전트가 누구인지 명확히 적습니다.
- 사용 가능한 도구: 어떤 상황에서 어떤 Tool을 써야 하는지 적습니다.
- 금지 행동: 추측 금지, 내부 정보 노출 금지 같은 선을 그어둡니다.
- 판단 기준: confidence 기준, 재질문 조건, 상담원 연결 조건을 넣습니다.
- 응답 톤: 친절한지, 짧게 말할지, 정책처럼 단정적으로 말할지 정합니다.
이 정도만 잡아도 에이전트가 훨씬 덜 흔들립니다. 사람도 마찬가지잖아요. “알아서 잘해”보다 “이럴 땐 이렇게 하고, 이 선은 넘지 마”라고 말해주는 게 훨씬 일하기 편합니다.
Tool 스키마는 귀찮아도 꼼꼼히 잡는 게 결국 시간을 아껴줍니다
Mastra를 쓰면서 제가 초반에 제일 많이 실수한 부분은 Tool 스키마였습니다. 처음엔 빨리 결과 보고 싶어서 inputSchema에 이것저것 넣었어요. query, category, userId, locale 같은 걸 한 번에 다 넣어버렸죠. 그런데 에이전트 입장에서는 선택지가 많아질수록 헷갈릴 수 있습니다.
실제로 한 번은 에이전트가 이런 식으로 툴을 호출했습니다.
{
"query": "환불",
"category": "billing",
"userId": "환불 규정이 궁금한 사용자"
}
userId에는 실제 사용자 ID가 들어가야 하는데, 에이전트가 자연어 설명을 넣어버린 겁니다. 처음엔 DB 쪽 문제인 줄 알고 한참 봤어요. 나중에 보니 스키마 설명이 애매했습니다. 제가 잘못한 거죠.
이후에는 툴을 만들 때 원칙을 좀 세웠습니다.
| 항목 | 처음 했던 방식 | 바꾼 방식 |
|---|---|---|
| 입력 필드 | 필요할 것 같은 값을 한꺼번에 추가 | 에이전트가 반드시 알아야 하는 값만 남김 |
| 필드 설명 | 짧고 추상적으로 작성 | 예시와 금지 케이스까지 적음 |
| 출력값 | DB 결과를 거의 그대로 반환 | answer, confidence, source처럼 의미 있는 값으로 정리 |
| 예외 처리 | 에러를 그대로 throw | 에이전트가 이해할 수 있는 메시지로 변환 |
이렇게 바꾸고 나니까 에이전트가 툴을 훨씬 안정적으로 호출했습니다. 개발도 결국 대화라고 생각해요. 사람과 사람 사이의 대화도 애매하면 오해가 생기고, 에이전트와 Tool 사이의 대화도 스키마가 애매하면 이상한 일이 생깁니다.
비용을 아끼려면 모델을 한 가지로 고정하지 않는 게 좋았습니다
Mastra를 업무성으로 써보면 금방 현실적인 고민이 옵니다. 바로 비용이에요. 프로토타입 단계에서는 GPT-4o를 막 써도 괜찮아 보이지만, 실제 사용자 요청이 늘어나면 이야기가 달라집니다. 작은 요청 하나하나가 쌓이면 꽤 됩니다.
저는 FAQ 응답처럼 비교적 단순한 작업에는 GPT-4o mini를 사용하고, 복잡한 정책 판단이나 긴 문맥이 필요한 경우에만 GPT-4o를 쓰는 식으로 나눴습니다. Mastra 자체에서 모든 상황을 자동으로 전환해주는 기능을 완벽하게 제공한다고 보긴 어렵지만, 환경 변수나 에이전트 구성을 나눠서 충분히 비슷하게 운영할 수 있었습니다.
const model =
process.env.AGENT_MODEL === 'advanced'
? openai.gpt4o()
: openai.gpt4oMini();
const agent = createAgent({
name: 'FAQ Agent',
model,
instructions,
tools: [searchFaqTool],
});
이런 식으로 해두면 배포 환경에 따라 모델을 바꾸기 편합니다. 개발 환경에서는 mini 모델로 빠르게 돌리고, 운영에서 특정 시나리오만 고성능 모델을 쓰게 만드는 식이죠.
제가 실제로 테스트하면서 느낀 비용 감각은 이랬습니다.
| 업무 유형 | 추천 모델 운용 | 이유 |
|---|---|---|
| 단순 FAQ 검색 | GPT-4o mini | 질문 의도 파악과 짧은 답변에는 충분한 경우가 많음 |
| 복잡한 정책 판단 | GPT-4o | 예외 조건이 많고 문맥 이해가 필요함 |
| 내부 운영 도구 | mini 모델 또는 오픈소스 모델 | 응답 품질보다 비용과 속도가 더 중요할 때가 있음 |
| 고객에게 바로 노출되는 답변 | 상황별 혼합 | 비용도 중요하지만 신뢰도가 더 중요함 |
AI 기능은 만들 때보다 운영할 때 진짜 성격이 드러납니다. 데모에서는 멋진데, 한 달 비용 보고 표정 굳어지는 경우를 몇 번 봤거든요. 그래서 처음부터 모델 선택 전략을 조금이라도 넣어두는 게 좋습니다.
디버깅하면서 만난 에러들, 결국 기본을 놓친 경우가 많았어요
Mastra가 비교적 편하다고 해도, 에러가 안 나는 건 아닙니다. 저도 처음 붙이면서 꽤 삽질했습니다. 다만 다행인 건, 에러 원인을 따라갈 수 있는 단서가 어느 정도 보인다는 점이에요.
처음 만난 에러는 이런 메시지였습니다.
Tool execution failed: function not found
처음엔 패키지 버전 문제인가 싶었어요. 의존성 지우고 다시 설치하고, Node 버전도 확인하고, 별짓을 다 했습니다. 그런데 알고 보니 제가 Tool의 id를 중복으로 줬더라고요. 비슷한 검색 툴을 두 개 만들면서 둘 다 search-faq로 둔 겁니다. 부끄럽지만 이런 일이 제일 흔합니다. 프레임워크 문제가 아니라 제 손가락 문제였죠.
또 하나 기억나는 건 이 에러입니다.
Maximum depth exceeded
이건 에이전트가 툴을 계속 호출하면서 사실상 루프에 빠진 상황이었습니다. FAQ 결과의 confidence가 낮으면 다시 검색하고, 다시 낮으면 또 검색하고, 그러다 보니 계속 돌았던 거죠. 이때는 maxSteps를 설정해서 해결했습니다.
const agent = createAgent({
name: 'FAQ Agent',
model: openai.gpt4o(),
instructions,
tools: [searchFaqTool],
maxSteps: 5,
});
이 설정 하나로 훨씬 마음이 편해졌습니다. AI 에이전트는 어느 정도 자율성을 줘야 쓸모가 있지만, 울타리 없이 풀어두면 가끔 엉뚱한 데까지 뛰어갑니다. 저는 그래서 자율성보다 안전장치를 먼저 둔다는 쪽을 선호합니다.
- Tool execution failed: Tool id 중복 여부, 함수 등록 여부, inputSchema와 실제 입력값 타입을 확인해보는 게 좋습니다.
- Maximum depth exceeded:
maxSteps를 설정하고, Tool 호출 조건을 더 엄격하게 잡아야 합니다. - Model response empty: instructions에 출력 형식을 명확히 넣어보면 해결되는 경우가 많았습니다.
- 스키마 검증 실패: Zod 필드 설명이 애매하지 않은지, optional을 남발하지 않았는지 확인해보세요.
- 응답이 자꾸 지어낸 내용처럼 보임: FAQ나 RAG 결과에 근거가 없으면 답하지 말라는 규칙을 instructions에 넣는 게 좋습니다.
디버깅할 때 제가 자주 보는 순서는 이렇습니다. 에이전트 instructions, Tool schema, Tool id, 실제 Tool 실행 로그, 모델 응답 원문. 이 순서로 보면 대부분의 문제는 어느 정도 감이 옵니다. 반대로 DB부터 뒤지거나 모델 탓부터 하면 시간이 길어지더라고요.
Mastra를 쓰면서 좋았던 점과 아쉬웠던 점
좋았던 점부터 말하면, 개발 흐름이 가볍다는 게 가장 컸습니다. 에이전트 하나 만들어서 실험하기까지 시간이 짧고, TypeScript 기반이라 기존 Node.js 백엔드 프로젝트에 붙이기도 편했습니다. 제가 손에 익은 도구 안에서 AI 에이전트를 만들 수 있다는 게 생각보다 큰 장점이에요.
또 하나는 디버깅 경험입니다. AI 에이전트 개발에서 제일 답답한 게 “얘가 왜 이랬지?”거든요. Mastra는 개발 서버와 UI를 통해 그 흐름을 비교적 잘 보여줘서, 적어도 완전히 깜깜한 상태로 헤매는 느낌은 덜했습니다.
아쉬운 점도 있습니다. 커뮤니티가 아주 크진 않아서, 막히는 지점이 생기면 Stack Overflow 검색만으로 해결되진 않을 때가 있어요. 그리고 빠르게 발전하는 프레임워크다 보니 버전 변화에 따라 API가 달라질 가능성도 염두에 둬야 합니다. 운영 시스템에 바로 깊게 박아 넣기보다는, 작은 기능부터 붙여보는 게 안전하다고 봅니다.
| 좋았던 점 | 아쉬웠던 점 |
|---|---|
| TypeScript 개발자에게 익숙한 구조 | 커뮤니티 규모는 아직 크지 않음 |
| 에이전트와 Tool 연결이 단순함 | 버전 변화에 민감할 수 있음 |
| 개발 서버와 디버깅 UI가 편함 | 고급 운영 사례는 아직 많이 쌓이는 중 |
| AI 바이브 코딩 흐름과 잘 맞음 | 복잡한 멀티 에이전트 운영은 별도 설계가 필요함 |
제가 생각하는 Mastra 활용 기준
저는 어떤 기술을 볼 때 “이게 제일 유명한가?”보다 “내 문제를 빨리, 안전하게 풀어주는가?”를 더 봅니다. 20년 넘게 개발하다 보니 유행은 정말 많이 지나갔거든요. 반짝이는 기술은 많았지만, 실제 현장에서 오래 살아남는 건 결국 팀이 이해할 수 있고, 고장 났을 때 고칠 수 있고, 비용이 감당되는 기술이었습니다.
그 기준에서 Mastra는 꽤 괜찮은 선택지였습니다. 특히 아래 상황이라면 한 번쯤 써볼 만합니다.
- AI 바이브 코딩으로 빠르게 에이전트 프로토타입을 만들고 싶은 개발자
- LangChain이 조금 무겁게 느껴졌던 TypeScript 개발자
- FAQ 챗봇, 내부 운영 도우미, 문서 검색 에이전트처럼 작게 시작할 AI 기능이 있는 팀
- 에이전트가 어떤 Tool을 호출하는지 눈으로 보면서 디버깅하고 싶은 분
- LLM 기능을 백엔드 서비스 안에 자연스럽게 넣고 싶은 Node.js 기반 팀
반대로 아주 복잡한 멀티 에이전트 오케스트레이션이나, 이미 LangChain 기반으로 탄탄하게 구축된 시스템이 있다면 굳이 무리해서 갈아탈 필요는 없다고 봅니다. 기술 전환은 늘 비용이 있으니까요. 새 도구가 좋아 보여도, 우리 팀의 맥락에 안 맞으면 그냥 멋진 장난감으로 끝납니다.
제 개인적인 총평은 이렇습니다. Mastra는 작게 시작해서 빠르게 검증하기 좋은 에이전트 프레임워크입니다. 아직 모든 걸 맡기기엔 조심스러운 부분도 있지만, TypeScript 개발자가 AI 에이전트 개발에 발을 들이기에는 꽤 친절한 도구였어요.
AI 에이전트를 처음 만들어보려는 분, 기존 프레임워크가 너무 무겁게 느껴졌던 분, 그리고 “일단 이번 주말에 돌아가는 걸 하나 만들어보고 싶다”는 분이라면 Mastra를 한번 만져보셔도 좋겠습니다. 저도 당분간은 계속 지켜보려고요. 다음에는 여기에 RAG를 붙이거나, 내부 문서 검색 에이전트로 확장해본 이야기도 한 번 풀어보겠습니다.
뭐, 개발이 늘 그렇잖아요. 직접 손에 묻혀봐야 감이 옵니다. 문서만 보면 다 좋아 보이고, 남의 후기는 늘 반만 맞습니다. 그래도 제 경험이 Mastra를 처음 만져보려는 분들에게 작은 힌트 정도는 되었으면 좋겠습니다.
댓글
댓글 쓰기