포스트

환경 변수 안전하게 관리하기: Doppler와 dotenv 비교

LLM과 함께 프로젝트 코드를 작성하다 보면 환경변수 파일을 LLM이 읽어들이는 경우가 있습니다.

Cursor의 경우는 cursorignore에 .env 등을 등록해 놓으면 해당 파일을 외부로 전송하거나 하지는 않습니다.

하지만, mcp의 기능을 사용하다 보면 이 규칙을 우회하여 직접 .env 등을 읽고 수정하는 일이 가능해집니다.

그래서 가장 안전한 해결책은, 프로젝트 내에 중요한 key를 보관하지 않는 것입니다.

따라서 프로젝트 외부에서 따로 환경변수를 관리할 수 있는 방법을 찾아보는 것이 좋습니다.

그 방법으로 Doppler라는 개발도구가 있습니다.

중요 시크릿은 Doppler에서 관리하고, 일반 설정은 `.env`에 보관하는 것이 좋습니다.

그렇게 하면 환경변수의 우선순위에 따라 적절하게 프로젝트 내에서 읽어서 사용하게 됩니다.

그리고 python의 load_dotenv를 그대로 사용할 수가 있습니다. .env가 만약 비어있더라도 Doppler에 필요한 환경변수가 이미 등록이 되어있다면 문제 없습니다.


환경 변수의 일반적인 우선순위: **

  1. 명령줄에서 직접 설정된 변수 (최우선)
  2. OS 환경 변수
  3. `.env` 파일의 변수 (최하위)
1
2
3
4
5
6
7
8
9
10
11
# python
# 환경 변수 로드 코드 예시
from dotenv import load_dotenv
import os

# .env 파일 로드 (override=True는 기존 환경 변수를 덮어씀)
load_dotenv(override=True)

# 환경 변수 사용
api_key = os.getenv("API_KEY")
debug_mode = os.getenv("DEBUG", "false").lower() == "true"

( override=True의 의미)

  • `override=True`: OS에 이미 있는 환경 변수도 `.env` 파일의 값으로 덮어씁니다.
  • `override=False`(기본값): OS에 이미 있는 환경 변수는 유지하고, 없는 변수만 `.env`에서 가져옵니다.

추천 설정 방법

  1. 중요 시크릿(API 키, DB 비밀번호): Doppler 또는 Vault에서 관리
  2. 일반 설정(포트, 디버그 모드): 프로젝트 내 `.env`에 저장
  3. 개발/테스트용 비밀: `.env.development`/`.env.test`로 분리
  4. CI/CD 환경: 시크릿 관리 도구와 통합

코드 예시

1
2
3
4
5
6
7
8
9
10
11
# secrets.py
from dotenv import load_dotenv
import os

# 1. Doppler/Vault 변수가 먼저 OS 환경에 로드됨 (시스템 단계)
# 2. .env 변수 로드 (override=False로 하면 Doppler 값 유지)
load_dotenv(override=False)

# API 키는 Doppler에서, 포트는 .env에서 설정
API_KEY = os.getenv("API_KEY")
PORT = int(os.getenv("PORT", "8000"))

이 방식을 사용하면 중요 시크릿은 안전하게 관리하면서도 개발 편의성을 유지할 수 있습니다.

https://github.com/DopplerHQ/cli

GitHub - DopplerHQ/cli: The official CLI for interacting with your Doppler secrets and configuration. The official CLI for interacting with your Doppler secrets and configuration. - DopplerHQ/cli The official CLI for interacting with your Doppler secrets and configuration. - DopplerHQ/cli

도플러 설치

1
2
3
winget install --id Doppler.doppler
doppler --version

만일 외부 cmd나 pwsh에서는 doppler –version이 조회되지만

1
where doppler 혹은 Get-command doppler

cusor에서 winget으로 설치한 doppler를 인식하지 못한다면, .vscode/settings.json에 다음과 같이 추가합니다.

1
2
3
  "terminal.integrated.env.windows": {
    "PATH": "C:\\Users\\username\\AppData\\Local\\Microsoft\\WinGet\\Links;${env:PATH}"
  }

+추가) 그러나 doppler는 중앙화된 비밀 관리 시스템이기 때문에, doppler계정이 탈취되면 모든 키가 탈취될 수 있는 위험이 있습니다.

그래서 .env를 그대로 사용하되 중요한 .env는 프로젝트 바깥에서 불러오도록 하거나,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from dotenv import load_dotenv
import os

EXTERNAL_ENV = "C:/Users/username/my-secrets/.env"
INTERNAL_ENV = ".env"

# 외부 키 먼저
load_dotenv(dotenv_path=EXTERNAL_ENV)
# 내부에서 필요한 것만 덮어쓰기 (선택)
load_dotenv(dotenv_path=INTERNAL_ENV, override=False)

# 확인
print("🔐 OPENAI_API_KEY =", os.getenv("OPENAI_API_KEY"))
print("🐞 DEBUG =", os.getenv("DEBUG"))

vault라는 로컬비밀번호관리도구를 설치해서 사용하는 방법이 있습니다.

각자의 상황에 맞추어 필요한 방법대로 하면 될 것 같습니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.