업그레이드 가이드
최신 Nuxt 버전으로 업그레이드하는 방법을 배웁니다.
Nuxt 업그레이드
최신 릴리스
Nuxt를 최신 릴리스로 업그레이드하려면 nuxt upgrade
명령어를 사용하세요.
npx nuxt upgrade
Nightly 릴리스 채널
최신 Nuxt 빌드를 사용하고 릴리스 전에 기능을 테스트하려면 nightly 릴리스 채널 가이드를 읽어보세요.
nightly 릴리스 채널 latest
태그는 현재 Nuxt v4 브랜치를 추적하고 있으며, 이는 현재 특히 파괴적인 변경 사항이 있을 가능성이 높다는 것을 의미합니다 — 주의하세요! Nuxt v3.x 브랜치의 nightly 릴리스를 사용하려면 "nuxt": "npm:nuxt-nightly@3x"
를 선택할 수 있습니다.
Nuxt 4 테스트
Nuxt 4는 2025년 2분기에 릴리스될 예정입니다. 이는 현재 compatibilityVersion: 4
를 통해 사용할 수 있는 모든 기능을 포함할 것입니다.
릴리스 전까지는 Nuxt 버전 3.12+에서 Nuxt 4의 많은 파괴적인 변경 사항을 테스트할 수 있습니다.
Nuxt 4에 참여하기
먼저, Nuxt를 최신 릴리스로 업그레이드하세요.
그런 다음 compatibilityVersion
을 Nuxt 4 동작에 맞게 설정할 수 있습니다:
export default defineNuxtConfig({
future: {
compatibilityVersion: 4,
},
// 모든 Nuxt v3 동작을 다시 활성화하려면 다음 옵션을 설정하세요:
// srcDir: '.',
// dir: {
// app: 'app'
// },
// experimental: {
// scanPageMeta: 'after-resolve',
// sharedPrerenderData: false,
// compileTemplate: true,
// resetAsyncDataToUndefined: true,
// templateUtils: true,
// relativeWatchPaths: true,
// normalizeComponentNames: false,
// spaLoadingTemplateLocation: 'within',
// parseErrorData: false,
// pendingWhenIdle: true,
// alwaysRunFetchOnKeyChange: true,
// defaults: {
// useAsyncData: {
// deep: true
// }
// }
// },
// features: {
// inlineStyles: true
// },
// unhead: {
// renderSSRHeadOptions: {
// omitLineBreaks: false
// }
// }
})
현재로서는 Nuxt 4 동작에 참여하는 각 레이어에서 호환성 버전을 정의해야 합니다. Nuxt 4가 릴리스된 후에는 필요하지 않습니다.
compatibilityVersion
을 4
로 설정하면 Nuxt 구성 전반에 걸쳐 기본값이 Nuxt v4 동작에 참여하도록 변경되지만, 테스트 시 위의 주석 처리된 줄을 따라 Nuxt v3 동작을 세부적으로 다시 활성화할 수 있습니다. 그렇게 할 경우 문제를 제기하여 Nuxt 또는 생태계에서 해결할 수 있도록 해주세요.
파괴적이거나 중요한 변경 사항은 여기에서 후방/전방 호환성을 위한 마이그레이션 단계와 함께 기록될 것입니다.
이 섹션은 최종 릴리스까지 변경될 수 있으므로 compatibilityVersion: 4
를 사용하여 Nuxt 4를 테스트하는 경우 정기적으로 다시 확인하세요.
Codemods를 사용한 마이그레이션
업그레이드 프로세스를 용이하게 하기 위해 Codemod 팀과 협력하여 일부 오픈 소스 codemods로 많은 마이그레이션 단계를 자동화했습니다.
문제가 발생하면 npx codemod feedback
으로 Codemod 팀에 보고해 주세요 🙏
Nuxt 4 codemods의 전체 목록, 각 codemod의 자세한 정보, 소스 및 실행 방법에 대한 다양한 방법은 Codemod Registry를 방문하세요.
이 가이드에서 언급된 모든 codemods는 다음 codemod
레시피를 사용하여 실행할 수 있습니다:
npx codemod@latest nuxt/4/migration-recipe
이 명령은 실행하지 않으려는 codemod를 선택 해제할 수 있는 옵션과 함께 모든 codemod를 순차적으로 실행합니다. 각 codemod는 해당 변경 사항과 함께 아래에 나열되어 있으며 독립적으로 실행할 수 있습니다.
새로운 디렉토리 구조
🚦 영향 수준: 중요
Nuxt는 이제 새로운 디렉토리 구조를 기본으로 사용하며, 이전 구조와의 호환성을 제공합니다 (Nuxt가 최상위 pages/
디렉토리와 같은 이전 구조를 사용하고 있음을 감지하면 이 새로운 구조가 적용되지 않습니다).
변경 사항
- 새로운 Nuxt 기본
srcDir
은 기본적으로app/
이며 대부분의 항목이 여기에서 해결됩니다. serverDir
은 이제<srcDir>/server
가 아닌<rootDir>/server
를 기본값으로 합니다.layers/
,modules/
및public/
은 기본적으로<rootDir>
을 기준으로 해결됩니다.- Nuxt Content v2.13+를 사용하는 경우
content/
는<rootDir>
을 기준으로 해결됩니다. - 새로운
dir.app
이 추가되었으며, 이는router.options.ts
및spa-loading-template.html
을 찾는 디렉토리입니다 - 기본값은<srcDir>/
입니다.
v4 폴더 구조 예시.
.output/
.nuxt/
app/
assets/
components/
composables/
layouts/
middleware/
pages/
plugins/
utils/
app.config.ts
app.vue
router.options.ts
content/
layers/
modules/
node_modules/
public/
server/
api/
middleware/
plugins/
routes/
utils/
nuxt.config.ts
👉 이 변경 사항을 구현한 PR에 대한 자세한 내용을 참조하세요.
변경 이유
- 성능 - 리포지토리의 루트에 모든 코드를 배치하면
.git/
및node_modules/
폴더가 FS 감시자에 의해 스캔/포함되어 비 Mac OS에서 시작이 상당히 지연될 수 있습니다. - IDE 타입 안전성 -
server/
와 나머지 앱은 서로 다른 전역 가져오기가 가능한 완전히 다른 컨텍스트에서 실행되며,server/
가 앱의 나머지 부분과 동일한 폴더 내에 있지 않도록 하는 것이 IDE에서 좋은 자동 완성을 보장하는 첫 번째 단계입니다.
마이그레이션 단계
app/
이라는 새 디렉토리를 만듭니다.assets/
,components/
,composables/
,layouts/
,middleware/
,pages/
,plugins/
및utils/
폴더를 그 아래로 이동하고,app.vue
,error.vue
,app.config.ts
도 이동합니다.app/router-options.ts
또는app/spa-loading-template.html
이 있는 경우 이러한 경로는 동일하게 유지됩니다.nuxt.config.ts
,content/
,layers/
,modules/
,public/
및server/
폴더가app/
폴더 외부, 프로젝트의 루트에 남아 있는지 확인합니다.tailwindcss
또는eslint
구성과 같은 타사 구성 파일을 새로운 디렉토리 구조에 맞게 업데이트해야 합니다 (필요한 경우 -@nuxtjs/tailwindcss
는tailwindcss
를 자동으로 올바르게 구성해야 합니다).
이 마이그레이션은 npx codemod@latest nuxt/4/file-structure
를 실행하여 자동화할 수 있습니다.
그러나 마이그레이션은 필수는 아닙니다. 현재 폴더 구조를 유지하려면 Nuxt가 자동으로 감지해야 합니다. (그렇지 않은 경우 문제를 제기하세요.) 한 가지 예외는 이미 사용자 정의 srcDir
이 있는 경우입니다. 이 경우 modules/
, public/
및 server/
폴더가 사용자 정의 srcDir
이 아닌 rootDir
에서 해결된다는 점을 알아야 합니다. 필요에 따라 dir.modules
, dir.public
및 serverDir
을 구성하여 이를 재정의할 수 있습니다.
다음 구성을 사용하여 v3 폴더 구조를 강제할 수도 있습니다:
export default defineNuxtConfig({
// 새로운 srcDir 기본값을 루트 디렉토리로 되돌립니다
srcDir: '.',
// `app/router.options.ts` 및 `app/spa-loading-template.html`의 디렉토리 접두사를 지정합니다
dir: {
app: 'app'
}
})
싱글톤 데이터 페칭 레이어
🚦 영향 수준: 중간
변경 사항
Nuxt의 데이터 페칭 시스템 (useAsyncData
및 useFetch
)이 성능과 일관성을 위해 크게 재구성되었습니다:
-
동일한 키에 대한 공유 refs: 동일한 키로
useAsyncData
또는useFetch
를 호출하면 이제 동일한data
,error
및status
refs를 공유합니다. 이는 명시적 키가 있는 모든 호출이 충돌하는deep
,transform
,pick
,getCachedData
또는default
옵션을 가지지 않아야 한다는 것을 의미합니다. -
getCachedData
에 대한 더 많은 제어:getCachedData
함수는 이제 데이터가 페칭될 때마다 호출되며, 이는 감시자에 의해 발생하거나refreshNuxtData
를 호출할 때도 마찬가지입니다. (이전에는 항상 새 데이터를 페칭했으며 이러한 경우 이 함수가 호출되지 않았습니다.) 캐시된 데이터를 사용할 때와 다시 페칭할 때를 더 잘 제어할 수 있도록 이 함수는 요청의 원인을 포함하는 컨텍스트 객체를 받습니다. -
반응형 키 지원: 이제 계산된 refs, 일반 refs 또는 getter 함수를 키로 사용할 수 있으며, 이는 자동 데이터 다시 페칭을 가능하게 합니다 (그리고 데이터를 별도로 저장합니다).
-
데이터 정리:
useAsyncData
로 페칭된 데이터를 사용하는 마지막 컴포넌트가 언마운트되면 Nuxt는 메모리 사용량이 계속 증가하는 것을 방지하기 위해 해당 데이터를 제거합니다.
변경 이유
이러한 변경은 메모리 사용량을 개선하고 useAsyncData
호출 간의 로딩 상태의 일관성을 높이기 위해 이루어졌습니다.
마이그레이션 단계
-
일관되지 않은 옵션 확인: 동일한 키를 사용하여 다른 옵션이나 페칭 함수를 사용하는 컴포넌트를 검토하세요.
// 이제 경고가 발생합니다 const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false }) const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })
명시적 키를 공유하는
useAsyncData
호출 (및 사용자 정의 옵션이 있는 경우)을 별도의 composable로 추출하는 것이 유익할 수 있습니다:composables/useUserData.tsexport function useUserData(userId: string) { return useAsyncData( `user-${userId}`, () => fetchUser(userId), { deep: true, transform: (user) => ({ ...user, lastAccessed: new Date() }) } ) }
-
getCachedData
구현 업데이트:useAsyncData('key', fetchFunction, { - getCachedData: (key, nuxtApp) => { - return cachedData[key] - } + getCachedData: (key, nuxtApp, ctx) => { + // ctx.cause - 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch'일 수 있습니다 + + // 예: 수동 새로 고침 시 캐시 사용 안 함 + if (ctx.cause === 'refresh:manual') return undefined + + return cachedData[key] + } })
대안으로, 현재로서는 다음과 같이 이 동작을 비활성화할 수 있습니다:
export default defineNuxtConfig({
experimental: {
granularCachedData: false,
purgeCachedData: false
}
})
라우트 메타데이터의 중복 제거
🚦 영향 수준: 최소
변경 사항
definePageMeta
를 사용하여 name
, path
등과 같은 일부 라우트 메타데이터를 설정할 수 있습니다. 이전에는 이것들이 라우트와 라우트 메타데이터 모두에서 사용할 수 있었습니다 (예: route.name
및 route.meta.name
).
이제, 그것들은 라우트 객체에서만 접근할 수 있습니다.
변경 이유
이는 experimental.scanPageMeta
를 기본값으로 활성화한 결과이며, 성능 최적화입니다.
마이그레이션 단계
마이그레이션은 간단해야 합니다:
const route = useRoute()
- console.log(route.meta.name)
+ console.log(route.name)
정규화된 컴포넌트 이름
🚦 영향 수준: 중간
Vue는 이제 Nuxt의 컴포넌트 명명 패턴과 일치하는 컴포넌트 이름을 생성합니다.
변경 사항
기본적으로 수동으로 설정하지 않은 경우, Vue는 컴포넌트의 파일 이름과 일치하는 컴포넌트 이름을 할당합니다.
├─ components/
├─── SomeFolder/
├───── MyComponent.vue
이 경우, Vue가 인식하는 컴포넌트 이름은 MyComponent
가 됩니다. <KeepAlive>
와 함께 사용하거나 Vue DevTools에서 식별하려면 이 이름을 사용해야 합니다.
하지만 자동으로 가져오려면 SomeFolderMyComponent
를 사용해야 했습니다.
이 변경으로 인해 이 두 값이 일치하게 되며, Vue는 Nuxt의 컴포넌트 명명 패턴과 일치하는 컴포넌트 이름을 생성합니다.
마이그레이션 단계
@vue/test-utils
의 findComponent
를 사용하는 테스트와 컴포넌트 이름에 의존하는 <KeepAlive>
에서 업데이트된 이름을 사용하는지 확인하세요.
대안으로, 현재로서는 다음과 같이 이 동작을 비활성화할 수 있습니다:
export default defineNuxtConfig({
experimental: {
normalizeComponentNames: false
}
})
Unhead v2
🚦 영향 수준: 최소
변경 사항
<head>
태그 생성을 위해 사용되는 Unhead가 버전 2로 업데이트되었습니다. 대부분 호환되지만, 하위 수준 API에 몇 가지 파괴적인 변경 사항이 포함되어 있습니다.
- 제거된 props:
vmid
,hid
,children
,body
. - Promise 입력은 더 이상 지원되지 않습니다.
- 태그는 이제 기본적으로 Capo.js를 사용하여 정렬됩니다.
마이그레이션 단계
위의 변경 사항은 앱에 최소한의 영향을 미쳐야 합니다.
문제가 있는 경우 다음을 확인해야 합니다:
- 제거된 props를 사용하지 않는지 확인하세요.
useHead({
meta: [{
name: 'description',
// 메타 태그에는 vmid나 key가 필요하지 않습니다
- vmid: 'description'
- hid: 'description'
}]
})
import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'
export default defineNuxtPlugin({
setup() {
const unhead = injectHead()
unhead.use(TemplateParamsPlugin)
unhead.use(AliasSortingPlugin)
}
})
필수는 아니지만 @unhead/vue
에서 #imports
또는 nuxt/app
으로 가져오기를 업데이트하는 것이 좋습니다.
-import { useHead } from '@unhead/vue'
+import { useHead } from '#imports'
문제가 계속 발생하면 head.legacy
구성을 활성화하여 v1 동작으로 되돌릴 수 있습니다.
export default defineNuxtConfig({
unhead: {
legacy: true,
}
})
SPA 로딩 화면의 새로운 DOM 위치
🚦 영향 수준: 최소
변경 사항
클라이언트 전용 페이지를 렌더링할 때 (ssr: false
), Nuxt 앱 루트 내에서 로딩 화면을 선택적으로 렌더링합니다 (app/spa-loading-template.html
에서).
<div id="__nuxt">
<!-- spa 로딩 템플릿 -->
</div>
이제, Nuxt 앱 루트와 함께 템플릿을 렌더링하는 것이 기본값입니다:
<div id="__nuxt"></div>
<!-- spa 로딩 템플릿 -->
변경 이유
이렇게 하면 Vue 앱 서스펜스가 해결될 때까지 spa 로딩 템플릿이 DOM에 남아 있어 흰색 플래시를 방지할 수 있습니다.
마이그레이션 단계
CSS 또는 document.queryElement
로 spa 로딩 템플릿을 타겟팅한 경우 선택기를 업데이트해야 합니다. 이를 위해 새로운 app.spaLoaderTag
및 app.spaLoaderAttrs
구성 옵션을 사용할 수 있습니다.
대안으로, 이전 동작으로 되돌릴 수 있습니다:
export default defineNuxtConfig({
experimental: {
spaLoadingTemplateLocation: 'within',
}
})
파싱된 error.data
🚦 영향 수준: 최소
data
속성이 있는 오류를 던질 수 있었지만, 이는 파싱되지 않았습니다. 이제, 이는 파싱되어 error
객체에서 사용할 수 있습니다. 이는 수정이지만, 이전 동작에 의존하고 수동으로 파싱한 경우 기술적으로 파괴적인 변경입니다.
마이그레이션 단계
추가적인 error.data
파싱을 제거하도록 사용자 정의 error.vue
를 업데이트하세요:
<script setup lang="ts">
import type { NuxtError } from '#app'
const props = defineProps({
error: Object as () => NuxtError
})
- const data = JSON.parse(error.data)
+ const data = error.data
</script>
대안으로, 이 변경을 비활성화할 수 있습니다:
export default defineNuxtConfig({
experimental: {
parseErrorData: false
},
})
더 세분화된 인라인 스타일
🚦 영향 수준: 중간
Nuxt는 이제 글로벌 CSS가 아닌 Vue 컴포넌트에 대해서만 스타일을 인라인합니다.
변경 사항
이전에는 Nuxt가 모든 CSS를 인라인하고, 글로벌 스타일을 포함하여 별도의 CSS 파일에 대한 <link>
요소를 제거했습니다. 이제 Nuxt는 Vue 컴포넌트에 대해서만 이를 수행합니다 (이전에는 별도의 CSS 청크를 생성했습니다). 이는 개별 .css
파일에 대한 별도의 네트워크 요청을 줄이는 것과 마찬가지로 초기 로드 시 페이지 또는 컴포넌트별로 별도의 요청이 없도록 하며, 단일 글로벌 CSS 파일의 캐싱을 허용하고 초기 요청의 문서 다운로드 크기를 줄이는 더 나은 균형이라고 생각합니다.
마이그레이션 단계
이 기능은 완전히 구성 가능하며, inlineStyles: true
를 설정하여 글로벌 CSS뿐만 아니라 컴포넌트별 CSS도 인라인하여 이전 동작으로 되돌릴 수 있습니다.
export default defineNuxtConfig({
features: {
inlineStyles: true
}
})
해상 후 페이지 메타 스캔
🚦 영향 수준: 최소
변경 사항
이제 pages:extend
훅을 호출한 후 페이지 메타데이터 (definePageMeta
에 정의됨)를 스캔합니다.
변경 이유
이는 사용자가 pages:extend
에서 추가하려는 페이지에 대한 메타데이터를 스캔할 수 있도록 하기 위함입니다. 여전히 새로운 pages:resolved
훅에서 페이지 메타데이터를 변경하거나 재정의할 기회를 제공합니다.
마이그레이션 단계
페이지 메타데이터를 재정의하려면 pages:extend
가 아닌 pages:resolved
에서 수행하세요.
export default defineNuxtConfig({
hooks: {
- 'pages:extend'(pages) {
+ 'pages:resolved'(pages) {
const myPage = pages.find(page => page.path === '/')
myPage.meta ||= {}
myPage.meta.layout = 'overridden-layout'
}
}
})
대안으로, 이전 동작으로 되돌릴 수 있습니다:
export default defineNuxtConfig({
experimental: {
scanPageMeta: true
}
})
공유 프리렌더 데이터
🚦 영향 수준: 중간
변경 사항
useAsyncData
및 useFetch
호출에서 데이터를 다른 페이지 간에 공유하는 이전의 실험적 기능을 활성화했습니다. 원래 PR을 참조하세요.
변경 이유
이 기능은 프리렌더된 페이지 간에 페이로드 _데이터_를 자동으로 공유합니다. 이는 useAsyncData
또는 useFetch
를 사용하고 다른 페이지에서 동일한 데이터를 페칭하는 사이트를 프리렌더링할 때 성능을 크게 향상시킬 수 있습니다.
예를 들어, 사이트가 모든 페이지에 대해 useFetch
호출이 필요한 경우 (예: 메뉴에 대한 탐색 데이터 또는 CMS에서 사이트 설정을 가져오기 위해), 이 데이터는 이를 사용하는 첫 번째 페이지를 프리렌더링할 때 한 번만 페칭되고, 다른 페이지를 프리렌더링할 때 캐시되어 사용됩니다.
마이그레이션 단계
데이터의 고유 키가 항상 동일한 데이터로 해석될 수 있는지 확인하세요. 예를 들어, 특정 페이지와 관련된 데이터를 가져오기 위해 useAsyncData
를 사용하는 경우, 해당 데이터를 고유하게 일치시키는 키를 제공해야 합니다. (useFetch
는 이를 자동으로 수행해야 합니다.)
// 이 경우, 동적 페이지 (예: `[slug].vue`)에서 안전하지 않습니다. 왜냐하면 라우트 슬러그가 페칭된 데이터에 영향을 미치지만, Nuxt는 키에 반영되지 않기 때문에 이를 알 수 없습니다.
const route = useRoute()
const { data } = await useAsyncData(async () => {
return await $fetch(`/api/my-page/${route.params.slug}`)
})
// 대신, 페칭된 데이터를 고유하게 식별하는 키를 사용해야 합니다.
const { data } = await useAsyncData(route.params.slug, async () => {
return await $fetch(`/api/my-page/${route.params.slug}`)
})
대안으로, 이 기능을 비활성화할 수 있습니다:
export default defineNuxtConfig({
experimental: {
sharedPrerenderData: false
}
})
useAsyncData
및 useFetch
의 기본 data
및 error
값
🚦 영향 수준: 최소
변경 사항
useAsyncData
에서 반환된 data
및 error
객체는 이제 기본적으로 undefined
입니다.
변경 이유
이전에는 data
가 null
로 초기화되었지만 clearNuxtData
에서 undefined
로 재설정되었습니다. error
는 null
로 초기화되었습니다. 이 변경은 더 큰 일관성을 가져오기 위함입니다.
마이그레이션 단계
data.value
또는 error.value
가 null
인지 확인하는 경우, 이러한 확인을 undefined
로 업데이트할 수 있습니다.
이 단계를 자동화하려면 npx codemod@latest nuxt/4/default-data-error-value
를 실행하세요.
문제가 발생하면 다음과 같이 이전 동작으로 되돌릴 수 있습니다:
export default defineNuxtConfig({
experimental: {
defaults: {
useAsyncData: {
value: 'null',
errorValue: 'null'
}
}
}
})
이렇게 하는 경우 문제를 보고해 주세요. 이는 구성 가능하게 유지할 계획이 없습니다.
useAsyncData
및 useFetch
에서 refresh
호출 시 dedupe
옵션의 boolean
값 제거
🚦 영향 수준: 최소
변경 사항
이전에는 refresh
에 dedupe: boolean
을 전달할 수 있었습니다. 이는 cancel
(true
) 및 defer
(false
)의 별칭이었습니다.
const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt!' }))
async function refreshData () {
await refresh({ dedupe: true })
}
변경 이유
이 별칭은 더 큰 명확성을 위해 제거되었습니다.
문제는 useAsyncData
에 대한 옵션으로 dedupe
를 추가할 때 발생했으며, 우리는 boolean 값을 제거했습니다. 왜냐하면 그것들이 _반대_가 되었기 때문입니다.
refresh({ dedupe: false })
는 기존 요청을 취소하지 않고 새 요청을 선호하지 않습니다. 그러나 useAsyncData
의 옵션 내에서 dedupe: true
를 전달하면 기존 대기 중인 요청이 있는 경우 새 요청을 만들지 않습니다. (참조 PR.)
마이그레이션 단계
마이그레이션은 간단해야 합니다:
const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' }))
async function refreshData () {
- await refresh({ dedupe: true })
+ await refresh({ dedupe: 'cancel' })
- await refresh({ dedupe: false })
+ await refresh({ dedupe: 'defer' })
}
이 단계를 자동화하려면 npx codemod@latest nuxt/4/deprecated-dedupe-value
를 실행하세요.
useAsyncData
및 useFetch
에서 data
지울 때 기본값 존중
🚦 영향 수준: 최소
변경 사항
useAsyncData
에 대한 사용자 정의 default
값을 제공하는 경우, 이제 clear
또는 clearNuxtData
를 호출할 때 기본값으로 재설정됩니다.
변경 이유
종종 사용자는 빈 배열과 같은 적절한 빈 값을 설정하여 이를 반복할 때 null
/undefined
를 확인할 필요가 없도록 합니다. 이는 데이터를 재설정/지울 때 존중되어야 합니다.
마이그레이션 단계
문제가 발생하면, 현재로서는 다음과 같이 이전 동작으로 되돌릴 수 있습니다:
export default defineNuxtConfig({
experimental: {
resetAsyncDataToUndefined: true,
}
})
이렇게 하는 경우 문제를 보고해 주세요. 이는 구성 가능하게 유지할 계획이 없습니다.
useAsyncData
및 useFetch
에서 pending
값 정렬
🚦 영향 수준: 중간
useAsyncData
, useFetch
, useLazyAsyncData
및 useLazyFetch
에서 반환된 pending
객체는 이제 status
가 대기 중일 때만 true
인 계산된 속성입니다.
변경 사항
이제 immediate: false
가 전달되면, 첫 번째 요청이 이루어질 때까지 pending
은 false
입니다. 이는 첫 번째 요청이 이루어질 때까지 항상 true
였던 이전 동작에서의 변경입니다.
변경 이유
이는 pending
의 의미를 요청이 진행 중일 때 pending
인 status
속성과 일치시킵니다.
마이그레이션 단계
pending
속성에 의존하는 경우, pending
이 이제 상태가 대기 중일 때만 true
가 되는 새로운 동작을 고려하여 로직을 확인하세요.
<template>
- <div v-if="!pending">
+ <div v-if="status === 'success'">
<p>Data: {{ data }}</p>
</div>
<div v-else>
<p>Loading...</p>
</div>
</template>
<script setup lang="ts">
const { data, pending, execute, status } = await useAsyncData(() => fetch('/api/data'), {
immediate: false
})
onMounted(() => execute())
</script>
대안으로, 이전 동작으로 일시적으로 되돌릴 수 있습니다:
export default defineNuxtConfig({
experimental: {
pendingWhenIdle: true
}
})
useAsyncData
및 useFetch
의 키 변경 동작
🚦 영향 수준: 중간
변경 사항
useAsyncData
또는 useFetch
에서 반응형 키를 사용할 때, Nuxt는 키가 변경될 때 자동으로 데이터를 다시 페칭합니다. immediate: false
가 설정된 경우, useAsyncData
는 데이터가 한 번 페칭된 경우에만 키가 변경될 때 데이터를 페칭합니다.
이전에는 useFetch
가 약간 다른 동작을 했습니다. 키가 변경될 때마다 항상 데이터를 페칭했습니다.
이제 useFetch
와 useAsyncData
는 일관되게 동작합니다 - 데이터가 한 번 페칭된 경우에만 키가 변경될 때 데이터를 페칭합니다.
변경 이유
이는 useAsyncData
와 useFetch
간의 일관된 동작을 보장하고, 예기치 않은 페칭을 방지합니다. immediate: false
를 설정한 경우, 데이터를 페칭하려면 refresh
또는 execute
를 호출해야 합니다.
마이그레이션 단계
이 변경은 일반적으로 예상 동작을 개선해야 하지만, 비즉시 useFetch
의 키 또는 옵션을 변경할 것으로 예상한 경우, 이제 처음에는 수동으로 트리거해야 합니다.
const id = ref('123')
const { data, execute } = await useFetch('/api/test', {
query: { id },
immediate: false
)
+ watch(id, execute, { once: true })
이 동작을 선택 해제하려면:
// 또는 Nuxt 구성에서 전역적으로
export default defineNuxtConfig({
experimental: {
alwaysRunFetchOnKeyChange: true
}
})
useAsyncData
및 useFetch
의 얕은 데이터 반응성
🚦 영향 수준: 최소
useAsyncData
, useFetch
, useLazyAsyncData
및 useLazyFetch
에서 반환된 data
객체는 이제 ref
가 아닌 shallowRef
입니다.
변경 사항
새 데이터가 페칭될 때, data
에 의존하는 모든 것은 여전히 반응적입니다. 왜냐하면 전체 객체가 교체되기 때문입니다. 그러나 코드가 해당 데이터 구조 내의 속성을 변경하는 경우, 이는 앱에서 반응성을 트리거하지 않습니다.
변경 이유
이는 Vue가 모든 속성/배열의 수정을 감시할 필요가 없기 때문에 깊이 중첩된 객체 및 배열에 대해 상당한 성능 향상을 가져옵니다. 대부분의 경우, data
는 불변이어야 합니다.
마이그레이션 단계
대부분의 경우, 마이그레이션 단계가 필요하지 않지만, 데이터 객체의 반응성에 의존하는 경우 두 가지 옵션이 있습니다:
- 개별 composable 기반으로 깊은 반응성에 세부적으로 참여할 수 있습니다:
- const { data } = useFetch('/api/test') + const { data } = useFetch('/api/test', { deep: true })
- 프로젝트 전체에서 기본 동작을 변경할 수 있습니다 (권장하지 않음):
nuxt.config.ts
export default defineNuxtConfig({ experimental: { defaults: { useAsyncData: { deep: true } } } })
필요한 경우, npx codemod@latest nuxt/4/shallow-function-reactivity
를 실행하여 이 단계를 자동화할 수 있습니다.
builder:watch
의 절대 경로 감시
🚦 영향 수준: 최소
변경 사항
Nuxt builder:watch
훅은 이제 프로젝트 srcDir
에 상대적인 경로가 아닌 절대 경로를 방출합니다.
변경 이유
이는 srcDir
외부의 경로를 감시하는 것을 지원하고, 레이어 및 기타 더 복잡한 패턴에 대한 더 나은 지원을 제공합니다.
마이그레이션 단계
이 훅을 사용하는 공개 Nuxt 모듈을 사전에 마이그레이션했습니다. issue #25339를 참조하세요.
그러나 builder:watch
훅을 사용하는 모듈 작성자이고, 이전/이후 호환성을 유지하려는 경우, 다음 코드를 사용하여 Nuxt v3 및 Nuxt v4에서 동일하게 작동하도록 할 수 있습니다:
+ import { relative, resolve } from 'node:fs'
// ...
nuxt.hook('builder:watch', async (event, path) => {
+ path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path))
// ...
})
이 단계를 자동화하려면 npx codemod@latest nuxt/4/absolute-watch-path
를 실행하세요.
window.__NUXT__
객체 제거
변경 사항
앱이 수화(hydration)를 완료한 후 전역 window.__NUXT__
객체를 제거합니다.
변경 이유
이는 다중 앱 패턴 (#21635)으로의 길을 열고, Nuxt 앱 데이터를 액세스하는 단일 방법인 useNuxtApp()
에 집중할 수 있게 합니다.
마이그레이션 단계
데이터는 여전히 사용할 수 있지만, useNuxtApp().payload
로 액세스할 수 있습니다:
- console.log(window.__NUXT__)
+ console.log(useNuxtApp().payload)
디렉토리 인덱스 스캔
🚦 영향 수준: 중간
변경 사항
middleware/
폴더의 하위 폴더도 index
파일에 대해 스캔되며, 이제 프로젝트에서 미들웨어로 등록됩니다.
변경 이유
Nuxt는 middleware/
및 plugins/
을 포함한 여러 폴더를 자동으로 스캔합니다.
plugins/
폴더의 하위 폴더는 index
파일에 대해 스캔되며, 우리는 스캔된 디렉토리 간의 이 동작을 일관되게 만들고자 했습니다.
마이그레이션 단계
아마도 마이그레이션이 필요하지 않지만, 이전 동작으로 되돌리려면 이러한 미들웨어를 필터링하는 훅을 추가할 수 있습니다:
export default defineNuxtConfig({
hooks: {
'app:resolve'(app) {
app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path))
}
}
})
템플릿 컴파일 변경
🚦 영향 수준: 최소
변경 사항
이전에는 Nuxt가 .ejs
파일 형식/구문을 사용하여 파일 시스템에 있는 템플릿을 컴파일하기 위해 lodash/template
를 사용했습니다.
또한, 코드 생성에 사용될 수 있는 일부 템플릿 유틸리티 (serialize
, importName
, importSources
)를 제공했으며, 이제 제거됩니다.
변경 이유
Nuxt v3에서는 getContents()
함수가 있는 '가상' 구문으로 이동했으며, 이는 훨씬 더 유연하고 성능이 뛰어납니다.
또한, lodash/template
는 일련의 보안 문제를 겪었습니다. 이는 빌드 타임에 사용되며, 신뢰할 수 있는 코드에 의해 사용되기 때문에 Nuxt 프로젝트에는 실제로 적용되지 않습니다. 그러나 여전히 보안 감사에 나타납니다. 게다가, lodash
는 대부분의 프로젝트에서 사용되지 않는 무거운 종속성입니다.
마지막으로, Nuxt 내에서 코드 직렬화 기능을 제공하는 것은 이상적이지 않습니다. 대신, 프로젝트의 종속성이 될 수 있는 unjs/knitwork와 같은 프로젝트를 유지 관리하며, 보안 문제는 Nuxt 자체의 업그레이드 없이 직접 보고/해결할 수 있습니다.
마이그레이션 단계
EJS 구문을 사용하는 모듈을 업데이트하기 위해 PR을 제출했지만, 직접 수행해야 하는 경우, 세 가지 이전/이후 호환 가능한 대안이 있습니다:
- 문자열 보간 로직을
getContents()
로 직접 이동합니다. - https://github.com/nuxt-modules/color-mode/pull/240에서와 같이 대체를 처리하는 사용자 정의 함수를 사용합니다.
es-toolkit/compat
(lodash 템플릿의 드롭인 대체)를 Nuxt가 아닌 귀하의 프로젝트의 종속성으로 사용합니다:
+ import { readFileSync } from 'node:fs'
+ import { template } from 'es-toolkit/compat'
// ...
addTemplate({
fileName: 'appinsights-vue.js'
options: { /* some options */ },
- src: resolver.resolve('./runtime/plugin.ejs'),
+ getContents({ options }) {
+ const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8')
+ return template(contents)({ options })
+ },
})
마지막으로, 템플릿 유틸리티 (serialize
, importName
, importSources
)를 사용하는 경우, 이를 knitwork
의 유틸리티로 다음과 같이 대체할 수 있습니다:
import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork'
const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"(?=,?$)/gm, r => JSON.parse(r).replace(/^{(.*)}$/, '$1'))
const importSources = (sources: string | string[], { lazy = false } = {}) => {
return toArray(sources).map((src) => {
if (lazy) {
return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}`
}
return genImport(src, genSafeVariableName(src))
}).join('\n')
}
const importName = genSafeVariableName
이 단계를 자동화하려면 npx codemod@latest nuxt/4/template-compilation-changes
를 실행하세요.
실험적 기능 제거
🚦 영향 수준: 최소
변경 사항
Nuxt 4에서는 네 가지 실험적 기능이 더 이상 구성 가능하지 않습니다:
experimental.treeshakeClientOnly
는true
가 됩니다 (v3.0 이후 기본값)experimental.configSchema
는true
가 됩니다 (v3.3 이후 기본값)experimental.polyfillVueUseHead
는false
가 됩니다 (v3.4 이후 기본값)experimental.respectNoSSRHeader
는false
가 됩니다 (v3.4 이후 기본값)vite.devBundler
는 더 이상 구성 가능하지 않으며, 기본적으로vite-node
를 사용합니다
변경 이유
이 옵션들은 현재 값으로 설정된 지 오래되었으며, 구성 가능하게 유지할 필요가 없다고 믿고 있습니다.
마이그레이션 단계
-
polyfillVueUseHead
는 이 플러그인을 사용하여 사용자 영역에서 구현할 수 있습니다. -
respectNoSSRHeader
는 서버 미들웨어를 사용하여 사용자 영역에서 구현할 수 있습니다.
Nuxt 2 vs. Nuxt 3+
아래 표에는 Nuxt의 3가지 버전 간의 빠른 비교가 있습니다:
기능 / 버전 | Nuxt 2 | Nuxt Bridge | Nuxt 3+ |
---|---|---|---|
Vue | 2 | 2 | 3 |
안정성 | 😊 안정적 | 😊 안정적 | 😊 안정적 |
성능 | 🏎 빠름 | ✈️ 더 빠름 | 🚀 가장 빠름 |
Nitro 엔진 | ❌ | ✅ | ✅ |
ESM 지원 | 🌙 부분적 | 👍 더 나음 | ✅ |
TypeScript | ☑️ 선택적 | 🚧 부분적 | ✅ |
Composition API | ❌ | 🚧 부분적 | ✅ |
Options API | ✅ | ✅ | ✅ |
컴포넌트 자동 가져오기 | ✅ | ✅ | ✅ |
<script setup> 구문 | ❌ | 🚧 부분적 | ✅ |
자동 가져오기 | ❌ | ✅ | ✅ |
webpack | 4 | 4 | 5 |
Vite | ⚠️ 부분적 | 🚧 부분적 | ✅ |
Nuxt CLI | ❌ 오래됨 | ✅ nuxt | ✅ nuxt |
정적 사이트 | ✅ | ✅ | ✅ |
Nuxt 2에서 Nuxt 3+로
마이그레이션 가이드는 Nuxt 2 기능을 Nuxt 3+ 기능과 비교하고 현재 애플리케이션을 적응시키기 위한 지침을 제공합니다.
이것도 참고 migration > overviewNuxt 2에서 Nuxt Bridge로
Nuxt 2 애플리케이션을 Nuxt 3으로 점진적으로 마이그레이션하려는 경우, Nuxt Bridge를 사용할 수 있습니다. Nuxt Bridge는 Nuxt 2에서 Nuxt 3+ 기능을 선택적으로 사용할 수 있는 호환성 레이어입니다.
이것도 참고 bridge > overview※이 페이지는 Nuxt.js 공식 문서의 비공식 번역 페이지입니다.
공식 문서의 해당 페이지는 여기 있습니다:
https://nuxt.com/docs/3.x/getting-started/upgrade