개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

Next.js 환경 변수 — 클라이언트와 서버 환경 변수 분리

2025년 6월 4일
Next.js환경 변수NEXT_PUBLIC_.env.local보안

Next.js 앱을 배포하다 보면 API 키가 브라우저 번들에 노출되는 실수를 흔히 저지른다. 서버에서만 써야 할 시크릿이 클라이언트 자바스크립트에 포함되면, 누구나 개발자 도구로 꺼낼 수 있다. Next.js는 NEXT_PUBLIC_ 접두사 하나로 이 경계를 명확히 구분한다.

환경 변수 흐름

빌드 타임
  ├── NEXT_PUBLIC_* → 번들에 인라인 삽입 → 브라우저에서 읽힘
  └── 그 외 변수   → 서버 런타임에만 존재 → 브라우저 접근 불가

런타임 (서버)
  ├── process.env.DB_URL          (서버 컴포넌트 / Route Handler)
  ├── process.env.NEXT_PUBLIC_URL (서버 + 클라이언트 양쪽)
  └── (브라우저) process.env.DB_URL → undefined

빌드 시 주입(build-time injection): Next.js가 next build 시점에 NEXT_PUBLIC_ 변수를 리터럴 값으로 번들에 삽입하는 방식. 런타임에 변경해도 반영되지 않는다.

.env 파일 우선순위

Next.js는 아래 순서로 .env 파일을 병합한다. 위쪽이 우선권을 가진다.

파일용도커밋 여부
.env.local로컬 오버라이드커밋 안 함 (.gitignore)
.env.development개발 환경 기본값커밋
.env.production프로덕션 기본값커밋
.env모든 환경 공통커밋

.env.local은 항상 가장 높은 우선순위를 가지며, 시크릿 키는 여기에만 보관한다.

서버 전용 변수 사용

// app/api/data/route.ts — 서버에서만 실행됨
export async function GET() {
  const db = await connect(process.env.DATABASE_URL!) // 서버 전용
  const rows = await db.query('SELECT * FROM posts')
  return Response.json(rows)
}

서버 컴포넌트와 Route Handler에서는 NEXT_PUBLIC_ 없이도 모든 환경 변수에 접근할 수 있다.

클라이언트 변수 사용

// components/Analytics.tsx — 클라이언트 번들에 포함됨
'use client'

export function Analytics() {
  // NEXT_PUBLIC_ 접두사가 없으면 undefined
  const id = process.env.NEXT_PUBLIC_GA_ID
  return <script data-ga={id} />
}
# .env.local
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
DATABASE_URL=postgresql://user:secret@host/db

빌드 후 번들을 확인하면 G-XXXXXXXXXX는 문자열 리터럴로 삽입되어 있고, DATABASE_URL은 흔적조차 없다.

보안 주의사항

절대 NEXT_PUBLIC_로 시작하면 안 되는 변수들:

  • DATABASE_URL — DB 접속 정보
  • JWT_SECRET — 토큰 서명 키
  • STRIPE_SECRET_KEY — 결제 API 시크릿
  • SMTP_PASSWORD — 이메일 서버 비밀번호

실수로 노출됐다면, 해당 서비스에서 키를 즉시 갱신해야 한다. 번들에 박힌 값은 재배포 전까지 누구나 읽을 수 있다.

타입 안전성 확보

환경 변수는 기본적으로 string | undefined다. 런타임에 누락된 변수를 잡으려면 앱 시작 시점에 검증 로직을 둔다.

// lib/env.ts
function requireEnv(key: string): string {
  const value = process.env[key]
  if (!value) throw new Error(`환경 변수 누락: ${key}`)
  return value
}

export const env = {
  databaseUrl: requireEnv('DATABASE_URL'),
  gaId: process.env.NEXT_PUBLIC_GA_ID ?? '',
}

서버 전용 변수(server-only variable): NEXT_PUBLIC_ 접두사가 없는 변수. Node.js 프로세스 메모리에만 존재하며, 클라이언트 번들에 포함되지 않는다.

규칙은 단순하다. 브라우저에서 읽어야 하면 NEXT_PUBLIC_을 붙이고, 그렇지 않으면 붙이지 않는다.

← 이전 글Next.js Middleware — 요청 처리 전에 실행되는 함수
다음 글 →Next.js 배포 — Vercel과 self-hosting 옵션