업그레이드 가이드

최신 Nuxt 버전으로 업그레이드하는 방법을 알아보세요.

Nuxt 업그레이드

최신 릴리스

Nuxt를 최신 릴리스로 업그레이드하려면 nuxt upgrade 명령을 사용하세요.

npx nuxt upgrade

Nightly 릴리스 채널

최신 Nuxt 빌드를 사용하고 정식 릴리스 전에 기능을 테스트하려면, nightly 릴리스 채널 가이드를 읽어보세요.

Nuxt 5 테스트

Nuxt 5는 현재 개발 중입니다. 정식 릴리스 전까지는 Nuxt 4.2+ 버전에서 Nuxt 5의 많은 파괴적 변경 사항을 테스트할 수 있습니다.

Nuxt 5 옵트인

먼저 Nuxt를 최신 릴리스로 업그레이드하세요.

그런 다음 future.compatibilityVersion을 설정하여 Nuxt 5 동작과 일치시킬 수 있습니다:

nuxt.config.ts
export default defineNuxtConfig({
  future: {
    compatibilityVersion: 5,
  },
})

future.compatibilityVersion5로 설정하면, Nuxt 설정 전반의 기본값이 Nuxt v5 동작에 옵트인하도록 변경됩니다. 여기에는 다음이 포함됩니다:

  • Vite Environment API: 향상된 빌드 구성을 위해 새로운 Vite Environment API를 자동으로 활성화
  • 향후 제공되는 기타 Nuxt 5 개선 사항 및 변경 사항
이 섹션은 최종 릴리스 전까지 변경될 수 있으므로, future.compatibilityVersion: 5를 사용해 Nuxt 5를 테스트 중이라면 정기적으로 이곳을 다시 확인해 주세요.

하위/상위 호환성을 위한 마이그레이션 단계와 함께 파괴적이거나 중요한 변경 사항은 아래에 표시됩니다.

Vite Environment API로의 마이그레이션

🚦 영향 수준: 중간

변경 사항

Nuxt 5는 Vite 6의 새로운 Environment API로 마이그레이션합니다. 이 API는 환경의 개념을 공식화하고 환경별 구성에 대한 더 나은 제어를 제공합니다.

이전에는 Nuxt가 클라이언트와 서버에 대해 별도의 Vite 구성을 사용했습니다. 이제 Nuxt는 공유 Vite 구성을 사용하며, 특정 환경을 대상으로 하기 위해 applyToEnvironment() 메서드를 사용하는 환경별 플러그인을 사용합니다.

future.compatibilityVersion: 5를 설정하여 이 기능을 미리 테스트할 수 있습니다. ( Nuxt 5 테스트 참고) 또는 experimental.viteEnvironmentApi: true를 명시적으로 활성화해도 됩니다.

주요 변경 사항:

  1. 환경별 extendViteConfig() 사용 중단: extendViteConfig()serverclient 옵션은 사용 중단되었으며 사용 시 경고가 표시됩니다.
  2. 플러그인 등록 방식 변경: addVitePlugin()으로 등록되고 하나의 환경만을 대상으로 하는 Vite 플러그인(server: false 또는 client: false 전달)은 더 이상 config 또는 configResolved 훅이 호출되지 않습니다.
  3. 공유 구성: vite:extendConfigvite:configResolved 훅은 이제 클라이언트/서버 별도 구성 대신 공유 구성을 사용합니다.

변경 이유

Vite Environment API는 다음을 제공합니다:

  • 개발 및 프로덕션 빌드 간의 더 나은 일관성
  • 환경별 구성에 대한 더 세밀한 제어
  • 향상된 성능 및 플러그인 아키텍처
  • 클라이언트와 서버를 넘어선 사용자 정의 환경 지원

마이그레이션 단계

1. Vite 플러그인 사용으로 마이그레이션

extendViteConfig, vite:configResolved, vite:extendConfig 대신 Vite 플러그인을 사용하는 것을 권장합니다.

// Before
extendViteConfig((config) => {
  config.optimizeDeps.include.push('my-package')
}, { server: false })

nuxt.hook('vite:extendConfig' /* or vite:configResolved */, (config, { isClient }) => {
  if (isClient) {
    config.optimizeDeps.include.push('my-package')
  }
})

// After
addVitePlugin(() => ({
  name: 'my-plugin',
  config (config) {
    // 여기에서 전역 vite 구성을 설정할 수 있습니다
  },
  configResolved (config) {
    // 여기에서 완전히 해석된 vite 구성을 액세스할 수 있습니다
  },
  configEnvironment (name, config) {
    // 여기에서 환경별 vite 구성을 설정할 수 있습니다
    if (name === 'client') {
      config.optimizeDeps ||= {}
      config.optimizeDeps.include ||= []
      config.optimizeDeps.include.push('my-package')
    }
  },
  applyToEnvironment (environment) {
    return environment.name === 'client'
  },
}))
2. Vite 플러그인을 환경 사용 방식으로 마이그레이션

server: false 또는 client: false와 함께 addVitePlugin을 사용하는 대신, 플러그인 내에서 새로운 applyToEnvironment 훅을 사용할 수 있습니다.

// Before
addVitePlugin(() => ({
  name: 'my-plugin',
  config (config) {
    config.optimizeDeps.include.push('my-package')
  },
}), { client: false })

// After
addVitePlugin(() => ({
  name: 'my-plugin',
  config (config) {
    // 여기에서 전역 vite 구성을 설정할 수 있습니다
  },
  configResolved (config) {
    // 여기에서 완전히 해석된 vite 구성을 액세스할 수 있습니다
  },
  configEnvironment (name, config) {
    // 여기에서 환경별 vite 구성을 설정할 수 있습니다
    if (name === 'client') {
      config.optimizeDeps ||= {}
      config.optimizeDeps.include ||= []
      config.optimizeDeps.include.push('my-package')
    }
  },
  applyToEnvironment (environment) {
    return environment.name === 'client'
  },
}))
Vite의 Environment API에 대해 더 알아보기

Nuxt 4로 마이그레이션

Nuxt 4에는 상당한 개선 사항과 변경 사항이 포함되어 있습니다. 이 가이드는 기존 Nuxt 3 애플리케이션을 Nuxt 4로 마이그레이션하는 데 도움을 줍니다.

먼저 Nuxt 4로 업그레이드하세요:

npm install nuxt@^4.0.0

업그레이드 후 대부분의 Nuxt 4 동작은 이제 기본값입니다. 그러나 마이그레이션 중에 하위 호환성을 유지해야 하는 경우 일부 기능은 여전히 구성할 수 있습니다.

다음 섹션에서는 Nuxt 4로 업그레이드할 때 필요한 주요 변경 사항과 마이그레이션을 자세히 설명합니다.

파괴적이거나 중요한 변경 사항은 아래에 마이그레이션 단계 및 사용 가능한 구성 옵션과 함께 문서화되어 있습니다.

Codemod를 사용한 마이그레이션

업그레이드 프로세스를 용이하게 하기 위해, 우리는 Codemod 팀과 협력하여 여러 오픈 소스 codemod로 많은 마이그레이션 단계를 자동화했습니다.

문제가 발생하면 npx codemod feedback으로 Codemod 팀에 보고해 주세요 🙏

Nuxt 4 codemod의 전체 목록, 각 codemod에 대한 자세한 정보, 소스 및 다양한 실행 방법은 Codemod Registry를 방문하세요.

이 가이드에서 언급된 모든 codemod는 다음 codemod 레시피를 사용하여 실행할 수 있습니다:

# https://github.com/codemod/codemod/issues/1710 때문에 고정 버전 사용
npx codemod@0.18.7 nuxt/4/migration-recipe

이 명령은 실행을 원하지 않는 codemod를 선택 해제할 수 있는 옵션과 함께 모든 codemod를 순차적으로 실행합니다. 각 codemod는 아래에 해당 변경 사항과 함께 나열되어 있으며, 독립적으로 실행할 수도 있습니다.

새로운 디렉터리 구조

🚦 영향 수준: 상당함

Nuxt는 이제 새로운 디렉터리 구조를 기본값으로 사용하며, 하위 호환성을 제공합니다. (예: 최상위에 app/pages/ 디렉터리가 있는 기존 구조를 Nuxt가 감지하면 이 새로운 구조는 적용되지 않습니다.)

👉 전체 RFC 보기

변경 사항

  • 새로운 Nuxt 기본 srcDir는 기본적으로 app/이며, 대부분의 항목은 이곳에서 해석됩니다.
  • serverDir는 이제 <srcDir>/server 대신 <rootDir>/server를 기본값으로 사용합니다.
  • layers/, modules/, public/은 기본적으로 <rootDir> 기준으로 해석됩니다.
  • Nuxt Content v2.13+를 사용하는 경우, content/<rootDir> 기준으로 해석됩니다.
  • 새로운 dir.app이 추가되었으며, 이 디렉터리는 router.options.tsspa-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/
shared/
server/
  api/
  middleware/
  plugins/
  routes/
  utils/
nuxt.config.ts
이 새로운 구조에서 ~ 별칭은 기본적으로 app/ 디렉터리(즉, srcDir)를 가리킵니다. 즉, ~/componentsapp/components/, ~/pagesapp/pages/로 해석됩니다.

👉 자세한 내용은 이 변경을 구현한 PR을 참조하세요.

변경 이유

  1. 성능 - 리포지토리 루트에 모든 코드를 배치하면 .git/node_modules/ 폴더가 FS 워처에 의해 스캔/포함되어, 특히 Mac이 아닌 OS에서 시작 시간이 크게 지연될 수 있습니다.
  2. IDE 타입 안정성 - server/와 앱의 나머지 부분은 서로 완전히 다른 컨텍스트에서 실행되며, 사용 가능한 전역 import도 다릅니다. server/가 앱의 나머지 부분과 같은 폴더 _내부_에 있지 않도록 하는 것은 IDE에서 좋은 자동 완성을 보장하기 위한 중요한 첫 단계입니다.

마이그레이션 단계

  1. app/이라는 새 디렉터리를 생성합니다.
  2. assets/, components/, composables/, app/layouts/, app/middleware/, app/pages/, app/plugins/, utils/ 폴더와 app.vue, error.vue, app.config.ts를 이 디렉터리 아래로 이동합니다. app/router-options.ts 또는 app/spa-loading-template.html이 있는 경우, 이 경로는 그대로 유지됩니다.
  3. nuxt.config.ts, content/, layers/, modules/, public/, server/ 폴더는 app/ 폴더 밖, 프로젝트 루트에 그대로 두세요.
  4. tailwindcsseslint 설정과 같은 서드파티 설정 파일이 새로운 디렉터리 구조에서 올바르게 동작하는지 확인하세요. (필요한 경우에만 - @nuxtjs/tailwindcsstailwindcss를 자동으로 올바르게 구성해야 합니다.)
npx codemod@latest nuxt/4/file-structure를 실행하여 이 마이그레이션을 자동화할 수 있습니다.

그러나 마이그레이션은 필수는 아닙니다. 현재 폴더 구조를 유지하려면 Nuxt가 이를 자동으로 감지해야 합니다. (그렇지 않다면 이슈를 등록해 주세요.) 한 가지 예외는 이미 사용자 정의 srcDir를 사용 중인 경우입니다. 이 경우 modules/, public/, server/ 폴더는 사용자 정의 srcDir가 아니라 rootDir 기준으로 해석된다는 점을 알아야 합니다. 필요하다면 dir.modules, dir.public, serverDir를 구성하여 이를 재정의할 수 있습니다.

다음 설정을 사용하여 v3 폴더 구조를 강제로 사용할 수도 있습니다:

nuxt.config.ts
export default defineNuxtConfig({
  // 새로운 srcDir 기본값 `app`을 루트 디렉터리로 되돌립니다
  srcDir: '.',
  // `router.options.ts`와 `spa-loading-template.html`의 디렉터리 접두사를 지정합니다
  dir: {
    app: 'app',
  },
})

싱글톤 데이터 페칭 레이어

🚦 영향 수준: 중간

변경 사항

Nuxt의 데이터 페칭 시스템(useAsyncDatauseFetch)이 성능과 일관성을 위해 크게 재구성되었습니다:

  1. 동일 키에 대한 공유 ref: 동일한 키로 useAsyncData 또는 useFetch를 호출하면 이제 동일한 data, error, status ref를 공유합니다. 즉, 명시적 키를 사용하는 모든 호출은 deep, transform, pick, getCachedData, default 옵션이 서로 충돌하지 않아야 합니다.
  2. getCachedData에 대한 더 많은 제어: getCachedData 함수는 이제 데이터가 페칭될 때마다 호출됩니다. 이는 워처에 의해 또는 refreshNuxtData 호출로 인해 발생하는 경우도 포함됩니다. (이전에는 이러한 경우 항상 새 데이터를 페칭했으며 이 함수는 호출되지 않았습니다.) 캐시 데이터를 언제 사용할지, 언제 다시 페칭할지에 대한 더 많은 제어를 위해, 이 함수는 요청의 원인을 포함하는 컨텍스트 객체를 받습니다.
  3. 반응형 키 지원: 이제 키로 계산된 ref, 일반 ref 또는 getter 함수를 사용할 수 있으며, 이를 통해 자동 데이터 재페칭(및 데이터의 별도 저장)이 가능합니다.
  4. 데이터 정리: useAsyncData로 페칭된 데이터를 사용하는 마지막 컴포넌트가 언마운트되면, Nuxt는 메모리 사용량이 계속 증가하는 것을 방지하기 위해 해당 데이터를 제거합니다.

변경 이유

이러한 변경은 메모리 사용량을 개선하고 useAsyncData 호출 간의 로딩 상태 일관성을 높이기 위해 이루어졌습니다.

마이그레이션 단계

  1. 일관되지 않은 옵션 확인: 동일한 키를 다른 옵션 또는 페칭 함수와 함께 사용하는 컴포넌트를 검토하세요.
// 이제 경고가 발생합니다
const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false })
const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })

명시적 키를 공유하고(그리고 사용자 정의 옵션을 가진) useAsyncData 호출은 별도의 composable로 추출하는 것이 좋을 수 있습니다:

app/composables/useUserData.ts
export function useUserData (userId: string) {
  return useAsyncData(
    `user-${userId}`,
    () => fetchUser(userId),
    {
      deep: true,
      transform: user => ({ ...user, lastAccessed: new Date() }),
    },
  )
}
  1. 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]
+  }
})

또는, 당분간 다음 설정으로 이 동작을 비활성화할 수 있습니다:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    granularCachedData: false,
    purgeCachedData: false,
  },
})

레이어에서 모듈 로딩 순서 수정

🚦 영향 수준: 최소

변경 사항

Nuxt 레이어를 사용할 때 모듈이 로드되는 순서가 수정되었습니다. 이전에는 프로젝트 루트의 모듈이 확장 레이어의 모듈보다 먼저 로드되었는데, 이는 기대되는 동작과 반대였습니다.

이제 모듈은 올바른 순서로 로드됩니다:

  1. 레이어 모듈 먼저 (extend 순서 - 더 깊은 레이어가 먼저)
  2. 프로젝트 모듈 마지막 (우선순위 가장 높음)

이는 다음 모두에 영향을 미칩니다:

  • nuxt.config.tsmodules 배열에 정의된 모듈
  • modules/ 디렉터리에서 자동으로 발견된 모듈

변경 이유

이 변경은 다음을 보장합니다:

  • 확장 레이어는 이를 사용하는 프로젝트보다 낮은 우선순위를 가집니다.
  • 모듈 실행 순서가 직관적인 레이어 상속 패턴과 일치합니다.
  • 다중 레이어 설정에서 모듈 구성 및 훅이 예상대로 동작합니다.

마이그레이션 단계

대부분의 프로젝트는 변경이 필요하지 않습니다. 이 변경은 로딩 순서를 기대되는 동작과 일치하도록 수정합니다.

그러나 이전의 잘못된 순서에 의존하던 프로젝트라면 다음이 필요할 수 있습니다:

  1. 모듈 의존성 검토: 특정 로딩 순서에 의존하는 모듈이 있는지 확인합니다.
  2. 모듈 구성 조정: 잘못된 순서를 우회하기 위해 구성된 모듈이 있다면 수정합니다.
  3. 철저한 테스트: 수정된 순서에서 모든 기능이 예상대로 동작하는지 확인합니다.

새로운 올바른 순서의 예:

// 레이어: my-layer/nuxt.config.ts
export default defineNuxtConfig({
  modules: ['layer-module-1', 'layer-module-2'],
})

// 프로젝트: nuxt.config.ts
export default defineNuxtConfig({
  extends: ['./my-layer'],
  modules: ['project-module-1', 'project-module-2'],
})

// 로딩 순서 (수정됨):
// 1. layer-module-1
// 2. layer-module-2
// 3. project-module-1 (레이어 모듈을 오버라이드할 수 있음)
// 4. project-module-2 (레이어 모듈을 오버라이드할 수 있음)

훅을 등록해야 해서 모듈 순서 의존성에 문제가 발생한다면, 훅을 호출해야 하는 모듈에 modules:done 사용을 고려하세요. 이 훅은 다른 모든 모듈이 로드된 후 실행되므로 안전하게 사용할 수 있습니다.

👉 자세한 내용은 PR #31507이슈 #25719를 참조하세요.

라우트 메타데이터 중복 제거

🚦 영향 수준: 최소

변경 사항

definePageMeta를 사용하여 name, path 등 일부 라우트 메타데이터를 설정할 수 있습니다. 이전에는 이러한 값이 라우트와 라우트 메타데이터 모두에서 사용 가능했습니다. (예: route.nameroute.meta.name)

이제 이 값들은 라우트 객체에서만 접근할 수 있습니다.

변경 이유

이는 experimental.scanPageMeta를 기본값으로 활성화한 결과이며, 성능 최적화입니다.

마이그레이션 단계

마이그레이션은 간단해야 합니다:

  const route = useRoute()
  
- console.log(route.meta.name)
+ console.log(route.name)

컴포넌트 이름 정규화

🚦 영향 수준: 중간

이제 Vue는 컴포넌트 이름을 Nuxt의 컴포넌트 네이밍 패턴과 일치하도록 생성합니다.

변경 사항

기본적으로, 수동으로 설정하지 않았다면 Vue는 컴포넌트 파일 이름과 일치하는 컴포넌트 이름을 할당합니다.

Directory structure
├─ components/
├─── SomeFolder/
├───── MyComponent.vue

이 경우 Vue 관점에서 컴포넌트 이름은 MyComponent입니다. <KeepAlive>에서 사용하거나 Vue DevTools에서 식별하려면 이 이름을 사용해야 합니다.

하지만 자동 import를 사용하려면 SomeFolderMyComponent를 사용해야 했습니다.

이 변경으로 두 값이 일치하게 되며, Vue는 Nuxt의 컴포넌트 네이밍 패턴과 일치하는 컴포넌트 이름을 생성합니다.

마이그레이션 단계

@vue/test-utilsfindComponent를 사용하는 테스트와, 컴포넌트 이름에 의존하는 <KeepAlive>에서 업데이트된 이름을 사용하는지 확인하세요.

또는, 당분간 다음 설정으로 이 동작을 비활성화할 수 있습니다:

nuxt.config.ts
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', 
    // meta 태그에는 vmid나 key가 필요하지 않습니다    
-   vmid: 'description' 
-   hid: 'description'
  }]
})
import { AliasSortingPlugin, TemplateParamsPlugin } from '@unhead/vue/plugins'

export default defineNuxtPlugin({
  setup () {
    const unhead = injectHead()
    unhead.use(TemplateParamsPlugin)
    unhead.use(AliasSortingPlugin)
  },
})

필수는 아니지만, @unhead/vue에서의 import를 #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에서 가져옴 - Nuxt 4에서는 ~/spa-loading-template.html로 변경되었음을 참고)을 렌더링했습니다:

<div id="__nuxt">
  <!-- spa loading template -->
</div>

이제 기본적으로 Nuxt 앱 루트와 나란히 템플릿을 렌더링합니다:

<div id="__nuxt"></div>
<!-- spa loading template -->

변경 이유

이 변경으로 Vue 앱의 suspense가 resolve될 때까지 spa 로딩 템플릿이 DOM에 남아 있어, 흰 화면이 잠깐 보이는 현상을 방지할 수 있습니다.

마이그레이션 단계

CSS 또는 document.queryElement로 spa 로딩 템플릿을 선택하고 있었다면, 선택자를 업데이트해야 합니다. 이를 위해 새로운 app.spaLoaderTagapp.spaLoaderAttrs 구성 옵션을 사용할 수 있습니다.

또는, 다음 설정으로 이전 동작으로 되돌릴 수 있습니다:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    spaLoadingTemplateLocation: 'within',
  },
})

파싱된 error.data

🚦 영향 수준: 최소

data 속성이 있는 에러를 throw하는 것이 가능했지만, 이 값은 파싱되지 않았습니다. 이제 이 값이 파싱되어 error 객체에서 사용할 수 있습니다. 이는 버그 수정이지만, 이전 동작에 의존해 수동으로 파싱하고 있었다면 기술적으로는 파괴적 변경입니다.

마이그레이션 단계

사용자 정의 error.vue에서 error.data에 대한 추가 파싱을 제거하세요:

  <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>

더 세분화된 인라인 스타일

🚦 영향 수준: 중간

Nuxt는 이제 전역 CSS가 아닌 Vue 컴포넌트에 대해서만 스타일을 인라인합니다.

변경 사항

이전에는 Nuxt가 전역 스타일을 포함한 모든 CSS를 인라인하고, 별도의 CSS 파일에 대한 <link> 요소를 제거했습니다. 이제 Nuxt는 Vue 컴포넌트에 대해서만(이전에는 별도의 CSS 청크를 생성하던) 이 작업을 수행합니다. 이는 초기 로드 시 페이지별 또는 컴포넌트별 개별 .css 파일에 대한 별도 요청이 없다는 점에서 네트워크 요청을 줄이는 동시에, 단일 전역 CSS 파일의 캐싱을 허용하고 초기 요청의 문서 다운로드 크기를 줄이는 더 나은 균형이라고 생각합니다.

마이그레이션 단계

이 기능은 완전히 구성 가능하며, 전역 CSS와 컴포넌트별 CSS 모두를 인라인하려면 inlineStyles: true를 설정하여 이전 동작으로 되돌릴 수 있습니다.

nuxt.config.ts
export default defineNuxtConfig({
  features: {
    inlineStyles: true,
  },
})

페이지 메타 스캔 시점 변경

🚦 영향 수준: 최소

변경 사항

이제 definePageMeta에 정의된 페이지 메타데이터는 pages:extend 훅을 호출한 이후에 스캔됩니다.

변경 이유

이는 사용자가 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'
      }
    }
  })

또는, 다음 설정으로 이전 동작으로 되돌릴 수 있습니다:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    scanPageMeta: true,
  },
})

공유 프리렌더 데이터

🚦 영향 수준: 중간

변경 사항

useAsyncDatauseFetch 호출에서 데이터를 페이지 간에 공유하는, 이전의 실험적 기능을 활성화했습니다. 원래 PR을 참조하세요.

변경 이유

이 기능은 프리렌더링된 페이지 간에 payload _데이터_를 자동으로 공유합니다. 이는 useAsyncData 또는 useFetch를 사용하고 서로 다른 페이지에서 동일한 데이터를 페칭하는 사이트를 프리렌더링할 때 상당한 성능 향상을 가져올 수 있습니다.

예를 들어, 사이트의 모든 페이지에서 useFetch 호출이 필요하다면(예: 메뉴용 내비게이션 데이터 또는 CMS에서 사이트 설정을 가져오기 위해), 이 데이터는 이를 사용하는 첫 번째 페이지를 프리렌더링할 때 한 번만 페칭되고, 이후 다른 페이지를 프리렌더링할 때 캐시된 데이터가 사용됩니다.

마이그레이션 단계

데이터의 고유 키가 항상 동일한 데이터로 해석될 수 있도록 하세요. 예를 들어, 특정 페이지와 관련된 데이터를 페칭하기 위해 useAsyncData를 사용하는 경우, 해당 데이터를 고유하게 식별하는 키를 제공해야 합니다. (useFetch는 이를 자동으로 처리해야 합니다.)

app/pages/test/[slug].vue
// 이 코드는 동적 페이지(예: `[slug].vue`)에서 안전하지 않습니다.
// 라우트 slug가 페칭되는 데이터에 영향을 주지만, 키에 반영되지 않아 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}`)
})

또는, 다음 설정으로 이 기능을 비활성화할 수 있습니다:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    sharedPrerenderData: false,
  },
})

useAsyncDatauseFetch의 기본 dataerror

🚦 영향 수준: 최소

변경 사항

useAsyncData에서 반환되는 dataerror 객체는 이제 기본값이 undefined입니다.

변경 이유

이전에는 datanull로 초기화되었지만, clearNuxtData에서 undefined로 재설정되었습니다. errornull로 초기화되었습니다. 이번 변경은 더 큰 일관성을 위한 것입니다.

마이그레이션 단계

data.value 또는 error.valuenull인지 확인하고 있었다면, 이제 undefined를 확인하도록 업데이트할 수 있습니다.

npx codemod@latest nuxt/4/default-data-error-value를 실행하여 이 단계를 자동화할 수 있습니다.

useAsyncDatauseFetch에서 refresh 호출 시 dedupe 옵션의 boolean 값 제거

🚦 영향 수준: 최소

변경 사항

이전에는 refreshdedupe: boolean을 전달할 수 있었습니다. 이 값들은 각각 cancel(true)과 defer(false)의 별칭이었습니다.

app/app.vue
const { refresh } = await useAsyncData(() => Promise.resolve({ message: 'Hello, Nuxt!' }))

async function refreshData () {
  await refresh({ dedupe: true })
}

변경 이유

이 별칭들은 더 명확성을 위해 제거되었습니다.

useAsyncDatadedupe 옵션을 추가할 때 이 문제가 발생했으며, 두 값이 결국 반대 의미를 가지게 되어 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를 실행하여 이 단계를 자동화할 수 있습니다.

useAsyncDatauseFetch에서 data를 지울 때 기본값 존중

🚦 영향 수준: 최소

변경 사항

useAsyncData에 사용자 정의 default 값을 제공한 경우, 이제 clear 또는 clearNuxtData를 호출할 때 이 값이 사용되며, 단순히 해제되는 대신 기본값으로 재설정됩니다.

변경 이유

사용자는 종종 반복 시 null/undefined를 확인할 필요가 없도록 빈 배열과 같은 적절한 비어 있는 값을 설정합니다. 데이터 재설정/삭제 시 이러한 값이 존중되어야 합니다.

useAsyncDatauseFetch에서 pending 값 정렬

🚦 영향 수준: 중간

useAsyncData, useFetch, useLazyAsyncData, useLazyFetch에서 반환되는 pending 객체는 이제 status가 pending일 때만 true가 되는 계산 속성입니다.

변경 사항

이제 immediate: false가 전달되면, 첫 번째 요청이 이루어질 때까지 pendingfalse입니다. 이는 이전 동작(첫 번째 요청이 이루어질 때까지 pending이 항상 true였던 것)과 다릅니다.

변경 이유

이는 요청이 진행 중일 때 pendingpendingstatus 속성과 의미를 일치시키기 위한 것입니다.

마이그레이션 단계

pending 속성에 의존하고 있다면, 이제 pendingstatus가 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>

또는, 다음 설정으로 이전 동작으로 임시로 되돌릴 수 있습니다:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    pendingWhenIdle: true,
  },
})

useAsyncDatauseFetch에서 키 변경 동작

🚦 영향 수준: 중간

변경 사항

useAsyncData 또는 useFetch에서 반응형 키를 사용할 때, 키가 변경되면 Nuxt는 자동으로 데이터를 다시 페칭합니다. immediate: false가 설정된 경우, useAsyncData는 데이터가 한 번 이상 페칭된 경우에만 키 변경 시 데이터를 페칭합니다.

이전에는 useFetch가 약간 다른 동작을 했습니다. 키가 변경될 때마다 항상 데이터를 페칭했습니다.

이제 useFetchuseAsyncData는 일관되게 동작합니다. 즉, 데이터가 한 번 이상 페칭된 경우에만 키 변경 시 데이터를 페칭합니다.

변경 이유

이는 useAsyncDatauseFetch 간의 동작을 일관되게 하고, 예기치 않은 페칭을 방지하기 위함입니다. immediate: false를 설정한 경우, useFetch 또는 useAsyncData에서 데이터를 페칭하려면 refresh 또는 execute를 호출해야 합니다. 그렇지 않으면 데이터는 절대 페칭되지 않습니다.

마이그레이션 단계

이 변경은 일반적으로 기대되는 동작을 개선하지만, immediate가 아닌 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,
  },
})

useAsyncDatauseFetch에서 얕은 데이터 반응성

🚦 영향 수준: 최소

useAsyncData, useFetch, useLazyAsyncData, useLazyFetch에서 반환되는 data 객체는 이제 ref가 아닌 shallowRef입니다.

변경 사항

새 데이터가 페칭되면, 전체 객체가 교체되므로 data에 의존하는 모든 것은 여전히 반응형입니다. 하지만 코드에서 해당 데이터 구조 _내부_의 속성을 변경하는 경우, 이는 앱의 반응성을 트리거하지 않습니다.

변경 이유

이는 깊게 중첩된 객체와 배열에 대해 Vue가 모든 속성/배열의 변경을 감시할 필요가 없어지므로 상당한 성능 향상을 가져옵니다. 대부분의 경우 data는 불변이어야 하기도 합니다.

마이그레이션 단계

대부분의 경우 마이그레이션 단계가 필요 없지만, 데이터 객체의 반응성에 의존하는 경우 두 가지 선택지가 있습니다:

  1. composable 단위로 깊은 반응성을 선택적으로 활성화할 수 있습니다:
    - const { data } = useFetch('/api/test')
    + const { data } = useFetch('/api/test', { deep: true })
    
  2. 프로젝트 전체 기본 동작을 변경할 수 있습니다(권장하지 않음):
    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 모듈은 이미 사전에 마이그레이션했습니다. 이슈 #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)

디렉터리 인덱스 스캔

🚦 영향 수준: 중간

변경 사항

app/middleware/ 폴더의 하위 폴더도 index 파일을 스캔하며, 이제 이 파일들도 프로젝트의 미들웨어로 등록됩니다.

변경 이유

Nuxt는 app/middleware/app/plugins/를 포함한 여러 폴더를 자동으로 스캔합니다.

app/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와 같이 치환을 처리하는 사용자 정의 함수를 사용합니다.
  • Nuxt가 아닌 사용자 프로젝트의 의존성으로 es-toolkit/compat(lodash template의 드롭인 대체)를 사용합니다:
+ 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를 실행하여 이 단계를 자동화할 수 있습니다.

기본 TypeScript 설정 변경

🚦 영향 수준: 최소

변경 사항

compilerOptions.noUncheckedIndexedAccessfalse에서 true로 변경되었습니다.

변경 이유

이 변경은 대부분 TotalTypeScript의 권장 사항을 따르며, 이전 3.12 설정 업데이트의 후속 조치입니다.

마이그레이션 단계

두 가지 접근 방식이 있습니다:

  1. 앱에서 타입 체크를 실행하고 새로 발생한 오류를 수정합니다(권장).
  2. nuxt.config.ts에서 새로운 기본값을 재정의합니다:
export default defineNuxtConfig({
  typescript: {
    tsConfig: {
      compilerOptions: {
        noUncheckedIndexedAccess: false,
      },
    },
  },
})

TypeScript 설정 분리

🚦 영향 수준: 최소

변경 사항

Nuxt는 더 나은 타입 체크 경험을 제공하기 위해 서로 다른 컨텍스트에 대해 별도의 TypeScript 설정을 생성합니다:

  1. 새로운 TypeScript 설정 파일: Nuxt는 다음과 같은 추가 TypeScript 설정을 생성합니다:
    • .nuxt/tsconfig.app.json - 앱 코드용 (Vue 컴포넌트, composable 등)
    • .nuxt/tsconfig.server.json - 서버 사이드 코드용 (Nitro/server 디렉터리)
    • .nuxt/tsconfig.node.json - 빌드 타임 코드용 (모듈, nuxt.config.ts 등)
    • .nuxt/tsconfig.shared.json - 앱과 서버 컨텍스트 간에 공유되는 코드용 (타입 및 환경 비의존 유틸리티 등)
    • .nuxt/tsconfig.json - 하위 호환성을 위한 레거시 설정
  2. 하위 호환성: .nuxt/tsconfig.json을 확장하는 기존 프로젝트는 이전과 동일하게 동작합니다.
  3. 옵트인 프로젝트 참조: 새로운 프로젝트 또는 더 나은 타입 체크를 원하는 프로젝트는 TypeScript의 프로젝트 참조 기능을 채택할 수 있습니다.
  4. 컨텍스트별 타입 체크: 각 컨텍스트는 해당 환경에 맞는 컴파일러 옵션과 include/exclude를 갖게 됩니다.
  5. 새로운 typescript.nodeTsConfig 옵션: 이제 Node.js 빌드 타임 코드에 대한 TypeScript 설정을 사용자 정의할 수 있습니다.

변경 이유

이 변경은 다음과 같은 이점을 제공합니다:

  1. 더 나은 타입 안정성: 각 컨텍스트(앱, 서버, 빌드 타임)는 컨텍스트별 전역 및 API와 함께 적절한 타입 체크를 받습니다.
  2. 향상된 IDE 경험: 코드베이스의 서로 다른 부분에 대해 더 나은 IntelliSense 및 오류 보고를 제공합니다.
  3. 더 깔끔한 분리: 서버 코드는 클라이언트 사이드 API를 잘못 제안하지 않고, 그 반대도 마찬가지입니다.
  4. 성능: TypeScript는 적절히 범위가 지정된 설정으로 코드를 더 효율적으로 검사할 수 있습니다.

예를 들어, nuxt.config.ts에서는 auto-import가 사용 가능하지 않지만, 이전에는 TypeScript가 이를 표시하지 않았습니다. 또한 IDE는 server/ 디렉터리의 tsconfig.json으로 별도 컨텍스트를 인식했지만, 이는 타입 체크에 반영되지 않아 별도의 단계가 필요했습니다.

마이그레이션 단계

마이그레이션은 필요하지 않습니다. 기존 프로젝트는 이전과 동일하게 동작합니다.

그러나 향상된 타입 체크를 활용하려면, 새로운 프로젝트 참조 방식을 옵트인할 수 있습니다:

  1. 루트 tsconfig.json을 프로젝트 참조를 사용하도록 업데이트:
tsconfig.json"extends": "./.nuxt/tsconfig.json" 줄이 있다면, 참조를 추가하기 전에 제거하세요. 프로젝트 참조와 extends는 상호 배타적입니다.
{
  // 존재한다면 "extends": "./.nuxt/tsconfig.json"을 제거하세요
  "files": [],
  "references": [
    { "path": "./.nuxt/tsconfig.app.json" },
    { "path": "./.nuxt/tsconfig.server.json" },
    { "path": "./.nuxt/tsconfig.shared.json" },
    { "path": "./.nuxt/tsconfig.node.json" }
  ]
}
  1. .nuxt/tsconfig.server.json을 확장하던 server/tsconfig.json과 같은 수동 서버 tsconfig.json 파일을 제거합니다.
  2. 타입 체크 스크립트를 프로젝트 참조용 빌드 플래그를 사용하도록 업데이트:
- "typecheck": "nuxt prepare && vue-tsc --noEmit"
+ "typecheck": "nuxt prepare && vue-tsc -b --noEmit"
  1. 타입 보강 파일을 적절한 컨텍스트로 이동:
    • 앱 컨텍스트에 대한 타입을 보강하는 경우, 파일을 app/ 디렉터리로 이동합니다.
    • 서버 컨텍스트에 대한 타입을 보강하는 경우, 파일을 server/ 디렉터리로 이동합니다.
    • 앱과 서버 간에 공유되는 타입을 보강하는 경우, 파일을 shared/ 디렉터리로 이동합니다.
app/, server/, shared/ 디렉터리 밖에서 타입을 보강하는 것은 새로운 프로젝트 참조 설정에서 동작하지 않습니다.
  1. 필요하다면 TypeScript 옵션 구성:
export default defineNuxtConfig({
  typescript: {
    // tsconfig.app.json 사용자 정의
    tsConfig: {
      // ...
    },
    // tsconfig.shared.json 사용자 정의
    sharedTsConfig: {
      // ...
    },
    // tsconfig.node.json 사용자 정의
    nodeTsConfig: {
      // ...
    },
  },
  nitro: {
    typescript: {
      // tsconfig.server.json 사용자 정의
      tsConfig: {
        // ...
      },
    },
  },
})
  1. TypeScript 체크를 실행하는 CI/빌드 스크립트가 새로운 프로젝트 참조 방식을 사용하도록 업데이트합니다.

새로운 설정은 옵트인하는 프로젝트에 더 나은 타입 안정성과 IntelliSense를 제공하며, 기존 설정에 대한 완전한 하위 호환성을 유지합니다.

실험적 기능 제거

🚦 영향 수준: 최소

변경 사항

Nuxt 4에서는 네 가지 실험적 기능을 더 이상 구성할 수 없습니다:

  • experimental.treeshakeClientOnlytrue입니다 (v3.0부터 기본값)
  • experimental.configSchematrue입니다 (v3.3부터 기본값)
  • experimental.polyfillVueUseHeadfalse입니다 (v3.4부터 기본값)
  • experimental.respectNoSSRHeaderfalse입니다 (v3.4부터 기본값)
  • vite.devBundler는 더 이상 구성할 수 없으며, 기본적으로 vite-node를 사용합니다.

변경 이유

이 옵션들은 오랫동안 현재 값으로 설정되어 있었으며, 계속 구성 가능하게 유지해야 할 이유가 없습니다.

마이그레이션 단계

  • polyfillVueUseHead이 플러그인을 사용해 사용자 코드에서 구현할 수 있습니다.
  • respectNoSSRHeader서버 미들웨어를 사용해 사용자 코드에서 구현할 수 있습니다.

최상위 generate 설정 제거

🚦 영향 수준: 최소

변경 사항

Nuxt 4에서는 최상위 generate 설정 옵션을 더 이상 사용할 수 없습니다. 여기에는 다음 속성이 포함됩니다:

  • generate.exclude - 프리렌더링에서 라우트를 제외
  • generate.routes - 프리렌더링할 라우트 지정

변경 이유

최상위 generate 설정은 Nuxt 2에서 이어진 것입니다. 우리는 이미 한동안 nitro.prerender를 지원해 왔으며, 이는 Nuxt 3+에서 프리렌더링을 구성하는 선호되는 방법입니다.

마이그레이션 단계

generate 설정을 해당하는 nitro.prerender 옵션으로 교체하세요:

export default defineNuxtConfig({
- generate: {
-   exclude: ['/admin', '/private'],
-   routes: ['/sitemap.xml', '/robots.txt']
- }
+ nitro: {
+   prerender: {
+     ignore: ['/admin', '/private'],
+     routes: ['/sitemap.xml', '/robots.txt']
+   }
+ }
})
Nitro의 프리렌더 설정 옵션에 대해 더 읽어보세요.

Nuxt 2 vs. Nuxt 3+

아래 표는 세 가지 Nuxt 버전을 간단히 비교한 것입니다:

Feature / VersionNuxt 2Nuxt BridgeNuxt 3+
Vue223
Stability😊 Stable😊 Stable😊 Stable
Performance🏎 Fast✈️ Faster🚀 Fastest
Nitro Engine
ESM support🌙 Partial👍 Better
TypeScript☑️ Opt-in🚧 Partial
Composition API🚧 Partial
Options API
Components Auto Import
<script setup> syntax🚧 Partial
Auto Imports
webpack445
Vite⚠️ Partial🚧 Partial
Nuxt CLI❌ Old✅ nuxt✅ nuxt
Static sites

Nuxt 2에서 Nuxt 3+로

마이그레이션 가이드는 Nuxt 2 기능과 Nuxt 3+ 기능을 단계별로 비교하고, 현재 애플리케이션을 적응시키는 방법을 안내합니다.

Nuxt 2에서 Nuxt 3로 마이그레이션하는 가이드를 확인하세요.

Nuxt 2에서 Nuxt Bridge로

Nuxt 2 애플리케이션을 Nuxt 3로 점진적으로 마이그레이션하고 싶다면, Nuxt Bridge를 사용할 수 있습니다. Nuxt Bridge는 Nuxt 2에서 Nuxt 3+ 기능을 옵트인 방식으로 사용할 수 있게 해주는 호환성 레이어입니다.

Nuxt 2에서 Nuxt Bridge로 마이그레이션