본문 바로가기

프로그래밍

Next.js App Router .env 완전 정복: NEXT_PUBLIC, 로드 순서, 빌드·런타임 구분 가이드

반응형

왜 .env가 먼저 정리돼야 할까요

 

Next.js App Router에서 .env 설정이 흔들리면 클라이언트에 노출되면 안 되는 값이 공개되거나, 환경값이 빌드 시점에 고정되어 배포 후 수정이 먹지 않는 문제가 생깁니다. 특히 NEXT_PUBLIC 접두사, 로드 순서, 빌드·런타임 구분을 놓치면 디버깅이 길어집니다.

 

초기 설정 단계에서 “어떤 값이 브라우저에 노출되는가”와 “언제 읽혀서 캐싱되는가”만 명확히 잡아두면 대부분의 실수를 예방할 수 있습니다. 이 글은 그 두 가지를 짧은 규칙과 예시로 정리해 바로 적용할 수 있게 돕습니다.

 

실무에서는 API 엔드포인트, 공개 키, 비밀 키처럼 성격이 다른 값이 섞여 있습니다. 공개 가능 값은 브라우저로 보내고, 비밀 값은 서버 전용으로 남기는 선을 먼저 긋는 것이 안전합니다.

 

> 안티패턴: 테스트 편의를 위해 모든 키에 NEXT_PUBLIC을 붙이는 습관은 금물입니다. 브라우저 번들에 포함되어 네트워크 탭·소스맵에서 노출될 수 있습니다.

 

NEXT_PUBLIC, 빌드·런타임, 로드 순서를 한눈에

 

브라우저로 보내야 하는 값은 이름에 NEXT_PUBLIC을 붙입니다. 예를 들어 공개 API 도메인, 이미지 CDN 주소처럼 화면 코드에서 직접 참조하는 값이 대상입니다.

 

비밀 키나 데이터베이스 URL은 서버 전용으로 두고 접두사를 붙이지 않습니다. App Router에서는 서버 컴포넌트와 Route Handler가 이 값을 사용합니다.

 

로드 시점은 `.env.local -> .env.development/.production -> .env` 순으로 덮어쓰기 됩니다. 같은 키가 있으면 더 구체적인 파일이 최종값을 만듭니다.

 

빌드 타임에 고정되는 값과 요청 시 읽히는 값을 구분하세요. 클라이언트 번들에 포함되는 NEXT_PUBLIC 값은 빌드 시점에 결정되고, 서버 전용 값은 요청 처리 중에도 변경 반영이 가능합니다.

 

- 공개 값: NEXT_PUBLIC_API_BASE, NEXT_PUBLIC_CDN_URL
- 비밀 값: DATABASE_URL, STRIPE_SECRET_KEY
- 우선순위: .env.local > .env.production/.env.development > .env

 

> 안티패턴: 배포 후 값을 자주 바꿔야 하는데 NEXT_PUBLIC로 노출해 두면, 변경해도 새 빌드 전까지 브라우저에 반영되지 않습니다. 클라이언트가 아닌 서버에서 프록시·환경 스위치를 처리하는 편이 안전합니다.

 

.env 시작 가이드

 

처음엔 키를 두 축으로 나눕니다. 공개/비공개와 빌드/런타임을 먼저 구분하면 혼란이 줄어듭니다.

 

- 파일 우선순위: .env.local > .env.development/.production > .env
- 공개 키: NEXT_PUBLIC_ 접두사(브라우저에서 씀)
- 비밀 키: 접두사 없음(서버 전용)
- 빌드 타임: NEXT_PUBLIC_ 값은 번들에 박힘
- 런타임: 서버는 process.env로 읽음

 

환경별 파일은 미리 만들어 둡니다. 값은 예시일 뿐, 구조를 잡는 용도로 보세요.

 

# .env
NEXT_PUBLIC_CDN_URL=https://cdn.example.com
API_INTERNAL_BASE=https://internal-api.example.com

# .env.development
NEXT_PUBLIC_API_BASE=http://localhost:3001
DB_URL=postgres://user:pass@localhost:5432/dev

# .env.production
NEXT_PUBLIC_API_BASE=https://api.example.com
DB_URL=postgres://user:pass@prod-host:5432/prod

 

클라이언트 컴포넌트는 NEXT_PUBLIC 값을 그대로 씁니다. 서버 코드는 요청 시점에 비밀 값을 읽습니다.

 

// app/page.tsx (Client Component)
const apiBase = process.env.NEXT_PUBLIC_API_BASE;
const cdn = process.env.NEXT_PUBLIC_CDN_URL;

// app/api/hello/route.ts (Server)
export async function GET() {
  const dbUrl = process.env.DB_URL;
  return Response.json({ ok: !!dbUrl });
}

 

- 클라이언트의 process.env는 빌드 시 문자열로 바뀌므로 값이 바뀌면 재빌드가 필요합니다.
- 서버 값은 배포 환경에 따라 프로세스 재시작이 필요할 수 있습니다.

 

> 안티패턴: 디버깅을 위해 비밀 키에 NEXT_PUBLIC을 붙이면 번들과 네트워크 탭에 그대로 노출됩니다. 비밀은 접두사 없이 서버 코드로만 사용하세요.

 

흔한 실수와 선택 기준 정리

 

초보자가 가장 많이 틀리는 지점은 “공개/비공개”와 “빌드/런타임”을 섞어 쓰는 것입니다. 브라우저에서 필요한 값만 NEXT_PUBLIC로, 나머지는 서버 전용으로 고정하세요.

 

- 테스트 편의로 비밀 키에 NEXT_PUBLIC 붙임
- .env.local를 깃에 올림
- 배포 후 값이 안 바뀌는데 캐시 문제로 오해함(사실 빌드 타임 고정)
- 서버 코드에서 NEXT_PUBLIC 값을 요청마다 바뀔 거라 기대함

 

안전한 선택을 위해 키를 성격별로 나눕니다. 프런트에서 직접 사용하는 도메인·플래그만 공개하고, 토큰·DB·서버 간 주소는 비공개로 유지합니다.

 

- 브라우저 필요: NEXT_PUBLIC_API_BASE, NEXT_PUBLIC_CDN_URL
- 서버 전용: DATABASE_URL, INTERNAL_API_BASE, THIRD_PARTY_SECRET
- 빌드 타임 고정 허용: 기능 플래그, 공개 도메인
- 런타임 반영 필요: 회전 가능한 토큰, 내부 엔드포인트

 

> 안티패턴: 환경변수 값을 코드 상수로 하드코딩하고 잊는 습관. 나중에 환경만 바꿔도 반영되지 않아 전체 재배포로 이어집니다. 항상 process.env 또는 NEXT_PUBLIC_ 변수로 주입하세요.

 

빌드·런타임 혼동으로 생기는 증상은 간단히 분기해 진단합니다. “클라이언트에서 참조 + 값 변경 후 재빌드 전인가?”를 먼저 확인하세요.

 

- 클라이언트 값 변경이 반영 안 됨: NEXT_PUBLIC 값은 재빌드 필요
- 서버 값 변경이 반영 안 됨: 배포 환경 변수 주입/재시작 여부 확인
- 로컬/배포 값이 뒤섞임: 파일 우선순위와 캐시(.next 폴더) 삭제 점검

 

지금 정리하고 적용할 체크리스트

 

먼저 공개/비공개, 빌드/런타임 두 축으로 키를 나눕니다. 브라우저에서 쓰면 NEXT_PUBLIC, 나머지는 서버 전용으로 시작점을 고정하세요.

 

환경 파일을 우선순위에 맞춰 준비합니다. 아래 순서로 덮어쓰기를 전제로 비밀값은 .env.local에만 두세요.

 

- .env.local > .env.development/.production > .env
- 공개: NEXT_PUBLIC_API_BASE, NEXT_PUBLIC_CDN_URL
- 비공개: DATABASE_URL, INTERNAL_API_BASE, THIRD_PARTY_SECRET

 

빌드 전에 NEXT_PUBLIC 값이 고정된다는 점을 검증합니다. 클라이언트에서 참조하는 키는 빌드 후 변경이 반영되지 않으니, 값 변경 시 재빌드·재배포를 포함하세요.

 

> 실무 팁: 서버 전용 값은 process.env로 요청마다 읽힙니다. “배포 후 값이 안 바뀐다”면 캐시보다 빌드 타임 고정 여부부터 점검하세요.

반응형