server/ 디렉토리는 API 및 서버 핸들러를 애플리케이션에 등록하는 데 사용된다.
Nuxt는 이러한 디렉터리 내의 파일을 자동으로 스캔하여 HMR(Hot Module replacement) 지원으로 API 및 서버 핸들러를 등록한다.
디렉토리 구조
-| server/
---| api/
-----| hello.ts # /api/hello
---| routes/
-----| bonjour.ts # /bonjour
---| middleware/
-----| log.ts # log all requests
각 파일은 defineEventHandler() 또는 eventHandler()(별칭)으로 정의된 기본 함수를 내보내야 한다.
핸들러는 JSON 데이터인 Promise를 직접 반환하거나 event.node.res.end()를 사용하여 응답을 보낼 수 있다.
server/api/hello.ts
// server/api/hello.ts
export default defineEventHandler((event) => {
return {
hello: 'world'
}
})
이제 페이지와 컴포넌트에서 이 API를 보편적으로 호출할 수 있습니다.
pages/index.vue
<!--
pages/index.vue
-->
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>
<template>
<pre>{{ data }}</pre>
</template>
서버 경로
~/server/api 내부의 파일에는 해당 경로에 자동으로 /api 접두사가 붙는다.
접두사 없이 /api 서버 경로를 추가하려면 해당 경로를 ~/server/routes 디렉토리에 넣는다.
예:
server/routes/hello.ts
// server/routes/hello.ts
export default defineEventHandler(() => 'Hello World!')
위의 예에서 /hello 경로는 http://localhost:3000/hello
현재 서버 경로는 페이지 처럼 동적 경로의 전체 기능을 지원하지 않는다.
서버 미들웨어
Nuxt는 ~/server/middleware의 모든 파일을 자동으로 읽어 프로젝트에 대한 서버 미들웨어를 생성한다.
미들웨어 핸들러는 헤더를 추가하거나 확인하고, 요청을 기록하거나, 이벤트의 요청 개체를 확장하기 위해 다른 서버 경로 이전에 모든 요청에 대해 실행된다.
미들웨어 핸들러는 아무것도 반환하지 않아야 하며(요청을 닫거나 응답하지 않아도 됨) 요청 컨텍스트를 검사 또는 확장하거나 오류를 발생시키기만 하면 된다.
사용 예
server/middleware/log.ts
// server/middleware/log.ts
export default defineEventHandler((event) => {
console.log('New request: ' + getRequestURL(event))
})
server/middleware/auth.ts
// server/middleware/auth.ts
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
서버 플러그인
Nuxt는 ~/server/plugins 디렉토리의 모든 파일을 자동으로 읽고 이를 Nitro 플러그인으로 등록한다. 이를 통해 Nitro의 런타임 동작을 확장하고 수명 주기 이벤트에 연결할 수 있다.
예:
server/plugins/nitroPlugin.ts
// server/plugins/nitroPlugin.ts
export default defineNitroPlugin((nitroApp) => {
console.log('Nitro plugin', nitroApp)
})
참고) Nitro plugins
서버 유틸리티
서버 경로는 편리한 헬퍼 세트와 함께 제공되는 unjs/h3에 의해 구동된다.
참고) H3 요청 헬퍼
~/server/utils 디렉토리에 더 많은 헬퍼를 직접 추가할 수 있다.
예를 들어 원래 핸들러를 래핑하고 최종 응답을 반환하기 전에 추가 작업을 수행하는 사용자 정의 핸들러 유틸리티를 정의할 수 있다.
예:
server/utils/handler.ts
// server/utils/handler.ts
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 {
// do something before the route handler
const response = await handler(event)
// do something after the route handler
return { response }
} catch (err) {
// Error handling
return { err }
}
})
서버 Types ( Nuxt >= 3.5)
IDE 내에서 'nitro'와 'vue'의 자동 임포트 사이의 명확성을 높이려면 아래 내용이 포함된 ~/server/tsconfig.json을 추가할 수 있다.
server/tsconfig.json
// server/tsconfig.json
{
"extends": "../.nuxt/tsconfig.server.json"
}
현재 이러한 값은 유형 검사(nuxi typecheck) 시 고려되지 않지만 IDE에서 더 나은 유형 힌트를 얻을 수 있다.
레시피
경로 매개변수
서버 경로는 /api/hello/[name].ts과 같은 파일 이름의 대괄호 안에 동적 매개변수를 사용할 수 있으며 event.context.params를 통해 액세스할 수 있다.
server/api/hello/[name].ts
// server/api/hello/[name].ts
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
return `Hello, ${name}!`
})
이제 /api/hello/nuxt에서 이 API를 보편적으로 호출하고 Hello, nuxt!를 얻을 수 있다.
HTTP 메서드 매칭
핸들 파일 이름에는 HTTP 메서드 와 일치시키기 위해 .get, .post, .put, .delete, ... 등의 접미사를 넣을 수 있다.
server/api/test.get.ts
// server/api/test.get.ts
export default defineEventHandler(() => 'Test get handler')
server/api/test.post.ts
// server/api/test.post.ts
export default defineEventHandler(() => 'Test post handler')
위의 예에서 다음을 사용하여 /test 가져옵니다:
- GET 메소드: Test get handler 반환
- POST 메소드: Test post handler 반환
- 기타 방법: 405 오류를 반환한다.
또한 디렉토리 내에서 index.[method].ts를 사용하여 코드를 다르게 구성할 수 있으며 이는 API 네임스페이스를 생성하는 데 용이하다.
server/api/foo/index.get.ts
// server/api/foo/index.get.ts
export default defineEventHandler((event) => {
// handle GET requests for the `api/foo` endpoint
})
server/api/foo/index.post.ts
// server/api/foo/index.post.ts
export default defineEventHandler((event) => {
// handle GET requests for the `api/foo` endpoint
})
server/api/foo/bar.get.ts
// server/api/foo/bar.get.ts
export default defineEventHandler((event) => {
// handle GET requests for the `api/foo` endpoint
})
포괄 경로 (Catch-all Route)
포괄 경로는 대체 경로 처리에 유용하다.
예를 들어 ~/server/api/foo/[...].ts이라는 파일을 생성하면 /api/foo/bar/baz와 같은 경로 핸들러와 일치하지 않는 모든 요청에 대해 포괄 경로가 등록된다.
server/api/foo/[...].ts
// server/api/foo/[...].ts
export default defineEventHandler((event) => {
// event.context.path to get the route path: '/api/foo/bar/baz'
// event.context.params._ to get the route segment: 'bar/baz'
return `Default foo handler`
})
~/server/api/foo/[...slug].ts을 사용하여 포괄 경로의 이름을 설정하고 event.context.params.slug를 통해 액세스할 수 있다.
server/api/foo/[...slug].ts
// server/api/foo/[...slug].ts
export default defineEventHandler((event) => {
// event.context.params.slug to get the route segment: 'bar/baz'
return `Default foo handler`
})
요청 Body 처리 (Body Handling)
server/api/submit.post.ts
// server/api/submit.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
이제 다음을 사용하여 이 API를 보편적으로 호출할 수 있다.
app.vue
<!--
app.vue
-->
<script setup>
async function submit() {
const { body } = await $fetch('/api/submit', {
method: 'post',
body: { test: 123 }
})
}
</script>
요청 본문을 수락할 수 있는 POST 메서드와 요청을 일치시키기 위해서만 파일 이름에 submit.post.ts를 사용하고 있다. GET 요청 내에서 readBody를 사용할 때 readBody는 405 Method Not Allowed HTTP 오류를 발생시킨다.
Query Parameters
샘플 쿼리 /api/query?foo=bar&baz=qux
server/api/query.get.ts
// server/api/query.get.ts
export default defineEventHandler((event) => {
const query = getQuery(event)
return { a: query.foo, b: query.baz }
})
오류 처리
오류가 발생하지 않으면 200 OK 상태 코드가 반환된다.
잡히지 않은 오류는 500 Internal Server Error HTTP 오류를 반환한다.
다른 오류 코드를 반환하려면 createError 를 사용하여 예외를 발생시켜야한다.
server/api/validation/[id].ts
// server/api/validation/[id].ts
export default defineEventHandler((event) => {
const id = parseInt(event.context.params.id) as number
if (!Number.isInteger(id)) {
throw createError({
statusCode: 400,
statusMessage: 'ID should be an integer',
})
}
return 'All good'
})
상태 코드
다른 상태 코드를 반환하려면 setResponseStatus 유틸리티를 사용한다.
아래 예를 들어, 202 Accepted 를 반환하려면...
server/api/validation/[id].ts
// server/api/validation/[id].ts
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
런타임 구성
server/api/foo.ts
// server/api/foo.ts
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
})
nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
githubToken: ''
}
})
.env
# .env
NUXT_GITHUB_TOKEN='<my-super-token>'
요청 쿠키
server/api/cookies.ts
// server/api/cookies.ts
export default defineEventHandler((event) => {
const cookies = parseCookies(event)
return { cookies }
})
고급 사용
Nitro 구성
nitro 키를 사용하여 nuxt.config 에 직접 Nitro 구성.
이는 고급 옵션이므로, Nuxt의 semver-minor 버전에서 Nitro가 업그레이드되면 구성 인터페이스가 시간이 지남에 따라 변경될 수 있으므로 사용자 지정 구성은 프로덕션 배포에 영향을 미칠 수 있다.
nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
// https://nitro.unjs.io/config
nitro: {}
})
참고) Nuxt-개요 > 서버
중첩 라우터
server/api/hello/[...slug].ts
// server/api/hello/[...slug].ts
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()
router.get('/test', defineEventHandler(() => 'Hello World'))
export default useBase('/api/hello', router.handler)
스트림 보내기
이는 실험적인 기능이며 모든 환경에서 사용할 수 있다.
server/api/foo.get.ts
// server/api/foo.get.ts
import fs from 'node:fs'
import { sendStream } from 'h3'
export default defineEventHandler((event) => {
return sendStream(event, fs.createReadStream('/path/to/file'))
})
리디렉션 보내기
server/api/foo.get.ts
// server/api/foo.get.ts
export default defineEventHandler(async (event) => {
await sendRedirect(event, '/path/redirect/to', 302)
})
레거시 핸들러 또는 미들웨어
server/api/legacy.ts
// server/api/legacy.ts
export default fromNodeMiddleware((req, res) => {
res.end('Legacy handler')
})
레거시 지원은 unjs/h3 을 사용하여 가능하지만 레거시 핸들러는 최대한 피하는 것이 좋다.
서버 스토리지
Nitro는 크로스 플랫폼 저장 계층을 제공한다. 추가 스토리지 마운트 지점을 구성하려면 nitro.storage 또는 서버 플러그인을 사용할 수 있다.
Redis 스토리지 추가의 예:
nitro.storage 사용 :
nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
storage: {
redis: {
driver: 'redis',
/* redis connector options */
port: 6379, // Redis port
host: "127.0.0.1", // Redis host
username: "", // needs Redis >= 6
password: "",
db: 0, // Defaults to 0
tls: {} // tls/ssl
}
}
}
})
이 구성을 API 핸들러에서 사용하려면...
server/api/storage/test.ts
// server/api/storage/test.ts
export default defineEventHandler(async (event) => {
// List all keys with
const keys = await useStorage('redis').getKeys()
// Set a key with
await useStorage('redis').setItem('foo', 'bar')
// Remove a key with
await useStorage('redis').removeItem('foo')
return {}
})
참고) Nitro 스토리지 레이어
또는 서버 플러그인 및 런타임 구성을 사용하여 스토리지 마운트 지점을 생성할 수 있다.
server/plugins/storage.ts
// server/plugins/storage.ts
import redisDriver from 'unstorage/drivers/redis'
export default defineNitroPlugin(() => {
const storage = useStorage()
// Dynamically pass in credentials from runtime configuration, or other sources
const driver = redisDriver({
base: 'redis',
host: useRuntimeConfig().redis.host,
port: useRuntimeConfig().redis.port,
/* other redis connector options */
})
// Mount driver
storage.mount('redis', driver)
})
nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
redis: { // Default values
host: '',
port: 0,
/* other redis connector options */
}
}
})
'Nuxt 공식문서 번역 > Directories' 카테고리의 다른 글
.env (0) | 2023.12.18 |
---|---|
utils (1) | 2023.12.18 |
public (0) | 2023.12.17 |
plugins (1) | 2023.12.17 |
pages (1) | 2023.12.17 |