.env 파일은 현대 개발에서 가장 널리 사용되는 파일 중 하나입니다. Twelve-Factor App 방법론을 따르는 거의 모든 프로젝트가 사용합니다. 하지만 .env에는 공식 명세가 없고, 따옴표 누락·불필요한 공백·중복 키 같은 미묘한 문법 오류는 런타임 버그가 발생하기 전까지 눈에 띄지 않습니다. 온라인 env 파일 파서를 사용하면 서버 업로드나 도구 설치 없이 즉시 파일을 검증할 수 있습니다.
.env 파일이란
.env 파일은 평문 키-값 쌍으로 환경 변수를 저장합니다. 이 관행은 Node.js용 dotenv 라이브러리가 대중화했으며, 현재 거의 모든 언어 생태계로 확산되었습니다.
# 애플리케이션 설정
APP_NAME=MyApp
APP_ENV=production
APP_PORT=3000
# 데이터베이스
DATABASE_URL=postgres://user:password@localhost:5432/mydb
DATABASE_POOL_SIZE=10
# API 키
STRIPE_SECRET_KEY=sk_live_...
SENDGRID_API_KEY=SG.xxxxxxxx
python-dotenv·godotenv·dotenv-java 같은 라이브러리는 시작 시 이 값을 읽어 process.env(Node.js)나 os.environ(Python)에 주입합니다.
.env 문법 규칙
단순해 보이지만, .env에는 구현마다 다른 여러 문법 규칙이 있습니다.
기본 키-값
KEY=value
= 앞뒤에 공백을 넣지 않습니다. 키는 관례적으로 UPPER_SNAKE_CASE를 사용하지만, 소문자도 유효합니다.
따옴표가 있는 값
공백·특수 문자가 포함된 값이나 앞뒤 공백을 유지하려면 따옴표를 사용합니다:
APP_DESCRIPTION="My awesome application"
GREETING='Hello, World!'
작은따옴표와 큰따옴표 모두 유효합니다. 큰따옴표는 \n·\t·\" 같은 이스케이프 시퀀스를 지원합니다.
여러 줄 값
큰따옴표로 감싼 값은 여러 줄에 걸칠 수 있습니다:
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"
주석
#으로 시작하는 줄은 주석입니다. 인라인 주석은 일부 구현에서 지원됩니다:
# 이것은 주석입니다
PORT=8080 # 이 인라인 주석은 일부 라이브러리에서 파싱됩니다
변수 보간
일부 dotenv 라이브러리는 변수 확장을 지원합니다:
BASE_URL=https://api.example.com
API_ENDPOINT=${BASE_URL}/v1
export 문법
셸 호환성을 위한 export 접두사는 대부분의 파서에서 지원됩니다:
export DATABASE_URL=postgres://localhost/mydb
자주 하는 .env 문법 실수
텍스트 에디터에서는 보이지 않지만 실제 런타임 오류를 일으키는 실수입니다.
= 앞뒤에 공백 추가
# NG — 대부분의 파서가 "KEY "를 키 이름으로 처리
KEY = value
# OK
KEY=value
특수 문자가 포함된 값을 따옴표로 감싸지 않음
# NG — #이 주석 시작으로 인식되어 값이 잘림
PASSWORD=abc#123
# OK
PASSWORD="abc#123"
중복 키
DATABASE_URL=postgres://dev-host/mydb
DATABASE_URL=postgres://prod-host/mydb # 경고 없이 첫 번째 값을 덮어씀
Windows 스타일 줄 바꿈(CRLF)
Windows에서 만든 .env 파일에는 \r\n 줄 바꿈이 포함될 수 있습니다. 미지원 파서에서는 KEY\r이라는 키 이름이 되어 KEY\r !== KEY 비교 오류가 발생합니다.
= 가 포함된 값을 따옴표로 감싸지 않음
# NG — 일부 파서는 두 번째 =에서 값을 자름
API_KEY=abc=def=ghi
# OK
API_KEY="abc=def=ghi"
코드에서 .env 파싱하기
Node.js(dotenv)
npm install dotenv
require('dotenv').config();
// process.env.KEY로 사용 가능
console.log(process.env.DATABASE_URL);
ES 모듈의 경우:
import 'dotenv/config';
console.log(process.env.DATABASE_URL);
Python(python-dotenv)
pip install python-dotenv
from dotenv import load_dotenv
import os
load_dotenv() # 현재 디렉터리의 .env 읽기
database_url = os.getenv('DATABASE_URL')
환경 변수에 로드하지 않고 프로그래밍 방식으로 파싱:
from dotenv import dotenv_values
config = dotenv_values(".env")
print(config) # 키-값 쌍의 OrderedDict
Go(godotenv)
go get github.com/joho/godotenv
package main
import (
"fmt"
"log"
"os"
"github.com/joho/godotenv"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
fmt.Println(os.Getenv("DATABASE_URL"))
}
Shell(bash)
# .env의 모든 변수를 현재 셸에 내보내기
set -a
source .env
set +a
.env 모범 사례
.env를 버전 관리에 커밋하지 마세요. 즉시 .gitignore에 추가하고, 대신 플레이스홀더 값을 포함한 .env.example을 커밋합니다.
# .gitignore
.env
.env.local
.env.production
환경별로 파일을 분리하세요. 많은 프레임워크가 .env.development·.env.production·.env.test를 지원하고, NODE_ENV에 따라 자동으로 로드합니다.
시작 시 필수 변수를 검증하세요. envalid(Node.js)이나 pydantic-settings(Python) 같은 라이브러리로 환경 변수 스키마를 정의하고, 필수 값이 누락된 경우 빠르게 실패하도록 합니다.
// envalid 예시
import { cleanEnv, str, port, url } from 'envalid';
const env = cleanEnv(process.env, {
DATABASE_URL: url(),
APP_PORT: port({ default: 3000 }),
NODE_ENV: str({ choices: ['development', 'test', 'production'] }),
});
시크릿을 주기적으로 교체하세요. .env에는 API 키와 DB 자격 증명이 포함됩니다. 민감한 정보로 취급하고, 특히 팀원이 떠날 때 정기적으로 교체하세요.
.env를 JSON으로 내보내기
.env를 JSON으로 변환하면 유용한 경우:
- JSON을 기대하는 도구에 설정 전달(AWS Lambda, Terraform
var-file) - 환경 간 설정 비교
- CI/CD 파이프라인에서 설정 문서화
from dotenv import dotenv_values
import json
config = dotenv_values(".env")
print(json.dumps(dict(config), indent=2))
출력 예:
{
"APP_NAME": "MyApp",
"APP_ENV": "production",
"APP_PORT": "3000",
"DATABASE_URL": "postgres://user:password@localhost:5432/mydb"
}
모든 값은 JSON 문자열로 출력됩니다. .env에는 타입 시스템이 없으며, 타입 변환은 애플리케이션 레이어에서 처리됩니다.
온라인 .env 파일 파서
코드 없이 빠르게 확인·디버깅·형식 변환을 원한다면, ZeroTool env 파일 파서가 모두 브라우저 내에서 동작합니다. .env 내용을 붙여 넣으면:
- 키-값 쌍을 깔끔한 테이블로 시각화
- 줄 단위 하이라이트로 문법 오류 감지
- 중복 키 감지
- 파싱된 데이터를 JSON으로 내보내기
어떤 서버에도 데이터가 전송되지 않으며, 모두 클라이언트 사이드에서 처리됩니다.