- 환경 변수는 프로세스가 해당 환경으로부터 받는 키-값 설정입니다.
- 포트 및 데이터베이스 URL과 같은 구성을 코드에서 유지합니다.
- 값은 항상 문자열이므로 구문 분석하지 않는 한 “false”는 진실입니다.
- 실제 비밀이 포함된
.env파일을 커밋하지 말고, 앱이 시작될 때 필수 설정을 확인하세요.
앱에 데이터베이스 URL이 필요합니다. 랩탑에서는 로컬 데이터베이스이고, CI에서는 일회용 데이터베이스이고, 프로덕션에서는 관리형 클러스터입니다. 이들 중 하나를 하드 코딩한다는 것은 각 환경에 맞게 코드를 변경하는 것을 의미합니다. 환경 변수가 이 문제를 해결합니다. 코드는 DATABASE_URL이며 각 환경은 고유한 값을 제공합니다.
이 아이디어는 배포마다 달라지는 구성이 환경에 속한다는 Twelve-Factor App 방법의 핵심입니다. Windows에서 Python 3.13을 사용하여 아래 동작을 테스트했습니다. 표시된 모든 출력은 해당 실행에서 나온 것입니다.
환경 변수란 무엇입니까?
실행 중인 모든 프로세스에는 환경이라는 명명된 텍스트 값 집합이 있습니다. 익숙한 예로는 쉘에 프로그램을 찾을 위치를 알려주는 PATH과 HOME 또는 USERPROFILE이 있습니다. 여러분의 앱은 APP_PORT, LOG_LEVEL 또는 DATABASE_URL과 같은 설정에 대해 동일한 메커니즘을 사용할 수 있습니다.
환경 변수 설정
| 쉘 | 이번 세션의 경우 | 하나의 명령에 대해 |
|---|---|---|
| 배쉬 / zsh | APP_PORT=8080 내보내기 |
APP_PORT=8080 파이썬 app.py |
| 파워셸 | $env:APP_PORT = "8080" |
설정하고 실행한 다음 제거하세요. |
| 윈도우 cmd | APP_PORT=8080으로 설정 |
내장되지 않음 |
이는 현재 터미널 세션 동안에만 지속됩니다. 이를 영구적으로 만들려면 ~/.bashrc과 같은 셸 프로필에 추가하거나 Windows의 경우 시스템 환경 설정 또는 setx를 사용합니다. 애플리케이션의 경우 일반적으로 시스템 전체에 설정하는 것보다 배포 시 제공하는 것이 더 좋습니다.
코드로 읽기
import os
port = os.environ.get("APP_PORT", "8000") # default if missing
print(port) # 8000
db = os.environ["DATABASE_URL_MISSING_DEMO"]
# KeyError: 'DATABASE_URL_MISSING_DEMO'
선택적 설정에는 기본값으로 .get()을 사용하십시오. 앱이 없으면 작동하지 않는 설정에는 KeyError를 나타내는 대괄호를 사용합니다. 다른 언어도 비슷합니다. Node.js에서는 process.env.APP_PORT, Java에서는 System.getenv("APP_PORT"), Go에서는 os.Getenv("APP_PORT")입니다.
모든 것은 문자열이다
이것은 모든 언어로 사람들을 사로잡습니다. 포트를 설정한 다음 “1”을 추가했습니다.
os.environ["APP_PORT"] = "8080"
port = os.environ["APP_PORT"]
print(type(port).__name__, repr(port), port + "1")
# str '8080' 80801
os.environ["APP_PORT"] = 8080
# TypeError: str expected, not int
값이 텍스트로 반환되었으며 추가하면 8081이 아닌 80801이 생성되었습니다. int(os.environ["APP_PORT"])와 같이 명시적으로 변환합니다. 부울은 더 나쁩니다.
os.environ["DEBUG"] = "false"
print(bool(os.environ["DEBUG"])) # True
비어 있지 않은 문자열은 Python에서 참이므로 "false"은 True가 되었습니다. 디버그 모드는 프로덕션 환경에서 켜져 있었을 것입니다. 부울을 의도적으로 구문 분석합니다.
def env_bool(name, default=False):
value = os.environ.get(name)
if value is None:
return default
return value.strip().lower() in {"1", "true", "yes", "on"}
env_bool("DEBUG") # False
env_bool("NOT_SET", default=True) # True
프로세스가 이를 상속하는 방법
하위 프로세스는 상위 환경의 사본을 받습니다. 우리는 이를 확인하기 위해 하위 Python 프로세스를 시작했습니다.
child sees: 8080 inherited # default: full copy of the parent's environment
child sees: 9000 None # given a custom env: only what we passed
parent sees child var: None # a child's changes never flow back
세 가지 실습 수업이 이어집니다. 내보낸 변수는 작성하지 않은 스크립트를 포함하여 시작하는 모든 프로그램에서 볼 수 있으며 이는 비밀에 중요합니다. 사용자 정의 환경을 전달하면 모든 것이 대체되므로 자녀에게 필요한 모든 것을 포함하십시오. 그리고 스크립트는 상위 쉘의 변수를 변경할 수 없습니다. 이것이 바로 도구가 파일을 실행하는 대신 파일을 source하도록 요청하는 이유입니다.
.env 파일
많은 변수를 직접 입력하는 것은 지루하므로 대부분의 프로젝트에서는 로컬 개발 값을 .env 파일에 유지합니다.
# local development only
APP_PORT=8080
DATABASE_URL="postgres://dev:devonly@localhost:5432/shop"
FEATURE_SEARCH=true
Python-dotenv 및 Node.js용 dotenv와 같은 라이브러리는 시작 시 이 파일을 로드합니다. Node.js 20.6 이상에서는 --env-file을 사용하여 기본적으로 로드할 수도 있습니다. 아이디어를 확인하기 위해 위의 파일을 다음과 같이 변환하는 작은 파서를 작성했습니다.
{'APP_PORT': '8080',
'DATABASE_URL': 'postgres://dev:devonly@localhost:5432/shop',
'FEATURE_SEARCH': 'true'}
실제로 실제 라이브러리를 사용하십시오. 10줄 버전에서는 무시되는 인용, 이스케이프, 주석 및 여러 줄 값을 처리합니다. 기본적으로 대부분의 dotenv 로더는 이미 설정된 변수를 재정의하지 않으므로 실제 환경 값이 파일을 우선합니다. 그것은 일반적으로 당신이 원하는 것입니다.
팁: 첫 번째 커밋 전에 .gitignore에 .env를 추가하고 대신 모든 변수 이름과 안전한 자리 표시자 값을 사용하여 .env.example를 커밋하세요. 새로운 개발자는 이를 복사하여 자신만의 가치를 채워넣습니다.
시작 시 구성 유효성 검사
변수가 누락되면 명확한 메시지와 함께 앱이 즉시 중지되어야 하며, 한 시간 후에 혼란스러운 오류가 발생하지 않아야 합니다. 로드할 때 필수 설정을 확인했습니다.
REQUIRED = ["DATABASE_URL", "SECRET_KEY"]
def check_required(env):
missing = [k for k in REQUIRED if not env.get(k)]
if missing:
raise SystemExit(f"Missing required settings: {', '.join(missing)}")
# Missing required settings: SECRET_KEY
형식화된 구성 라이브러리가 더 발전합니다. Python의 Pydantic 설정, Node.js의 zod 또는 envalid, Java의 Spring 구성 속성은 모두 유형을 변환하고 모든 문제를 한 번에 보고합니다.
환경 변수 및 비밀
환경 변수는 API 키와 같은 비밀을 앱에 전달하는 일반적인 방법이며, 하드 코딩하는 것보다 훨씬 낫습니다. 그들이 알아야 할 약점은 다음과 같습니다.
- 테스트에서 알 수 있듯이 하위 프로세스는 이를 상속합니다.
- 이는 충돌 보고서, 디버그 페이지 및 환경을 덤프하는 로그에 나타날 수 있습니다.
- 일부 시스템에서는 동일한 사용자가 실행하는 다른 프로세스에서 이를 읽을 수 있습니다.
프로덕션의 경우 많은 팀이 HashiCorp Vault, AWS Secrets Manager, Azure Key Vault 또는 Google Secret Manager와 같은 전용 관리자에 비밀을 보관하고 런타임에 주입합니다. 일부는 변수 대신 파일로 비밀을 마운트하는 것을 선호합니다. 어느 쪽이든 로그에 구성을 인쇄하지 말고 Git에 커밋된 비밀을 기록에서 제거한 후에도 교체하세요.
도커와 CI에서
# Docker (not run for this article)
docker run -e APP_PORT=8080 --env-file .env.production myapp
# GitHub Actions (not run for this article)
env:
APP_PORT: 8080
API_KEY: ${{ secrets.API_KEY }}
CI 플랫폼은 비밀을 암호화하여 저장하고 로그에 마스킹합니다. Dockerfile의 ENV을 사용하여 비밀 정보를 Docker 이미지에 굽지 마세요. 이미지 레이어에서 읽을 수 있는 상태로 유지되기 때문입니다. 전체 설정은 Docker Compose 튜토리얼 및 GitHub Actions CI/CD 가이드를 참조하세요.
모범 사례
SHOP_DB_URL과 같이 접두사가 붙은 명확한 대문자 이름을 사용하십시오.- 하나의 모듈에서 모든 구성을 읽고 거기에서 유형을 변환하고 시작 시 유효성을 검사합니다.
- 민감하지 않은 설정에만 안전한 기본값을 제공하세요.
- Git에서
.env을 유지하고.env.example를 유지합니다. - 민감한 값에 대해서는 프로덕션 환경에서 비밀 관리자를 사용하세요.
- 모든 변수, 해당 유형 및 예제 값을 문서화하십시오.
환경 변수 문제 디버깅
앱이 설정이 누락된 것처럼 작동하는 경우 다음 검사를 순서대로 수행하세요.
- 올바른 위치에 설정되어 있습니까? 한 터미널 탭에서 내보낸 변수가 다른 터미널 탭이나 시스템에서 시작한 서비스에 존재하지 않습니다.
- 앱이 다시 시작되었나요? 실행 중인 프로세스는 시작된 환경을 유지합니다. 나중에 변수를 변경해도 다시 시작할 때까지는 아무런 영향이 없습니다.
- 이름이 정확합니까? Linux와 macOS에서는 이름이 대소문자를 구분하므로
App_Port와APP_PORT은 다른 변수입니다. Windows에서는 이를 동일하게 취급합니다. - .env 파일이 이를 재정의합니까, 아니면 무시됩니까? 로더가 기존 값을 재정의하는지 여부와 실제로 어떤 파일을 읽는지 확인하십시오.
- 흩어진 따옴표나 공백이 있습니까? 후행 공백이나 추가 따옴표와 함께 복사된 값은 찾기 어려운 방식으로 구문 분석에 실패하거나 일치하지 못할 수 있습니다.
프로세스가 실제로 수신한 내용을 확인하려면 비밀 값이 아닌 변수 이름을 인쇄하세요. Linux 및 macOS에서 printenv은 현재 셸 환경을 나열하고 PowerShell Get-ChildItem Env:에서는 동일한 작업을 수행합니다. 컨테이너 내부에서 env부터 docker exec까지 실행하면 컨테이너의 보기가 표시되는데, 이는 종종 사용자의 셸과 다릅니다.
마지막 팁: 시작할 때 기본값을 사용하는 옵션 설정을 나열하는 한 줄을 기록하십시오. 비용도 들지 않고, “왜 그러는 거지?”라는 질문에 대부분 답해 줍니다. 바로 질문.
마무리
환경 변수는 간단하지만 몇 가지 함정을 숨깁니다. 모든 것이 문자열이고 자식이 이를 상속하며 비밀이 이를 통해 유출될 수 있습니다. 이를 앱에 대한 입력으로 처리하십시오. 한 곳에서 읽고, 조기에 변환 및 검증하고, 커밋되는 파일에 대한 실제 비밀을 유지하세요.
자주 묻는 질문
환경 변수란 무엇입니까?
이는 APP_PORT=8080과 같이 프로세스가 해당 환경에서 받는 명명된 텍스트 값입니다. 앱은 환경 간에 변경되는 설정에 이를 사용합니다.
.env 파일이란 무엇입니까?
python-dotenv와 같은 라이브러리에 의해 시작 시 로드되는 KEY=값 행의 텍스트 파일입니다. 주로 지역 개발을 위해 사용되며 실제 비밀을 가지고 커밋해서는 안 됩니다.
내 환경 변수가 문자열인 이유는 무엇입니까?
운영 체제는 환경 값을 텍스트로 저장합니다. 문자열 “false”는 true로 간주되므로 int()를 사용하여 숫자를 변환하고 부울을 명시적으로 구문 분석합니다.
환경 변수는 비밀에 대해 안전합니까?
하드 코딩보다 낫지만 하위 프로세스가 이를 상속하고 로그가 이를 노출할 수 있습니다. 프로덕션 시스템에서는 전용 비밀 관리자를 사용하는 경우가 많습니다.
스크립트가 상위 쉘의 변수를 변경할 수 있습니까?
아니요. 하위 프로세스는 환경의 복사본을 가져오므로 해당 변경 사항이 다시 흐르지 않습니다. 현재 셸에서 변수를 설정하려면 대신 파일을 소스로 지정하세요.
계속 학습하세요
테스트, WordPress 또는 SEO에 도움이 필요하십니까?
우리는 팀이 더 나은 소프트웨어를 출시하고 사이트를 성장시킬 수 있도록 돕습니다. 현재 진행 중인 작업을 알려주시면 명확한 다음 단계를 제안해 드리겠습니다.
추가 가이드 테스트, AI 및 최신 개발
이 기사가 마음에 드셨나요? 주제를 선택하고 최신 튜토리얼로 바로 이동하세요.
AI 환각 설명: 언어 모델이 사물을 구성하는 이유와 이를 줄이는 방법
AI 환각은 그것이 무엇인지, 언어 모델이 환각을 생성하는 이유, 일반적인 유형, 접지 및 프롬프트를 통해 환각을 줄이는 방법, 그리고 우리가 실행한 간단한 점검에 대해 설명했습니다.
기사 읽기 →DevOps환경 변수 설명: .env 파일, 비밀 및 구성이 올바르게 완료되었습니다.
우리가 실행한 Python 예제를 통해 설명된 환경 변수: 읽기 및 설정, 모든 값이 문자열인 이유, .env 파일, 프로세스 상속, 비밀 및 유효성 검사.
기사 읽기 →소프트웨어 개발API 속도 제한 설명: 알고리즘, 헤더 및 429 오류 처리
우리가 실행한 시뮬레이션을 통해 설명된 API 속도 제한: 토큰 버킷, 고정 창, 슬라이딩 창, 429 응답, 속도 제한 헤더, 백오프를 사용한 재시도 및 테스트 제한.
기사 읽기 →테스트 자동화Python에서 조롱: unittest.mock, 패치 및 테스트를 거짓말로 만드는 실수
Unittest.mock을 사용하여 Python에서 모의, 테스트됨: 모의 및 return_value, 부작용, 이름이 사용되는 패치, 사양 및 자동 사양, 모의 날짜 및 일반적인 실수.
기사 읽기 →소프트웨어 테스트코드 커버리지 설명: 회선 커버리지와 분기 커버리지 및 100%의 실제 의미
실제 Coverage.py 실행을 통해 설명되는 코드 적용 범위: 라인, 분기 및 조건 적용 범위, 보고서 읽기, 100% 적용 범위가 여전히 버그를 놓칠 수 있는 이유 및 합리적인 대상.
기사 읽기 →데이터베이스 및 SQLSQL의 DELETE vs TRUNCATE vs DROP: 차이점, 롤백 및 사용 시기
테스트된 SQL을 통해 DELETE 대 TRUNCATE 대 DROP에 대해 설명합니다. 각각의 제거, 롤백, 트리거, ID 재설정, 속도 및 PostgreSQL, MySQL, SQL Server 및 SQLite의 차이점.
기사 읽기 →데이터베이스 및 SQLSQL 뷰 설명: 예제를 통해 함정 생성, 사용 및 방지
우리가 실행한 쿼리로 설명된 SQL 뷰: 뷰 생성, 실시간 결과, 열 숨기기, 성능, 업데이트 가능하고 구체화된 뷰, 손상된 종속성 및 모범 사례.
기사 읽기 →소프트웨어 개발TypeScript 인터페이스와 유형: 실제 차이점, 컴파일러로 테스트
TypeScript 인터페이스 대 유형, 실제 컴파일러로 확인: 둘 다 할 수 있는 것, 하나만 할 수 있는 것, 선언 병합, 확장 대 교차점 및 따라야 할 간단한 규칙.
기사 읽기 →소프트웨어 개발Python *args 및 **kwargs 설명: 명확한 예가 포함된 유연한 함수
Python *args 및 **kwargs는 우리가 실행한 코드(패킹 및 언패킹, 매개변수 순서, 키워드 전용 및 위치 전용 인수, 데코레이터 및 일반적인 오류)를 설명했습니다.
기사 읽기 →소프트웨어 개발Python Lambda 함수: 구문, 실제 사용 및 사용하지 말아야 할 경우
Python 람다 함수는 구문, 키를 사용한 정렬, 맵 및 필터, 클로저, 후기 바인딩 트랩, 일반 def가 더 나은 선택인 경우 등 우리가 실행한 코드로 설명되었습니다.
기사 읽기 →인공지능AI 에이전트란 무엇입니까? 작동 방식, 간단한 에이전트 루프를 사용하여 실행할 수 있음
AI 에이전트란 무엇입니까? 우리가 실행한 작은 Python 루프, 실제 위험 및 간단한 챗봇이 더 나은 선택일 때 에이전트 루프, 도구, 메모리 및 가드레일에 대한 일반 가이드입니다.
기사 읽기 →DevOps모니터링과 관찰 가능성: 로그, 지표, 추적을 통한 실제 차이점
모니터링과 관찰 가능성 설명: 알려진 오류와 알려지지 않은 오류, 로그, 지표 및 추적, 결제 사건 안내, 골든 신호, OpenTelemetry 및 유용한 경고.
기사 읽기 →DevOpsDocker 볼륨과 바인딩 마운트: 컨테이너 데이터를 올바른 방식으로 유지
Docker 볼륨과 바인드 마운트 설명: 컨테이너 데이터가 사라지는 이유, 명명된 볼륨, 바인드 마운트, tmpfs, Compose 예제, 백업, 권한 및 각 사용 시기.
기사 읽기 →테스트 자동화TDD와 BDD: 주요 차이점, 두 가지 방식으로 구축된 하나의 기능 및 각 사용 시기
TDD와 BDD는 두 가지 방식으로 구축된 하나의 할인 기능, 즉 우리가 실행한 코드를 사용한 적록 리팩토링, 주어진 시점 시나리오, 도구, 장단점 및 각 사용 시기를 설명합니다.
기사 읽기 →소프트웨어 개발Git merge vs rebase: 차이점, 예제, 언제 무엇을 쓸까
Git merge vs rebase를 실제 로그 출력, 비교표, rebase의 황금률, 안전한 팀 워크플로로 설명합니다.
기사 읽기 →소프트웨어 개발파이썬 리스트 컴프리헨션: 문법, 예제, 피해야 할 때
파이썬 리스트 컴프리헨션 설명: 문법, 필터링, if-else, 중첩 반복문, 실제 측정 시간, 그리고 일반 for 반복문이 더 나은 경우.
기사 읽기 →데이터베이스 및 SQL예제로 쉽게 배우는 SQL JOIN (Inner, Left, Right, Full)
샘플 테이블과 쿼리로 SQL JOIN을 단계별로 이해하세요. INNER, LEFT, RIGHT, FULL, CROSS, SELF와 흔한 실수, 성능 팁까지 정리했습니다.
기사 읽기 →