학습 기록
Python 공부 기록 #23 - python-dotenv로 API 키를 env 파일에 분리하기
0. 목차
- 인트로: API 키를 코드에 그대로 적어도 될까?
.env파일은 어떤 역할을 할까?python-dotenv설치하기.env에 API 키 저장하기- Python에서 환경 변수 읽어오기
- 키가 없을 때 오류 남기기
.gitignore로 업로드 막기- 삽질했던 부분들
- 아웃트로: 코드에서 비밀값 걷어내기
1. 인트로: API 키를 코드에 그대로 적어도 될까?
지난 글에서는 configparser의 기본값과 예외 처리로 설정 파일이 빠져도 자동 실행이 바로 멈추지 않게 만들었습니다.
파일 경로나 로그 개수는 config.ini에 넣어도 괜찮았습니다. 근데 ChatGPT API나 Discord Webhook처럼 외부 서비스에 연결하려고 보니 코드 안에 긴 인증 키를 적어야 했습니다.
api_key = "여기에-실제-API-키"
혼자 쓰는 코드라 처음에는 별문제 없어 보였습니다. 하지만 GitHub에 올리거나 다른 사람에게 파일을 보내면 키까지 같이 넘어갈 수 있었습니다.
코드에는 키의 이름만 남기고, 실제 값은 별도 파일에서 읽게 만들 필요가 있었습니다.
찾아보니까 python-dotenv 패키지로 .env 파일의 값을 환경 변수처럼 불러올 수 있었습니다.
2. .env 파일은 어떤 역할을 할까?
.env는 이름=값 형태로 설정을 적는 텍스트 파일입니다.
OPENAI_API_KEY=실제_API_키
DISCORD_WEBHOOK_URL=실제_웹훅_주소
Python 파일에는 실제 키가 보이지 않고, 실행할 때 .env의 값을 읽습니다. 회사 PC와 집 PC에서 서로 다른 값을 넣을 수도 있습니다.
다만 .env 자체가 암호화되는 것은 아닙니다. 메모장으로 열면 값이 그대로 보입니다. 그래서 코드와 분리하는 것만큼 파일을 외부에 올리지 않는 일이 중요했습니다.
3. python-dotenv 설치하기
python-dotenv는 Python 기본 모듈이 아니어서 먼저 설치해야 합니다.
python -m pip install python-dotenv
설치 이름은 python-dotenv지만 코드에서는 dotenv에서 불러옵니다.
from dotenv import load_dotenv
처음에는 설치 이름과 import 이름이 달라서 잘못 설치한 줄 알았습니다. 명령어와 코드의 이름이 다르다는 것만 기억하면 됐습니다.
4. .env에 API 키 저장하기
Python 파일이 있는 폴더에 .env 파일을 만들었습니다.
OPENAI_API_KEY=sk-example-key
CRAWLER_MODE=headless
이름 앞에 $를 붙이거나 값 전체를 Python 문자열처럼 작성할 필요는 없었습니다.
# 이렇게 작성하지 않기
$OPENAI_API_KEY = "sk-example-key",
쉼표까지 넣으면 쉼표도 값의 일부가 될 수 있습니다. 가장 간단하게 이름=값으로 적는 편이 헷갈리지 않았습니다.
5. Python에서 환경 변수 읽어오기
이제 .env를 읽고 os.getenv()로 값을 가져왔습니다.
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
crawler_mode = os.getenv(
"CRAWLER_MODE",
"normal"
)
print("크롤러 모드:", crawler_mode)
print("API 키 존재 여부:", bool(api_key))
load_dotenv()가 .env의 값을 환경 변수로 불러옵니다. 그다음 os.getenv("OPENAI_API_KEY")로 원하는 값을 꺼냈습니다.
두 번째 인수에는 환경 변수가 없을 때 쓸 기본값을 넣을 수 있습니다. CRAWLER_MODE가 없다면 normal이 들어갑니다.
API 키 자체를 출력하지 않고 bool(api_key)로 존재 여부만 확인하니 로그에 비밀값을 남기지 않을 수 있었습니다. 이거 되겠는데? 싶었습니다.
6. 키가 없을 때 오류 남기기
인증 키는 기본값으로 대신하기 어려운 필수 설정입니다. 값이 없으면 분명한 오류를 남기도록 했습니다.
import logging
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
logging.error(
"OPENAI_API_KEY가 설정되지 않았습니다."
)
raise RuntimeError(
".env 파일의 OPENAI_API_KEY를 확인하세요."
)
logging.info("API 키를 정상적으로 불러왔습니다.")
키가 없는데 외부 서비스에 요청을 보내면 인증 오류가 납니다. 요청을 보내기 전에 확인하면 문제 위치를 더 빨리 찾을 수 있었습니다.
로그에는 키의 실제 값이나 앞뒤 일부도 남기지 않았습니다. 정상적으로 읽었는지만 기록했습니다.
7. .gitignore로 업로드 막기
.env를 만들었어도 GitHub에 같이 올리면 코드에서 분리한 의미가 줄어듭니다.
프로젝트 폴더의 .gitignore에 아래 한 줄을 추가했습니다.
.env
다른 사람이 필요한 항목 이름을 알 수 있도록 실제 값이 없는 .env.example도 만들 수 있습니다.
OPENAI_API_KEY=
CRAWLER_MODE=normal
공유할 때는 .env.example만 올리고, 각자 .env에 자신의 값을 넣으면 됩니다.
.env는 코드에서 비밀값을 분리하는 파일이지, 그대로 공유해도 되는 안전 보관함은 아니었습니다.
8. 삽질했던 부분들
처음에는 아래 패키지를 설치했습니다.
python -m pip install dotenv
제가 필요했던 패키지 이름은 python-dotenv였습니다. 설치 후에도 import 오류가 난다면 현재 실행 중인 Python 환경에 설치했는지 확인해야 했습니다.
또 .env.txt로 파일이 저장된 적도 있었습니다. Windows 탐색기에서 확장자가 숨겨져 있어서 겉으로는 .env처럼 보였습니다. 파일 확장명 표시를 켜고 실제 이름을 확인하니 해결됐습니다.
마지막으로 키가 잘 읽히는지 확인하려고 print(api_key)를 사용했습니다. 터미널 기록이나 화면 캡처에 키가 남을 수 있어서 바로 지웠습니다. 비밀값은 출력하지 않고 존재 여부만 확인하는 편이 좋았습니다.
이미 Git에 커밋한 키는 .gitignore를 추가해도 과거 기록에서 자동으로 사라지지 않습니다. 그런 경우에는 해당 키를 폐기하고 새로 발급받아야 합니다.
9. 아웃트로: 코드에서 비밀값 걷어내기
오늘은 python-dotenv로 API 키를 .env 파일에 분리하고 환경 변수로 읽어봤습니다.
핵심 정리
python-dotenv를 설치하고load_dotenv()호출하기.env에는이름=값형태로 설정 적기os.getenv()로 환경 변수 가져오기- 필수 키가 없으면 요청 전에 오류 남기기
.gitignore에.env를 추가하고 실제 키는 출력하지 않기
이제 할 수 있는 것들
- API 키를 Python 코드 밖으로 분리하기
- PC마다 서로 다른 인증 정보 사용하기
- GitHub에 비밀값이 올라가는 실수 막기
- 자동 실행 전에 필수 환경 변수 확인하기
비전공자 Python 공부를 하면서 코드가 실행되는지만 확인했는데, 업무 자동화에서는 인증 정보를 어떻게 보관하는지도 같이 봐야 했습니다. Python 독학 중 만든 작은 코드라도 외부 서비스와 연결되는 순간부터는 같은 주의가 필요했습니다.
다음 포스팅에서는 .env에 저장한 Discord Webhook 주소를 읽고, 크롤링 성공과 실패 결과를 내 Discord 채널로 보내는 방법을 공부해 보겠습니다.