
AI 에이전트 네이티브
풀스택 프레임워크
AI가 코드를 쏟아내도, 만두는 안 터집니다. 🥟
엔지니어링 고민, 이제 프레임워크가. 100+ MCP 툴 · 실전 Skills 워크플로우 · 런타임 Guard.
하루 1만줄 쏟아내는 코딩 에이전트,
누가 해결 하나요?
구조적 가드레일 없이 AI가 생성한 코드는 순식간에 기술 부채 덩어리가 됩니다.
기능 확산
AI 에이전트가 중복 유틸리티를 만들고 기존 패턴을 무시해 코드베이스를 비대하게 만듭니다.
→ negotiate + Skills로 구조 일관성
조용한 실패
생성된 함수 속 깊이 숨겨진 로직 오류. 기본 테스트는 통과하지만 프로덕션에서 터집니다.
→ ATE가 테스트를 쓰고 자동 힐링
아키텍처 드리프트
AI가 경계를 이해하지 못한 채 코드를 붙여넣으면 클린 아키텍처가 무너집니다.
→ Guard가 런타임에 경계 강제
무엇이 들어있나요?
같은 명령, 세 번째인가요?
스키마 한 파일. API·타입·테스트·검증 자동 생성. 에이전트가 벗어나면 Guard가 런타임에 차단.
아키텍처, 벌써 무너졌나요?
레이어·네이밍·의존성 규칙 런타임 강제. FSD·Clean·Hexagonal 6개 프리셋 내장. 에이전트가 벗어나면 그 자리에서 거부.
// 레이어 위반 감지
Agent "claude" blocked.
Reason: layer-violation: shared → features
아키텍처 보존됨 ✅
API 30줄이 Filling 3줄로.
Mandu.filling(contract, ctx => ctx.ok(data)). 미들웨어 체인은 .guard()로 합성. ctx 하나에 세션·CSRF·DB 전부.
에이전트는 Mandu를 이해합니다.
MCP 100+ 도구로 구조·계약·규칙 전부 조회 가능. Claude/Codex가 추측 없이 직접 실행. 프롬프트 엔지니어링 대신 툴 호출.
테스트, 아직도 직접 쓰세요?
ATE가 라우트에서 Playwright spec 자동 생성. L0~L3 오라클 레벨 선택 (스모크~계약 검증). 실패하면 LLM이 수리 diff 제안.
AI 실수, 원클릭 되돌리기.
mandu change begin으로 AI 편집 전 스냅샷. 실수하면 rollback, 괜찮으면 commit. 브랜치 없이 파일 단위 원자 복구.
Next · Remix · Hono 와 뭐가 다른가요?
| 기능 | Mandu 🥟 | Next.js | Remix | Hono |
|---|---|---|---|---|
| 런타임 | Bun-native | Node/Edge | Node/CF | Bun/Node/Deno |
| 타입 ↔ API | 스키마 파일 1개 → 타입·OpenAPI·테스트 파생 | 수동 동기화 | 수동 동기화 | Zod middleware 수동 |
| 아키텍처 강제 | Guard 런타임 | ESLint | 없음 | 없음 |
| 테스트 자동화 | ATE 자동 생성·힐링 | 수동 | 수동 | 수동 |
| 에이전트 인터페이스 | MCP 100+ 도구 내장 | Plugin | Plugin | Plugin |
| AI 편집 롤백 | change begin/commit/rollback | git 수동 | git 수동 | git 수동 |
| 배포 타겟 | 4 edge + 8 deploy 어댑터 | Vercel 우선 | Multi | Multi |
| DB 클라이언트 | Bun.SQL (Postgres·MySQL·SQLite) | — | — | — |
| Auth / Session | 세션·JWT·OAuth·이메일 내장 | 서드파티 | 서드파티 | 서드파티 |
핸들러 6줄, 스키마 8줄.
30줄 한 파일이 두 파일 14줄로. 같은 /api/signup 엔드포인트.
import { z } from "zod";
import { NextRequest, NextResponse } from "next/server";
import { db, hash, isRateLimited } from "@/lib";
const SignupSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
export async function POST(req: NextRequest) {
const csrf = req.headers.get("x-csrf-token");
if (csrf !== req.cookies.get("__csrf")?.value)
return NextResponse.json({ error: "csrf" }, { status: 403 });
if (await isRateLimited(req.ip))
return NextResponse.json({ error: "rate" }, { status: 429 });
const raw = await req.json().catch(() => null);
const parsed = SignupSchema.safeParse(raw);
if (!parsed.success)
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
try {
const user = await db.user.create({
data: {
email: parsed.data.email,
password: await hash(parsed.data.password),
},
});
return NextResponse.json({ id: user.id }, { status: 201 });
} catch (err) {
if ((err as any).code === "P2002")
return NextResponse.json({ error: "duplicate" }, { status: 409 });
return NextResponse.json({ error: "internal" }, { status: 500 });
}
}import { Mandu } from "@mandujs/core"; import { SignupContract } from "@/spec/contracts/signup.contract"; export default Mandu.filling(SignupContract, async (ctx) => ctx.ok(await ctx.db.user.create({ ...ctx.body })) ).guard("auth").rateLimit({ rpm: 10 });
import { defineContract } from "@mandujs/core/contract"; import { z } from "zod"; export const SignupContract = defineContract({ method: "POST", request: z.object({ email: z.string().email(), password: z.string().min(8) }), response: z.object({ id: z.string() }), });
한 명령어로 8곳.
플랫폼 설정 파일은 Mandu가 자동 생성. wrangler.toml · vercel.json · Dockerfile · …
호환 가능
