DevOps

환경설정 파일, 왜 어떤 프로젝트는 git에 올리고 어떤 프로젝트는 서버에서 따로 관리할까

포도쿵야 2026. 4. 16. 15:02

예전 프로젝트에서는 소스코드만 배포하고 환경설정은 서버에서 직접 관리했다. 새 프로젝트에서는 환경설정도 git에 올리고 한번에 배포한다. 이게 왜 다른 건지 비교해봤다.


📌 결론 — 배포 철학 차이다

"Java는 외부 config 분리 방식이고 Python은 코드에 같이 넣는 방식인가?" — 아니다.

Python도 외부 config 분리가 가능하고, Java도 config를 코드와 함께 배포할 수 있다. 핵심 질문은 이거다.

서버가 설정을 관리하는가, Git/배포본이 설정을 관리하는가


📌 두 가지 방식 비교

방식 1 — 외부 Config 분리

소스코드만 배포하고, 환경설정 파일은 서버의 별도 경로에 둔다. 앱 실행 시점에 그 파일을 읽는다.

# Java
java -jar app.jar --spring.config.location=/opt/config/application.yml

# Python
python app.py --config=/opt/config/config.yaml

서버가 상태를 가진다. 배포해도 서버의 config는 그대로 유지된다.

방식 2 — 코드와 Config 함께 배포

config 값도 repository에 포함해서 CI/CD가 코드 + config를 함께 배포한다. 배포 시 기존 파일이 덮어쓰기된다.

Git/배포본이 상태를 가진다. 서버에서 직접 수정한 값은 다음 배포 때 사라진다.

구분 상태의 주인 배포 후 서버 수정

외부 Config 분리 서버 유지됨
코드와 함께 배포 Git / 배포본 다음 배포 때 덮어쓰기

📌 내가 겪은 두 프로젝트의 차이

예전 Java 프로젝트 (방식 1)

[Bamboo] jar 빌드 → 서버에 jar만 배포

[운영 서버] /opt/config/application.yml 별도 관리
→ 배포해도 config는 건드리지 않음
→ 환경설정 변경을 굳이 커밋할 필요 없었음

새 Python 프로젝트 (방식 2)

[Bamboo] config 포함 패키징 → 전체 배포 (덮어쓰기)

[운영 서버] 프로젝트 전체가 교체됨
→ 서버에서 직접 수정해도 다음 배포 때 사라짐
→ config 변경도 git으로 관리해야 함

새 프로젝트가 방식 2를 선택한 이유 — 방화벽이 많아 서버 직접 접근이 까다로운 환경이었다. Git과 CI/CD로만 관리하는 게 더 현실적이었다.


📌 프론트는 왜 다른가 — config를 읽는 시점이 다르다

백엔드는 서버에서 실행되는 프로세스라 실행 시점에 파일이나 환경변수를 읽을 수 있다. 프론트 CSR은 구조 자체가 다르다.

npm run build → 정적 파일 생성 → nginx 서빙 → 브라우저 실행

브라우저는 이미 완성된 정적 JS 파일을 실행할 뿐이다. 빌드 시 import.meta.env.VITE_API_URL 같은 코드는 실제 값으로 치환되어 번들에 박힌다.

// 코드
const apiUrl = import.meta.env.VITE_API_URL

// 빌드 결과물 — 값이 이미 확정됨
const apiUrl = "https://api.example.com"

배포 후 서버에서 .env만 바꿔서는 효과가 없다. 재빌드가 필요하다.

구분 config 반영 시점 배포 후 수정

백엔드 런타임 (실행 시) 가능
프론트 CSR 빌드타임 재빌드 필요

프론트에서 배포 후 값을 바꿔야 한다면 — 런타임 config

재빌드 없이 값을 바꿔야 하는 경우, 앱 시작 시 외부 파일을 fetch해서 읽는 구조를 별도로 만든다.

// 앱 시작 전 실행
async function initConfig() {
  const res = await fetch('/config.json')
  window.APP_CONFIG = await res.json()
}
await initConfig()

// 사용 — 런타임 우선, 없으면 빌드타임 fallback
const apiUrl =
  window.APP_CONFIG?.API_URL ??
  import.meta.env.VITE_API_URL

/config.json을 서버에서 직접 수정하면 재빌드 없이 반영된다. 다만 async 초기화 처리가 필요하고 구조가 복잡해지므로, 하나의 빌드로 여러 환경에 배포하거나 배포 없이 값을 자주 바꿔야 하는 경우에만 쓴다.


📌 혼합형 — 실무에서 가장 흔한 형태

기준은 하나다.

"이 값이 노출됐을 때 문제가 생기는가?"

  • 괜찮으면 → git에서 관리
  • 안 되면 → 서버 파일 / 환경변수 / Secret Manager
# git에 올라가는 것 — 공개 가능한 값
server:
  port: 8080
feature:
  cache:
    enabled: true
# 서버에만 있는 것 — 민감한 값
# /opt/config/secrets.yml
database:
  password: super_secret
api:
  jwt-secret: xyz789
# 실행 시 두 파일을 합쳐서 읽음
java -jar app.jar \
  --spring.config.location=classpath:/application.yml,/opt/config/secrets.yml

환경변수로 주입하는 방식도 흔하다. Docker, Kubernetes 환경에서 특히 많이 쓴다.

export DB_PASSWORD="super_secret"
python app.py

클라우드 환경이라면 파일이나 환경변수 대신 Secret Manager API를 직접 호출하기도 한다.

값의 종류 관리 방식

포트, 기능 플래그, 기본 옵션 git
DB 비밀번호, API 시크릿 서버 파일 또는 환경변수
JWT 시크릿, 인증 키 환경변수 또는 Secret Manager
프론트 API URL build-time env (공개 가능한 값만)
프론트 시크릿 절대 프론트에 포함하지 않음

⚠️ 헷갈리기 쉬운 포인트

"서버에서 config 수정했는데 배포 후 왜 원복됐지?" 방식 2(코드와 함께 배포)라면 당연하다. 서버 직접 수정이 아니라 git → CI/CD 흐름으로 변경해야 한다.

"stg용으로 빌드했는데 prod에 배포하면?" 프론트라면 stg 값이 번들에 박힌 채 prod에 올라간다. 빌드 환경과 배포 환경은 반드시 일치시켜야 한다.

"프론트 env에 시크릿 키 넣어도 되나?" 절대 안 된다. 번들에 포함되어 브라우저에서 노출된다.


📋 정리

항목 내용

언어 차이인가 아니다. Java/Python/Node 모두 두 방식 가능
외부 Config 분리 서버가 상태 관리. 배포해도 config 유지
코드와 함께 배포 Git이 상태 관리. 배포 시 덮어쓰기
프론트 CSR 빌드타임에 값 확정. 배포 후 env 수정은 효과 없음
혼합형 공개 설정은 git, 민감 정보는 외부 분리