IT 시행착오·

API 키를 코드에 넣지 않는 이유와 .env 관리

Notion·메모에 키를 모아 두다 생긴 실수와, Git·배포 환경에서 비밀값을 나누는 현실적인 방법을 정리합니다.

사이버보안·시크릿 관리 — Cybersecurity.png (cc0, wikimedia-commons)

API 키는 “프로그램용 비밀번호”입니다. AI, 지도, 결제, 클라우드, 메신저 봇까지 키가 있으면 그 권한으로 호출할 수 있습니다. 그래서 저장소에 평문으로 올리는 건 비밀번호를 이슈에 붙여 넣는 것과 비슷합니다.

저도 편하려고 Notion “API 키” 페이지에 Cloudflare·OpenRouter·Groq·텔레그램 봇 토큰을 한꺼번에 적어 둔 적이 있습니다. 작업할 때는 편한데, 그 페이지가 공유되거나 스크린샷·에이전트 로그에 섞이면 유출 면적이 커집니다. 블로그 글이나 공개 레포에는 절대 실키를 붙이지 않는 것을 원칙으로 바꿨습니다. 예시는 항상 sk-... / cfut_...처럼 마스킹합니다.

코드와 비밀을 나누는 이유

소스는 공유·리뷰·히스토리가 전제입니다. 키는 환경마다 다르고, 유출 시 바로 폐기·재발급할 수 있어야 합니다. 코드에 박아 두면:

  • Git 이력에 영원히 남는다
  • PR·CI 로그에 찍힐 수 있다
  • 개인 저장소가 공개로 바뀌는 순간 사고가 된다
  • 개발키와 운영키가 섞여 요금이 나온다

환경 변수로 빼면 코드는 그대로 두고 로컬/프리뷰/프로덕션 값만 바꿉니다.

로컬은 .env, Git에는 안 넣기

프로젝트 루트에 .env를 두고:

OPENROUTER_API_KEY=여기에_로컬용만
CLOUDFLARE_ACCOUNT_ID=계정ID는_공개돼도_되는_경우만

.gitignore.env, .env.*, !.env.example을 넣습니다. .env.example에는 키 이름만 남깁니다.

OPENROUTER_API_KEY=
CLOUDFLARE_API_TOKEN=

Astro/Vite처럼 클라이언트에 노출되는 접두사(PUBLIC_ 등)는 공개 값만 넣습니다. AdSense 클라이언트 ID처럼 원래 공개되는 값과, Workers 배포 토큰은 등급이 다릅니다.

배포 쪽

Cloudflare면 대시보드 Secrets / Environment variables, GitHub Actions면 Repository secrets에 넣습니다. 노션 배포 메모에 토큰 전체를 붙여 두고 wrangler deploy만 복사해 쓰던 방식은, 메모 유출 = 계정 유출입니다. 스크립트는 키를 읽기만 하고 출력하지 않게 합니다.

set -a; source .secrets/xxx.env; set +a처럼 로컬에서만 불러오는 패턴은 편하지만, .secrets/도 Git 제외가 필수입니다.

유출됐을 때

  1. 해당 서비스에서 키 폐기·재발급
  2. Git에 들어갔다면 히스토리 정리(단순 커밋 삭제로 부족할 수 있음)
  3. 같은 키가 붙은 봇·Webhook·Workers 재설정
  4. 노션/슬랙/이슈에 붙은 평문 삭제

OpenRouter·Groq처럼 무료 키가 여러 개면 “어디에 어떤 키가 붙었는지” 표로만 관리하고, 값 자체는 패스워드 매니저에 둡니다. 모델 비용 비교는 OpenRouter로 코딩할 때 모델 비용에 숫자 감각만 적어 두었습니다.

키 관리는 거창한 보안 체계보다, 평문을 한곳에 몰아두지 않는 습관이 먼저입니다. 새 서비스를 붙일 때마다 “이 값이 Git·노션·스크린샷 중 어디에 남는가?”만 물어봐도 사고가 많이 줄어듭니다.