| <rowcolor=#ffffff,#dddddd> 데이터 직렬화 형식과 설정 파일 | |||
| {{{#!wiki style="min-height: calc(1.5em + 5px); margin: 0 -10px -5px" {{{#!folding [ 펼치기 · 접기 ] {{{#!wiki style="margin: -5px -1px -11px" | <colbgcolor=#4d5767,#4d5767><colcolor=#ffffff,#dddddd> 데이터 직렬화 형식 | <colbgcolor=#4d5767,#4d5767><colcolor=#ffffff,#dddddd> 텍스트 | CSV · JSON(NDJSON/JSON Lines) · XML |
| 바이너리 | flatbuffers · protobuf | ||
| 설정 파일 | 텍스트 | JSON(YAML · JSON5 · JSONC) · XML · INI(dotenv · EditorConfig · TOML) | }}}}}}}}} |
1. 개요
dotenv응용 프로그램에 삽입될 환경변수(environment variable)를 담아 놓은 설정 파일. 주로 비대칭 서명키, 데이터베이스 접근 비밀번호 등 버전 관리 시스템에 커밋되면 안 되는 인증 정보를 설정하는 용도로 쓰이나, 네트워크 포트 번호, 로깅 레벨 등 정보의 민감성과 무관하게 서비스의 여러 설정을 담는 용도로도 사용할 수 있다.
Ruby 생태계의 라이브러리로 시작했으나, 현재는 언어나 분야에 무관하게 널리 받아들여져 대부분의 환경에서 범용적으로 사용할 수 있다.
2. 역사
.env 및 환경변수 설정 방법론(methodology)은 2011년 Heroku에서 발표한 The Twelve-Factor Manifesto의 III, 'Store config in the environment' 원칙에 기반한다.2011년 David Dollar의 foreman#@에서
.env 파일에서 환경변수를 읽어오는 방식의 구현을 처음으로 선보였고,## 이에 영감을 받아 Brandon Keepers가 2012년 독립적인 Ruby 라이브러리를 만들게 된다.[1]2013년 Scott Motte@가 npm 패키지 dotenv를 구현하며# 유명해졌다. 2014 JS.LA 컨퍼런스 발표
3. 원리
실행 가능한(빌드된) 응용 프로그램을 실행할 때, CWD(현재 디렉토리)에.env라는 이름의 파일을 만든다. 파일명이 .으로 시작하는 이유는 Unix 환경에서 숨김 파일로 처리되기 때문이다. 개발/프로덕션 등 환경마다 다른 설정을 사용하기 위한 경우, .env가 아닌 다른 파일명을 사용하기도 한다.해당 파일은 다음과 같이
<환경변수명>=<값> 형태의 설정을 담게 된다. 문법은 구현체마다 조금씩 다른데, 문자열 보간을 지원하는 경우도 있고 그렇지 않은 경우도 있다. 환경변수는 기본적으로 문자열만을 담을 수 있기에 정수 리터럴이나 따옴표 등은 일반적으로 필요하지 않다.#!syntax sh
USERNAME=sephiroth
PASSWORD=1q2w3e4r해당 응용 프로그램이 실행되었을 때,
.env 파일을 읽어들여 각각의 환경변수명을 파일에 저장된 <값>으로 등록하며, 이 과정에서 이미 존재하는 환경변수가 있다면 덮어씌우지 않고 실제 프로세스에 설정된 값을 우선한다. 경우에 따라, 파일에 저장된 값으로 덮어쓰는 구현도 존재한다.4. 특징
#!if (문단 == null) == (앵커 == null)
를#!if 문단 != null & 앵커 == null
의 [[환경변수#s-|]]번 문단을#!if 문단 == null & 앵커 != null
의 [[환경변수#환경변수 설정|환경변수 설정]] 부분을 참고하십시오..env의 기본적인 역할은 설정할 환경변수를 파일에 저장해 손쉽게 읽고 수정하기 위한 것으로, 기존의 애플리케이션이 이미 환경변수를 통한 설정 관리 방식을 채택하고 있다고 가정한다. 즉, 일반적인 설정 파일등을 사용하는 경우와 1:1로 비교하기는 어렵다. 만약 환경변수를 사용하는 방식의 장점이 필요가 없는 상황이라면, 구태여
.env를 사용할 당위성이 떨어지며, JSON이나 다른 설정 파일 포맷을 사용해도 상관이 없다. 특히 환경변수는 그 특성상 flat하기 때문에 여러 depth를 가진 복잡한 설정이 필요한 도메인에 애초에 적합하지 않다..env 파일은 근본적으로 환경변수에 의존하기 때문에 환경변수 자체에서 기인하는 보안적 문제 등을 근본적으로 해결할 능력은 없다. 이 경우
.env 이전에 환경변수로 시크릿을 전달하는 것 자체를 그만두고 프로덕션 수준의 secret manager 구현체 도입을 고려하는 것이 합리적이다.4.1. 장점
- 소스 비종속성
.env파일은 의미적으로 프로그램의 소스 코드로 간주되지 않는다. 즉, 빌드 시 종속성으로 요구되지 않는다는 의미이며, 코드베이스에서 안전하게 제거될 수 있다. 마찬가지로 버전 관리 시스템에 커밋되지 않으므로 하드코딩이나 히스토리 복원을 통한 유출을 방지할 수 있다. 의외로 실수하기 쉬운 내용인데,.env파일에 담긴 내용을 빌드 종속성으로 집어넣거나 빌드 중에 사용하는 것은 명백한 안티패턴이며,.env는 본질적으로 런타임 환경에서만 존재해야 의미가 있다. 더 극단적으로는,.env파일은 런타임에 없어도 동작해야 한다. 환경 변수의 validation 로직은 애플리케이션 단에서 구현하면 되며, .env는 그저 런타임에 환경변수를 로딩하기 위한 용도로만 사용되고, 이후로는 각 플랫폼별 환경변수 접근 API를 동일하게 사용할 수 있어야 한다. - 로컬 재사용성
환경변수를 파일에 저장하기 때문에 매 실행마다 일일히 입력하지 않아도 된다. 적절한 보안이 보장된다면,.env파일을 다른 개발자에게 넘겨 동일한 환경을 빠르게 재구성하는 용도로 사용될 수도 있다.
4.2. 단점
- 애플리케이션 수준 로딩 구현에 대한 비용
환경변수 자체는 platform-agnostic하지만, .env는 그렇지 않다. 즉, 같은 credential을 여러 서비스에서 공유해 사용하려면 각 서비스마다 일일히 .env 파서 및 주입 로직을 구현해야 한다. 하나의 microservice domain에 N개의 서로 다른 서비스가 돌아간다면 .env 구현만 N번이 필요하다는 것. 이는 .env가 환경 수준에서 구현되는 것이 아닌 각 애플리케이션 레이어에서 구현되기 때문에 발생하는 근본적 문제점으로, 후술할 더 모던한 방법으로 환경 자체에 주입하게 되면 애초에 .env loading 과정이 불필요해진다. - 낮은 유지보수성
하나의 microservice domain에서 N개의 여러 서비스가 각자 다른 .env 파일을 관리할 경우, 그에 비례하게 유지보수 비용이 증가한다. 일반적으로 .env는 커밋되지 않기에 버전을 관리하거나 하나의 서비스를 업데이트 할 때 그에 맞추어 변경하거나 추적하는 것도 불가능하고, 어떤 서비스가 어떤 변수를 요구하는지도 추적하기가 까다롭다. 예를 들어 데이터베이스 비밀번호가 바뀌었을 경우 메인 서비스, 메세지 큐, 로깅 시스템 등 같은 데이터베이스 서비스를 공유하는 여러 서비스의 .env를 전부 업데이트해야 한다. 특히 팀 내에서 같은 .env 설정을 공유하고 동기화하는 것은 더욱 어렵다. .env는 단순한 정적 파일이고, 복잡한 프로그래밍적 기능이나 중첩, 재사용, 네트워크 탐색 등의 기능이 없기 때문에 발생하는 문제이다. - 보안적 범위 리스크
.env파일은 정적인 파일이기 때문에 실제로 해당 정보가 쓰이지 않더라도 로드 시점에 무조건 해당 파일에 담긴 모든 정보를 불러오게 된다. 예를 들어 런타임 설정에 따라 데이터를 데이터베이스에 쓸 수도 있고 메모리만 저장할 수 있도록 구현되어있다 해도, .env파일에DB_PASSWORD=등의 엔트리가 있다면 해당 정보가 실제로 쓰이지 않더라도 불러오게 된다. 특히 하나의 .env 파일을 microservice내 여러 서비스에서 공유해서 쓰는 경우, 어떤 서비스는 실제로 DB 액세스가 필요하지만 어떤 서비스는 필요하지 않은데도 해당 정보를 얻어갈 수 있다. 이는 secret sprawl의 일종으로, 필요에 따라 on-demand로 시크릿을 요청하는 대부분의 시크릿 매니저 서비스와의 차이점이다. 때문에 특정 환경변수가 필요하지 않게 될 때마다 관련 로직과 함께 .env 엔트리를 제거하는 것이 좋지만, 앞서 언급한 근본적인 유지보수 문제로 인해 추적 비용이 증가하게 된다. - 유출에 대한 보안적 리스크
.env가 근본적으로 설계된 목적과 취지를 생각하면 100% 휴먼 에러가 맞지만, .env가 워낙 쓰기 쉽다 보니 정말 흔하게 발생하는 문제이다. 가장 흔한 예시는.env를 버전 관리되는 소스코드랑 같은 폴더에 담아두다가 ignore 처리 안하고 그대로 올려버리는 경우로, 당장 GitHub에 검색해 봐도 약 50만개의 .env파일이 그대로 노출되어있는 것을 볼 수 있다. example 용도인 것도 있지만, 자세히 보다 보면 실제 프로덕션 키가 들어가있는 경우도 흔하게 볼 수 있다. 또다른 흔한 실수는 컨테이너 이미지 빌드할 때 생각없이COPY . .으로.env파일까지 복사하는 경우로, 이 경우 인스펙션도 필요없이 exec만으로 손쉽게WORKDIR아래의 .env파일을 찾아갈 수 있다. 이런 이미지를 공개 레지스트리에 올리는 순간 소스코드에 커밋하는 것과 다를 바 없게 된다. 실수에 의한 .env 파일 유출은 실질적으로 큰 위협이 되는 보안 리스크로, 2024년 Cybernews 연구팀에서 인터넷에 공개된 .env 파일 정보를 통해 5만개 이상의 실제 웹사이트에서 100만개가 넘는 시크릿 정보를 찾아내기도 했다.[2] 이는 근본적으로 .env가 암호화되지 않는 평문 파일이고, 메모리가 아닌 파일 시스템에 저장되기 때문에 발생하는 문제로, 보안적 리스크를 줄이기 위해서는 .env는 로컬에서만 사용하고 프로덕션 환경에서는 아예 포함하지 않는 것이 좋다. - 버전 관리의 어려움
근본적으로 소스코드에 포함되지 않는 것이 목적인 것은 맞지만, 그 대가로 버전 관리의 장점을 상실한다. 로컬에서 쓴 .env와 프로덕션에서 쓴 .env가 다른 경우, 도중에 누군가 어떤 키 값을 바꾼 이후로 동작하지 않는 경우, 서비스를 이전 시점으로 롤백해야 하는 경우 등등을 추적하고 재분석하는 비용이 증가하게 된다. 반대로 대부분의 상용 시크릿 매니저 서버 구현체는 '누가', '언제', '어떤 값'을 변경했는지 등을 쉽게 추적할 수 있으며, 값을 수정할 경우 새 버전이 생성된다.
5. 지원
5.1. 내장 지원
5.2. 라이브러리
- JavaScript/TypeScript (Node.js 호환 API 기준)
- Ruby
- PHP
- Go
- Python
- Rust
5.3. 로더
6. 파생 소프트웨어
- dotenv-linter - Rust로 제작된
.env파일 린터 구현체.
7. 기타
- 파일명은 일반적으로
.env지만 식별자로.을 사용할 수 없기에 이를 구현하는 라이브러리는 대부분dotenv등의 이름을 사용한다. 때문에 해당 파일 자체를dotenv라 뭉뚱그려 부르기도 한다. - 장점 문단의 빌드타임 환경변수 부분에서 서술된 바와 같이, 빌드 과정에서 .env를 사용하는 것은 일반적인 사용 예시가 아니며, code smell일 가능성이 높다.
- 빌드 과정에서 환경변수를 사용하는 것 자체부터 reproducible build를 어렵게 만들거나 캐시 미스를 유발할 수 있으므로 신중할 필요가 있다. 빌드시에 사용되는 정보는 코드에 포함될 수 있으며, 이는 다시 말해 sensitive한 값을 넣을 수 없다는 것이고, 결국 이는 일반적인 설정파일로 대체 가능함을 의미한다. 코드에 삽입되지 않는 민감한 환경변수의 경우 대부분 빌드 환경 자체의 접근을 위해서인데, 이는 대체로 소스와 비종속적이다. 이 경우 대부분의 CI 환경에서 별도로 주입할 수 있다.
- Next.js의
NEXT_PUBLIC_이나 Vite의VITE_와 같이 프런트엔드에서 편의상.env파일 포맷을 빌드시에 사용하기도 한다. 프런트엔드를 빌드하는 경우 당연히 브라우저에는 환경변수라는 개념 자체가 없으며, 적절한 인라이닝이나import.meta를 통해 이를 유사하게 구현한 것에 불과하므로, 엄밀한 관점에서 이들은.env로더 구현체가 아니다. 프런트엔드 빌드시에 들어가는 값은 빌드된 코드에 그대로 들어가기에 일반 .env와 동일한 시맨틱으로 sensitive한 값을 넣는 것은 당연히 좋지 않다. 극단적으로, 여기서 쓰이는.env나.env.local파일은 prefixed된 변수만 남겨서 커밋해도 보안상의 문제가 없어야 한다. 사실 프런트엔드에서는 굳이 .env 형태를 사용할 필요가 없기 때문에 설정이 복잡하다면 자체적인 build-time 설정 로더를 구현하거나 JSON module 등을 사용하는 것이 이상적이다. - 나무위키 URL에서 이 문서의 주소를 직접 접근을 시도하면 Cloudflare 접근 거부 페이지가 떴었었다. 대부분의 웹 서버에서 탈취를 막기 위해 기본적으로 접근 시도로 보이는 모든 요청을 제한하기 때문. 다만 /w/문서:.env 형식으로 접근하면 에러가 나지 않는다. 현재는 접속이 가능하다.
8. 관련 문서
[1] Foreman provides this handy feature of loading variables from .env, which works great for anything that you want to put in your Procfile. ##@[2] An analysis by the Cybernews research discovered a million publicly exposed secrets from over 58 thousand websites’ exposed environment (.env) files. (...) The analysis of the most up-to-date indexes of environment files resulted in a dataset of 1,141,004 secrets cumulatively exposed from 58,364 unique websites. #@