자동화 스크립트를 하나둘 만들다 보면 어느 순간 코드 여기저기에 API 키가 흩어집니다. 노션 토큰, 구글 서비스 계정 키, 텔레그램 봇 토큰, 데이터베이스 비밀번호까지요. 처음에는 “나 혼자 쓰는 스크립트니까 괜찮겠지” 싶지만, 깃허브에 백업하려는 순간 문제가 됩니다. 실제로 공개 저장소에 올라간 키는 자동 스캐너에 몇 분 안에 발견됩니다. 유료 API 키라면 요금이 청구되고, 클라우드 키라면 계정 자체가 위험해집니다. 이번 글에서는 .env 파일로 API 키를 코드 밖으로 빼내고, 실수로 유출되지 않게 관리하는 방법을 처음부터 정리합니다.
왜 코드에 키를 직접 쓰면 안 되나
아래는 흔히 보는 형태입니다. 동작에는 문제가 없지만 위험 요소가 세 가지 있습니다.
# app.py -- 이렇게 쓰면 안 됩니다
import requests
OPENAI_KEY = "sk-proj-abc123def456..." # 코드에 그대로 박혀 있음
NOTION_TOKEN = "secret_xyz789..."
r = requests.get(
"https://api.notion.com/v1/users/me",
headers={"Authorization": "Bearer " + NOTION_TOKEN,
"Notion-Version": "2022-06-28"},
timeout=10,
)
print(r.status_code)
- 깃에 그대로 올라갑니다. 한 번 커밋되면 나중에 코드에서 지워도 커밋 기록에는 남습니다.
- 공유가 안 됩니다. 동료에게 코드를 보내려면 키를 지운 사본을 따로 만들어야 합니다.
- 환경 구분이 안 됩니다. 내 PC에서 쓰는 테스트 키와 서버에서 쓰는 실제 키를 바꿔 넣으려면 코드를 고쳐야 합니다.
세 문제 모두 원인이 같습니다. 바뀌는 값과 바뀌지 않는 로직이 한 파일에 섞여 있다는 것입니다. 환경변수는 이 둘을 분리하는 가장 표준적인 방법입니다.
1단계: .env 파일 만들기
프로젝트 폴더 맨 위에 .env라는 이름의 파일을 만듭니다. 확장자가 아니라 파일 이름 자체가 .env입니다. 내용은 키=값 형식 한 줄에 하나씩 씁니다.
# .env -- 프로젝트 루트에 만듭니다
# 따옴표 없이, 등호 앞뒤 공백 없이 쓰는 것이 기본입니다.
OPENAI_API_KEY=sk-proj-abc123def456
NOTION_TOKEN=secret_xyz789
DB_PASSWORD=my!local#pw
# 값에 공백이나 #이 들어가면 따옴표로 감쌉니다.
APP_NAME="My Side Project"
작성할 때 자주 틀리는 부분이 있습니다. 등호 앞뒤에 공백을 넣으면 안 되고(KEY = value는 키 이름이 KEY 가 됩니다), 값을 따옴표로 감싸면 그 따옴표까지 값에 포함되는 라이브러리도 있습니다. 값에 공백이나 #이 들어갈 때만 따옴표를 쓰세요.
2단계: 코드에서 읽어 쓰기
파이썬은 python-dotenv 패키지를 씁니다. pip install python-dotenv로 설치한 뒤, 코드 맨 위에서 한 번만 불러오면 됩니다.
# app.py -- .env에서 읽어 쓰는 버전
import os
import requests
from dotenv import load_dotenv
load_dotenv() # 같은 폴더의 .env를 읽어 환경변수로 등록
NOTION_TOKEN = os.environ["NOTION_TOKEN"]
r = requests.get(
"https://api.notion.com/v1/users/me",
headers={"Authorization": "Bearer " + NOTION_TOKEN,
"Notion-Version": "2022-06-28"},
timeout=10,
)
print(r.status_code)
load_dotenv()는 현재 폴더에서 .env를 찾아 읽고, 그 값을 프로세스의 환경변수로 등록합니다. 이후에는 os.environ으로 어디서든 꺼내 쓸 수 있습니다. 중요한 건 이제 코드에 실제 키 문자열이 한 글자도 없다는 점입니다. 이 파일은 그대로 깃허브에 올려도 안전합니다.
3단계: os.environ과 os.getenv 구분해서 쓰기
값을 꺼내는 방법이 세 가지인데, 잘못 고르면 원인을 찾기 어려운 버그가 생깁니다.
import os
# 1) 없으면 즉시 에러 -- 필수 값에 사용
token = os.environ["NOTION_TOKEN"]
# 2) 없으면 None -- 조용히 넘어가서 원인 찾기 어려움
token = os.getenv("NOTION_TOKEN")
# 3) 없으면 기본값 -- 선택 값에만
timeout = int(os.getenv("TIMEOUT", "30"))
# 시작 지점에서 한 번에 검사하는 방식을 추천합니다
REQUIRED = ("NOTION_TOKEN", "OPENAI_API_KEY")
missing = [k for k in REQUIRED if not os.getenv(k)]
if missing:
raise SystemExit("환경변수 누락: " + ", ".join(missing))
os.getenv()는 값이 없으면 조용히 None을 돌려줍니다. 그러면 API 요청 헤더에 Bearer None이 들어가고, 결국 401 오류로 되돌아옵니다. 키를 잘못 넣은 건지 파일을 못 읽은 건지 헷갈리게 되죠. 필수 값은 프로그램 시작 지점에서 한 번에 검사하고, 없으면 즉시 멈추게 만드는 편이 훨씬 낫습니다.
4단계: .gitignore에 반드시 추가하기
여기까지 해도 .env 파일 자체는 폴더에 그대로 있습니다. 깃이 이 파일을 무시하도록 .gitignore에 등록해야 비로소 안전해집니다.
# .gitignore
.env
.env.*
!.env.example
마지막 줄의 !.env.example은 “이 파일 하나만은 예외로 추적하라”는 뜻입니다. 실제 값 없이 어떤 키가 필요한지 목록만 담은 견본 파일인데, 나중에 다른 PC에서 코드를 내려받거나 동료에게 넘길 때 뭘 채워야 하는지 알려주는 역할을 합니다.
# .env.example -- 이 파일은 깃에 올립니다
# 실제 값 대신 형식만 적어둡니다.
OPENAI_API_KEY=
NOTION_TOKEN=
DB_PASSWORD=
APP_NAME=My Side Project
이 두 파일 조합이 사실상 표준입니다. .env는 각자 로컬에만 두고, .env.example은 깃에 올려 공유합니다.
Node.js와 다른 환경에서는
자바스크립트 쪽도 방식은 같습니다. 다만 Node 20.6부터는 별도 패키지 없이 실행 옵션만으로 됩니다.
// Node.js -- npm i dotenv
require('dotenv').config();
const token = process.env.NOTION_TOKEN;
if (!token) throw new Error("NOTION_TOKEN이 없습니다");
// Node 20.6 이상이면 패키지 없이도 됩니다
// node --env-file=.env app.js
깃허브 액션이나 클라우드 서버처럼 .env 파일을 올릴 수 없는 곳에서는, 그 플랫폼이 제공하는 비밀값 저장소에 같은 이름으로 등록하면 코드를 고칠 필요가 없습니다. 이것이 환경변수 방식의 진짜 장점입니다.
환경별 관리 방법 비교
| 환경 | 저장 위치 | 비용 | 메모 |
|---|---|---|---|
| 내 PC | .env 파일 | 무료 | 가장 간단. .gitignore 필수 |
| 깃허브 액션 | Repository secrets | 무료 | 설정 > Secrets and variables |
| 윈도우 작업 스케줄러 | 시스템 환경변수 | 무료 | 설정 후 재로그인해야 반영됨 |
| 팀 공유 | 1Password / Bitwarden 등 | 유료(무료 플랜 있음) | .env를 메신저로 주고받지 말 것 |
이미 키를 커밋해버렸다면
가장 흔한 사고입니다. 우선 추적에서 빼는 것부터 합니다.
# 이미 커밋해버린 .env를 추적에서 빼기
# (주의: 과거 커밋 기록에는 그대로 남습니다)
git rm --cached .env
echo ".env" >> .gitignore
git commit -m "chore: .env 추적 제외"
다만 이 명령은 앞으로의 커밋에서만 제외할 뿐, 이미 올라간 과거 커밋에는 키가 그대로 남아 있습니다. 저장소가 공개였다면 파일을 지우는 것보다 해당 키를 발급처에서 폐기하고 새로 발급받는 것이 유일하게 확실한 대응입니다.
키 폐기는 대부분 서비스 대시보드에서 몇 번의 클릭으로 끝납니다. 커밋 기록을 되돌리는 작업보다 훨씬 빠르고 안전하니, 고민하지 말고 재발급하세요.
주의할 점
.env를 만들었다면 같은 커밋에서.gitignore도 함께 수정하세요. 순서가 밀리면 그 사이에 커밋될 수 있습니다.- 에러 로그나
print()에 키 값이 찍히지 않는지 확인하세요. 깃허브 액션 로그는 저장소가 공개면 함께 공개됩니다. .env는 백업 대상에서도 빠지기 쉽습니다. PC를 옮길 때 잊지 말고 따로 챙기세요.- 스크린샷이나 화면 공유 때 편집기에
.env가 열려 있지 않은지 확인하는 습관을 들이면 좋습니다.
마무리
지금 작업 중인 스크립트를 하나 열어서, 코드에 박혀 있는 키 딱 하나만 .env로 옮겨보세요. .gitignore에 .env 한 줄 추가하는 것까지 합쳐도 5분이면 끝납니다. 나머지는 다음에 그 파일을 만질 때 하나씩 옮기면 됩니다.
다음 글에서는 이렇게 정리한 자동화 스크립트를 여러 대의 PC에서 똑같이 돌릴 수 있게 만드는 방법을 다뤄보겠습니다.