본문으로 건너뛰기 Nuxt 3 Complete Guide | Full-Stack Vue.js Framework

Nuxt 3 Complete Guide | Full-Stack Vue.js Framework

Nuxt 3 Complete Guide | Full-Stack Vue.js Framework

이 글의 핵심

Nuxt 3 is the full-stack Vue.js framework — file-based routing, server routes, composables, and automatic SSR/SSG. This guide covers the Nuxt 3 ecosystem with practical examples for building production apps.

What This Guide Covers

Nuxt 3 brings full-stack capabilities to Vue — file-based routing, server API routes, universal data fetching, and automatic SSR/SSG. This guide covers the core features with a practical blog/app example.

Real-world insight: Migrating a Vue SPA to Nuxt 3 cut Time to First Byte by 70% and improved Core Web Vitals scores enough to boost organic traffic by 40% in two months.


Setup

npx nuxi@latest init my-app
cd my-app
npm install
npm run dev

App runs on http://localhost:3000.


Project Structure

my-app/
  pages/           ← file-based routing
  components/      ← auto-imported components
  composables/     ← auto-imported composables (useX)
  server/
    api/           ← server API routes
    middleware/    ← server middleware
  layouts/         ← page layouts
  middleware/      ← client/universal route middleware
  plugins/         ← Nuxt plugins
  public/          ← static assets
  nuxt.config.ts

1. File-Based Routing

Create files in pages/ — Nuxt generates routes automatically:

pages/
  index.vue          → /
  about.vue          → /about
  blog/
    index.vue        → /blog
    [slug].vue       → /blog/:slug
  users/
    [id]/
      index.vue      → /users/:id
      posts.vue      → /users/:id/posts
  [...404].vue       → catch-all / 404
<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute()
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`)
</script>

<template>
  <article>
    <h1>{{ post?.title }}</h1>
    <p>{{ post?.body }}</p>
  </article>
</template>

2. Server Routes (API)

Create files in server/api/ — Nuxt generates server endpoints:

// server/api/posts/index.get.ts
export default defineEventHandler(async (event) => {
  // Access DB, external API, etc.
  return [
    { id: 1, title: 'Nuxt 3 Guide', slug: 'nuxt-3-guide' },
    { id: 2, title: 'Vue Composables', slug: 'vue-composables' },
  ]
})

// server/api/posts/[slug].get.ts
export default defineEventHandler(async (event) => {
  const slug = getRouterParam(event, 'slug')
  // Fetch from DB...
  return { id: 1, title: 'Nuxt 3 Guide', slug, body: '...' }
})

// server/api/posts/index.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  // Validate and save...
  return { created: body }
})

File naming convention: [name].[method].ts — GET, POST, PUT, DELETE, PATCH.


3. Data Fetching

<script setup lang="ts">
// useFetch: SSR-aware, cached, auto-typed
const { data, pending, error, refresh } = await useFetch('/api/posts')

// $fetch: manual fetch (works client and server side)
const post = await $fetch('/api/posts/my-slug')

// useAsyncData: custom async logic
const { data: user } = await useAsyncData('user', () =>
  $fetch(`/api/users/${userId}`)
)

// lazy loading (don't block navigation)
const { data: comments, pending } = useLazyFetch('/api/comments')
</script>

<template>
  <div>
    <div v-if="pending">Loading...</div>
    <ul v-else>
      <li v-for="post in data" :key="post.id">{{ post.title }}</li>
    </ul>
  </div>
</template>

useFetch automatically shares data between server and client — no duplicate requests.


4. Composables

Composables in composables/ are auto-imported anywhere in your app:

// composables/useAuth.ts
export function useAuth() {
  const user = useState<User | null>('auth-user', () => null)
  const isLoggedIn = computed(() => user.value !== null)

  async function login(email: string, password: string) {
    const data = await $fetch('/api/auth/login', {
      method: 'POST',
      body: { email, password },
    })
    user.value = data.user
    await navigateTo('/dashboard')
  }

  async function logout() {
    await $fetch('/api/auth/logout', { method: 'POST' })
    user.value = null
    await navigateTo('/login')
  }

  return { user, isLoggedIn, login, logout }
}
<script setup lang="ts">
// No import needed — auto-imported
const { user, isLoggedIn, logout } = useAuth()
</script>

5. Layouts

<!-- layouts/default.vue -->
<template>
  <div>
    <AppHeader />
    <main>
      <slot />  <!-- page content goes here -->
    </main>
    <AppFooter />
  </div>
</template>

<!-- layouts/dashboard.vue -->
<template>
  <div class="dashboard">
    <Sidebar />
    <main><slot /></main>
  </div>
</template>
<!-- pages/dashboard/index.vue -->
<script setup lang="ts">
definePageMeta({ layout: 'dashboard' })
</script>

6. Middleware

// middleware/auth.ts (client + server)
export default defineNuxtRouteMiddleware((to, from) => {
  const { isLoggedIn } = useAuth()
  if (!isLoggedIn.value && to.path !== '/login') {
    return navigateTo('/login')
  }
})
<!-- pages/dashboard/index.vue -->
<script setup lang="ts">
definePageMeta({ middleware: 'auth' })
</script>

Server middleware (runs on every request):

// server/middleware/logger.ts
export default defineEventHandler((event) => {
  console.log(`[${new Date().toISOString()}] ${event.method} ${event.path}`)
})

7. State Management

Nuxt’s useState shares state between server and client:

// composables/useCounter.ts
export const useCounter = () => useState('counter', () => 0)
<script setup lang="ts">
const count = useCounter()
</script>

<template>
  <button @click="count++">Count: {{ count }}</button>
</template>

For complex state, use Pinia (Nuxt’s recommended store):

npx nuxi module add pinia
// stores/cart.ts
export const useCartStore = defineStore('cart', () => {
  const items = ref<CartItem[]>([])
  const total = computed(() => items.value.reduce((sum, i) => sum + i.price, 0))

  function addItem(item: CartItem) {
    items.value.push(item)
  }

  return { items, total, addItem }
})

8. SEO and Meta

<script setup lang="ts">
useSeoMeta({
  title: 'My Page Title',
  ogTitle: 'My Page Title',
  description: 'This is a description of my page.. Nuxt 3 Complete Guide에 대한 완전한 가이드입니다. 실전 예제와 함께 핵심 개념부터 고급 활용까지 다룹니다.',
  ogDescription: 'This is a description of my page.',
  ogImage: 'https://example.com/image.png',
  twitterCard: 'summary_large_image',
})

// Dynamic meta
const { data: post } = await useFetch(`/api/posts/${slug}`)
useSeoMeta({
  title: () => post.value?.title,
  description: () => post.value?.excerpt,
})
</script>

9. nuxt.config.ts

export default defineNuxtConfig({
  // Rendering mode
  ssr: true,  // SSR (default)

  // Nitro server config
  nitro: {
    preset: 'cloudflare-pages',  // or 'vercel', 'netlify', etc.
  },

  // Runtime config (env vars)
  runtimeConfig: {
    // Server-only (secret)
    databaseUrl: process.env.DATABASE_URL,
    // Public (exposed to client)
    public: {
      apiBase: process.env.API_BASE_URL || '/api',
    },
  },

  // Modules
  modules: ['@pinia/nuxt', '@nuxtjs/tailwindcss', '@nuxt/image'],

  // TypeScript
  typescript: { strict: true },
})

Access runtime config:

const config = useRuntimeConfig()
console.log(config.public.apiBase)     // client or server
console.log(config.databaseUrl)        // server only

10. Deployment

# Build for production
npm run build

# Deploy to Node.js server
node .output/server/index.mjs

# Deploy to Cloudflare Pages
NITRO_PRESET=cloudflare-pages npm run build
# Upload .output/public to Cloudflare Pages

# Static generation (SSG)
npm run generate
# Upload .output/public anywhere (S3, GitHub Pages, etc.)

Key Takeaways

FeatureHow
RoutingFile-based in pages/
Server APIFiles in server/api/
Data fetchinguseFetch, useAsyncData, $fetch
StateuseState (built-in), Pinia (recommended)
Auto-importscomponents/, composables/ — no imports needed
SEOuseSeoMeta()
DeploymentAny platform via Nitro presets

Nuxt 3’s key strength is reducing boilerplate — auto-imports, file-based routing, and built-in SSR mean you focus on features, not configuration. If you know Vue 3, Nuxt 3 is a natural upgrade for any project that needs SEO or a backend.


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Build full-stack apps with Nuxt 3. Covers file-based routing, server routes, composables, Nitro server, data fetching, a…

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers Nuxt, Vue, JavaScript, SSR, Full-Stack, TypeScript.