# Nuxt 3: SSR과 정적 생성 완벽 가이드 > Nuxt 3로 SSR과 정적 생성을 마스터하십시오. useFetch부터 route rules까지, Vue.js 애플리케이션의 성능을 최적화하는 방법을 배웁니다. - Published: 2026-02-11 - Updated: 2026-04-28 - Author: SharpSkill - Tags: nuxt 3, vue.js, ssr, 정적 생성, 웹 성능 - Reading time: 12 min --- Nuxt 3은 다양한 사용 사례에 적합한 여러 렌더링 모드를 제공함으로써 Vue.js 애플리케이션을 구축하는 방식을 바꿔놓습니다. Server-Side Rendering(SSR)부터 정적 생성, 하이브리드 렌더링까지 이 프레임워크는 성능과 SEO를 최적화하기 위한 뛰어난 유연성을 제공합니다. > **사전 요구 사항** > > 이 튜토리얼은 Vue 3과 Composition API에 대한 기본 지식을 전제로 합니다. 서버 사이드 렌더링 개념에 익숙하면 도움이 되지만 필수는 아니며, 기본 개념은 가이드 전반에 걸쳐 설명합니다. ## Nuxt 3의 렌더링 모드 이해하기 코드를 살펴보기 전에 사용할 수 있는 렌더링 모드 간의 차이를 이해하는 것이 매우 중요합니다. 각 모드는 성능, SEO, 사용자 경험 측면에서 특정한 요구를 충족합니다. SSR(Server-Side Rendering)은 모든 요청에 대해 서버에서 HTML을 생성합니다. 정적 생성(SSG)은 빌드 시점에 모든 페이지를 미리 생성합니다. 하이브리드 모드는 페이지 단위로 이러한 접근 방식을 조합할 수 있게 해줍니다. ```typescript // nuxt.config.ts // 다양한 렌더링 모드의 설정 export default defineNuxtConfig({ // SSR 기본 활성화 (SEO를 위해 권장) ssr: true, // 정적 생성: 모든 페이지를 사전 렌더링 // 빌드에는 'npm run generate' 사용 // target: 'static', // Nuxt 2 문법 // 하이브리드 모드: 라우트 단위로 설정 가능 routeRules: { // 메인 페이지: 사전 렌더링 및 캐시 '/': { prerender: true }, // 블로그: 정적 생성 '/blog/**': { prerender: true }, // 대시보드: 클라이언트 사이드 렌더링만 사용 '/dashboard/**': { ssr: false }, // API: 사전 렌더링 없음 '/api/**': { prerender: false } } }) ``` 이 설정은 하이브리드 모드의 강력함을 보여줍니다. 애플리케이션의 각 영역이 자신의 요구에 가장 적합한 렌더링 모드를 사용합니다. ## useFetch와 useAsyncData를 활용한 데이터 가져오기 Nuxt 3은 데이터를 동형(isomorphic)으로 가져오기 위한 두 가지 주요 컴포저블을 제공합니다. 이 컴포저블들은 서버 측과 클라이언트 측 모두에서 동작하며 하이드레이션을 자동으로 관리합니다. `useFetch`는 HTTP 호출을 단순화하는 `useAsyncData`의 래퍼입니다. `useAsyncData`는 고급 사용 사례에서 더 많은 제어를 제공합니다. ```vue ``` 더 많은 제어가 필요한 경우 `useAsyncData`를 사용하면 임의의 비동기 함수를 실행할 수 있습니다. ```vue ``` 이 컴포저블들은 이중 페칭을 방지합니다. 서버에서 가져온 데이터는 HTML 페이로드에 직렬화되어 클라이언트 하이드레이션 시 재사용됩니다. ## 서버 훅으로 SSR 커스터마이즈하기 Nuxt 3의 SSR은 서버 훅을 통해 커스터마이즈할 수 있습니다. 이러한 훅은 렌더링 사이클의 다양한 단계에 개입하여 기본 동작을 수정할 수 있게 해줍니다. ```typescript // server/plugins/render-hooks.ts // SSR 렌더링을 커스터마이즈하기 위한 서버 플러그인 export default defineNitroPlugin((nitroApp) => { // 각 페이지 렌더링 전에 실행되는 훅 nitroApp.hooks.hook('render:html', (html, { event }) => { // 스크립트나 메타데이터 주입 html.head.push(` `) }) // 렌더링 캐시 관리를 위한 훅 nitroApp.hooks.hook('render:response', (response, { event }) => { // 사용자 정의 캐시 헤더 추가 const path = event.path if (path.startsWith('/blog/')) { // 블로그 기사에는 긴 캐시 response.headers['Cache-Control'] = 'public, max-age=3600, s-maxage=86400' } else if (path.startsWith('/api/')) { // API에는 캐시 사용 안 함 response.headers['Cache-Control'] = 'no-store' } }) }) ``` > **SSR 성능** > > `render:response` 훅은 HTTP 캐시 전략을 구현하기에 이상적입니다. SSR을 Cache-Control 헤더를 존중하는 CDN과 결합하면 사전 렌더링된 페이지를 제공하면서 무효화 능력을 유지할 수 있습니다. ## nuxt generate를 활용한 정적 생성 정적 생성은 빌드 시점에 모든 페이지를 미리 빌드합니다. 이 접근 방식은 블로그, 문서, 마케팅 사이트와 같이 콘텐츠가 안정적인 사이트에 이상적입니다. 동적 라우트의 경우 Nuxt가 생성해야 하는 모든 URL을 알아야 합니다. `prerender:routes` 훅을 통해 이러한 라우트를 프로그래밍 방식으로 정의할 수 있습니다. ```typescript // nuxt.config.ts // 정적 생성을 위한 완전한 설정 export default defineNuxtConfig({ // 정적 생성 활성화 nitro: { prerender: { // 자동 링크 크롤링 활성화 crawlLinks: true, // 항상 포함할 라우트 routes: ['/', '/about', '/contact'], // 일부 라우트 무시 ignore: ['/admin', '/api'] } }, hooks: { // 동적 라우트를 생성하기 위한 훅 async 'prerender:routes'(ctx) { // API 또는 DB에서 기사 가져오기 const articles = await fetch('https://api.example.com/articles') .then(res => res.json()) // 기사 라우트 추가 for (const article of articles) { ctx.routes.add(`/blog/${article.slug}`) } // 카테고리 가져오기 const categories = await fetch('https://api.example.com/categories') .then(res => res.json()) for (const category of categories) { ctx.routes.add(`/category/${category.slug}`) } } } }) ``` 페이지 수가 많은 프로젝트의 경우 자동 크롤러로는 부족할 수 있습니다. 별도의 설정 파일을 사용하는 더 견고한 접근 방식을 소개합니다. ```typescript // server/utils/generate-routes.ts // 동적 라우트 목록을 생성하는 유틸리티 import { prisma } from './prisma' export async function getAllStaticRoutes(): Promise { const routes: string[] = [] // 블로그 기사 const articles = await prisma.article.findMany({ where: { published: true }, select: { slug: true, category: { select: { slug: true } } } }) for (const article of articles) { routes.push(`/blog/${article.category.slug}/${article.slug}`) } // 상품 페이지 const products = await prisma.product.findMany({ where: { active: true }, select: { slug: true } }) for (const product of products) { routes.push(`/products/${product.slug}`) } // 태그 페이지 const tags = await prisma.tag.findMany({ select: { slug: true } }) for (const tag of tags) { routes.push(`/tags/${tag.slug}`) } return routes } ``` ## routeRules로 하이브리드 렌더링하기 하이브리드 렌더링은 Nuxt 3의 대표 기능입니다. 라우트마다 서로 다른 렌더링 규칙을 정의해 SSR과 SSG의 장점을 결합할 수 있게 해줍니다. ```typescript // nuxt.config.ts // 하이브리드 렌더링의 고급 설정 export default defineNuxtConfig({ routeRules: { // 마케팅 페이지: 사전 렌더링하고 장기간 캐시 '/': { prerender: true }, '/pricing': { prerender: true }, '/features/**': { prerender: true }, // 블로그: ISR (Incremental Static Regeneration) // 매시간 재검증 '/blog/**': { isr: 3600, prerender: true }, // 문서: 재검증을 포함한 CDN 캐시 '/docs/**': { swr: 86400, // Stale-while-revalidate prerender: true }, // 이커머스: 짧은 캐시를 가진 SSR '/products/**': { ssr: true, cache: { maxAge: 60, staleMaxAge: 300 } }, // 장바구니와 결제: 클라이언트 사이드 전용 '/cart': { ssr: false }, '/checkout/**': { ssr: false }, // 대시보드: SPA 모드 '/dashboard/**': { ssr: false, // 사전 렌더링 비활성화 prerender: false }, // API 라우트: 기본적으로 캐시 없음 '/api/**': { cors: true, headers: { 'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE' } } } }) ``` 이 설정은 일반적인 현대 애플리케이션 아키텍처를 보여줍니다. 공개 페이지는 SSG로 SEO에 최적화되고, 인터랙티브한 영역은 클라이언트 사이드 렌더링을 사용합니다. ## 데이터 캐싱을 통한 성능 최적화 페이지 캐싱뿐 아니라 Nuxt 3은 가져온 데이터를 캐싱할 수 있게 해줍니다. 이 전략은 API 부하를 줄이고 응답 시간을 개선합니다. ```typescript // server/api/articles/[slug].get.ts // 데이터 캐싱이 적용된 API 엔드포인트 import { getArticleBySlug } from '~/server/utils/articles' export default defineCachedEventHandler( async (event) => { const slug = getRouterParam(event, 'slug') if (!slug) { throw createError({ statusCode: 400, message: 'slug가 없습니다' }) } const article = await getArticleBySlug(slug) if (!article) { throw createError({ statusCode: 404, message: '기사를 찾을 수 없습니다' }) } return article }, { // slug 기반 캐시 키 getKey: (event) => `article-${getRouterParam(event, 'slug')}`, // 캐시 유지 시간: 1시간 maxAge: 3600, // Stale-while-revalidate: 업데이트 중에도 오래된 캐시 제공 staleMaxAge: 7200, // 태그 기반 무효화 tags: ['articles'] } ) ``` 콘텐츠가 변경될 때 캐시를 무효화하기 위해 Nuxt는 태그 시스템을 제공합니다. ```typescript // server/api/articles/[slug].put.ts // 캐시 무효화를 동반한 기사 업데이트 import { updateArticle } from '~/server/utils/articles' export default defineEventHandler(async (event) => { const slug = getRouterParam(event, 'slug') const body = await readBody(event) // 기사 업데이트 const article = await updateArticle(slug, body) // 이 기사에 대한 캐시 무효화 await useStorage('cache').removeItem(`nitro:handlers:article-${slug}`) // 또는 태그 기반 무효화 (모든 기사) // await useStorage('cache').clear('articles') return article }) ``` > **분산 캐시** > > 다중 인스턴스 환경의 프로덕션에서는 인메모리 캐시만으로는 충분하지 않습니다. 인스턴스 간 일관성을 보장하기 위해 Nitro 설정을 통해 Redis 또는 다른 분산 시스템을 구성하는 것을 권장합니다. ## SEO와 메타데이터 관리 SSR은 메타데이터를 서버 측에서 생성함으로써 SEO를 최적화할 수 있게 해줍니다. Nuxt 3은 메타 태그를 동적으로 관리하기 위한 여러 가지 접근 방식을 제공합니다. ```vue ``` 정적 페이지의 경우 메타데이터를 컴포넌트에서 직접 정의할 수 있습니다. ```vue ``` ## 배포와 프로덕션 고려 사항 배포 선택은 사용하는 렌더링 모드에 따라 달라집니다. 주요 옵션과 그 설정은 다음과 같습니다. ```typescript // nuxt.config.ts // 다양한 배포 환경을 위한 설정 export default defineNuxtConfig({ nitro: { // 대상 플랫폼에 맞춘 프리셋 // preset: 'vercel', // Vercel // preset: 'netlify', // Netlify // preset: 'cloudflare-pages', // Cloudflare // preset: 'node-server', // 일반적인 Node.js // 프로덕션 환경의 Node.js를 위한 설정 preset: 'node-server', // 응답 압축 compressPublicAssets: true, // 캐시 저장소 설정 storage: { cache: { driver: 'redis', url: process.env.REDIS_URL } } }, // 런타임 환경 변수 runtimeConfig: { // 비밀 값(클라이언트에 노출되지 않음) apiSecret: process.env.API_SECRET, // 공개 설정 public: { apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api' } } }) ``` 정적 배포의 경우 `npm run generate` 명령은 어떤 정적 파일 호스트에도 배포할 준비가 된 `.output/public` 폴더를 생성합니다. ```bash # 정적 생성 npm run generate # .output/public의 내용은 다음 위치에 배포할 수 있습니다: # - Vercel(자동 감지) # - Netlify(자동 설정) # - GitHub Pages # - S3 + CloudFront # - 임의의 CDN 또는 정적 파일 서버 ``` ## 결론 Nuxt 3은 Vue.js 애플리케이션을 렌더링하기 위한 탁월한 유연성을 제공합니다. SSR, SSG, 하이브리드 렌더링 중 무엇을 선택할지는 각 프로젝트의 구체적인 요구 사항에 달려 있습니다. **주요 포인트:** ✅ **SSR**: 좋은 SEO가 필요한 동적 콘텐츠에 이상적(이커머스, 뉴스 사이트) ✅ **SSG**: 안정적인 콘텐츠에 적합(블로그, 문서, 마케팅 사이트) ✅ **하이브리드**: 다양한 요구를 가진 복잡한 애플리케이션에 가장 적합한 접근 방식 ✅ **useFetch/useAsyncData**: 자동 하이드레이션과 캐시 관리 ✅ **routeRules**: 각 라우트의 동작에 대한 세밀한 설정 ✅ **캐싱**: 프로덕션 성능을 최적화하기 위한 다양한 전략 하이브리드 렌더링과 잘 설계된 캐싱 전략을 결합하면 SEO에 최적화된 고성능 애플리케이션을 구축하면서도 Single Page Applications의 인터랙티브함을 유지할 수 있습니다. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ko/blog/vue-nuxt/nuxt-3-ssr-static-generation