server
server/ 디렉토리는 애플리케이션에 API 및 서버 핸들러를 등록하는 데 사용됩니다.
Nuxt는 자동으로 이 디렉토리 내의 파일을 스캔하여 API 및 서버 핸들러를 Hot Module Replacement (HMR) 지원과 함께 등록합니다.
-| server/
---| api/
-----| hello.ts # /api/hello
---| routes/
-----| bonjour.ts # /bonjour
---| middleware/
-----| log.ts # 모든 요청을 기록
각 파일은 defineEventHandler()
또는 eventHandler()
(별칭)로 정의된 기본 함수를 내보내야 합니다.
핸들러는 JSON 데이터를 직접 반환하거나, Promise
를 반환하거나, event.node.res.end()
를 사용하여 응답을 보낼 수 있습니다.
export default defineEventHandler((event) => {
return {
hello: 'world'
}
})
이제 페이지와 컴포넌트에서 이 API를 보편적으로 호출할 수 있습니다:
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>
<template>
<pre>{{ data }}</pre>
</template>
서버 라우트
~/server/api
내의 파일은 자동으로 경로에 /api
가 접두사로 붙습니다.
/api
접두사 없이 서버 라우트를 추가하려면 ~/server/routes
디렉토리에 넣으세요.
예시:
export default defineEventHandler(() => 'Hello World!')
위의 예시를 보면, /hello
라우트는 http://localhost:3000/hello에서 접근할 수 있습니다.
현재 서버 라우트는 pages와 같은 동적 라우트의 전체 기능을 지원하지 않는다는 점에 유의하세요.
서버 미들웨어
Nuxt는 프로젝트를 위한 서버 미들웨어를 생성하기 위해 ~/server/middleware
내의 모든 파일을 자동으로 읽습니다.
미들웨어 핸들러는 헤더를 추가하거나 확인하고, 요청을 기록하거나, 이벤트의 요청 객체를 확장하기 위해 다른 서버 라우트보다 먼저 모든 요청에서 실행됩니다.
미들웨어 핸들러는 아무것도 반환하지 않아야 하며(요청을 닫거나 응답하지 않아야 함), 요청 컨텍스트를 검사하거나 확장하거나 오류를 던지는 것만 해야 합니다.
예시들:
export default defineEventHandler((event) => {
console.log('New request: ' + getRequestURL(event))
})
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
서버 플러그인
Nuxt는 ~/server/plugins
디렉토리 내의 모든 파일을 자동으로 읽고 Nitro 플러그인으로 등록합니다. 이를 통해 Nitro의 런타임 동작을 확장하고 라이프사이클 이벤트에 연결할 수 있습니다.
예시:
export default defineNitroPlugin((nitroApp) => {
console.log('Nitro plugin', nitroApp)
})
서버 유틸리티
서버 라우트는 h3js/h3에 의해 구동되며, 이는 유용한 헬퍼 세트를 제공합니다.
이것도 참고 Available H3 Request Helpers~/server/utils
디렉토리 내에 더 많은 헬퍼를 직접 추가할 수 있습니다.
예를 들어, 원래의 핸들러를 감싸고 최종 응답을 반환하기 전에 추가 작업을 수행하는 사용자 정의 핸들러 유틸리티를 정의할 수 있습니다.
예시:
import type { EventHandler, EventHandlerRequest } from 'h3'
export const defineWrappedResponseHandler = <T extends EventHandlerRequest, D> (
handler: EventHandler<T, D>
): EventHandler<T, D> =>
defineEventHandler<T>(async event => {
try {
// 라우트 핸들러 전에 작업 수행
const response = await handler(event)
// 라우트 핸들러 후에 작업 수행
return { response }
} catch (err) {
// 오류 처리
return { err }
}
})
서버 타입
이 기능은 Nuxt >= 3.5부터 사용할 수 있습니다.
IDE 내에서 'nitro'와 'vue'의 자동 임포트 간의 명확성을 개선하기 위해 다음 내용을 포함한 ~/server/tsconfig.json
을 추가할 수 있습니다:
{
"extends": "../.nuxt/tsconfig.server.json"
}
현재 이 값들은 타입 검사 시 (nuxt typecheck
) 존중되지 않지만, IDE에서 더 나은 타입 힌트를 받을 수 있습니다.
레시피
라우트 매개변수
서버 라우트는 파일 이름에 대괄호를 사용하여 동적 매개변수를 사용할 수 있으며, event.context.params
를 통해 접근할 수 있습니다.
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
return `Hello, ${name}!`
})
대안으로, Zod와 같은 스키마 검증기를 사용하여 getValidatedRouterParams
를 사용하여 런타임 및 타입 안전성을 확보할 수 있습니다.
이제 /api/hello/nuxt
에서 이 API를 보편적으로 호출하여 Hello, nuxt!
를 얻을 수 있습니다.
HTTP 메서드 매칭
파일 이름은 요청의 HTTP 메서드에 맞추기 위해 .get
, .post
, .put
, .delete
등의 접미사를 가질 수 있습니다.
export default defineEventHandler(() => 'Test get handler')
export default defineEventHandler(() => 'Test post handler')
위의 예시를 보면, /test
를 가져올 때:
- GET 메서드:
Test get handler
반환 - POST 메서드:
Test post handler
반환 - 다른 메서드: 405 오류 반환
또한 디렉토리 내에 index.[method].ts
를 사용하여 코드를 다르게 구조화할 수 있으며, 이는 API 네임스페이스를 생성하는 데 유용합니다.
export default defineEventHandler((event) => {
// `api/foo` 엔드포인트에 대한 GET 요청 처리
})
Catch-all 라우트
Catch-all 라우트는 폴백 라우트 처리를 위해 유용합니다.
예를 들어, ~/server/api/foo/[...].ts
라는 파일을 생성하면 /api/foo/bar/baz
와 같이 어떤 라우트 핸들러와도 일치하지 않는 모든 요청에 대한 catch-all 라우트를 등록합니다.
export default defineEventHandler((event) => {
// event.context.path로 라우트 경로 가져오기: '/api/foo/bar/baz'
// event.context.params._로 라우트 세그먼트 가져오기: 'bar/baz'
return `Default foo handler`
})
~/server/api/foo/[...slug].ts
를 사용하여 catch-all 라우트에 이름을 지정하고 event.context.params.slug
를 통해 접근할 수 있습니다.
export default defineEventHandler((event) => {
// event.context.params.slug로 라우트 세그먼트 가져오기: 'bar/baz'
return `Default foo handler`
})
본문 처리
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
대안으로, Zod와 같은 스키마 검증기를 사용하여 readValidatedBody
를 사용하여 런타임 및 타입 안전성을 확보할 수 있습니다.
이제 다음을 사용하여 이 API를 보편적으로 호출할 수 있습니다:
async function submit() {
const { body } = await $fetch('/api/submit', {
method: 'post',
body: { test: 123 }
})
}
우리는 요청 본문을 수락할 수 있는 POST
메서드와 일치시키기 위해 파일 이름에 submit.post.ts
를 사용하고 있습니다. GET 요청 내에서 readBody
를 사용할 때, readBody
는 405 Method Not Allowed
HTTP 오류를 던질 것입니다.
쿼리 매개변수
샘플 쿼리 /api/query?foo=bar&baz=qux
export default defineEventHandler((event) => {
const query = getQuery(event)
return { a: query.foo, b: query.baz }
})
대안으로, Zod와 같은 스키마 검증기를 사용하여 getValidatedQuery
를 사용하여 런타임 및 타입 안전성을 확보할 수 있습니다.
오류 처리
오류가 발생하지 않으면 200 OK
상태 코드가 반환됩니다.
잡히지 않은 오류는 500 Internal Server Error
HTTP 오류를 반환합니다.
다른 오류 코드를 반환하려면 createError
를 사용하여 예외를 던지세요:
export default defineEventHandler((event) => {
const id = parseInt(event.context.params.id) as number
if (!Number.isInteger(id)) {
throw createError({
statusCode: 400,
statusMessage: 'ID는 정수여야 합니다',
})
}
return 'All good'
})
상태 코드
다른 상태 코드를 반환하려면 setResponseStatus
유틸리티를 사용하세요.
예를 들어, 202 Accepted
를 반환하려면
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
런타임 구성
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event)
const repo = await $fetch('https://api.github.com/repos/nuxt/nuxt', {
headers: {
Authorization: `token ${config.githubToken}`
}
})
return repo
})
서버 라우트에서 런타임 시 환경 변수에 의해 덮어쓰여진 런타임 구성을 얻기 위해 event
를 useRuntimeConfig
에 인수로 전달하는 것이 선택 사항이지만 권장됩니다.
요청 쿠키
export default defineEventHandler((event) => {
const cookies = parseCookies(event)
return { cookies }
})
컨텍스트 및 헤더 전달
기본적으로 서버 라우트에서 fetch 요청을 할 때 들어오는 요청의 헤더나 요청 컨텍스트는 전달되지 않습니다. 서버 라우트에서 fetch 요청을 할 때 요청 컨텍스트와 헤더를 전달하려면 event.$fetch
를 사용할 수 있습니다.
export default defineEventHandler((event) => {
return event.$fetch('/api/forwarded')
})
전달되지 않아야 하는 헤더는 요청에 포함되지 않습니다. 이러한 헤더에는 예를 들어 transfer-encoding
, connection
, keep-alive
, upgrade
, expect
, host
, accept
등이 포함됩니다.
응답 후 Promise 대기
서버 요청을 처리할 때, 클라이언트에 대한 응답을 차단하지 않아야 하는 비동기 작업(예: 캐싱 및 로깅)을 수행해야 할 수 있습니다. event.waitUntil
을 사용하여 응답을 지연시키지 않고 백그라운드에서 Promise를 대기할 수 있습니다.
event.waitUntil
메서드는 핸들러가 종료되기 전에 대기할 Promise를 수락하여, 서버가 응답을 보낸 직후 핸들러를 종료할 경우에도 작업이 완료되도록 보장합니다. 이는 런타임 제공자와 통합되어 응답이 전송된 후 비동기 작업을 처리하기 위한 네이티브 기능을 활용합니다.
const timeConsumingBackgroundTask = async () => {
await new Promise((resolve) => setTimeout(resolve, 1000))
};
export default eventHandler((event) => {
// 응답을 차단하지 않고 백그라운드 작업 예약
event.waitUntil(timeConsumingBackgroundTask())
// 즉시 클라이언트에 응답 전송
return 'done'
});
고급 사용법
Nitro 구성
nuxt.config
에서 nitro
키를 사용하여 Nitro 구성을 직접 설정할 수 있습니다.
이것은 고급 옵션입니다. 사용자 정의 구성은 Nitro가 Nuxt의 semver-minor 버전으로 업그레이드될 때 구성 인터페이스가 변경될 수 있으므로 프로덕션 배포에 영향을 미칠 수 있습니다.
export default defineNuxtConfig({
// https://nitro.build/config
nitro: {}
})
중첩 라우터
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()
router.get('/test', defineEventHandler(() => 'Hello World'))
export default useBase('/api/hello', router.handler)
스트림 전송
이것은 실험적인 기능이며 모든 환경에서 사용할 수 있습니다.
import fs from 'node:fs'
import { sendStream } from 'h3'
export default defineEventHandler((event) => {
return sendStream(event, fs.createReadStream('/path/to/file'))
})
리다이렉트 전송
export default defineEventHandler(async (event) => {
await sendRedirect(event, '/path/redirect/to', 302)
})
레거시 핸들러 또는 미들웨어
export default fromNodeMiddleware((req, res) => {
res.end('Legacy handler')
})
레거시 지원은 h3js/h3를 사용하여 가능합니다. 그러나 가능한 한 레거시 핸들러를 피하는 것이 좋습니다.
export default fromNodeMiddleware((req, res, next) => {
console.log('Legacy middleware')
next()
})
next()
콜백을 async
또는 Promise
를 반환하는 레거시 미들웨어와 결합하지 마세요.
서버 스토리지
Nitro는 플랫폼 간 스토리지 레이어를 제공합니다. 추가 스토리지 마운트 포인트를 구성하려면 nitro.storage
또는 서버 플러그인을 사용할 수 있습니다.
Redis 스토리지를 추가하는 예시:
nitro.storage
사용:
export default defineNuxtConfig({
nitro: {
storage: {
redis: {
driver: 'redis',
/* redis 커넥터 옵션 */
port: 6379, // Redis 포트
host: "127.0.0.1", // Redis 호스트
username: "", // Redis >= 6 필요
password: "",
db: 0, // 기본값은 0
tls: {} // tls/ssl
}
}
}
})
그런 다음 API 핸들러에서:
export default defineEventHandler(async (event) => {
// 모든 키 나열
const keys = await useStorage('redis').getKeys()
// 키 설정
await useStorage('redis').setItem('foo', 'bar')
// 키 제거
await useStorage('redis').removeItem('foo')
return {}
})
대안으로, 서버 플러그인과 런타임 구성을 사용하여 스토리지 마운트 포인트를 생성할 수 있습니다:
import redisDriver from 'unstorage/drivers/redis'
export default defineNitroPlugin(() => {
const storage = useStorage()
// 런타임 구성 또는 다른 소스에서 자격 증명을 동적으로 전달
const driver = redisDriver({
base: 'redis',
host: useRuntimeConfig().redis.host,
port: useRuntimeConfig().redis.port,
/* 다른 redis 커넥터 옵션 */
})
// 드라이버 마운트
storage.mount('redis', driver)
})
※이 페이지는 Nuxt.js 공식 문서의 비공식 번역 페이지입니다.
공식 문서의 해당 페이지는 여기 있습니다:
https://nuxt.com/docs/3.x/guide/directory-structure/server