SHIN STORYSHIN STORY
홈포스트C#TypeScriptNext.jsNode.js시리즈
</>SHIN STORY

sdf

탐색

  • 홈
  • 모든 포스트
  • 시리즈
  • 검색

카테고리

  • C#
  • TypeScript
  • Next.js
  • Node.js
  • 알고리즘
  • 개발 도구

© 2025 Shin Blog. All rights reserved.

GitHubRSS
목록으로
Next.js#TypeScript#Next.js#Vercel

9편. Vercel 배포와 성능 최적화

SHIN

2025년 4월 10일7분 읽기0
📚

Next.js App Router로 풀스택 블로그 만들기

9편
  1. 11편. 프로젝트 소개와 기술 스택 선정
  2. 22편. 프로젝트 초기 설정과 디렉토리 구조
  3. 33편. Prisma 스키마 설계와 마이그레이션
  4. 44편. NextAuth.js로 관리자 인증 구현하기
  5. 55편. 관리자 대시보드 — 포스트 CRUD 구현
  6. 66편. 블로그 프론트엔드와 마크다운 렌더링
  7. 77편. 검색·카테고리·태그·시리즈 기능 구현
  8. 98편. SEO 최적화 — 메타태그·sitemap·RSS
  9. 9편. Vercel 배포와 성능 최적화현재

9편. Vercel 배포와 성능 최적화

Vercel 배포 전 체크리스트

CODE
□ .env.local에 모든 환경 변수 정의
□ prisma/dev.db가 .gitignore에 포함
□ public/uploads/가 .gitignore에 포함
□ npm run build 로컬 빌드 성공 확인
□ TypeScript 오류 없음 (tsc --noEmit)

Vercel CLI 배포

CODE
npm i -g vercel
vercel login

# 프로젝트 연결 및 배포
vercel

# 환경 변수 설정
vercel env add NEXTAUTH_SECRET
vercel env add NEXTAUTH_URL
vercel env add NEXT_PUBLIC_SITE_URL

# 프로덕션 배포
vercel --prod

SQLite와 Vercel

Vercel의 서버리스 함수는 읽기 전용 파일시스템을 사용합니다. 따라서 SQLite 파일은 /tmp 디렉토리에서만 쓸 수 있고, 함수 인스턴스가 재시작되면 데이터가 사라집니다.

프로덕션 배포를 위한 선택지:

옵션특징
TursoLibSQL 기반 SQLite 호환, 글로벌 복제
PlanetScaleMySQL 호환, 브랜치 기능
SupabasePostgreSQL, 오픈소스
Railway여러 DB 지원, 단순한 설정

Prisma의 datasource만 바꾸면 대부분의 코드는 그대로입니다.

CODE
// Turso로 전환 시
datasource db {
  provider = "sqlite"
  url      = env("TURSO_DATABASE_URL")
}

Next.js 이미지 최적화

CODE
import NextImage from 'next/image'

// 외부 도메인 허용 설정 (next.config.ts)
// images: { remotePatterns: [{ hostname: 'example.com' }] }

// 사용
<NextImage
  src={post.coverImage}
  alt={post.title}
  fill                              // 부모 컨테이너에 맞춤
  sizes="(max-width: 768px) 100vw, 700px"  // 반응형 힌트
  priority={isAboveFold}            // LCP 이미지는 priority
  className="object-cover"
/>

WebP/AVIF 자동 변환, 지연 로딩, 크기 최적화가 내장되어 있습니다.

캐싱 전략

CODE
// 포스트 조회 — 재사용 가능한 캐시
import { cache } from 'react'

const getPost = cache(async (slug: string) =>
  prisma.post.findUnique({ where: { slug } })
)
// 같은 요청 내에서 여러 번 호출해도 DB 쿼리 1회
CODE
// 설정 조회 — Next.js fetch 캐시
export async function getSettings() {
  const res = await fetch(`${siteUrl}/api/settings`, {
    next: { revalidate: 3600 }, // 1시간 캐시
  })
  return res.json()
}

번들 크기 분석

CODE
npm install -D @next/bundle-analyzer

# next.config.ts
import bundleAnalyzer from '@next/bundle-analyzer'
const withBundleAnalyzer = bundleAnalyzer({ enabled: process.env.ANALYZE === 'true' })
export default withBundleAnalyzer(nextConfig)

# 분석 실행
ANALYZE=true npm run build

Core Web Vitals 체크리스트

지표목표개선 방법
LCP< 2.5s커버 이미지 priority, 폰트 preload
FID/INP< 200ms클라이언트 컴포넌트 최소화
CLS< 0.1이미지 크기 고정, 폰트 font-display: swap
TTFB< 800ms서버 응답 캐싱, Edge 함수 활용

마치며

이 시리즈를 통해 처음부터 실제 배포까지 Next.js App Router 기반 풀스택 블로그를 완성했습니다.

핵심 배운 점:

  • RSC로 서버에서 직접 DB 쿼리 → API 레이어 최소화
  • 미들웨어로 인증 보호 → 엣지에서 빠른 처리
  • Prisma의 타입 안전 쿼리 → 런타임 오류 방지
  • URL 기반 상태로 필터/페이지네이션 → 공유 가능한 URL

전체 소스는 GitHub 저장소에서 확인할 수 있습니다.

공유
S

SHIN

.NET 개발자입니다

GitHub

이전 포스트

8편. SEO 최적화 — 메타태그·sitemap·RSS

다음 포스트

TypeScript 팁 #1 — strict 모드를 반드시 켜야 하는 이유

TypeScript 팁 #1 — strict 모드를 반드시 켜야 하는 이유

같은 카테고리 포스트

8편. SEO 최적화 — 메타태그·sitemap·RSS

2025년 4월 5일· 2분

7편. 검색·카테고리·태그·시리즈 기능 구현

2025년 3월 30일· 8분

6편. 블로그 프론트엔드와 마크다운 렌더링

2025년 3월 25일· 10분

댓글