플러그인
플러그인 (Plugins)
Vue 앱이 만들어지는 시점에 Vue 플러그인이나 전역 헬퍼, 커스텀 디렉티브 같은 것을 연결하고 싶을 때가 있어요. Nuxt는 이걸 위한 플러그인 시스템을 기본으로 제공해서, app/plugins/ 디렉터리에 파일을 두면 Vue 애플리케이션 생성 시점에 자동으로 로드해 줘요.
출처: 공식문서
본문
app/plugins/ 디렉터리의 모든 파일은 자동으로 등록되기 때문에 nuxt.config에 따로 추가할 필요가 없어요. 파일 이름에 .server 또는 .client 접미사를 붙이면 해당 플러그인을 서버 쪽 또는 클라이언트 쪽에서만 로드할 수 있어요.
등록되는 플러그인
디렉터리 최상위 파일(또는 하위 디렉터리의 index 파일)만 플러그인으로 자동 등록돼요.
-| plugins/
---| foo.ts // 스캔됨
---| bar/
-----| baz.ts // 스캔 안 됨
-----| foz.vue // 스캔 안 됨
-----| index.ts // 현재 스캔되지만 deprecated
이 경우 foo.ts와 bar/index.ts만 등록돼요. 하위 디렉터리에 플러그인을 추가하려면 nuxt.config.ts의 app/plugins 옵션을 쓰면 돼요.
// nuxt.config.ts
export default defineNuxtConfig({
plugins: [
'~/plugins/bar/baz',
'~/plugins/bar/foz',
],
})
플러그인 만들기
플러그인에 전달되는 인자는 nuxtApp 하나뿐이에요.
// plugins/hello.ts
export default defineNuxtPlugin((nuxtApp) => {
// nuxtApp 으로 무언가 하기
})
객체 문법 플러그인
더 고급 사용을 위해 객체 문법으로도 정의할 수 있어요. 다만 객체 문법을 쓰면 속성이 정적으로 분석되어 더 최적화된 빌드를 만들어 주기 때문에, 런타임에서 정의하면 안 돼요. 예를 들어 enforce: import.meta.server ? 'pre' : 'post'처럼 쓰면 앞으로의 최적화를 망칠 수 있어요.
// plugins/hello.ts
export default defineNuxtPlugin({
name: 'my-plugin',
enforce: 'pre', // 또는 'post'
async setup (nuxtApp) {
// 일반 함수형 플러그인과 동일
},
hooks: {
// 여기서 Nuxt 앱 런타임 훅을 직접 등록할 수 있어요
'app:created' () {
const nuxtApp = useNuxtApp()
// 훅에서 무언가 하기
},
},
env: {
// 서버 전용 또는 island 컴포넌트를 렌더링할 때 플러그인이 돌지 않게 하려면 false 로 설정
islands: true,
},
})
등록 순서
파일 이름 앞에 'alphabetical' 번호를 붙이면 플러그인이 등록되는 순서를 제어할 수 있어요. 어떤 플러그인이 다른 플러그인에 의존하는 상황일 때 유용해요.
plugins/
| - 01.myPlugin.ts
| - 02.myOtherPlugin.ts
여기서 02.myOtherPlugin.ts는 01.myPlugin.ts가 주입한 모든 것에 접근할 수 있어요. 'alphabetical' 번호에 익숙하지 않다면, 파일 이름이 숫자가 아니라 문자열로 정렬된다는 점을 기억하세요. 예를 들어 10.myPlugin.ts는 2.myOtherPlugin.ts보다 먼저 옵니다. 그래서 예시처럼 한 자리 숫자 앞에 0을 붙이는 거예요.
로딩 전략
병렬 플러그인
기본적으로 Nuxt는 플러그인을 순차적으로 로드해요. 어떤 플러그인을 병렬로 정의하면 그 플러그인의 실행이 끝나기를 기다리지 않고 다음 플러그인을 로드해요.
// plugins/my-plugin.ts
export default defineNuxtPlugin({
name: 'my-plugin',
parallel: true,
async setup (nuxtApp) {
// 다음 플러그인이 즉시 실행됨
},
})
의존성이 있는 플러그인
플러그인이 다른 플러그인이 끝나기를 기다려야 한다면, 그 플러그인의 이름을 dependsOn 배열에 추가하면 돼요.
// plugins/depending-on-my-plugin.ts
export default defineNuxtPlugin({
name: 'depends-on-my-plugin',
dependsOn: ['my-plugin'],
async setup (nuxtApp) {
// 이 플러그인은 `my-plugin` 의 실행이 끝나기를 기다린 뒤 실행됨
},
})
컴포저블 사용하기
플러그인 안에서도 컴포저블과 유틸을 쓸 수 있어요. 다만 몇 가지 제약이 있어요.
- 나중에 등록되는 다른 플러그인에 의존하는 컴포저블은 동작하지 않을 수 있어요. 플러그인은 순서대로, 그리고 다른 것보다 먼저 호출되기 때문이에요
- Vue.js 라이프사이클에 의존하는 컴포저블은 동작하지 않아요. Vue 컴포저블은 보통 현재 컴포넌트 인스턴스에 묶이는데, 플러그인은
nuxtApp인스턴스에만 묶이거든요
// app/plugins/hello.ts
export default defineNuxtPlugin((nuxtApp) => {
const foo = useFoo()
})
헬퍼 제공하기
NuxtApp 인스턴스에 헬퍼를 제공하고 싶다면, 플러그인에서 provide 키 아래로 돌려주면 돼요.
// plugins/hello.ts
export default defineNuxtPlugin(() => {
return {
provide: {
hello: (msg: string) => `Hello ${msg}!`,
},
}
})
그리고 컴포넌트에서 이렇게 사용해요.
<!-- app/components/Hello.vue -->
<script setup lang="ts">
const { $hello } = useNuxtApp()
</script>
<template>
<div>
{{ $hello('world') }}
</div>
</template>
헬퍼 제공보다는 컴포저블을 쓰는 걸 강력히 권장해요. 전역 네임스페이스를 오염시키지 않고 메인 번들 엔트리를 작게 유지할 수 있거든요. 또 플러그인이 ref나 computed를 제공하면 컴포넌트 <template>에서 풀리지(unwrap) 않는다는 점도 알아두세요.
플러그인 타입
플러그인에서 헬퍼를 반환하면 자동으로 타입이 잡혀요. useNuxtApp()의 반환과 템플릿 안에서 타입을 확인할 수 있죠. 고급 케이스라면 주입된 속성의 타입을 이렇게 직접 선언할 수 있어요.
// index.d.ts
declare module '#app' {
interface NuxtApp {
$hello (msg: string): string
}
}
declare module 'vue' {
interface ComponentCustomProperties {
$hello (msg: string): string
}
}
export {}
Vue 플러그인
vue-gtag 같은 Vue 플러그인을 사용하려면 Nuxt 플러그인으로 연결하면 돼요. 먼저 의존성을 설치하고,
npm install --save-dev vue-gtag-next
플러그인 파일을 만들어요.
// app/plugins/vue-gtag.client.ts
import VueGtag, { trackRouter } from 'vue-gtag-next'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueGtag, {
property: {
id: 'GA_MEASUREMENT_ID',
},
})
trackRouter(useRouter())
})
Vue 디렉티브
비슷한 방식으로 커스텀 Vue 디렉티브도 플러그인에서 등록할 수 있어요.
// plugins/my-directive.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive('focus', {
mounted (el) {
el.focus()
},
getSSRProps (binding, vnode) {
// 여기서 SSR 전용 props 를 제공할 수 있어요
return {}
},
})
})
Vue 디렉티브를 등록했다면, 한쪽에서만 쓸 게 아니라면 서버·클라이언트 양쪽 모두에 등록해야 해요. 디렉티브가 클라이언트에서만 의미가 있다면 ~/plugins/my-directive.client.ts로 옮기고 서버용 ~/plugins/my-directive.server.ts에 '스텁' 디렉티브를 제공하면 돼요.
더 알아보기
nuxtApp과useNuxtApp()의 상세 내용은 API 문서를 참고하세요- Vue 커스텀 디렉티브에 대한 더 자세한 내용은 Vue 공식 문서의 Custom Directives를 참고하세요