5.1 KiB
5.1 KiB
name, description
| name | description |
|---|---|
| api-server-route | Nuxt 3 서버 라우트(Nitro)와 API 통합 레이어를 작성할 때 사용합니다. "API 라우트 만들어줘", "서버 라우트", "server/api", "BFF", "프록시 API", "서버 미들웨어", "Nitro", "defineEventHandler" 등을 요청하면 트리거됩니다. |
Nuxt 서버 라우트 (Nitro)
이 skill은 Nuxt 3의 Nitro 서버 라우트를 생성합니다. BFF 프록시 패턴, API 엔드포인트, 서버 미들웨어, 서버 유틸리티를 포함합니다.
작업 순서
-
라우트 목적 파악
- BFF 프록시 (외부 API 중계)
- 내부 데이터 변환/가공
- 인증 엔드포인트
- 서버 전용 로직
-
기존 서버 라우트 탐색
server/api/구조 확인server/utils/에서 공유 유틸리티 확인server/middleware/에서 기존 미들웨어 확인
-
HTTP 메서드 및 파일 네이밍 결정
- 메서드별 파일:
xxx.get.ts,xxx.post.ts,xxx.delete.ts - 통합 파일:
xxx.ts(모든 메서드 처리)
- 메서드별 파일:
-
요청/응답 타입 정의
readBody<T>(),getQuery(),getRouterParam()의 타입 명시
-
핸들러 구현
defineEventHandler사용- 입력 검증 추가
- 에러 처리는
createError사용
-
클라이언트 연결
- 대응하는 composable에서
useFetch('/api/xxx')로 연결 안내
- 대응하는 composable에서
-
검증
- TypeScript 오류 확인
nuxt dev에서 API 라우트 정상 동작 확인 안내
디렉토리 구조
server/
├── api/
│ ├── auth/
│ │ ├── login.post.ts
│ │ └── logout.post.ts
│ └── users/
│ ├── index.get.ts # GET /api/users
│ ├── index.post.ts # POST /api/users
│ └── [id].get.ts # GET /api/users/:id
├── middleware/
│ └── auth.ts # 모든 요청에 적용
└── utils/
└── api-client.ts # 공유 유틸리티
GET 라우트 템플릿
// server/api/users/[id].get.ts
import type { UserProfile } from '~/types/user';
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id');
if (!id) {
throw createError({
statusCode: 400,
statusMessage: 'User ID is required',
});
}
const config = useRuntimeConfig();
const profile = await $fetch<UserProfile>(
`${config.apiBaseUrl}/users/${id}`,
{
headers: {
Authorization: getHeader(event, 'authorization') ?? '',
},
},
);
return profile;
});
POST 라우트 템플릿
// server/api/users/index.post.ts
interface CreateUserBody {
name: string;
email: string;
}
export default defineEventHandler(async (event) => {
const body = await readBody<CreateUserBody>(event);
if (!body.name || !body.email) {
throw createError({
statusCode: 400,
statusMessage: 'Name and email are required',
});
}
const config = useRuntimeConfig();
const result = await $fetch(`${config.apiBaseUrl}/users`, {
method: 'POST',
body,
});
return result;
});
서버 미들웨어 템플릿
// server/middleware/auth.ts
export default defineEventHandler((event) => {
const protectedPaths = ['/api/protected', '/api/admin'];
const isProtected = protectedPaths.some((path) =>
event.path.startsWith(path),
);
if (!isProtected) {
return;
}
const token = getHeader(event, 'authorization');
if (!token) {
throw createError({
statusCode: 401,
statusMessage: 'Unauthorized',
});
}
});
서버 유틸리티 템플릿
// server/utils/api-client.ts
export function createApiClient() {
const config = useRuntimeConfig();
return {
get: <T>(path: string, headers?: Record<string, string>) =>
$fetch<T>(`${config.apiBaseUrl}${path}`, { headers }),
post: <T>(path: string, body: unknown, headers?: Record<string, string>) =>
$fetch<T>(`${config.apiBaseUrl}${path}`, {
method: 'POST',
body,
headers,
}),
};
}
nuxt.config.ts 런타임 설정
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
// 서버 전용 (클라이언트에 노출 안 됨)
apiBaseUrl: process.env.API_BASE_URL || 'http://localhost:8080',
apiSecret: process.env.API_SECRET,
// 클라이언트에도 노출
public: {
appName: 'My App',
},
},
});
주의사항
- 비밀키는
useRuntimeConfig()로만 접근: 클라이언트 코드에 노출 금지.runtimeConfig(public이 아닌) 필드에 저장 - 서버 라우트는 Nitro(Node.js) 컨텍스트: Vue 반응성(
ref,computed), 브라우저 API(window,document) 사용 불가 readBody<T>()입력 검증 필수: 타입 단언만으로는 런타임 안전성 미보장. 필수 필드 체크 추가createError로 일관된 에러 응답:statusCode+statusMessage형태- 외부 API 프록시 시 헤더 전달 주의: 내부 전용 헤더가 외부로 유출되지 않도록 필요한 헤더만 선택 전달
- 파일 네이밍은
kebab-case.ts(rules/coding-conventions.md참조) any타입 사용 금지