server/api와 server/routes와 server/middleware가 각각 하는 일
문제 발생
프런트에서 외부 API를 직접 부르다가 CORS에 막혔고, 키까지 노출되는 걸 알고 서버 쪽으로 옮기려 했는데 파일을 어디에 둬야 할지, 메서드는 어떻게 나누는지 매번 헷갈렸습니다.
server/api/posts.ts → ?
server/routes/posts.ts → ?
server/middleware/*.ts → ?원인 분석
server/ 아래 세 디렉터리는 역할이 다릅니다.
server/api— 이 안의 파일은 자동으로/api접두사가 붙습니다.hello.ts는/api/hello가 됩니다.server/routes— 접두사 없이 매핑됩니다.hello.ts는/hello입니다. 웹훅 수신처럼 경로가 고정된 엔드포인트에 씁니다.server/middleware— 다른 라우트보다 먼저, 모든 요청에서 실행됩니다.
모든 파일은 기본 export로 defineEventHandler()(또는 별칭 eventHandler())를 내보내야 합니다.
미들웨어에는 제약이 하나 있습니다. 문서가 명시합니다 — 미들웨어 핸들러는 아무것도 반환해서는 안 되며(요청을 닫거나 응답해서도 안 됩니다) 요청 컨텍스트를 검사하거나 확장하거나, 오류를 던지기만 해야 합니다. 여기서 값을 반환하면 그 요청이 그대로 끝나 버립니다.
해결 방안
- 파일 이름이 곧 라우트입니다.
server/api/posts/index.get.ts → GET /api/posts
server/api/posts/index.post.ts → POST /api/posts
server/api/posts/[id].get.ts → GET /api/posts/:id
server/api/files/[...path].ts → /api/files/**.get/.post/.put/.delete 접미사가 HTTP 메서드를 매칭합니다. 접미사가 없으면 모든 메서드를 받습니다.
- 요청 데이터는 전용 헬퍼로 읽습니다.
// server/api/posts/index.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event);
const { page } = getQuery(event);
return await createPost(body);
});// server/api/posts/[id].get.ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, "id");
return await findPost(id);
});- 입력은 경계에서 검증합니다.
readBody/getQuery가 주는 값은 전부 외부 입력입니다. Zod 같은 스키마 검증기와 함께 쓰는 형태가 문서에도 안내되어 있습니다.
const body = await readValidatedBody(event, createPostSchema.parse);- 미들웨어에서는 반환하지 않습니다.
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
const token = getCookie(event, "session");
event.context.user = await resolveUser(token); // 컨텍스트 확장만
// return 하지 않는다
});거부해야 하면 오류를 던집니다.
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });-
미들웨어는 정말 모든 요청에서 돕니다. 정적 자산과 무관한 경로까지 포함하므로 무거운 작업을 넣지 않고, 필요한 경로에서만 동작하도록 조건을 겁니다.
-
프런트에서는
useFetch로 부릅니다. 같은 서버라 상대 경로면 충분하고, 서버 렌더 시에는 HTTP를 타지 않고 내부에서 직접 처리됩니다.
const { data } = await useFetch("/api/posts");
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.