📌 Next.js 16.3.4 공식 문서(Environment Variables, Data Security)를 기준으로 정리했다. 예제의 변수 이름과 값은 설명을 위한 가상의 값이다.
정의
.env 는 앱이 실행될 때 필요한 설정값을 이름=값 형태로 한 줄씩 적어 두는 텍스트 파일이다. 코드에는 변수 이름만 쓰고, 실제 값은 이 파일이나 서버의 환경 설정에 둔다.
Next.js 는 따로 설정하지 않아도 프로젝트 루트의 .env* 파일을 읽어 process.env 에 넣어 준다. 그래서 코드에서는 process.env.DATABASE_URL 처럼 꺼내 쓰면 된다.
개요
같은 코드가 여러 곳에서 돈다. 로컬의 next dev, 테스트, Vercel 같은 운영 서버가 그렇다. 곳마다 DB 주소와 API 키가 다르다. 값을 코드에 직접 적으면 환경이 바뀔 때마다 코드를 고쳐야 하고, 비밀번호가 git 이력에 그대로 남는다.
.env 를 쓰면 코드는 하나로 두고 값만 환경마다 바꿀 수 있다. Next.js 에서는 다음 세 가지를 알고 있어야 한다.
-
파일이 여러 종류이고, 읽는 순서가 정해져 있다.
-
기본은 서버에서만 읽힌다. 브라우저에서 쓰려면 이름 앞에
NEXT_PUBLIC_을 붙여야 한다. -
NEXT_PUBLIC_값은next build할 때 JS 파일에 박혀서 고정된다.
역할
1. 비밀값을 코드 밖에 둔다
DB 비밀번호, 외부 API 키, 로그인 토큰 서명 키 같은 값은 코드에 쓰지 않고 .env.local 이나 배포 환경 설정에 둔다. create-next-app 의 기본 .gitignore 는 .env* 를 모두 제외한다. 공식 문서도 이 파일들은 거의 커밋하지 않는다고 적고 있다.
2. 환경마다 다른 값을 쓴다
Next.js 는 NODE_ENV 값에 따라 읽는 파일이 달라진다. next dev 는 development, 그 밖의 명령(next build, next start)은 production 으로 자동 지정된다.
| 파일 | 언제 읽나 | git |
|---|---|---|
.env |
모든 환경의 기본값 | 비밀값이 없을 때만 |
.env.development |
next dev |
비밀값이 없을 때만 |
.env.production |
next build, next start |
비밀값이 없을 때만 |
.env.test |
NODE_ENV=test (Jest 같은 테스트 도구) |
올린다 |
.env.local |
로컬에서 위 값을 덮어쓸 때. 테스트 환경에서는 읽지 않는다 | 올리지 않는다 |
.env.development.local 등 |
특정 환경에서만 로컬 값으로 덮어쓸 때 | 올리지 않는다 |
같은 이름이 여러 곳에 있으면 아래 순서로 찾고, 먼저 찾은 값을 쓴다.
-
process.env(셸에서 지정한 값, Vercel 같은 배포 환경에 등록한 값) -
.env.$(NODE_ENV).local -
.env.local(테스트 환경에서는 건너뛴다) -
.env.$(NODE_ENV) -
.env
예를 들어 next dev 에서 .env.development.local 과 .env 에 같은 변수가 있으면 .env.development.local 값이 쓰인다.
3. 서버와 브라우저의 경계를 정한다
이름에 NEXT_PUBLIC_ 이 없는 변수는 서버(Node.js)에서만 읽힌다. 브라우저로 가는 JS 에는 들어가지 않는다.
NEXT_PUBLIC_ 을 붙이면 Next.js 가 빌드할 때 process.env.NEXT_PUBLIC_… 을 실제 값 문자열로 바꿔 JS 번들에 넣는다. 누구나 브라우저 개발자 도구에서 볼 수 있다는 뜻이다. 비밀값에는 절대 붙이지 않는다.
실전예제
파일 만들기
# .env.local (git 에 올리지 않는다)
DATABASE_URL=postgres://myuser:mypassword@localhost:5432/board
AUTH_SECRET=로컬에서만-쓰는-긴-임의-문자열
NEXT_PUBLIC_API_BASE_URL=http://localhost:4000
값 없이 이름만 적은 .env.example 을 함께 커밋해 두면, 다른 사람이나 나중에 다시 보는 사람이 어떤 변수가 필요한지 바로 알 수 있다. Next.js 가 읽는 파일은 아니고 팀에서 흔히 쓰는 관례다.
# .env.example (git 에 올린다)
DATABASE_URL=
AUTH_SECRET=
NEXT_PUBLIC_API_BASE_URL=
서버에서 읽기
비밀값은 DB 에 접근하는 모듈 한곳에서만 읽는다. import 'server-only' 를 넣어 두면, 이 모듈을 클라이언트 컴포넌트에서 import 했을 때 빌드 오류가 나서 실수를 막아 준다.
// lib/db.ts
import 'server-only'
const databaseUrl = process.env.DATABASE_URL
if (!databaseUrl) {
throw new Error('DATABASE_URL 이 없습니다. .env.local 을 확인하세요.')
}
Route Handler 에서도 똑같이 process.env 로 읽는다.
// app/api/health/route.ts
export async function GET() {
return Response.json({ db: Boolean(process.env.DATABASE_URL) })
}
브라우저에서 읽기
'use client'
export function ApiBase() {
// 빌드할 때 'http://localhost:4000' 문자열로 바뀐다
return <p>{process.env.NEXT_PUBLIC_API_BASE_URL}</p>
}
클라이언트 컴포넌트에서 process.env.DATABASE_URL 을 읽으면 브라우저에서 실행될 때 값이 없다. NEXT_PUBLIC_ 이 없는 변수는 브라우저로 보내지 않기 때문이다. 클라이언트 컴포넌트도 처음에는 서버에서 미리 렌더링되므로, 서버에서는 값이 보이다가 브라우저에서는 사라져 화면이 어긋날 수 있다.
배포 환경 (Vercel)
.env.local 은 .gitignore 에 걸려 있어서 배포 서버로 올라가지 않는다. 운영 값은 Vercel 프로젝트의 Settings → Environment Variables 에 Production, Preview, Development 별로 등록한다. 등록한 값은 읽는 순서의 1번 process.env 로 들어간다.
반대로 Vercel 에 등록한 값을 로컬로 가져오려면 vercel env pull 을 쓴다. 기본으로 .env.local 파일이 만들어진다.
NEXT_PUBLIC_ 값을 Vercel 에서 바꿨다면 다시 빌드(재배포)해야 반영된다. 이미 만들어진 JS 에는 예전 값이 박혀 있다.
심화내용
빌드할 때 정해지는 값과 실행할 때 읽는 값
NEXT_PUBLIC_ 값은 next build 를 실행한 환경의 값으로 고정된다. 빌드한 Docker 이미지 하나를 여러 환경에 그대로 옮겨 쓰면, 모든 환경에서 빌드 때 값이 쓰인다. 환경마다 달라야 하는 값을 브라우저에 주려면 서버에서 읽어 API 나 서버 컴포넌트로 내려 줘야 한다.
서버 쪽 변수도 요청 시점의 값을 확실히 읽으려면 동적 렌더링 안에서 읽는다. 공식 문서의 예시는 await connection() 뒤에 process.env 를 읽는 방식이다.
import { connection } from 'next/server'
export default async function Page() {
await connection() // 이 아래는 요청이 올 때 실행된다
const value = process.env.MY_VALUE
// ...
}
이름을 그대로 써야 값이 들어간다
Next.js 는 코드에 process.env.NEXT_PUBLIC_이름 이 그대로 적힌 곳만 값으로 바꾼다. 아래처럼 변수를 거쳐 꺼내면 바뀌지 않아 브라우저에서 값이 없다.
const key = 'NEXT_PUBLIC_ANALYTICS_ID'
process.env[key] // 바뀌지 않는다
const env = process.env
env.NEXT_PUBLIC_ANALYTICS_ID // 바뀌지 않는다
다른 변수 참조와 여러 줄 값
.env* 안에서 $변수이름 으로 다른 변수를 가져다 쓸 수 있다. 값에 $ 문자가 실제로 들어가야 하면 \$ 로 적는다.
API_HOST=localhost:4000
API_URL=http://$API_HOST/v1
인증서 같은 여러 줄 값은 큰따옴표로 감싸서 줄바꿈 그대로 적거나, 한 줄에 \n 을 넣어 적는다.
Next.js 밖에서 읽을 때
ORM 설정 파일이나 테스트 실행기처럼 Next.js 가 실행되지 않는 곳에서는 .env 가 자동으로 읽히지 않는다. 이때는 @next/env 의 loadEnvConfig 로 Next.js 와 같은 규칙으로 불러온다.
// envConfig.ts
import { loadEnvConfig } from '@next/env'
loadEnvConfig(process.cwd())
그 밖에 알아 둘 것
-
src폴더를 써도.env*파일은 프로젝트 루트(package.json옆)에 둔다.src안에 두면 읽지 않는다. -
next dev는.env*파일이 바뀌면 다시 읽고 터미널에Reload env: .env.local같은 로그를 남긴다. -
공식 문서는
process.env를 데이터 접근 계층(DB·외부 API 를 다루는 모듈)에서만 읽으라고 권한다. 비밀값이 앱의 다른 부분으로 퍼지지 않게 하기 위해서다.
정리
-
.env는 코드와 설정값을 나누는 파일이다. 코드는 하나로 두고 값만 환경마다 바꾼다. -
비밀값은
.env.local과 배포 환경 설정에만 두고, git 에는 이름만 적은.env.example을 올린다. -
같은 이름이 여러 곳에 있으면
process.env→.env.$(NODE_ENV).local→.env.local→.env.$(NODE_ENV)→.env순서로 먼저 찾은 값을 쓴다. -
기본은 서버 전용이다. 브라우저에 필요한 값만
NEXT_PUBLIC_을 붙이고, 그 값은 누구나 볼 수 있다고 생각한다. -
NEXT_PUBLIC_값은 빌드할 때 고정된다. 바꾸면 다시 빌드해야 한다. -
Next.js 밖(ORM 설정, 테스트)에서는
@next/env의loadEnvConfig로 불러온다.

답글 남기기