DevToolBox무료
블로그

.env 파일 가이드: 환경 변수 모범 사례

10분 읽기by DevToolBox

환경 변수는 시크릿을 소스 코드에서 분리하고 재배포 없이 설정을 변경할 수 있게 합니다. .env 파일은 로컬에서 환경 변수를 관리하는 표준 방법입니다. 이 가이드에서는 구문 규칙, 프레임워크 설정, 보안, 환경별 파일, Docker 통합, 일반적인 오류 및 프로덕션 대안을 다룹니다.

.env 구문 규칙

.env 파일은 한 줄에 하나의 변수가 있는 일반 텍스트 파일입니다. 모든 개발자가 알아야 할 규칙:

기본 구문

각 줄은 KEY=VALUE 패턴을 따릅니다. 등호 주위에 공백을 넣지 마세요.

# .env
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=sk-1234567890abcdef
PORT=3000
DEBUG=true

따옴표 규칙

값은 따옴표 없이, 작은따옴표, 큰따옴표로 사용할 수 있습니다. 동작이 다릅니다:

# Unquoted — trailing whitespace trimmed
APP_NAME=My Application

# Single quotes — literal, no interpolation
PASSWORD='p@$$w0rd#123'

# Double quotes — escape sequences + interpolation
GREETING="Hello\nWorld"
FULL_URL="${BASE_URL}/api/v1"
  • 따옴표 없음: 후행 공백이 제거되고, 이스케이프 시퀀스 없음.
  • 작은따옴표: 값이 리터럴로 처리되며, 보간이나 이스케이프 없음.
  • 큰따옴표: 이스케이프 시퀀스(\n, \t)와 변수 보간 지원.

여러 줄 값

큰따옴표와 \n으로 줄 바꿈을 표현하거나, 큰따옴표 안에서 실제 줄 바꿈 사용:

# Method 1: \n in double quotes
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIB...\n-----END RSA PRIVATE KEY-----"

# Method 2: actual newlines in double quotes
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"

주석

#으로 시작하는 줄은 주석입니다. 일부 파서에서는 따옴표 없는 값에 인라인 주석을 사용할 수 있습니다:

# This is a full-line comment
DATABASE_URL=postgres://localhost/mydb  # inline comment (some parsers)

# Empty lines are ignored

API_KEY=abc123  # This may or may not work depending on the parser

변수 보간

${VAR} 구문으로 다른 변수를 참조 (대부분의 파서가 큰따옴표 값에서 지원):

BASE_URL=https://api.example.com
API_V1=${BASE_URL}/v1
API_V2=${BASE_URL}/v2

DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DATABASE_URL=postgres://${DB_HOST}:${DB_PORT}/${DB_NAME}

프레임워크 설정

Node.js (dotenv)

Node.js에서 가장 인기 있는 .env 로더. 설치 및 설정:

# Install
npm install dotenv

# --- app.js (CommonJS) ---
require('dotenv').config();
console.log(process.env.DATABASE_URL);

# --- app.ts (ES Modules) ---
import 'dotenv/config';
console.log(process.env.DATABASE_URL);

# --- Or load from CLI ---
node -r dotenv/config app.js
node -r dotenv/config app.js dotenv_config_path=.env.local

Next.js, Vite, Create React App은 .env 파일을 자동으로 로드합니다. 패키지 설치가 필요 없습니다.

Python (python-dotenv)

Python 프로젝트에서 .env 로드:

# Install
pip install python-dotenv

# --- app.py ---
from dotenv import load_dotenv
import os

load_dotenv()  # loads .env from current directory
# load_dotenv('.env.local')  # or specify a path

database_url = os.getenv('DATABASE_URL')
print(database_url)

# --- Django: manage.py or settings.py ---
from dotenv import load_dotenv
from pathlib import Path

env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)

Go (godotenv)

Go 프로젝트에서 .env 로드:

// Install
// go get github.com/joho/godotenv

package main

import (
    "fmt"
    "log"
    "os"
    "github.com/joho/godotenv"
)

func main() {
    err := godotenv.Load()  // loads .env
    if err != nil {
        log.Fatal("Error loading .env file")
    }
    dbURL := os.Getenv("DATABASE_URL")
    fmt.Println(dbURL)
}

PHP (vlucas/phpdotenv)

Laravel과 대부분의 PHP 프레임워크에서 사용:

// Install
// composer require vlucas/phpdotenv

<?php
require 'vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

// Access variables
$dbUrl = $_ENV['DATABASE_URL'];
// or
$dbUrl = getenv('DATABASE_URL');

// Required variables (throws exception if missing)
$dotenv->required(['DATABASE_URL', 'API_KEY']);

Ruby (dotenv gem)

Rails와 기타 Ruby 프로젝트에서 사용:

# Gemfile
gem 'dotenv-rails', groups: [:development, :test]

# Or for non-Rails Ruby:
gem 'dotenv'

# --- app.rb ---
require 'dotenv/load'
puts ENV['DATABASE_URL']

# --- Rails: .env is loaded automatically ---
# Access via ENV['DATABASE_URL'] anywhere in your app

보안: .env 파일을 절대 커밋하지 마세요

.env 파일에는 시크릿(API 키, 데이터베이스 비밀번호, 토큰)이 포함되어 있습니다. 이를 버전 관리에 커밋하는 것은 가장 큰 보안 실수입니다.

.gitignore 패턴

다음 패턴을 즉시 .gitignore 파일에 추가하세요:

# .gitignore

# Ignore all .env files
.env
.env.local
.env.*.local
.env.development
.env.production

# More aggressive pattern — ignore all .env variants
.env*

# But DO commit the example file
!.env.example

.env.example 사용

빈 값으로 .env.example 파일을 커밋하여 팀원에게 필요한 변수를 알려주세요:

# .env.example — commit this file
# Copy to .env and fill in your values

DATABASE_URL=
API_KEY=
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
REDIS_URL=
JWT_SECRET=

# Optional
DEBUG=false
LOG_LEVEL=info

이미 .env를 커밋한 경우

실수로 .env 파일을 커밋한 경우, 추적에서 제거하고 모든 시크릿을 교체하세요:

# Step 1: Remove .env from Git tracking (keeps local file)
git rm --cached .env

# Step 2: Add to .gitignore
echo ".env" >> .gitignore

# Step 3: Commit the removal
git add .gitignore
git commit -m "Remove .env from tracking, add to .gitignore"

# Step 4: CRITICAL — Rotate ALL secrets in the .env file
# Every API key, password, and token that was exposed
# must be regenerated immediately

환경별 파일

대부분의 프레임워크는 다양한 환경을 위한 여러 .env 파일을 지원합니다. 로딩 순서를 이해하는 것이 중요합니다.

로딩 순서 (Next.js / Vite / CRA)

파일은 다음 우선순위로 로드됩니다 (나중 파일이 이전 파일을 덮어씀):

# Loading priority (highest to lowest):

# 1. Shell environment variables (always win)
# 2. .env.{NODE_ENV}.local   (e.g. .env.production.local)
# 3. .env.local               (NOT loaded in test)
# 4. .env.{NODE_ENV}          (e.g. .env.production)
# 5. .env                     (default fallback)

# Example for NODE_ENV=production:
# .env                     → loaded first (base defaults)
# .env.production          → overrides .env
# .env.local               → overrides .env.production
# .env.production.local    → overrides everything above

.env.local은 항상 git에서 무시됩니다 (.gitignore에 추가). 테스트의 결정성을 유지하기 위해 테스트 중에는 이 파일이 로드되지 않습니다.

어떤 파일을 어디에 사용?

FilePurposeGit
.envDefault values, non-secret configCommit
.env.exampleTemplate with empty valuesCommit
.env.localLocal secrets & overridesIgnore
.env.developmentDev-specific (shared)Commit
.env.productionProduction-specific (non-secret)Commit
.env.testTest environment configCommit
.env.production.localProduction secrets (local only)Ignore

Docker 및 Docker Compose 통합

env_file 지시자

Docker Compose는 .env 파일을 컨테이너에 직접 로드할 수 있습니다:

# docker-compose.yml (or compose.yml)
services:
  web:
    image: node:20-alpine
    env_file:
      - .env                  # base defaults
      - .env.production       # production overrides
    ports:
      - "3000:3000"

  db:
    image: postgres:16
    env_file:
      - .env.db               # separate file for DB secrets
    volumes:
      - pgdata:/var/lib/postgresql/data

environment vs env_file

환경 변수를 인라인으로 설정할 수도 있습니다. 차이점:

# Using env_file (loads from file)
services:
  web:
    env_file:
      - .env

# Using environment (inline)
services:
  web:
    environment:
      - NODE_ENV=production
      - PORT=3000
      - DATABASE_URL=${DATABASE_URL}   # from shell or .env

# Using environment (mapping syntax)
services:
  web:
    environment:
      NODE_ENV: production
      PORT: 3000
  • env_file: 파일에서 로드, compose.yml을 깔끔하게 유지, 환경별로 쉽게 교체.
  • environment: compose.yml에서 확인 가능, 비밀이 아닌 값에 적합, 변수 치환 지원.

Docker Compose 변수용 .env

Docker Compose는 프로젝트 루트의 .env 파일을 자동으로 읽어 compose.yml의 변수 치환에 사용:

# .env (in same directory as compose.yml)
POSTGRES_VERSION=16
NODE_VERSION=20
APP_PORT=3000

# compose.yml — uses .env for variable substitution
services:
  web:
    image: node:${NODE_VERSION}-alpine
    ports:
      - "${APP_PORT}:3000"

  db:
    image: postgres:${POSTGRES_VERSION}
    environment:
      POSTGRES_DB: myapp

일반적인 .env 오류 10가지와 수정 방법

개발자가 가장 자주 겪는 .env 문제:

#오류원인수정
1process.env.VAR가 undefineddotenv가 로드되지 않았거나 사용 후 로드됨엔트리 파일 최상단에서 dotenv.config() 호출
2프로덕션에서 변수가 비어 있음.env 파일이 배포되지 않음; 플랫폼 환경 변수 대신 파일에 의존호스팅 플랫폼(Vercel, AWS 등)에서 환경 변수 설정
3.env 값에 따옴표가 포함됨파서가 따옴표를 리터럴 문자로 처리파서 문서 확인; 대부분은 큰따옴표를 제거하지만 전부는 아님
4잘못된 .env 파일이 로드됨작업 디렉터리가 예상 경로와 다름path 옵션 사용: dotenv.config({ path: ".env.local" })
5여러 줄 값이 잘림값이 올바르게 큰따옴표로 감싸지지 않음여러 줄 값을 큰따옴표로 감싸기
6특수 문자가 값을 손상$ 또는 #이 보간/주석으로 해석됨작은따옴표로 보간 방지, 또는 \로 이스케이프
7BOM 인코딩 오류Windows 편집기가 UTF-8 BOM으로 .env를 저장BOM 없는 UTF-8로 저장; 그렇지 않으면 첫 번째 변수를 읽을 수 없을 수 있음
8Docker 컨테이너 환경 변수가 비어 있음env_file 경로가 잘못되었거나 파일이 빌드 컨텍스트 밖에 있음경로가 compose.yml 위치 기준 상대 경로인지 확인
9= 주위의 공백으로 파싱 실패KEY=VALUE 대신 KEY = VALUE등호 주위의 공백 제거
10브라우저에서 변수를 사용할 수 없음 (React/Next)필수 접두사(NEXT_PUBLIC_ 또는 REACT_APP_)가 없음프레임워크가 요구하는 접두사를 추가하여 클라이언트 측 코드에 노출

프로덕션 대안

프로덕션에서는 .env 파일이 권장되지 않습니다. 다음 대안을 사용하세요:

플랫폼 환경 변수

모든 주요 호스팅 플랫폼이 환경 변수 설정을 위한 UI 또는 CLI를 제공:

# Vercel
vercel env add DATABASE_URL production
vercel env ls

# AWS (Parameter Store)
aws ssm put-parameter \
  --name "/myapp/prod/DATABASE_URL" \
  --value "postgres://..." \
  --type SecureString

# Heroku
heroku config:set DATABASE_URL=postgres://...
heroku config

# Railway
railway variables set DATABASE_URL=postgres://...

# Fly.io
fly secrets set DATABASE_URL=postgres://...

Docker Secrets

Docker Swarm 또는 Compose에서 민감한 데이터에 secrets 사용:

# compose.yml with Docker secrets
services:
  web:
    image: myapp:latest
    secrets:
      - db_password
      - api_key
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt    # for Compose
    # external: true                    # for Swarm
  api_key:
    file: ./secrets/api_key.txt

# In your app, read from file:
# const secret = fs.readFileSync('/run/secrets/db_password', 'utf8').trim();

시크릿 매니저 (Vault, AWS Secrets Manager)

엔터프라이즈 애플리케이션에는 전용 시크릿 매니저 사용:

# HashiCorp Vault
vault kv put secret/myapp/production \
  DATABASE_URL="postgres://..." \
  API_KEY="sk-..."

# Read in application
vault kv get -field=DATABASE_URL secret/myapp/production

# AWS Secrets Manager
aws secretsmanager create-secret \
  --name "myapp/production/db" \
  --secret-string '{"url":"postgres://...","password":"..."}'

# Read in Node.js with AWS SDK
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManager({ region: 'us-east-1' });
const { SecretString } = await client.getSecretValue({
  SecretId: 'myapp/production/db'
});
const secrets = JSON.parse(SecretString);

시크릿 매니저는 교체, 감사 로그, 접근 제어, 저장 시 암호화를 제공합니다. 이는 .env 파일로는 불가능한 기능입니다.

자주 묻는 질문

.env 파일을 Git에 커밋해야 하나요?

시크릿(API 키, 비밀번호, 토큰)이 포함된 .env 파일은 절대 버전 관리에 커밋하지 마세요. 대신 빈 플레이스홀더 값이 있는 .env.example 파일을 커밋하세요. .gitignore에 .env* 패턴을 추가하세요.

.env, .env.local, .env.production의 차이점은?

.env는 모든 환경에서 로드되는 기본값을 포함합니다. .env.local은 로컬 오버라이드를 포함하며 git에 커밋하지 않습니다. .env.production은 NODE_ENV=production일 때만 로드되는 프로덕션 전용 값을 포함합니다.

Node.js에서 process.env.MY_VAR가 undefined인 이유는?

가장 일반적인 원인: 1) 엔트리 파일 상단에서 require("dotenv").config() 호출을 잊음, 2) .env 파일이 Node가 실행되는 루트 디렉터리에 없음, 3) 변수 이름 오타, 4) ES 모듈을 사용하여 "import dotenv/config"가 필요.

Docker Compose에서 .env 변수를 사용하는 방법은?

Docker Compose는 compose.yml과 같은 디렉터리의 .env 파일을 자동으로 읽어 ${VAR} 구문으로 변수 치환을 수행합니다. 컨테이너에 변수를 전달하려면 env_file 지시자로 파일에서 로드하거나 environment 키로 인라인 설정합니다.

프로덕션에서 .env 파일을 사용해도 되나요?

권장되지 않습니다. 프로덕션에서는 플랫폼 제공 환경 변수, Docker secrets, 또는 HashiCorp Vault나 AWS Secrets Manager 같은 시크릿 매니저를 사용하세요.

React나 Next.js에서 .env 변수를 브라우저에 노출하는 방법은?

Next.js에서는 변수 이름에 NEXT_PUBLIC_ 접두사를 붙입니다 (예: NEXT_PUBLIC_API_URL). Create React App에서는 REACT_APP_, Vite에서는 VITE_ 접두사를 사용합니다. 접두사가 있는 변수만 클라이언트 번들에 포함됩니다.

𝕏 Twitterin LinkedIn
도움이 되었나요?

최신 소식 받기

주간 개발 팁과 새 도구 알림을 받으세요.

스팸 없음. 언제든 구독 해지 가능.

Try These Related Tools

🐳Docker Compose Generator.gi.gitignore GeneratorNXNginx Config Generator

Related Articles

Docker Compose 치트시트: 서비스, 볼륨, 네트워크

Docker Compose 레퍼런스: 서비스 정의, 볼륨, 네트워크, 환경 변수, 스택 예시.

Docker Compose env_file vs environment: 언제 어떤 것을 사용할까 (예제 포함)

Docker Compose의 env_file과 environment의 차이를 이해하세요. 사용 시기, 변수 우선순위, .env 파일 동작, 다중 환경 설정.