Next.js 환경 변수 노출 기준 한 줄 정의
Next.js는 빌드 시점에 NEXT_PUBLIC_ 접두사가 붙은 환경 변수만 클라이언트 번들에 포함시켜 브라우저에 노출하고, 접두사가 없는 변수는 서버 환경에서만 접근 가능하도록 분리합니다.
환경 변수 노출 핵심 항목
1. 서버 전용 환경 변수
정의: 접두사 없이 선언된 환경 변수는 서버 측 코드(API 라우트, getServerSideProps, getStaticProps, Server Components)에서만 접근 가능하며 클라이언트 번들에 포함되지 않습니다.
원리: Next.js는 빌드 과정에서 클라이언트 코드를 번들링할 때 NEXT_PUBLIC_ 접두사가 없는 변수를 제외합니다. 이 변수들은 Node.js 런타임 환경에서만 process.env를 통해 읽히며, 브라우저로 전송되는 JavaScript 파일에는 포함되지 않습니다.
예시:
DATABASE_URL=postgresql://user:pass@localhost/db
API_SECRET_KEY=sk_live_abc123
위 변수들은 서버 컴포넌트나 API 라우트에서 process.env.DATABASE_URL로 접근 가능하지만, 클라이언트 컴포넌트에서는 undefined를 반환합니다.
오해: 서버 컴포넌트에서 사용한 환경 변수는 자동으로 안전하다고 생각하는 경우가 있습니다. 하지만 서버 컴포넌트가 환경 변수 값을 props로 클라이언트 컴포넌트에 전달하면 해당 값은 HTML에 직렬화되어 브라우저에 노출됩니다.
2. NEXT_PUBLIC_ 접두사 변수
정의: NEXT_PUBLIC_ 접두사로 시작하는 환경 변수는 빌드 시점에 클라이언트 번들에 인라인으로 삽입되어 브라우저에서 접근 가능합니다.
원리: 빌드 과정에서 Next.js는 NEXT_PUBLIC_로 시작하는 모든 변수를 찾아 해당 값을 문자열로 치환합니다. 예를 들어 process.env.NEXT_PUBLIC_API_URL은 빌드된 JavaScript 파일에서 실제 값으로 대체됩니다. 이는 정적 분석을 통해 이루어지므로 동적 키 접근은 작동하지 않습니다.
예시:
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
NEXT_PUBLIC_API_ENDPOINT=https://api.example.com
클라이언트 컴포넌트에서:
const gaId = process.env.NEXT_PUBLIC_GA_ID;
// 빌드 후 → const gaId = "G-XXXXXXXXXX";
오해: 런타임에 환경 변수를 변경하면 반영될 것으로 기대하는 경우가 있습니다. NEXT_PUBLIC_ 변수는 빌드 시점에 고정되므로, 배포 후 환경 변수를 변경하려면 재빌드가 필요합니다.
3. 빌드 시점 vs 런타임
정의: NEXT_PUBLIC_ 변수는 빌드 시점에 값이 결정되고, 서버 전용 변수는 런타임에 읽힙니다.
원리: 빌드 시 Next.js는 .env 파일을 읽고 NEXT_PUBLIC_ 변수를 정적 값으로 치환합니다. 반면 서버 전용 변수는 실행 환경의 process.env에서 매 요청마다 읽히므로, 컨테이너나 서버 재시작 시 새 값이 적용됩니다.
예시: Docker 컨테이너에서 환경 변수를 주입할 때, DATABASE_URL은 컨테이너 시작 시 설정한 값이 즉시 반영되지만, NEXT_PUBLIC_API_URL은 이미 빌드된 JavaScript에 하드코딩되어 있어 변경되지 않습니다.
오해: 모든 환경 변수가 런타임에 동적으로 변경 가능하다고 생각하는 경우입니다. 클라이언트 노출 변수는 빌드 아티팩트에 포함되므로 배포 환경별로 다른 값을 사용하려면 각각 빌드해야 합니다.
4. 브라우저 개발자 도구 노출
정의: NEXT_PUBLIC_ 변수는 빌드된 JavaScript 파일에 평문으로 포함되어 브라우저 개발자 도구에서 확인 가능합니다.
원리: 번들링된 JavaScript 파일을 브라우저가 다운로드하면, 소스 코드에서 환경 변수 값을 검색할 수 있습니다. 난독화나 압축을 하더라도 문자열 값 자체는 그대로 남아 있습니다.
예시: 브라우저 개발자 도구 Network 탭에서 _next/static/chunks/*.js 파일을 열면 NEXT_PUBLIC_ 변수의 실제 값을 찾을 수 있습니다.
오해: 환경 변수 파일을 .gitignore에 추가하면 안전하다고 생각하는 경우가 있습니다. NEXT_PUBLIC_ 변수는 빌드 결과물에 포함되므로, Git에서 제외하더라도 배포된 사이트에서는 누구나 확인할 수 있습니다.
핵심 원리 정리
Next.js 환경 변수 노출 기준은 빌드 시점 정적 분석에 기반합니다. NEXT_PUBLIC_ 접두사는 "이 값을 클라이언트에 노출해도 안전함"을 명시적으로 선언하는 규칙입니다. 서버 전용 변수는 Node.js 프로세스 메모리에만 존재하며 HTTP 응답으로 전송되지 않습니다.
안전한 사용을 위한 기준:
- API 키, 데이터베이스 자격 증명, 암호화 비밀키는 절대
NEXT_PUBLIC_접두사를 붙이지 않습니다 - Google Analytics ID, 공개 API 엔드포인트, 기능 플래그처럼 공개되어도 무방한 값만
NEXT_PUBLIC_을 사용합니다 - 서버 컴포넌트에서 민감한 환경 변수를 읽더라도, 그 값을 클라이언트 컴포넌트 props로 전달하지 않습니다
- 런타임 환경별 다른 설정이 필요하면 서버 전용 변수를 사용하고, API 라우트를 통해 클라이언트에 필요한 값만 선택적으로 제공합니다
흔한 오해
동적 키 접근이 가능하다는 오해:
const key = 'API_URL';
const value = process.env[`NEXT_PUBLIC_${key}`]; // 작동하지 않음
Next.js는 빌드 시 정적 분석으로 process.env.NEXT_PUBLIC_* 형태만 인식합니다. 동적으로 키를 생성하면 undefined를 반환합니다.
서버 컴포넌트는 항상 안전하다는 오해: 서버 컴포넌트에서 환경 변수를 읽는 것은 안전하지만, 다음처럼 클라이언트로 전달하면 노출됩니다:
// 서버 컴포넌트
const secret = process.env.API_SECRET;
return <ClientComponent secret={secret} />; // secret이 HTML에 직렬화됨
환경 변수 파일만 보호하면 된다는 오해:
.env 파일을 Git에서 제외하는 것은 소스 코드 보안이지, 배포된 애플리케이션 보안이 아닙니다. NEXT_PUBLIC_ 변수는 빌드 결과물에 포함되므로 배포 후에는 누구나 접근 가능합니다.
모든 환경에서 같은 빌드를 재사용할 수 있다는 오해:
개발/스테이징/프로덕션 환경에서 NEXT_PUBLIC_ 변수가 다르다면, 각 환경별로 별도 빌드가 필요합니다. 서버 전용 변수만 다르다면 하나의 빌드를 여러 환경에서 재사용할 수 있습니다.
이해 확인 요약
Next.js 환경 변수 노출 여부는 NEXT_PUBLIC_ 접두사로 결정됩니다. 이 접두사가 있으면 빌드 시점에 클라이언트 번들에 포함되어 브라우저에서 확인 가능하고, 없으면 서버 환경에서만 접근 가능합니다.
안전한 사용을 위해서는 공개되어도 무방한 값만 NEXT_PUBLIC_을 사용하고, 민감한 정보는 서버 전용 변수로 관리해야 합니다. 서버 컴포넌트를 사용하더라도 환경 변수 값을 클라이언트 props로 전달하지 않도록 주의해야 하며, 빌드 시점에 값이 고정되는 특성을 이해하고 배포 전략을 수립해야 합니다.
