Ch 02. 환경 변수 관리하기
코드 안에 데이터베이스 비밀번호, API 키, 인증 비밀 키를 직접 써넣으면 어떻게 될까요? GitHub에 코드를 올리는 순간 전 세계에 공개됩니다. 실수로 비밀 키를 커밋한 사고는 생각보다 자주 일어납니다.
환경 변수는 이런 민감한 값들을 코드 밖에서 관리하는 방법입니다.
.env 파일 종류
Next.js는 여러 종류의 .env 파일을 지원합니다.
| 파일 | 용도 | git 포함 |
|---|---|---|
.env |
모든 환경의 기본값 | 가능 (비밀값 없는 경우만) |
.env.local |
로컬 개발용, 개인 비밀값 | 절대 금지 |
.env.development |
개발 환경 전용 | 가능 |
.env.production |
프로덕션 환경 전용 | 가능 |
.env.test |
테스트 환경 전용 | 가능 |
.env.local은 .gitignore에 반드시 포함되어야 합니다. Next.js 기본 .gitignore에는 이미 포함되어 있습니다.
# .gitignore (이미 포함되어 있음).env*.local```text### NEXT_PUBLIC_ 접두사 — 브라우저 노출 여부환경 변수 접두사에 따라 어디서 접근할 수 있는지 달라집니다.**`NEXT_PUBLIC_` 접두사 있음**: 서버와 브라우저 모두에서 접근 가능합니다. 빌드 시 코드에 인라인됩니다. 즉, 번들을 분석하면 값을 볼 수 있습니다.```typescript// 브라우저에서도 접근 가능const apiUrl = process.env.NEXT_PUBLIC_API_URL;```text**`NEXT_PUBLIC_` 접두사 없음**: 서버에서만 접근 가능합니다. 브라우저 코드에서 `process.env.SECRET_KEY`를 읽으면 `undefined`입니다. 데이터베이스 URL, API 비밀 키 같은 민감한 값은 이 방식을 사용합니다.```typescript// 서버 컴포넌트, API 라우트, Server Action에서만 접근 가능const dbUrl = process.env.DATABASE_URL; // 클라이언트에서는 undefinedconst secret = process.env.AUTH_SECRET; // 클라이언트에서는 undefined```text### 로컬 개발 환경 설정```bash# 파일: .env.local# 데이터베이스DATABASE_URL="postgresql://user:password@localhost:5432/myblog"# NextAuthAUTH_SECRET="openssl rand -base64 32로 생성한 값"# Google OAuth (PART 06 참조)AUTH_GOOGLE_ID="your_google_client_id"AUTH_GOOGLE_SECRET="your_google_client_secret"# 공개 가능한 값NEXT_PUBLIC_APP_URL="http://localhost:3000"NEXT_PUBLIC_SITE_NAME="나의 블로그"```text`.env.example`을 만들어 팀원과 공유합니다. 실제 값 대신 설명을 적습니다.```bash# 파일: .env.example (git에 포함)DATABASE_URL="postgresql://user:password@host:port/dbname"AUTH_SECRET="openssl rand -base64 32으로 생성"AUTH_GOOGLE_ID="Google Cloud Console에서 발급"AUTH_GOOGLE_SECRET="Google Cloud Console에서 발급"NEXT_PUBLIC_APP_URL="앱 URL"NEXT_PUBLIC_SITE_NAME="사이트 이름"```text### Vercel 대시보드에서 환경변수 설정1. Vercel 프로젝트 → `Settings` → `Environment Variables`로 이동합니다.2. 변수명과 값을 입력합니다.3. 적용할 환경을 선택합니다. (Production, Preview, Development)4. `Save`를 클릭합니다.5. 새로 배포해야 변경사항이 반영됩니다.Vercel에서는 환경별로 다른 값을 설정할 수 있습니다. 개발 환경(`Preview`)과 프로덕션(`Production`)의 데이터베이스 URL을 다르게 설정하는 것이 좋습니다.### Docker 배포 시 환경변수 주입Docker 컨테이너에서는 `.env.local` 파일이 없습니다. 환경변수를 컨테이너에 주입하는 방법은 두 가지입니다.**방법 1: docker-compose.yml에서 .env 파일 참조**```yaml# 파일: docker-compose.ymlservices: app: image: my-blog-app env_file: - .env # 서버의 .env 파일을 읽음 (git에 포함하지 않음) ports: - "3000:3000"```text서버에 `.env` 파일을 직접 만들어 관리합니다.**방법 2: docker-compose.yml에 직접 환경변수 선언**```yaml# 파일: docker-compose.ymlservices: app: image: my-blog-app environment: - DATABASE_URL=${DATABASE_URL} # 호스트의 환경변수를 전달 - AUTH_SECRET=${AUTH_SECRET} - NODE_ENV=production ports: - "3000:3000"```text### 환경변수 유효성 검사잘못된 환경변수 설정으로 런타임에 에러가 발생하는 것을 방지합니다. `zod`로 빌드 시점에 검증합니다.```typescript// 파일: lib/env.tsimport { z } from "zod";const envSchema = z.object({ DATABASE_URL: z.string().url(), AUTH_SECRET: z.string().min(32), NODE_ENV: z.enum(["development", "production", "test"]), NEXT_PUBLIC_APP_URL: z.string().url(),});export const env = envSchema.parse(process.env);
env.ts를 앱 시작 시 import하면 필수 환경변수가 없을 때 명확한 에러 메시지와 함께 실패합니다.
다음 챕터에서는 Docker로 자체 서버에 배포하는 방법을 살펴봅니다.