이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Compose에서 .env, env_file, environment는 각각 어떻게 다르게 동작하는가?
  • 로컬 Secret과 운영 Secret을 섞지 않으려면 어떤 규칙이 필요한가?
  • 환경 변수 주입 문제를 어떻게 확인하고 안전하게 공유해야 하는가?

개요

Compose 로컬 환경은 많은 설정 값을 환경 변수로 주입한다.

Spring profile, DB URL, Redis host, DB password, 외부 API mock 주소가 모두 환경 변수로 들어갈 수 있다.

문제는 .env, env_file, environment가 같은 것으로 보이지만 동작 위치가 다르다는 점이다.

이 차이를 모르면 local에서는 되는데 CI나 팀원 환경에서는 실패하는 일이 생긴다.

.env 파일

Compose의 .env는 Compose 파일에서 변수 치환에 쓰인다.

services:
  api:
    image: order-api:${APP_VERSION}
    ports:
      - "${API_PORT}:8080"

.env:

APP_VERSION=local
API_PORT=8080

이 값은 Compose 파일을 해석할 때 사용된다.

container 내부 환경 변수로 자동 주입된다고 생각하면 안 된다.

env_file

env_file은 container 내부 environment로 값을 넣는다.

services:
  api:
    env_file:
      - .env.local

.env.local:

SPRING_PROFILES_ACTIVE=local-compose
DB_URL=jdbc:mysql://mysql:3306/app
DB_USERNAME=app
DB_PASSWORD=app-password
REDIS_HOST=redis

이 값들은 Spring Boot가 읽을 수 있다.

하지만 .env.local에는 운영 Secret을 넣지 않는다.

repository에는 .env.example만 두고 실제 .env.local은 각자 만들게 하는 방식이 안전하다.

environment

environment는 Compose 파일 안에서 직접 container env를 정의한다.

services:
  api:
    environment:
      SPRING_PROFILES_ACTIVE: local-compose
      DB_URL: jdbc:mysql://mysql:3306/app

간단한 local 값에는 편하지만, Secret 값을 직접 넣으면 Compose 파일이 민감 정보 저장소가 된다.

반복되는 값이나 개인별 값은 env_file로 분리하는 편이 낫다.

우선순위와 확인

환경 변수는 여러 위치에서 올 수 있다.

최종 결과는 docker compose config와 container 내부 env로 확인한다.

docker compose config
docker compose exec api printenv | grep -E 'SPRING|DB_|REDIS'

Secret 값은 그대로 공유하지 않는다.

문제 공유에는 key 존재 여부, source, version, 일부 masking만 쓴다.

DB_PASSWORD: present, source=.env.local, masked=app****

Spring 설정 연결

Spring 설정 파일은 env key를 참조한다.

spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  data:
    redis:
      host: ${REDIS_HOST}

key 이름이 Compose와 Spring에서 다르면 startup 시점에 실패하거나 기본값으로 떠 버릴 수 있다.

기본값을 넣을 때는 local 전용인지 운영에서도 허용되는지 조심해야 한다.

운영 체크리스트

  • .env는 Compose 치환용, env_file은 container env 주입용임을 구분했는가?
  • repository에는 .env.example만 두는가?
  • .env.local.gitignore에 들어 있는가?
  • 운영 Secret을 local env 파일에 복사하지 않는가?
  • Spring 설정 key와 Compose env key가 일치하는가?
  • docker compose configprintenv로 최종 값을 확인했는가?
  • 문제 공유 시 Secret 값이 노출되지 않는가?

장애 신호

  • Compose 파일 치환은 되었지만 container 내부 env에는 값이 없다.
  • Spring startup에서 placeholder 값을 찾지 못한다.
  • 팀원마다 .env.local key 이름이 달라 로컬 실행이 다르다.
  • .env.local이 Git에 올라가 Secret이 노출된다.
  • 운영 DB URL이 local env에 들어 있어 local 테스트가 운영 데이터에 닿는다.
  • 기본값 때문에 잘못된 DB나 Redis host로 조용히 연결된다.

안전한 완화 조치

환경 변수 문제가 의심되면 먼저 최종 Compose config를 확인한다.

그 다음 container 내부 env key 존재 여부를 확인한다.

Secret 값은 출력하지 말고 key 존재 여부와 source만 기록한다.

잘못된 local env가 Git에 올라갔다면 파일 삭제만으로 끝나지 않는다.

노출된 Secret은 회전해야 하고, Git history와 CI log 노출 범위도 확인해야 한다.

인프라 담당자와 공유할 자료

문제: api container에서 DB_URL placeholder unresolved
Compose: env_file=.env.local
확인: docker compose config에는 env_file 존재
container printenv: DB_URL 없음
원인 후보: .env.local 파일 누락 또는 key 오타
주의: DB_PASSWORD 값은 공유하지 않음

환경 변수 문제는 값보다 key, source, 적용 위치를 공유해야 한다.

실전 팁

  • .env.example에 필요한 key와 local 예시를 남긴다.
  • Secret 값은 local이라도 최소한의 가짜 값으로 둔다.
  • env key는 Spring property 이름과 가까운 규칙으로 통일한다.
  • docker compose config를 실행 절차에 넣는다.
  • local env 파일과 운영 Secret 관리 체계를 섞지 않는다.

위험 신호!

  • .env.local이 repository에 포함된다.
  • 운영 DB password를 local 파일에 복사한다.
  • .envenv_file의 차이를 모르고 값이 들어갔다고 가정한다.
  • Secret 값을 디버깅 메시지에 그대로 붙인다.
  • 기본값이 있어 잘못된 설정도 실패하지 않는다.

확인 질문

확인 질문

  • Compose의 .envenv_file은 무엇이 다른가?
    • .env는 Compose 파일 변수 치환에 쓰이고, env_file은 container 내부 environment로 값을 주입한다.
  • 환경 변수 문제가 있을 때 어떤 순서로 확인해야 하는가?
    • docker compose config로 Compose 해석 결과를 보고, container 내부 printenv로 Spring이 읽을 값을 확인한다.
  • local Secret이 노출되면 왜 회전이 필요한가?
    • local이라도 실제 외부 시스템 credential이면 Git history, CI log, 채팅에 복제될 수 있어 더 이상 신뢰할 수 없기 때문이다.

참고 문서