infra
Platform

모듈 맵

[Infra Ops] dev/stg/prod 환경변수와 설정 파일 분리 전략

0 / 52 완료

펼치기
0 / 52 완료0%

Infra-ops · 33 / 52

[Infra Ops] dev/stg/prod 환경변수와 설정 파일 분리 전략

환경별 properties/yml/env 분리, 민감 정보 관리, endpoint 분리 전략까지 — 배포 사고를 막는 설정 관리 실무

🚨INCIDENT ALERT
HIGH

신규 서비스 배포를 앞두고 운영팀에서 연락이 왔습니다. "스테이징에서는 잘 됐는데 운영에서 오류가 납니다." 로그를 열어보니 운영 서버가 개발 DB에 연결 시도하고 있었습니다. 개발자가 application.yml 하나에 모든 환경 설정을 때려넣고, 프로파일 지정 없이 배포했던 것입니다.

이 모듈은 그런 사고를 막기 위한 것입니다. 환경별 설정 파일을 어떻게 분리하고, 민감한 정보를 어디에 두어야 하며, 운영 서버에서 설정을 어떻게 적용하는지를 다룹니다.

이번 챕터에서 배울 것
  • 1application-dev.yml / application-stg.yml / application-prod.yml 파일 분리 구조를 설명할 수 있다
  • 2OS 환경변수 > JVM 옵션 > properties 파일의 우선순위를 이해하고 활용할 수 있다
  • 3.env 파일, 환경변수, Vault의 용도 차이를 설명하고 .gitignore에 포함할 파일을 알 수 있다
  • 4Tomcat setenv.sh로 JVM 환경변수를 주입하는 방법을 실습할 수 있다
  • 5설정 변경 후 무중단 적용 여부를 판단하고 올바른 재기동 절차를 선택할 수 있다

환경 분리 — 왜 파일을 나눠야 하나

💡개념

환경별 설정 파일 분리

개발/스테이징/운영은 DB 주소, 로그 레벨, API 엔드포인트가 모두 다릅니다. 하나의 파일에 모든 환경 설정을 넣으면 배포 실수가 났을 때 어느 환경이 잘못 연결됐는지 추적하기 어렵고, 민감한 운영 정보가 개발 환경에 노출될 위험도 생깁니다.

환경별 설정 파일 분리

Spring Boot는 application-{profile}.yml 규칙으로 환경별 파일을 자동으로 읽습니다.

src/main/resources/
├── application.yml          # 공통 설정 (프로파일 무관하게 항상 로드)
├── application-dev.yml      # 개발 환경 전용
├── application-stg.yml      # 스테이징 환경 전용
└── application-prod.yml     # 운영 환경 전용
YAML
# application.yml (공통)
spring:
  application:
    name: my-service
  jpa:
    open-in-view: false

logging:
  pattern:
    console: "%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
YAML
# application-dev.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb_dev
    username: dev_user
    password: dev_pass   # 개발 환경은 .env나 로컬 파일 허용
  jpa:
    show-sql: true        # 개발에서만 SQL 로그 출력

logging:
  level:
    root: DEBUG
YAML
# application-prod.yml
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST}:3306/${DB_NAME}   # 환경변수로 주입
    username: ${DB_USER}
    password: ${DB_PASSWORD}
  jpa:
    show-sql: false       # 운영에서는 SQL 로그 off

logging:
  level:
    root: WARN

핵심 원칙: 운영 설정 파일(application-prod.yml)에는 실제 비밀값을 절대 하드코딩하지 않습니다. ${환경변수명} 플레이스홀더만 두고, 실제 값은 OS 환경변수나 Vault에서 주입합니다.

설정 우선순위 — 어디서 값이 오는가

💡개념

Spring Boot 설정 우선순위

같은 키가 여러 곳에 정의되어 있을 때 어떤 값이 적용되는지 알아야 설정 충돌을 추적할 수 있습니다. Spring Boot는 명확한 우선순위를 따릅니다.

높음 ──────────────────────────────────────────
  1. OS 환경변수        (SPRING_DATASOURCE_URL)
  2. JVM 시스템 옵션    (-Dspring.datasource.url=...)
  3. 프로파일 yml 파일  (application-prod.yml)
  4. application.yml   (공통 파일)
낮음 ──────────────────────────────────────────

OS 환경변수가 가장 강하다는 것이 핵심입니다. 파일에 값이 있더라도 환경변수를 설정하면 그 값이 우선 적용됩니다. 운영 서버에서 DB 비밀번호를 파일에 넣지 않고 환경변수로 주입하는 이유가 여기 있습니다.

로컬 터미널
# 운영 서버에서 환경변수 확인
env | grep -E "SPRING|DB|APP|JAVA"

# 실행 중인 JVM 프로세스의 환경변수 확인 (PID는 ps aux로 찾기)
cat /proc/$(pgrep -f myapp.jar)/environ | tr '\0' '\n' | grep -E "DB|SPRING"

민감 정보 관리 — .env, 환경변수, Vault

💡개념

비밀값을 어디에 두어야 하나

DB 비밀번호, API 키, JWT 시크릿 같은 민감 정보는 코드와 분리해야 합니다. 규모와 보안 요구에 따라 세 가지 방법 중 하나를 선택합니다.

방법적합한 상황주의사항
.env 파일개발 로컬 / 소규모 스테이징git에 절대 커밋 금지, .gitignore 필수
OS 환경변수단일 서버 운영 환경setenv.sh 또는 systemd 서비스 파일에 주입
HashiCorp Vault멀티 서버/클라우드/보안 규정 있는 운영별도 Vault 서버 구성 필요, 접근 권한 관리
로컬 터미널
# .env 파일 예시 (로컬 개발용)
DB_HOST=localhost
DB_NAME=mydb_dev
DB_USER=dev_user
DB_PASSWORD=dev_secret_password
APP_SECRET_KEY=localdev_secret_key_only

.gitignore 에 반드시 포함할 파일들:

로컬 터미널
# .gitignore
.env
.env.*
!.env.example          # 예시 파일(실제 값 없음)은 허용
application-prod.yml   # 운영 설정에 비밀이 있을 경우
*.properties.bak
/config/secrets/
로컬 터미널
# .env.example (저장소에 포함하는 템플릿)
DB_HOST=localhost
DB_NAME=
DB_USER=
DB_PASSWORD=               # 팀원이 이 파일을 복사해 .env 를 만든다
APP_SECRET_KEY=

환경별 설정 분리 — 12 Factor App 원칙

Tomcat 환경변수 주입 — setenv.sh

💡개념

setenv.sh 로 JVM 환경변수 설정

Tomcat은 기동 시 $CATALINA_HOME/bin/setenv.sh 파일이 있으면 자동으로 실행합니다. 이 파일에 환경변수와 JVM 옵션을 설정하면 Tomcat 프로세스 전체에 적용됩니다. 파일이 없으면 직접 만들면 됩니다.

로컬 터미널
# /opt/tomcat/bin/setenv.sh
export JAVA_OPTS="
  -server
  -Xms512m
  -Xmx2048m
  -Dspring.profiles.active=prod
  -Dspring.config.location=/etc/myapp/application-prod.yml
"

# 민감 정보는 OS 환경변수에서 읽어서 JVM에 전달
export JAVA_OPTS="$JAVA_OPTS
  -DDB_HOST=${DB_HOST}
  -DDB_PASSWORD=${DB_PASSWORD}
"
로컬 터미널
# setenv.sh 적용 확인 — Tomcat 재기동 후
ps aux | grep java | grep spring.profiles.active

# 또는 실행 중인 프로세스 JVM 옵션 확인
jps -v | grep Catalina

setenv.sh 권한 설정:

로컬 터미널
chmod 750 /opt/tomcat/bin/setenv.sh
chown tomcat:tomcat /opt/tomcat/bin/setenv.sh

실습

1현재 적용된 환경변수 확인

실행 중인 환경에서 어떤 설정 관련 환경변수가 있는지 확인합니다. 운영 서버라면 DB 연결 정보, 프로파일 설정 등이 나와야 합니다. 아무것도 나오지 않는다면 환경변수 주입이 누락된 상태입니다.

로컬 터미널
# 실행 중인 Java 프로세스의 환경변수 직접 확인
# 먼저 PID 찾기
ps aux | grep java | grep -v grep | awk '{print $2}'

# PID 로 환경변수 읽기 (예: PID=12345)
cat /proc/12345/environ | tr '\0' '\n' | grep -E "DB|SPRING|APP"
OUTPUT
SPRING_PROFILES_ACTIVE=prod
DB_HOST=db-prod.internal
DB_NAME=mydb_prod
DB_USER=app_user
env | grep -E 'JAVA|SPRING|DB|APP'
🔍실행 후 확인할 것
  • SPRING_PROFILES_ACTIVE 또는 -Dspring.profiles.active 에 prod(또는 의도한 환경)가 설정돼 있는가
  • DB_HOST, DB_NAME 같은 연결 정보 환경변수가 올바른 환경을 가리키는가
  • .env 파일이 있다면 ls -la .env 로 git 트래킹 여부 확인 — git ls-files .env 가 빈 결과여야 한다
  • cat /opt/tomcat/bin/setenv.sh 에 민감 정보가 하드코딩돼 있지 않은가
2setenv.sh 설정 확인 및 프로파일 지정

Tomcat을 사용하는 서버라면 setenv.sh에서 어떤 프로파일이 지정됐는지 확인합니다. spring.profiles.active가 없거나 dev로 돼 있으면 운영에서 개발 설정이 적용됩니다.

로컬 터미널
# setenv.sh 가 없으면 확인
ls -la /opt/tomcat/bin/setenv.sh

# JVM 옵션에서 프로파일 확인
grep -E "profiles|JAVA_OPTS" /opt/tomcat/bin/setenv.sh

# 실행 중인 프로세스에서 직접 확인
ps aux | grep "[Dd]spring.profiles.active"
OUTPUT
export JAVA_OPTS="-server -Xms512m -Xmx2048m -Dspring.profiles.active=prod"
cat /opt/tomcat/bin/setenv.sh
🔍실행 후 확인할 것
  • setenv.sh 에 -Dspring.profiles.active=prod 가 포함돼 있는가
  • JAVA_OPTS 에 DB 비밀번호가 직접 하드코딩되지 않고 환경변수 참조(${DB_PASSWORD}) 방식인가
  • setenv.sh 파일 권한이 644가 아닌 750 또는 700인가 (다른 계정이 읽으면 안 됨)
  • Tomcat 재기동 후 ps aux | grep java 에서 프로파일 값이 의도대로 나타나는가

트러블슈팅

원인: Spring Boot 프로파일이 지정되지 않으면 기본 application.yml만 로드됩니다. 개발 DB 주소가 기본 파일에 있거나, setenv.sh에 -Dspring.profiles.active 설정이 누락된 경우입니다.

로컬 터미널
# 1. 실행 중인 프로세스의 프로파일 확인
ps aux | grep java | grep profiles.active
# 아무것도 안 나오면 프로파일 미지정 상태

# 2. 어떤 설정 파일이 로드됐는지 애플리케이션 로그에서 확인
grep -E "profiles|config.*loaded|active profile" /opt/tomcat/logs/catalina.out | head -20

# 3. setenv.sh 수정 — 프로파일 지정 추가
vi /opt/tomcat/bin/setenv.sh
# 아래 줄 추가
# export JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=prod"

# 4. Tomcat 재기동
systemctl restart tomcat

# 5. 재기동 후 확인
grep "The following profiles are active" /opt/tomcat/logs/catalina.out | tail -3

예방: 배포 체크리스트에 "setenv.sh 프로파일 확인" 항목을 추가하고, 배포 스크립트에서 기동 후 로그로 프로파일을 검증합니다.

원인: Java 애플리케이션은 JVM 기동 시점에 설정을 읽어 메모리에 올립니다. 파일을 수정해도 실행 중인 JVM에는 변경이 반영되지 않습니다.

로컬 또는 서버
# 설정 파일 수정 후 현재 적용된 값 확인 (Spring Actuator가 있는 경우)
curl http://localhost:8080/actuator/env | python3 -m json.tool | grep -A3 "datasource.url"

# 파일이 수정됐지만 프로세스는 구 값을 갖고 있음을 확인
# /proc/PID/environ 은 기동 시점 환경변수를 반영 → 변경 없음

# 일부 앱은 HUP 시그널로 재로드 지원 (NGINX 등) — Java 앱은 일반적으로 미지원
# kill -HUP $(pgrep -f myapp.jar)   ← 대부분의 Spring 앱에서 동작 안 함

# 올바른 방법: 점검 후 재기동
systemctl stop myapp
# 또는 Tomcat이라면
systemctl stop tomcat

# 설정 변경 확인
grep "datasource.url" /etc/myapp/application-prod.yml

# 재기동
systemctl start myapp
systemctl status myapp

무중단이 필요한 경우: 로드밸런서에서 해당 인스턴스를 제외(Drain) → 재기동 → 헬스체크 통과 후 복귀 순서로 처리합니다.

설정 파일 백업 전략

💡개념

설정 파일 변경 이력 관리

운영 서버의 설정 파일은 변경 전 반드시 백업합니다. 잘못 수정했을 때 빠르게 복구하기 위해서입니다.

로컬 터미널
# 수정 전 백업 (날짜 포함)
cp /etc/myapp/application-prod.yml \
   /etc/myapp/application-prod.yml.$(date +%Y%m%d_%H%M%S).bak

# 백업 목록 확인
ls -lt /etc/myapp/*.bak | head -5

# 이전 버전으로 복구
cp /etc/myapp/application-prod.yml.20260530_143022.bak \
   /etc/myapp/application-prod.yml

# 설정 변경 이력은 git으로도 관리 (비밀값 제외한 템플릿)
# 민감 정보가 없는 application-prod.yml.template 을 저장소에 유지
💼
실무 맥락
현업 패턴

실제 업무에서 이 지식이 쓰이는 상황:

인프라 엔지니어가 환경 설정 관리에서 가장 자주 마주치는 상황 세 가지입니다.

1. 신규 서버 환경 구성 시: 운영 서버를 새로 세팅할 때 개발자가 만든 application.yml 그대로 올리면 안 됩니다. setenv.sh에 SPRING_PROFILES_ACTIVE=prod를 설정하고, DB 접속 정보는 OS 환경변수로 주입하는 것이 기본 체크리스트입니다.

2. 설정 변경 요청 처리:

로컬 터미널
# 인프라 엔지니어의 설정 변경 표준 절차
# 1. 현재 설정 백업
cp /opt/tomcat/bin/setenv.sh /opt/tomcat/bin/setenv.sh.$(date +%Y%m%d).bak

# 2. 변경
vi /opt/tomcat/bin/setenv.sh

# 3. 적용을 위한 재기동 (무중단 필요 시 LB에서 먼저 제외)
systemctl restart tomcat

# 4. 적용 확인
sleep 10
curl -s http://localhost:8080/actuator/health | python3 -m json.tool
grep "profiles are active" /opt/tomcat/logs/catalina.out | tail -3

3. 보안 감사 대응: Git 저장소에 비밀번호가 커밋됐는지 점검 요청이 오면 아래를 씁니다.

로컬 터미널
# 저장소에 .env 파일이 들어간 적 있는지 확인
git log --all --full-history -- .env
git log --all --full-history -- "**/*.properties"

설정 관리를 잘 하는 팀은 배포 사고의 절반을 예방합니다. 다음 모듈에서는 이런 설정 변경 이력을 체계적으로 관리하는 Git 형상관리 실무를 다룹니다.

지식 확인

퀴즈 — 4문제

Q1

dev/stg/prod 설정을 분리하는 방법 중 가장 안전한 방식은?

Q2

.env 파일을 git에 올리면 안 되는 가장 큰 이유는?

Q3

Spring Boot에서 운영 환경 프로파일을 지정하는 방법으로 올바른 것은?

Q4

운영 서버에서 설정 파일을 변경한 후 반영하는 올바른 절차는?

0 / 4 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

infra-ops중급 · 55
[Infra Ops] Maven/Gradle/npm 빌드와 산출물 관리
인프라 서비스 운영 트랙 계속
linux입문 · 30
[Linux] 개발자가 왜 리눅스 서버와 커맨드라인을 반드시 배워야 하는가
Linux 트랙 시작점