환경변수 .env 파일 차이: local, example, development 기준

2026.06.25·수정 2026.07.19·약 17분

환경변수를 처음 보면 이름부터 헷갈립니다. .env, .env.local, .env.example처럼 비슷한 파일이 여러 개 있고, Next.js에서는 NEXT_PUBLIC_, Vite에서는 VITE_ 같은 prefix까지 붙습니다. 그래서 처음에는 “그냥 중요한 값을 숨기는 파일인가?” 정도로 이해하기 쉽습니다.

하지만 환경변수는 단순히 비밀키를 숨기는 용도가 아닙니다. 더 정확히는 코드와 실행 환경마다 달라지는 설정값을 분리해서 관리하는 방식입니다. 로컬 개발에서는 테스트 API를 보고, 운영 배포에서는 실제 API를 보며, 팀원마다 다른 로컬 설정을 사용할 수 있게 만드는 기준에 가깝습니다.

이 글에서 확인할 내용

아래 섹션 링크를 통해 필요한 내용을 바로 확인할 수 있습니다.

환경변수는 왜 필요한가

코드에 직접 적은 설정값을 환경변수 파일로 분리하는 흐름

프로젝트 안에는 코드와 함께 여러 설정값이 들어갑니다. API 서버 주소, Firebase 프로젝트 ID, 이미지 업로드 경로, 관리자 기능에서 사용할 서버 전용 Secret 등이 대표적입니다. 이 값을 코드에 직접 적으면 처음에는 편해 보이지만, 환경이 바뀌는 순간 문제가 생깁니다.

const API_BASE_URL = "https://dev-api.example.com";
const FIREBASE_PROJECT_ID = "my-project-dev";

위처럼 코드 안에 개발용 주소를 직접 적어두면 운영 배포 때마다 코드를 수정해야 합니다. 수정 자체도 번거롭지만, 더 큰 문제는 “운영 주소로 바꾸는 작업”이 코드 변경 이력에 섞인다는 점입니다. 설정값 변경과 기능 수정이 같은 커밋에 들어가면, 나중에 문제가 생겼을 때 원인을 추적하기도 애매해집니다.

더 위험한 경우도 있습니다. 외부 API Secret이나 데이터베이스 접속 문자열처럼 노출되면 안 되는 값이 코드에 들어가면, GitHub에 올라가는 순간 값이 함께 공개될 수 있습니다. 공개 저장소가 아니어도 팀원, 배포 서버, 자동화 도구가 모두 같은 코드 이력을 보게 되므로 비밀값을 코드에 직접 두는 구조는 피해야 합니다.

환경변수를 사용하면 코드는 “어떤 값이 필요하다”는 사실만 알고, 실제 값은 실행 환경에서 주입받습니다. 같은 코드라도 로컬에서는 로컬 값, 개발 서버에서는 개발 값, 운영 서버에서는 운영 값을 읽게 만들 수 있습니다.

const API_BASE_URL = process.env.API_BASE_URL;

여기서 볼 기준은 “비밀값인가?” 하나가 아닙니다. “환경마다 달라지는가?”, “브라우저에 노출되어도 되는가?”, “팀원 모두가 같은 값을 써야 하는가?”까지 함께 봐야 합니다.

.env, .env.local, .env.example 차이

.env .env.local .env.example .env.development .env.production 파일 역할 비교

.env 계열 파일은 이름이 비슷하지만 역할이 다릅니다. 파일명을 외우기보다 먼저 세 가지로 나누면 됩니다. 공유 가능한 기본값인지, 내 컴퓨터에서만 쓰는 값인지, 실제 값 없이 변수 이름만 알려주는 예시 파일인지 확인합니다. 같은 변수 이름이 여러 파일에 있을 때는 프레임워크의 로드 순서와 실행 모드에 따라 최종 값이 달라질 수 있으므로, 파일 역할을 먼저 분리해두는 것이 좋습니다.

파일명 주요 역할 커밋 여부
.env 프로젝트에서 공통으로 사용할 기본 환경값 팀 기준에 따라 다름
.env.local 내 로컬 컴퓨터에서만 사용할 개인 설정값 보통 커밋하지 않음
.env.example 필요한 변수 이름과 형식을 알려주는 예시 파일 커밋함
.env.development 개발 모드에서 사용할 값 비밀값 포함 여부에 따라 판단
.env.production 운영 모드에서 사용할 값 비밀값이 있거나 팀 기준상 비공개라면 커밋하지 않음

.env는 기본 환경값을 담는다

.env는 프로젝트에서 기본으로 읽을 환경값을 담는 파일입니다. 예를 들어 로컬과 운영에서 크게 다르지 않은 공개 설정값이나, 개발자가 참고할 수 있는 기본 주소를 넣을 수 있습니다. 다만 팀마다 운영 방식이 다르기 때문에 .env를 무조건 커밋한다고 단정하면 안 됩니다.

API_BASE_URL=https://api.example.com
APP_ENV=local

이 파일에 비밀키가 들어간다면 커밋하면 안 됩니다. 이름이 .env라고 해서 안전한 것도 아니고, 프로젝트 안에 있는 파일인 이상 Git 추적 대상이 될 수 있습니다.

.env.local은 내 컴퓨터에서만 쓰는 값이다

.env.local은 개인 또는 머신별 비공개 오버라이드 값에 가깝습니다. 같은 프로젝트를 쓰더라도 팀원마다 로컬 API 서버 주소, 테스트 계정, 임시 토큰, Firebase 테스트 프로젝트가 다를 수 있습니다. 이런 값은 공용 파일에 넣기보다 각자 .env.local에 둡니다. 다만 프레임워크에 따라 배포 환경에서도 .env.local이 로드될 수 있으므로, 보통 저장소에 올리지 않고 배포 플랫폼의 환경변수나 별도 Secret 설정으로 관리합니다.

API_BASE_URL=http://localhost:4000
LOCAL_TEST_USER_EMAIL=test@example.com
LOCAL_FEATURE_FLAG=true

Next.js에서 .env.local 값을 수정했는데 화면에 이전 값이 계속 보인다면 파일을 잘못 쓴 것만의 문제는 아닐 수 있습니다. 개발 서버 재시작, 빌드 시점, 클라이언트 prefix까지 함께 봐야 하므로 Next.js 환경변수 적용 오류 해결 흐름으로 이어서 점검하면 좋습니다.

.env.example은 실제 실행 파일이 아니라 안내서에 가깝다

.env.example은 보통 실제 앱 실행에 직접 쓰기보다, 프로젝트를 받은 사람이 어떤 환경변수를 준비해야 하는지 알려주는 파일로 사용합니다. 핵심은 실제 값은 빼고 변수 이름과 형식만 남기는 것입니다.

API_BASE_URL=
NEXT_PUBLIC_API_BASE_URL=
VITE_API_BASE_URL=
SECRET_API_KEY=

팀원이 저장소를 처음 받은 뒤 .env.example을 복사해서 .env.local을 만들면, 어떤 변수가 필요한지 빠르게 파악할 수 있습니다. 값은 각자 채우되, 변수 이름은 팀 전체가 같은 기준으로 맞추는 방식입니다.

.env.development와 .env.production은 실행 모드별 파일이다

.env.development.env.production은 실행 모드에 따라 다른 값을 쓰고 싶을 때 사용합니다. 개발 모드에서는 개발 서버 주소를 보고, 운영 빌드에서는 운영 서버 주소를 보게 만드는 식입니다.

# .env.development
API_BASE_URL=https://dev-api.example.com

# .env.production
API_BASE_URL=https://api.example.com

이때도 파일명보다 값의 성격이 우선입니다. 운영용 값이라고 해서 전부 비밀값은 아니고, 개발용 값이라고 해서 전부 공개해도 되는 것도 아닙니다. 특히 실제 서비스의 Secret, DB 접속 문자열, 관리자 토큰은 모드와 관계없이 커밋하지 않는 기준으로 봐야 합니다.

또 하나 헷갈리는 지점은 우선순위입니다. 예를 들어 Vite에서는 기본 .env 파일과 모드별 .env.production 파일을 함께 사용할 수 있고, 로컬 전용 값을 담는 .env.production.local 같은 파일도 둘 수 있습니다. 이런 구조에서는 “어떤 파일에 값이 있느냐”만 보지 말고, 지금 실행 중인 모드가 development인지 production인지도 함께 확인해야 합니다.

환경변수 값은 보통 문자열로 들어온다는 점도 기억해야 합니다. LOCAL_FEATURE_FLAG=true라고 적어도 코드에서 읽을 때는 불리언 true가 아니라 문자열 "true"로 다뤄지는 경우가 많습니다. 숫자나 불리언처럼 사용해야 하는 값은 코드에서 명시적으로 변환하는 기준을 잡아두는 것이 안전합니다.

Next.js와 Vite에서 환경변수를 읽는 방식

Next.js NEXT_PUBLIC과 Vite VITE prefix의 클라이언트 노출 차이 비교

프론트엔드 프로젝트에서 환경변수를 다룰 때 가장 조심해야 하는 부분은 클라이언트 노출입니다. 환경변수를 파일에 넣었다고 해서 모두 숨겨지는 것이 아닙니다. 브라우저에서 실행되는 코드에 포함되는 값은 사용자가 개발자 도구나 번들 파일을 통해 확인할 수 있다고 보는 편이 맞습니다.

Next.js는 process.env와 NEXT_PUBLIC_ 기준을 구분한다

Next.js에서는 서버 쪽 코드에서 process.env로 환경변수를 읽을 수 있습니다. 반면 클라이언트 번들에 포함해도 되는 값은 보통 NEXT_PUBLIC_ prefix를 붙입니다. 이름만 보면 공개용 변수처럼 느껴지지 않을 수 있지만, 실제 의미는 “브라우저에서 접근 가능한 값”에 가깝습니다.

NEXT_PUBLIC_API_BASE_URL=https://api.example.com
SECRET_API_KEY=__SERVER_ONLY_SECRET__
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;

여기서 NEXT_PUBLIC_은 “비밀값”이라는 뜻이 아닙니다. 오히려 브라우저 쪽 코드에 포함되어도 되는 값이라는 표시로 이해해야 합니다. API 주소, 공개 Firebase 설정값처럼 사용자가 알아도 큰 문제가 없는 값에는 사용할 수 있지만, 결제 Secret, 관리자 토큰, 외부 API Secret에는 붙이면 안 됩니다. 이름에 PUBLIC이 들어간 변수는 사용자가 볼 수 있는 값이라고 생각하는 쪽이 안전합니다.

Next.js의 NEXT_PUBLIC_ 값은 빌드할 때 클라이언트 JavaScript에 포함됩니다. 그래서 배포가 끝난 뒤 서버 환경변수만 바꿔도 이미 빌드된 화면에는 바로 반영되지 않을 수 있습니다. 공개 변수는 빌드 시점 값인지, 런타임에 바뀌어야 하는 값인지도 함께 확인해야 합니다.

Vite는 import.meta.env와 VITE_ 기준을 사용한다

브라우저로 번들되는 Vite 앱 코드에서는 process.env 대신 import.meta.env를 기준으로 환경변수에 접근합니다. 그리고 클라이언트 코드에서 읽을 변수는 기본적으로 VITE_ prefix가 필요합니다.

VITE_API_BASE_URL=https://api.example.com
SECRET_API_KEY=__SERVER_ONLY_SECRET__
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL;

Vite에서 .env에 값을 넣었는데 import.meta.env에서 undefined가 나온다면 변수 이름, prefix, 파일 위치, 실행 모드, 개발 서버 재시작 여부를 함께 확인해야 합니다. 실제 오류 상황은 Vite import.meta.env undefined 해결 글과 연결해서 보면 흐름을 잡기 쉽습니다.

Vite도 VITE_가 붙은 값은 빌드 결과물에 포함될 수 있으므로 민감한 값을 넣지 않습니다. 또한 .env.[mode] 파일은 실행 모드에 따라 우선순위가 달라지고, 이미 셸이나 배포 환경에 등록된 변수는 파일 값보다 우선할 수 있습니다.

.gitignore와 .env.example을 같이 쓰는 이유

환경변수 파일을 다룰 때 가장 많이 나오는 조합은 .gitignore.env.example입니다. 둘은 서로 반대 역할을 합니다. .gitignore는 실제 값을 Git에 올리지 않기 위한 장치이고, .env.example은 필요한 변수 목록을 Git에 남기기 위한 장치입니다.

.env*
!.env.example

비밀키를 커밋하지 않는 이유는 단순히 “보안상 좋지 않아서”가 아닙니다. Git은 변경 이력을 남기기 때문에, 한 번 커밋된 비밀값은 나중에 파일에서 지워도 이력에 남을 수 있습니다. 공개 저장소라면 검색이나 자동 탐지 도구에 잡힐 가능성도 있습니다. 실수로 커밋했다면 파일에서 삭제하는 것만으로 끝내지 말고, 해당 키를 폐기하고 새 키를 발급하는 단계까지 봐야 합니다.

단, .gitignore는 앞으로 새로 추가되는 파일을 막는 장치입니다. 이미 Git이 추적 중인 파일은 ignore 규칙만 추가해도 자동으로 사라지지 않으므로, 추적 제거와 키 폐기 여부를 따로 확인해야 합니다.

그래서 실제 값이 들어가는 파일은 Git에서 제외하고, 대신 .env.example에는 변수 이름만 남깁니다. 이렇게 하면 팀원은 어떤 값이 필요한지 알 수 있고, 저장소에는 실제 Secret이 남지 않습니다.

# .env.example
NEXT_PUBLIC_API_BASE_URL=
VITE_API_BASE_URL=
FIREBASE_PROJECT_ID=
SECRET_API_KEY=

이 구조에서 실수하기 쉬운 부분은 SECRET_API_KEY 같은 값을 프론트엔드에서 직접 쓰려고 하는 경우입니다. 외부 API Secret처럼 브라우저에 노출되면 안 되는 값은 prefix를 붙여 해결할 문제가 아니며, Firebase를 사용한다면 Firebase Functions Secret 처리처럼 서버 쪽에서 다루는 구조를 따로 잡아야 합니다.

실제 프로젝트에서 나눠 쓰는 예시

실제 프로젝트에서는 값의 이름보다 노출 범위를 먼저 정하는 것이 좋습니다. 같은 Firebase 관련 값이라도 브라우저에서 사용해도 되는 공개 설정값이 있고, 서버에서만 다뤄야 하는 Secret이 있습니다. 같은 API 관련 값이라도 단순한 API 주소는 공개되어도 괜찮을 수 있지만, 요청 권한을 가진 Secret Key는 공개되면 안 됩니다. 변수 이름에 PUBLIC이나 VITE가 들어갔는지보다, 그 값이 최종적으로 브라우저 번들에 들어가는지를 기준으로 판단해야 합니다.

값의 종류 예시 권장 위치
공개 API 주소 NEXT_PUBLIC_API_BASE_URL, VITE_API_BASE_URL 클라이언트 노출 가능
Firebase 공개 설정값 NEXT_PUBLIC_FIREBASE_PROJECT_ID, VITE_FIREBASE_PROJECT_ID 사용 방식에 따라 클라이언트 노출 가능
외부 API Secret SECRET_API_KEY 서버 환경변수 또는 서버 함수
DB 접속 문자열 DATABASE_URL 서버 전용
로컬 테스트 값 LOCAL_TEST_USER_EMAIL .env.local

Firebase Auth를 사용할 때 로그인 도메인 오류가 난다면 환경변수만 보지 말고, Firebase Auth unauthorized-domain 오류 해결처럼 배포 도메인 기준 설정도 함께 확인해야 합니다.

초보 단계에서는 “환경변수에 넣었으니 안전하다”는 식으로 이해하지 않는 것이 중요합니다. 더 정확한 기준은 “이 값이 최종적으로 어디에서 읽히는가”입니다. 서버에서만 읽히면 비교적 안전하게 관리할 수 있지만, 브라우저에서 읽히는 값은 사용자에게 공개된 값으로 봐야 합니다.

환경변수 작업 전 체크리스트

환경변수를 추가하거나 수정한 뒤에는 코드보다 설정 문제에서 막히는 경우가 많습니다. 값이 안 바뀌거나 undefined가 나온다면 바로 코드를 고치기보다 아래 순서로 확인하는 것이 좋습니다.

  • 환경변수 파일을 수정한 뒤 개발 서버를 재시작했는가?
  • Next.js 클라이언트 변수에는 NEXT_PUBLIC_ prefix를 붙였는가?
  • Vite 클라이언트 변수에는 VITE_ prefix를 붙였는가?
  • 비밀키에 클라이언트 노출 prefix를 붙이지 않았는가?
  • 운영 배포 환경에는 별도로 환경변수를 등록했는가?
  • 지금 실행 중인 모드가 development인지 production인지 확인했는가?
  • .env.local 같은 개인 설정 파일이 GitHub에 올라가지 않도록 처리했는가?
  • .env.example에 필요한 변수 이름은 남겨두었는가?

환경변수 파일은 이름이 비슷해서 처음에는 복잡해 보이지만, 기준은 단순합니다. .env는 기본값, .env.local은 내 로컬 값, .env.example은 공유할 변수 목록, .env.development.env.production은 실행 모드별 값으로 보면 됩니다.

마지막으로 보안 기준은 파일명이 아니라 노출 위치로 판단해야 합니다. NEXT_PUBLIC_이나 VITE_가 붙은 값은 브라우저 번들에 포함될 수 있으므로 Secret을 넣지 않습니다. 반대로 팀원이 알아야 하는 변수 이름은 .env.example에 남겨두고, 실제 값은 각자의 로컬 환경이나 배포 플랫폼 환경변수에 따로 등록하는 방식으로 관리하는 것이 안전합니다.

공식 기준은 프레임워크별로 조금씩 다릅니다. Next.js는 NEXT_PUBLIC_ 값이 빌드 시 클라이언트 번들에 포함될 수 있다고 설명하고, Vite는 VITE_ 값에 민감한 정보를 넣지 말라고 안내합니다. 세부 로드 순서와 모드별 파일 규칙은 Next.js 환경변수 문서Vite Env Variables and Modes 문서를 기준으로 확인하는 편이 안전합니다.

이 글이 마음에 드세요?

RSS 피드를 구독하세요!

댓글 남기기