환경 변수 설정
환경별 값을 env 파일과 Vercel에 분리하고 NEXT_PUBLIC 접두사에 따른 서버·브라우저 노출 경계를 구분합니다.
소프트웨어 개발에서 환경 변수(environment variables)는 애플리케이션의 동작을 제어하고 민감한 정보를 관리하는 핵심 수단입니다. 개발, 테스트, 운영 환경마다 설정값이 다르거나, DB 비밀번호와 API 키 같은 보안 정보를 안전하게 다뤄야 할 때 특히 중요합니다. Next.js는 환경 변수를 관리하는 유연한 방식을 제공해 배포와 운영을 더 안전하고 효율적으로 만듭니다. 이 글에서는 환경 변수의 개념과 중요성, Next.js에서 환경 변수를 설정하고 쓰는 여러 방법, 그리고 보안과 배포 환경에 맞는 관리 전략을 알아봅니다.
환경 변수의 개념 및 중요성
환경 변수는 운영 체제 수준에서 설정되거나 애플리케이션을 실행할 때 주입되는 변수입니다. 코드 안에 값을 직접 적는 대신 환경 변수를 쓰는 데는 몇 가지 이유가 있습니다.
먼저 보안입니다. 데이터베이스 비밀번호, 서드파티 API 키, 민감한 인증 정보는 코드 저장소에 그대로 드러나서는 안 되며, 환경 변수를 쓰면 이런 정보를 코드와 나누어 관리할 수 있습니다. 다음은 환경별 설정의 분리입니다. 개발 서버, 스테이징 서버, 운영 서버는 서로 다른 데이터베이스 연결 문자열과 API 엔드포인트를 가질 수 있는데, 환경 변수를 쓰면 코드를 바꾸지 않고 환경마다 알맞은 설정을 적용할 수 있습니다.
같은 코드를 환경마다 다른 값으로 실행할 수 있어 유연하기도 합니다. 서버 런타임에서 읽는 값은 프로세스를 다시 시작하거나 새 배포에 주입하면 바뀌지만, NEXT_PUBLIC_ 값과 빌드 단계에서 읽은 값은 새로 빌드하고 배포해야 바뀝니다. 설정값을 한곳에 모아 관리하므로 바꾸기 쉽고 코드도 읽기 쉬워집니다.
Next.js에서 환경 변수 설정하기
Next.js는 .env 파일로 로컬 환경 변수를 관리하는 방식과 Vercel 같은 플랫폼에서 클라우드 환경 변수를 관리하는 방식을 모두 지원하며, 빌드할 때 환경 변수를 번들에 넣는 방식과 런타임에 읽는 방식도 모두 지원합니다. 어느 쪽이든 코드는 process.env.이름으로 값을 읽으며, 달라지는 것은 값이 정해지는 시점과 그 값이 어디까지 노출되는지입니다. 아래 두 절은 이 구분을 따라, 먼저 로컬에서 어떤 .env 파일을 어떤 순서로 읽는지 보고, 이어서 접두사에 따라 서버 전용 값과 브라우저 번들에 들어가는 값이 어떻게 갈리는지 봅니다.
로컬 환경 변수 (.env 파일)
로컬 개발 환경에서 환경 변수를 쓰려면 프로젝트 루트 디렉터리에 .env 파일을 만듭니다. Next.js는 기본적으로 다음 파일들을 읽습니다. .env는 기본 환경 변수입니다. .env.local은 모든 모드에서 쓸 수 있는 로컬 덮어쓰기 파일이지만 test 모드에서는 읽지 않으며, Git에 커밋하지 않습니다. .env.development, .env.production, .env.test 같은 .env.$(NODE_ENV)는 현재 실행 모드 전용 값이고, .env.$(NODE_ENV).local은 현재 실행 모드의 로컬 덮어쓰기 값입니다.
값을 찾는 우선순위는 이미 설정된 process.env, .env.$(NODE_ENV).local, .env.local(test 모드 제외), .env.$(NODE_ENV), .env 순서입니다. NODE_ENV는 보통 development, production, test 가운데 하나이며, next dev는 development 모드로, next build는 production 모드로 실행됩니다. 다음은 .env.local 파일의 예입니다.
# .env.local
DATABASE_URL=mongodb://localhost:27017/my-local-db
API_SECRET_KEY=my_local_secret_key_for_server
# NEXT_PUBLIC_ 접두사가 붙은 변수는 클라이언트 측 코드에서도 접근 가능합니다.
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000/apinext dev로 실행하면 이 파일의 네 값이 모두 서버의 process.env에 들어가지만, 브라우저 코드에서 쓸 수 있는 값은 NEXT_PUBLIC_으로 시작하는 아래 두 줄뿐입니다. 같은 이름을 .env와 .env.local에 함께 적었다면 우선순위가 높은 .env.local의 값이 쓰이고, 셸에서 이미 설정해 둔 같은 이름의 환경 변수가 있으면 그 값이 두 파일보다 앞섭니다.
더 깊이: Next.js 밖에서 같은 규칙으로 .env를 읽기
.env 파일을 읽어 process.env에 넣는 일은 Next.js가 실행될 때 일어납니다. 그래서 ORM의 설정 파일이나 테스트 도구의 전역 설정처럼 Next.js 바깥에서 실행되는 코드에서는 이 값들이 비어 있을 수 있습니다. Next.js의 환경 변수 안내는 이럴 때 Next.js가 내부에서 쓰는 @next/env 패키지의 loadEnvConfig(process.cwd())를 불러, 같은 파일을 같은 우선순위로 읽으라고 안내합니다. 보통 테스트 도구가 설정해 주는 대로 NODE_ENV가 test일 때는 .env.local뿐 아니라 .env.development와 .env.production도 읽지 않습니다. 사람마다 다른 로컬 값 때문에 테스트 결과가 달라지지 않게 하려는 것이며, 그래서 .env.test는 저장소에 넣고 .env.test.local은 넣지 않습니다.
.env 파일 안의 $ 기호에도 주의합니다. Next.js는 TWITTER_URL=https://x.com/$TWITTER_USER처럼 $이름으로 쓴 부분을 다른 변수의 값으로 펼치므로, 비밀번호처럼 값 자체에 $가 들어 있다면 \$로 이스케이프해야 합니다. 큰따옴표로 감싸면 여러 줄 값이나 \n이 들어간 값도 적을 수 있습니다. 또 NODE_ENV를 따로 정하지 않으면 Next.js가 next dev에는 development를, 그 밖의 명령에는 production을 넣으며, src 폴더를 쓰더라도 .env 파일은 src가 아닌 프로젝트 루트에 둡니다.
환경 변수 접근 방식
Next.js에서는 process.env로 값을 읽습니다. 다음 두 예제는 서버 전용 값과 공개 값을 어디에서 쓰는지 비교합니다.
먼저 접두사가 없는 process.env.DATABASE_URL 같은 서버 전용 환경 변수입니다. 이 변수는 브라우저 번들에 공개 값으로 들어가지 않으며, 서버 컴포넌트, Route Handler, 서버 액션 같은 서버 코드에서 읽습니다. 서버 전용 코드에서만 쓰면 클라이언트 JavaScript 번들에 들어가지 않으므로 민감한 값을 보호할 수 있지만, 로그나 응답 본문으로 값을 그대로 내보내지 않도록 주의합니다.
export async function GET() {
const dbUrl = process.env.DATABASE_URL; // 서버에서만 접근 가능
if (!dbUrl) {
return Response.json({ error: 'DATABASE_URL is not configured' }, { status: 500 });
}
return Response.json({ databaseConfigured: true });
}이 응답은 변수에 값이 있는지만 확인할 뿐, 데이터베이스 연결이나 자격 증명이 유효한지는 검사하지 않습니다.
다음은 NEXT_PUBLIC_ 접두사가 필요한 process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID 같은 클라이언트용 환경 변수입니다. 이 변수는 빌드할 때 클라이언트 JavaScript 번들에 들어가므로, 웹 브라우저의 개발자 도구에서 쉽게 볼 수 있습니다. 따라서 민감한 정보를 이 방식으로 절대 드러내서는 안 되며, 구글 애널리틱스 ID나 공개 API 키처럼 클라이언트에서 필요한 민감하지 않은 정보에만 씁니다.
"use client";
import { useEffect } from 'react';
export default function AnalyticsTracker() {
useEffect(() => {
const gaId = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID;
if (gaId) {
// console.log(`Google Analytics ID: ${gaId}`);
// 실제로는 여기에 GA 초기화 스크립트가 들어갑니다.
}
}, []);
return null;
}이 예제의 GA 초기화 부분은 주석으로 남아 있어 실제 분석 스크립트를 실행하지 않습니다.
Vercel을 이용한 클라우드 환경 변수 설정
Vercel은 Development, Preview, Production이라는 배포 환경에 따라 환경 변수를 안전하고 효율적으로 관리하는 UI를 제공합니다. .env 파일과 다른 점은 같은 이름의 변수에 환경마다 다른 값을 넣을 수 있다는 것입니다. 예를 들어 DATABASE_URL 하나에 Preview는 테스트 데이터베이스를, Production은 운영 데이터베이스를 가리키게 해 두면, 코드는 그대로 둔 채 배포 종류에 따라 다른 값이 들어갑니다. 아래에서는 값을 추가하는 화면 순서와, 저장한 값이 언제 배포에 반영되는지를 차례로 봅니다.
Vercel 대시보드에서 환경 변수 추가
vercel.com에 로그인해 환경 변수를 설정할 Next.js 프로젝트를 고르고, 위쪽의 Settings 탭에서 왼쪽 사이드바의 Environment Variables를 고릅니다. 새 환경 변수의 Name에는 DATABASE_URL이나 NEXT_PUBLIC_MAPBOX_TOKEN 같은 이름을, Value에는 실제 값을 넣고, Environments에서 적용할 배포 환경을 고른 뒤 Add 버튼을 눌러 저장합니다.
Development는 Vercel CLI로 로컬에서 개발할 때(vercel dev) 쓰는 값이고, Preview는 운영 브랜치가 아닌 Git 푸시나 Preview 배포 명령으로 만드는 미리보기 환경, Production은 main 브랜치에 병합되거나 운영 배포를 할 때 쓰는 최종 운영 환경입니다. 필요에 따라 환경마다 다른 값을 설정할 수 있습니다.
Vercel 환경 변수의 동작 방식
Vercel에 설정한 환경 변수는 고른 환경 범위에 따라 빌드 과정과 배포된 서버리스 함수에서 쓸 수 있습니다. Production 환경 변수는 main 같은 기본 운영 브랜치에 푸시되어 배포될 때 쓰입니다. Preview 환경 변수는 미리보기 배포의 빌드와 서버 처리에 쓰이므로, 운영 데이터에 영향을 주지 않도록 별도의 백엔드 값을 설정합니다. Development 환경 변수는 vercel dev로 실행할 때 Vercel CLI가 로컬 프로젝트 설정과 함께 쓰며, 일반 next dev에서 같은 값을 쓰려면 먼저 vercel env pull로 환경 파일을 만듭니다.
Vercel에 저장한 변경은 기존 배포를 바꾸지 않으며, 다음 배포부터 적용됩니다.
Next · Build and runtime values
환경 값의 읽기 시점
공개 값 직접 참조, 빌드 중 정적 생성, 요청 시 동적 서버 처리를 구분합니다. 값의 읽기 시점에 따라 다시 빌드할지, 정적 결과를 갱신할지, 실행 환경을 바꿀지가 달라집니다.
| 읽는 위치 | 값이 결정되는 시점 | 변경 반영 |
|---|---|---|
| 공개 값 직접 참조 | process.env.NEXT_PUBLIC_NAME처럼 직접 참조한 값을 빌드 때 인라인 | 새 값으로 다시 빌드·배포 |
| 정적 페이지 생성 | 서버 코드가 빌드 중 읽은 값 | 정적 결과를 다시 생성 |
| 동적 서버 처리 | 실행 중인 서버 환경에서 조회 | 플랫폼의 새 배포·프로세스 갱신 정책 적용 |
- 공개 값 직접 참조
- 값이 결정되는 시점
process.env.NEXT_PUBLIC_NAME처럼 직접 참조한 값을 빌드 때 인라인- 변경 반영
- 새 값으로 다시 빌드·배포
- 정적 페이지 생성
- 값이 결정되는 시점
- 서버 코드가 빌드 중 읽은 값
- 변경 반영
- 정적 결과를 다시 생성
- 동적 서버 처리
- 값이 결정되는 시점
- 실행 중인 서버 환경에서 조회
- 변경 반영
- 플랫폼의 새 배포·프로세스 갱신 정책 적용
접두사가 없는 값도 응답·로그에 직접 내보내면 노출됩니다. 환경 변수 이름만으로 비밀이 보호되지는 않습니다.
위 표는 환경 값을 읽는 위치에 따라 값이 정해지는 시점과 변경을 반영하는 방법이 다르다는 점을 정리합니다. process.env.NEXT_PUBLIC_NAME처럼 공개 값을 직접 참조하면 빌드할 때 그 값이 코드에 들어가므로, 바꾸려면 새 값으로 다시 빌드하고 배포해야 합니다. 정적 페이지를 생성하는 서버 코드가 빌드 중에 읽은 값은 정적 결과를 다시 생성해야 바뀝니다. 요청을 받을 때 동적으로 처리하는 서버 코드는 실행 중인 서버 환경에서 값을 읽으므로, 플랫폼의 새 배포나 프로세스 갱신 정책에 따라 반영됩니다. 표의 아래 설명처럼 접두사가 없는 값도 응답이나 로그에 그대로 내보내면 노출되며, 환경 변수의 이름만으로 비밀이 보호되지는 않습니다.
환경 변수 관리 모범 사례 및 보안 팁
데이터베이스 자격 증명이나 서버 API 키 같은 민감한 정보는 클라이언트에 노출되지 않도록 반드시 NEXT_PUBLIC_ 접두사 없이 씁니다. 로컬 개발용 .env.local 파일은 실수로 Git 저장소에 커밋되지 않도록 .gitignore에 반드시 넣습니다. 이름은 DB_HOST, API_URL, STRIPE_SECRET_KEY처럼 분명하고 일관된 규칙으로 짓고, 개발, 테스트, 운영 환경마다 알맞은 값을 설정해 실수로 운영 데이터베이스에 개발 데이터를 쓰는 일을 막습니다.
공개 값은 process.env.NEXT_PUBLIC_NAME처럼 직접 참조해야 빌드할 때 코드에 들어갑니다. 변수 이름을 계산하는 process.env[name]이나 process.env를 다른 이름에 담아 쓰는 방식은 같은 식으로 들어가지 않습니다.
GitHub Actions나 GitLab CI/CD 같은 파이프라인을 쓴다면, 그 서비스의 비밀 변수(Secrets) 관리 기능으로 환경 변수를 안전하게 주입합니다. Vercel의 Git 배포는 Vercel 프로젝트에 등록한 값을 쓰며, GitHub Actions의 Secrets가 Vercel 환경 변수로 저절로 복사되지는 않습니다. 정기적으로 환경 변수 설정과 쓰임새를 검토해 불필요한 노출이나 보안 취약점이 없는지 확인합니다.
환경별 값이 올바른지는 변수가 있는지만이 아니라 실제 기능의 응답으로 확인하며, 로그에는 비밀 값 자체를 남기지 않습니다.
정리
- Next.js는
process.env,.env.$(NODE_ENV).local,.env.local,.env.$(NODE_ENV),.env순서로 값을 찾으며,test모드에서는.env.local을 읽지 않습니다. - 접두사 없는 변수는 서버 코드에서만 읽히고,
NEXT_PUBLIC_변수는 빌드할 때 브라우저 번들에 들어갑니다. - 공개 값은
process.env.NEXT_PUBLIC_NAME처럼 직접 참조해야 빌드할 때 코드에 들어갑니다. - Vercel의 환경 변수는 Development, Preview, Production 범위마다 따로 설정하며, 바꾼 값은 다음 배포부터 적용됩니다.
- 값이 있는지 확인하는 것과 실제 연결이 유효한지 확인하는 것은 다르며, 비밀 값은 로그나 응답에 내보내지 않습니다.
연습 문제
1. 값이 선택되는 과정 확인하기
NODE_ENV=test이고 프로세스에는 DATABASE_URL이 없다고 합시다. .env.local에는 로컬 DB 주소가, .env.test에는 테스트 DB 주소가 있습니다. GET 예제는 어느 값을 읽을까요? 이어서 실행하기 전에 프로세스에 DATABASE_URL=not-a-database-url을 넣었다면 상태 코드와 본문은 무엇일까요?
해설 보기
test 모드는 .env.local을 건너뛰므로, 첫 조건에서는 .env.test의 테스트 주소를 읽습니다. 프로세스에 이미 있는 값은 파일의 값보다 우선합니다. 두 번째 조건의 문자열은 유효한 DB 주소가 아니어도 비어 있지 않으므로, 함수는 기본 상태 코드 200과 databaseConfigured: true를 돌려줍니다. 실제로 연결을 시도하는 코드가 없기 때문입니다.
운영 점검에서 이 응답만 보고 DB 연결에 성공했다고 판단하지 않습니다. 연결 검사를 추가하려면 쓸 DB 드라이버, 연결 기한, 실패 응답을 먼저 정해야 하며, 클라이언트에 자격 증명 자체를 돌려주지 않는 조건도 유지합니다.
2. 배포된 공개 값 확인하기
NEXT_PUBLIC_API_BASE_URL을 A 주소로 빌드한 뒤 대시보드에서 B로 바꾸고, 기존 배포의 브라우저를 새로 고치면 어느 값을 쓸까요? AnalyticsTracker에 ID만 넣으면 방문 기록도 전송될까요?
해설 보기
기존 번들에는 A가 고정되어 있으므로, 새로 빌드하고 배포해야 B가 쓰입니다. 단순히 새로 고친다고 B가 주입되지는 않습니다. AnalyticsTracker는 ID를 읽지만 초기화 코드가 주석이므로, ID만 설정해서는 분석 요청을 보내지 않습니다. 값이 있는지, 코드가 실행되는지, 외부 서비스가 받았는지를 각각 확인합니다.
3. 계산한 이름으로 공개 값 읽기
클라이언트 컴포넌트에서 const key = 'NEXT_PUBLIC_' + 'MAPBOX_TOKEN'; const token = process.env[key];처럼 이름을 만들어 값을 읽었더니 undefined가 나왔습니다. 왜 그럴까요?
해설 보기
Next.js는 빌드할 때 코드에서 process.env.NEXT_PUBLIC_MAPBOX_TOKEN처럼 직접 쓴 참조를 찾아 그 자리에 값을 넣습니다. 이름을 실행 중에 계산하는 process.env[key]는 빌드할 때 어떤 변수인지 알 수 없으므로 값이 들어가지 않고, 브라우저에는 process.env의 실제 내용도 없으므로 undefined가 됩니다. 공개 값은 process.env.NEXT_PUBLIC_MAPBOX_TOKEN처럼 직접 참조해야 합니다.
Next.js의 환경 변수 규칙은 공식 문서의 Environment Variables 안내에서 확인할 수 있습니다.
다음 글 커스텀 도메인 연결에서는 배포한 애플리케이션에 직접 소유한 도메인을 연결하는 방법을 알아봅니다.