← 가이드 목록으로 ← Back to guides

Vue.js SSR과 Nuxt.js, Pinia 완벽 가이드 Vue.js SSR, Nuxt.js & Pinia Complete Guide

싱글 페이지 애플리케이션(SPA)의 단점을 보완하기 위해 서버 사이드 렌더링(SSR)이 필요할 때 Nuxt.js는 최고의 선택입니다. 또한 복잡해지는 상태를 효율적으로 관리하기 위해 Pinia를 활용하는 방법을 완벽히 마스터해봅시다.

When you need Server-Side Rendering (SSR) to overcome the limitations of SPAs, Nuxt.js is the perfect choice. Let's completely master how to utilize Pinia to efficiently manage complex state.

Nuxt.js로 시작하는 SSR SSR with Nuxt.js

Nuxt 3는 Vue 3와 Nitro 엔진을 기반으로, 하이브리드 로직 렌더링(SSR/SSG 등)과 자동 임포트(auto-imports) 기능을 지원합니다.

Based on Vue 3 and the Nitro engine, Nuxt 3 supports hybrid logic rendering (SSR/SSG, etc.) and auto-imports.

// app.vue - 기본 레이아웃
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

// pages/index.vue - 페이지 컴포넌트 (SSR 데이터 패칭)
<script setup>
// useAsyncData 또는 useFetch를 사용하여 서버 사이드에서 데이터 렌더링
const { data: posts } = await useFetch('https://api.example.com/posts')
</script>

<template>
  <div>
    <h1>최신 게시글</h1>
    <ul>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
    </ul>
  </div>
</template>
// app.vue - Default layout
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

// pages/index.vue - Page component (SSR Data Fetching)
<script setup>
// Use useAsyncData or useFetch to render data on server side
const { data: posts } = await useFetch('https://api.example.com/posts')
</script>

<template>
  <div>
    <h1>Latest Posts</h1>
    <ul>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
    </ul>
  </div>
</template>

Pinia로 상태 관리하기 State Management with Pinia

Pinia는 Vuex를 대체하는 현대적인 상태 관리 라이브러리로, 타입스크립트 지원이 뛰어나고 Composition API 문법에 최적화되어 있습니다.

Pinia is a modern state management library replacing Vuex, offering excellent TypeScript support and optimized for Composition API syntax.

// stores/cart.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

// Setup Store 형태 (Composition API와 유사)
export const useCartStore = defineStore('cart', () => {
  // state
  const items = ref([])

  // getters
  const totalCount = computed(() => items.value.length)
  const totalPrice = computed(() =>
    items.value.reduce((total, item) => total + item.price, 0)
  )

  // actions
  function addItem(item) {
    items.value.push(item)
  }

  function clearCart() {
    items.value = []
  }

  return { items, totalCount, totalPrice, addItem, clearCart }
})
// stores/cart.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

// Setup Store pattern (Similar to Composition API)
export const useCartStore = defineStore('cart', () => {
  // state
  const items = ref([])

  // getters
  const totalCount = computed(() => items.value.length)
  const totalPrice = computed(() =>
    items.value.reduce((total, item) => total + item.price, 0)
  )

  // actions
  function addItem(item) {
    items.value.push(item)
  }

  function clearCart() {
    items.value = []
  }

  return { items, totalCount, totalPrice, addItem, clearCart }
})

컴포넌트에서 Pinia 사용하기 Using Pinia in Components

<script setup>
import { storeToRefs } from 'pinia'
import { useCartStore } from '@/stores/cart'

const cartStore = useCartStore()
// 상태(state)나 게터(getters)의 반응성을 유지하며 구조해석(destructuring)하기
const { items, totalPrice } = storeToRefs(cartStore)
// 액션은 바로 구조분해 할당 가능
const { addItem, clearCart } = cartStore
</script>
<script setup>
import { storeToRefs } from 'pinia'
import { useCartStore } from '@/stores/cart'

const cartStore = useCartStore()
// Destructure while keeping reactivity for state/getters
const { items, totalPrice } = storeToRefs(cartStore)
// Actions can be destructured directly
const { addItem, clearCart } = cartStore
</script>

✨ Nuxt.js 주요 특징 ✨ Key Nuxt.js Features

  • 파일 시스템 라우팅: `pages/` 폴더 안의 파일이 곧 라우트가 됩니다.
  • Auto-imports: `ref`, `computed`, 컴포넌트들을 import 선언 없이 사용 가능.
  • 다양한 렌더링 모드: SSR, 클라이언트 사이드 단독, 정적 사이트 생성(SSG) 지원.
  • 서버 기능 내장: `server/` 디렉터리 내에서 엔드포인트(API) 라우팅 로직을 구성합니다.
  • File-system Routing: Files in the `pages/` directory automatically become routes.
  • Auto-imports: Use components, `ref`, and `computed` without manual imports.
  • Multiple Rendering target: Supports SSR, Client-side only, and Static Site Generation (SSG).
  • Built-in Server: Create API endpoints easily in the `server/` directory.

💡 Pinia vs Vuex 💡 Pinia vs Vuex

Pinia는 Vuex의 Mutation 개념을 없애고, Action만으로 상태를 변경하여 훨씬 코드가 직관적입니다. 또한 타입스크립트 타입 추론이 매우 강력합니다.

Pinia removes the Mutation concept from Vuex, making the code much more intuitive by altering states simply through Actions. It also has much stronger TypeScript inference.