데이터와 보안 ·
환경변수와 API 키를 서랍과 금고처럼 구분하는 법
먼저 답부터
환경변수는 주소나 실행 모드처럼 프로그램 밖에서 바꿀 설정을 담는 서랍이고, API 키는 외부 서비스 사용 권한을 가진 비밀번호에 가깝습니다. 비밀키는 브라우저 코드나 Git 저장소에 넣지 말고 서버의 비밀 저장소에서만 읽도록 해야 합니다.
AI에게 외부 API를 연결해 달라고 하면 .env 파일에 키를 넣으라는 답을 자주 봅니다. 여기서 "환경변수에 넣었으니 안전하다"고 생각하면 위험합니다. 환경변수는 설정을 코드에서 분리하는 방법이지, 그 자체로 모든 사람에게서 비밀을 숨겨 주는 금고는 아닙니다.
먼저 공개되어도 되는 설정과 노출되면 안 되는 비밀을 나눠야 합니다. 그다음 비밀을 누가, 어느 실행 환경에서, 어떤 권한으로 읽을 수 있는지 정해야 합니다.
설정값과 비밀값은 목적부터 다르다
사이트 이름, 언어, 기능 켜짐 여부처럼 공개돼도 큰 문제가 없는 값은 설정값입니다. 데이터베이스 비밀번호, 결제 비밀키, 관리자 토큰처럼 다른 사람이 사용하면 피해가 생기는 값은 비밀값입니다.
GitHub 문서는 일반 변수는 빌드 출력에 가려지지 않을 수 있으므로 비밀번호 같은 민감한 정보에는 secrets를 사용하라고 안내합니다. 이름이 TOKEN이라고 해서 자동으로 보호되는 것이 아니라 저장 장소와 출력 정책이 달라야 합니다.
- 공개 설정은 일반 환경변수나 설정 파일에 둘 수 있습니다.
- 비밀값은 배포 서비스나 저장소의 비밀 관리 기능에 넣습니다.
- 개발용과 운영용 값을 서로 분리합니다.
- 필요한 서비스와 작업에만 읽기 권한을 줍니다.
- 더 이상 쓰지 않는 키는 폐기합니다.
환경변수는 코드 밖에서 값을 주입하는 방법이다
환경변수는 프로그램을 실행하는 환경이 이름과 값을 전달하는 방식입니다. 같은 코드라도 개발 환경에서는 테스트 서버 주소를, 운영 환경에서는 실제 서버 주소를 받을 수 있습니다.
APP_ENV=development
API_BASE_URL=https://api.example.com
이 두 값은 대개 비밀이 아닙니다. 반면 PAYMENT_SECRET_KEY는 환경변수로 전달하더라도 접근 권한과 로그 출력을 별도로 막아야 합니다. 실행 중인 프로세스, 배포 설정을 볼 수 있는 사람, 오류 추적 도구가 값을 접할 가능성이 있기 때문입니다.
프론트엔드에 들어간 값은 사용자가 볼 수 있다
Vite, Next.js 같은 도구는 특정 접두사가 붙은 환경변수를 브라우저용 코드에 포함시킬 수 있습니다. 화면에 글자로 찍지 않아도 사용자가 내려받은 JavaScript 파일과 네트워크 요청을 조사하면 값을 찾을 수 있습니다.
따라서 브라우저에서 써야 하는 값은 공개되어도 되는 키여야 합니다. 비밀키가 필요한 외부 호출은 백엔드가 맡고, 프론트엔드에는 필요한 결과만 돌려주는 구조가 기본입니다.
- 키가 브라우저에서 꼭 필요한지 확인합니다.
- 공개용 키와 비밀키가 따로 있는지 공식 문서를 봅니다.
- 비밀키는 서버 환경에만 저장합니다.
- 공개용 키도 허용 도메인, API 범위, 사용량 제한을 설정합니다.
- 개발자 도구의 Network와 배포된 JavaScript에서 값이 노출되지 않는지 확인합니다.
Git에 올리지 않는 것과 이미 유출되지 않은 것은 다르다
.gitignore에 .env를 추가하면 앞으로 그 파일이 새로 추적되는 일을 막을 수 있습니다. 하지만 이미 커밋한 비밀은 파일을 지우는 것만으로 안전해지지 않습니다. 이전 커밋 기록과 원격 저장소, 복제본에 남을 수 있습니다.
비밀이 커밋됐다면 먼저 해당 키를 폐기하고 새 키를 발급해야 합니다. 기록 정리는 그다음 문제입니다. 키가 아직 유효한 상태에서 기록만 지우는 동안 누군가 사용할 수 있기 때문입니다.
커밋 전에는 git diff --staged로 다음 기록에 들어갈 내용을 확인합니다. 키의 실제 값을 검색하는 습관도 필요하지만 검색 결과를 공유 로그에 그대로 출력하지 않도록 주의합니다.
로그와 명령줄도 비밀이 새는 통로다
오류를 찾으려고 환경 전체를 출력하면 비밀값도 함께 남을 수 있습니다. GitHub는 비밀을 명령줄 인자로 전달하면 다른 사용자나 감사 기록에서 보일 수 있어 환경변수나 표준 입력 같은 방식을 고려하라고 안내합니다.
자동 마스킹도 완전한 보장은 아닙니다. 문자열을 변형하거나 JSON에 넣으면 가려지지 않을 수 있습니다. 애초에 비밀을 출력하지 않는 코드가 가장 안전합니다.
- 오류 메시지에는 키 값 대신 키가 설정됐는지만 기록합니다.
- 요청 헤더 전체를 로그에 남기지 않습니다.
- 화면 캡처와 지원 문의에 터미널 전체를 붙이기 전에 비밀을 확인합니다.
- AI 대화에 실제 키를 붙여 넣지 않습니다.
- 권한은 필요한 읽기와 쓰기 범위만 허용합니다.
키가 노출됐을 때의 순서
- 노출된 키를 즉시 폐기하거나 비활성화합니다.
- 새 키를 발급하고 안전한 비밀 저장소에 넣습니다.
- 어떤 저장소, 로그, 배포물에 노출됐는지 범위를 확인합니다.
- 키 사용 기록에서 낯선 호출이나 비용이 있는지 확인합니다.
- 원인을 고치고 재배포한 뒤 더 이상 노출되지 않는지 검사합니다.
- 필요하면 공급자와 결제 담당자에게 사고를 알립니다.
새 키부터 코드에 다시 붙여 넣으면 같은 사고가 반복됩니다. 저장 위치와 권한, 로그를 먼저 고친 뒤 교체해야 합니다.
오늘 바로 해볼 작은 실습
연습용 프로젝트의 환경변수 이름만 목록으로 적고 실제 값은 쓰지 마세요. 각 이름 옆에 "공개 설정" 또는 "비밀값"을 표시합니다. 사이트 제목은 공개 설정, 결제 비밀키는 비밀값처럼 분류합니다.
- 브라우저에서 사용되는 값인지 서버에서만 쓰이는 값인지 나눕니다.
- 저장소에 올라간 설정 파일에 실제 비밀이 없는지 확인합니다.
- 배포 서비스에서 누가 비밀 설정을 볼 수 있는지 확인합니다.
- 오류 로그에 요청 헤더나 환경 전체를 출력하는 코드가 없는지 찾습니다.
- 쓰지 않는 테스트 키가 있다면 공급자 화면에서 폐기할 대상을 기록합니다.
실습 결과에는 값 대신 이름과 보관 위치만 남깁니다. 예를 들어 "PAYMENT_SECRET_KEY는 운영 서버 비밀 저장소, 관리자만 접근" 정도면 충분합니다. 이 목록은 AI에게 보안 구조를 설명할 때도 실제 키를 노출하지 않는 안전한 입력이 됩니다.
환경변수는 정리 도구이고 비밀 저장소는 보호 장치입니다. 둘을 같은 것으로 생각하지 않으면 바이브코딩에서 가장 흔한 실수 하나를 피할 수 있습니다. 기능이 작동하는지 확인할 때만큼, 그 기능이 어떤 비밀을 어디에서 읽는지도 함께 확인해 보세요.
확인한 자료
- Variables — GitHub Docs
- Using secrets in GitHub Actions — GitHub Docs
- Secure use reference — GitHub Docs