2장. 개발 환경 준비
환경 변수와 .env
지갑 속 출입증은 필요할 때만 꺼내 보입니다. 가게 문을 열 때 지갑에서 꺼내 앞치마 주머니에 옮겨 두면, 일하는 동안에는 주머니에서 바로 꺼내 씁니다. 코드도 같습니다. 키는 .env 파일(지갑)에 두고, 프로그램이 시작할 때 load_dotenv() 로 환경 변수(앞치마 주머니)에 옮긴 뒤, 필요한 곳에서 os.getenv() 로 꺼냅니다.
윗줄의 파랑 화살표가 키가 흘러가는 길입니다. .env 파일의 키가 load_dotenv() 로 환경 변수에 올라가고, 우리 코드가 os.getenv() 로 꺼내, 요청에 실어 OpenAI 서버로 보냅니다. 아래 왼쪽의 회청 점선과 X 는 막아 둔 길로, .gitignore 가 .env 를 깃허브에 올리지 않게 막습니다. 아래 오른쪽 하늘색 점선 상자처럼 코드 파일에는 키가 아니라 키의 이름 OPENAI_API_KEY 만 적힙니다. 여기서 지갑이 .env, 앞치마 주머니가 환경 변수, 출입증을 내미는 손이 요청입니다. 비유와 다른 점은, 환경 변수는 프로그램이 끝나면 사라진다는 것입니다. 다음에 실행할 때 load_dotenv() 가 다시 올립니다.
.env 파일의 꼴
과목 폴더에는 견본 .env.example 이 있습니다. 이 파일을 복사해 이름을 .env 로 바꾸고, 발급받은_인증키 자리에 교수자가 준 키를 넣습니다.
# 이 줄처럼 # 으로 시작하면 설명이다
OPENAI_API_KEY=발급받은_인증키
OPENAI_MODEL=gpt-4.1-mini| 지킬 것 | 까닭 |
|---|---|
= 앞뒤에 빈칸을 두지 않는다 | 빈칸이 키의 일부로 읽힐 수 있다 |
| 따옴표로 감싸지 않아도 된다 | python-dotenv 는 따옴표가 있으면 떼고 읽는다 |
파일 이름은 .env 하나만 | .env.txt 로 저장되면 찾지 못한다. 탐색기에서 확장자를 켜고 확인한다 |
| 과목 폴더(맨 위)에 둔다 | find_dotenv(usecwd=True) 가 지금 폴더에서 위로 올라가며 찾는다 |
OPENAI_MODEL 은 없어도 된다 | 코드가 os.getenv("OPENAI_MODEL", "gpt-4.1-mini") 로 기본값을 쓴다 |
키를 읽는 세 함수
| 함수 | 원래 말 | 하는 일 |
|---|---|---|
find_dotenv(usecwd=True) | find(찾다), use cwd(current working directory, 지금 작업 폴더를 쓰다) | 지금 폴더에서 위로 올라가며 .env 를 찾아 경로를 돌려준다. 못 찾으면 빈 글자 |
load_dotenv(경로) | load(싣다, 올리다) | .env 의 줄을 환경 변수로 올린다. 이미 있는 환경 변수는 덮지 않는다 |
os.getenv(이름, 기본값) | get environment variable | 환경 변수를 읽는다. 없으면 기본값, 기본값도 없으면 None |
dotenv_values(경로) | values(값들) | .env 를 사전으로만 읽는다. 환경 변수는 건드리지 않는다 |
이 과정에서 모델을 부르는 코드는 모두 아래 세 줄로 시작합니다. 실습 코드는 data 폴더에서 돌므로, find_dotenv(usecwd=True) 가 한 칸 위의 과목 폴더에서 .env 를 찾습니다.
load_dotenv(find_dotenv(usecwd=True))
MODEL = os.getenv("OPENAI_MODEL", "gpt-4.1-mini")
model = ChatOpenAI(model=MODEL, temperature=0)짧은 예시
진짜 .env 를 건드리지 않도록 견본 글을 바로 읽어 봅니다. io.StringIO 는 글자열을 파일처럼 읽게 해 주는 도구입니다(String Input/Output). 견본의 키는 지어낸 시험용 글자입니다.
import io
import os
from dotenv import dotenv_values
# .env 파일에 적을 글과 같은 꼴의 견본. 진짜 키가 아니다
sample = "# 연습용 견본\nTEST_API_KEY=sk-test-0000000000000000\nTEST_MODEL=gpt-4.1-mini\n"
# stream=: 파일 경로 대신 글을 바로 넣는다. 설명 줄(#)은 빠진다
values = dotenv_values(stream=io.StringIO(sample))
print(dict(values))
# dotenv_values 는 사전만 돌려주므로 환경 변수에는 들어가지 않는다
print("환경 변수 TEST_MODEL:", os.getenv("TEST_MODEL"))
# 기본값을 주면 없을 때 그 값을 돌려준다
print("TEST_TIMEOUT:", os.getenv("TEST_TIMEOUT", "30")){'TEST_API_KEY': 'sk-test-0000000000000000', 'TEST_MODEL': 'gpt-4.1-mini'}
환경 변수 TEST_MODEL: None
TEST_TIMEOUT: 30# 으로 시작한 설명 줄은 빠지고 "이름=값" 두 줄만 사전이 되었습니다. 환경 변수에 올리려면 load_dotenv() 를 불러야 하고, 실습 2에서 연습용 파일로 해 봅니다.
코드가 의미하는 것
이 코드는 지갑을 열어 안의 출입증을 들여다보기만 하고, 앞치마 주머니에는 옮기지 않는 일입니다. 견본 글 읽기, 환경 변수 확인, 기본값 쓰기의 세 동작입니다.
| 코드 | 하는 일 | 챗봇에서 뜻하는 것 |
|---|---|---|
io.StringIO(sample) | 글자열을 파일처럼 읽게 한다 | 진짜 .env 를 건드리지 않고 같은 꼴의 글로 연습합니다 |
dotenv_values(stream=...) | "이름=값" 줄을 사전으로 돌려준다 | 키가 제대로 적혔는지 들여다볼 때 씁니다. 프로그램 전체에는 알리지 않습니다 |
os.getenv("TEST_MODEL") | 환경 변수를 읽는다 | 챗봇 코드가 키와 모델 이름을 꺼내는 곳이 환경 변수입니다. 지갑만 열어 본 상태라 아직 None 입니다 |
os.getenv("TEST_TIMEOUT", "30") | 없으면 기본값을 돌려준다 | .env 에 OPENAI_MODEL 이 없어도 gpt-4.1-mini 로 도는 것과 같은 장치입니다 |
결과에서 읽을 것은 두 가지입니다.
- 설명 줄은 빠지고 두 줄만 남았습니다. 사전에는
TEST_API_KEY와TEST_MODEL만 있습니다.#줄에 무엇을 적어도 값으로 읽히지 않습니다. - 사전과 환경 변수는 따로입니다.
TEST_MODEL이 사전에는 있지만 환경 변수로는None입니다. 모델을 부르는 코드가 키를 찾게 하려면load_dotenv()가 있어야 합니다.
주의할 점
.env를 고친 뒤에는 프로그램을 다시 실행합니다. 이미 돌고 있는 프로그램(주피터 노트북 등)은load_dotenv()를 다시 불러도 이미 있는 값을 덮지 않습니다. 덮으려면load_dotenv(override=True)를 씁니다. override 는 "위에 덮어쓰다"라는 뜻입니다..env파일을 열어 둔 채 화면을 공유하지 않습니다. 편집기 탭에 그대로 보입니다.- 과목 폴더의
.gitignore에.env가 적혀 있는지 한 번 열어 확인합니다.