제가 쓸 기능만 간단하게 정리해놨습니다.
나머지는 여기 공식 메뉴얼에서 확인해주시길..!
https://nuxt.com/docs/4.x/guide/directory-structure
app/components/
여기에 넣어둔 컴포넌트는 어디에서나 import가 가능합니다.
-| components/
---| base/
-----| foo/
-------| Button.vue
<BaseFooButton />
- 컴포넌트의 이름은 이런 식으로 결정
nuxt.config.ts
export default defineNuxtConfig({
components: [
{
path: '~/components',
pathPrefix: false,
},
],
});
nuxt.config.ts
파일을 이렇게 변경시~/components/Some/MyComponent.vue
컴포넌트는<SomeMyComponent>
가 아닌<MyComponent>
로 쓸 수 있습니다.
동적 컴포넌트, 임포트
<script setup lang="ts">
import { SomeComponent } from '#components'
const MyButton = resolveComponent('MyButton')
</script>
<template>
<component :is="clickable ? MyButton : 'div'" />
<component :is="SomeComponent" />
</template>
<component :is="someComputedComponent">
- 이런 식으로 사용하려면
resolveComponent
또는#components
에서 직접 import
<script setup lang="ts">
const show = ref(false)
</script>
<template>
<div>
<h1>Mountains</h1>
<LazyMountainsList v-if="show" />
<button v-if="!show" @click="show = true">Show List</button>
</div>
</template>
Lazy
접두사를 붙여서 동적으로 가져오는 방법도 있습니다.
app/composables/
app에 자동으로 Vue composable를 import 해줍니다.
app/composables/useFoo.ts
export const useFoo = () => {
return useState('foo', () => 'bar')
}
app/composables/use-foo.ts or composables/useFoo.ts
// It will be available as useFoo() (camelCase of file name without extension)
export default function () {
return useState('foo', () => 'bar')
}
app/app.vue
<script setup lang="ts">
const foo = useFoo()
</script>
<template>
<div>
{{ foo }}
</div>
</template>
파일 스캔 방식
-| composables/
---| index.ts // scanned
---| useFoo.ts // scanned
---| nested/
-----| utils.ts // not scanned
- nested 모듈은 자동으로 import 되진 않습니다.
nuxt.config.ts
export default defineNuxtConfig({
imports: {
dirs: [
// Scan top-level composables
'~/composables',
// ... or scan composables nested one level deep with a specific name and file extension
'~/composables/*/index.{ts,js,mjs,mts}',
// ... or scan all composables within given directory
'~/composables/**'
]
}
})
nuxt.config.ts
을 이렇게 변경하면 자동으로 스캔됩니다.
app/layouts/
app/app.vue
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
app.vue
에서<NuxtLayout>
를 사용
-| layouts/
---| default.vue
---| custom.vue
app/layouts/default.vue
<template>
<div>
<p>Some default layout content shared across all pages</p>
<slot />
</div>
</template>
default.vue
나 커스텀 레이아웃을 통해서 레이아웃 변경 가능합니다.- 페이지 내용이 표시될 곳에는
<slot />
컴포넌트를 사용합니다.
app/pages/about.vue
<script setup lang="ts">
definePageMeta({
layout: 'custom'
})
</script>
- custom 레이아웃을 쓸 때는
definePageMeta
를 사용합니다.
<script setup lang="ts">
// You might choose this based on an API call or logged-in status
const layout = "custom";
</script>
<template>
<NuxtLayout :name="layout">
<NuxtPage />
</NuxtLayout>
</template>
<NuxtLayout>
속성의name
property를 이용해서 사용하는 방법도 있습니다.
File | Layout Name |
---|---|
~/layouts/desktop/default.vue | desktop-default |
~/layouts/desktop-base/base.vue | desktop-base |
~/layouts/desktop/index.vue | desktop |
app/pages/
파일 기반 라우팅을 지원
-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue
pages/users-
[group]
/[id]
.vue
<template>
<p>{{ $route.params.group }} - {{ $route.params.id }}</p>
</template>
<!--
`/users-admins/123`는 아래와 같이 rendering
-->
<p>admins - 123</p>
$route
오브젝트를 통해서 접근 가능
<script setup lang="ts">
const route = useRoute()
if (route.params.group === 'admins' && !route.params.id) {
console.log('Warning! Make sure user is authenticated!')
}
</script>
- 또한
useRoute
함수를 이용해서 접근 가능
pages/
[...slug]
.vue
<template>
<p>{{ $route.params.slug }}</p>
</template>
- 파일명을
[...slug].vue
처럼 쓰면
<!-- Navigating to `/hello/world` would render: -->
<p>["hello", "world"]</p>
- 이렇게 렌더링 됩니다.
-| pages/
---| index.vue
---| (marketing)/
-----| about.vue
-----| contact.vue
(괄호)
로 묶어서 그룹화가 가능합니다.- 위 폴더 구조에서는
/
,/about
,/contact
이렇게 경로가 만들어집니다.- 괄호로 묶은 그룹명은 무시
Nested Routes
-| pages/
---| parent/
-----| child.vue
---| parent.vue
pages/parent.vue
<template>
<div>
<h1>I am the parent view</h1>
<NuxtPage :foobar="123" />
</div>
</template>
pages/parent/child.vue
<script setup lang="ts">
const props = defineProps(['foobar'])
console.log(props.foobar)
</script>
<NuxtPage>
컴포넌트를 이용해서 child.vue
컴포넌트를 삽입할 수 있습니다.
pageKey
prop를 이용해서 페이지 전환 시 어떻게 마운트될지를 제어 가능
app/plugins/
전역적으로 사용할 수 있는 기능을 확장할 때 사용합니다.
-| plugins/
---| foo.ts // scanned
---| bar/
-----| baz.ts // not scanned
-----| foz.vue // not scanned
-----| index.ts // currently scanned but deprecated
최상위, 또는 하위 디렉토리의 index.ts 파일은 자동으로 스캔됩니다.
nuxt.config.ts
export default defineNuxtConfig({
plugins: [
'~/plugins/bar/baz',
'~/plugins/bar/foz'
]
})
- 직접 등록하는 방법도 존재합니다.
plugins/hello.ts
export default defineNuxtPlugin((nuxtApp) => {
const foo = useFoo()
...
// Doing something with nuxtApp
})
defineNuxtPlugin
를 이용해서 만들 수 있습니다.- composables 사용도 가능합니다.
plugins/depending-on-my-plugin.ts
export default defineNuxtPlugin({
name: 'depends-on-my-plugin',
dependsOn: ['my-plugin'],
async setup (nuxtApp) {
// this plugin will wait for the end of `my-plugin`'s execution before it runs
}
})
- 의존성을 설정할 수도 있습니다.
Helpers 제공하기
plugins/hello.ts
export default defineNuxtPlugin(() => {
return {
provide: {
hello: (msg: string) => `Hello ${msg}!`
}
}
})
components/Hello.vue
<script setup lang="ts">
// alternatively, you can also use it here
const { $hello } = useNuxtApp()
</script>
<template>
<div>
{{ $hello('world') }}
</div>
</template>
app/utils/
자동으로 유틸리티 함수를 import 해줍니다.
utils/index.ts
export const { format: formatNumber } = Intl.NumberFormat('en-GB', {
notation: 'compact',
maximumFractionDigits: 1
})
utils/random-entry.ts or utils/randomEntry.ts
// It will be available as randomEntry()
// (camelCase of file name without extension)
export default function (arr: Array<any>) {
return arr[Math.floor(Math.random() * arr.length)]
}
- named export 또는 default export를 사용합니다.
app.vue
<template>
<p>{{ formatNumber(1234) }}</p>
</template>
- 그런 다음
.js
,.ts
,.vue
파일에서 유틸리티 함수를 사용하면 됩니다.
app/middleware/
- 페이지에 직접 정의되는 익명 라우트 미들웨어
middleware/
에 위치하는 네임드 라우트 미들웨어middleware/
에 위치하는 전역 라우트 미들웨어..global
접미사를 붙여서 사용할 수 있다.
middleware/my-middleware.ts
export default defineNuxtRouteMiddleware((to, from) => {
if (to.params.id === '1') {
return abortNavigation()
// Aborts the navigation, with an optional error message.
}
// In a real app you would probably not redirect every route to `/`
// however it is important to check `to.path` before redirecting or you
// might get an infinite redirect loop
if (to.path !== '/') {
return navigateTo('/')
// Redirects to the given route
}
})
가능한 리턴
return
return navigateTo('/')
return navigateTo('/', { redirectCode: 301 })
return abortNavigation()
return abortNavigation(error)
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
<script setup lang="ts">
definePageMeta({
middleware: [
function (to, from) {
// Custom inline middleware
},
'auth',
],
});
</script>
전역 미들웨어 이후에 페이지에 정의된 미들웨어 순으로 동작한다.
analytics.global.ts
setup.global.ts
- Custom inline middleware
auth.ts
미들웨어가 동작하는 타이밍
export default defineNuxtRouteMiddleware(to => {
// skip middleware on server
if (import.meta.server) return
// skip middleware on client side entirely
if (import.meta.client) return
// or only skip middleware on initial client load
const nuxtApp = useNuxtApp()
if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
})
동적으로 미들웨어 추가
export default defineNuxtPlugin(() => {
addRouteMiddleware('global-test', () => {
console.log('this global middleware was added in a plugin \
and will be run on every route change')
}, { global: true })
addRouteMiddleware('named-test', () => {
console.log('this named middleware was added in a plugin \
and would override any existing middleware of the same name')
})
})
addRouteMiddleware()
를 이용해서 동적으로 추가가 가능합니다.
server/
-| server/
---| api/
-----| hello.ts # /api/hello
---| routes/
-----| bonjour.ts # /bonjour
---| middleware/
-----| log.ts # log all requests
- routes 디렉토리에 넣으면
/api
접두사 없이 추가 가능
server/api/hello.ts
export default defineEventHandler((event) => {
return {
hello: 'world'
}
})
pages/index.vue
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>
<template>
<pre>{{ data }}</pre>
</template>
route parameters
server/api/hello/
[name].ts
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
return `Hello, ${name}!`
})
/api/hello/nuxt
로 요청하면Hello, nuxt!
을 받아온다.
HTTP Method
server/api/test.get.ts
export default defineEventHandler(() => 'Test get handler')
server/api/test.post.ts
export default defineEventHandler(() => 'Test post handler')
/test
로 접근하면
- GET 메소드:
Test get handler
반환 - POST 메소드:
Test post handler
반환 - 그 이외 메소드: 405 error 반환
경로 감지
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 Handling
server/api/submit.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
app.vue
<script setup lang="ts">
async function submit() {
const { body } = await $fetch('/api/submit', {
method: 'post',
body: { test: 123 }
})
}
</script>
Query Parameters
server/api/query.get.ts
// Sample query: /api/query?foo=bar&baz=qux
export default defineEventHandler((event) => {
const query = getQuery(event)
return { a: query.foo, b: query.baz }
})
Error Handling
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'
})
상태 코드
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})