Spring Cloud Config Server: 중앙화된 설정 관리

Spring Cloud Config Server로 마이크로서비스 설정을 Git에서 중앙 관리하는 방법, 프로파일별 분리, @RefreshScope 동적 갱신, 민감 정보 암호화까지 다룹니다.

· 9 min read · PALDYN Team

지난 글에서 Spring Cloud Gateway로 API 진입점을 구성하는 방법을 다뤘다. 이번 글에서는 MSA에서 수십 개의 서비스 설정을 일관되게 관리하는 방법인 Spring Cloud Config Server를 살펴본다.

설정 관리가 왜 어려운가

모놀리스에서는 application.yml 하나로 모든 설정을 관리했다. MSA로 전환하면 서비스가 10개라면 application.yml도 10개다. 더 심각한 문제는 환경(dev, staging, prod)이 늘어날 때다.

  • 설정 분산: 각 서비스가 자체 설정 파일을 가지면 동일 설정 변경 시 10곳을 수정해야 한다.
  • 비밀 관리: DB 비밀번호, API 키 같은 민감 정보가 코드 저장소에 평문으로 남는다.
  • 변경 이력 추적 불가: 누가 언제 어떤 설정을 바꿨는지 추적하기 어렵다.
  • 재배포 없이 설정 변경 불가: 설정 하나 바꾸려면 서비스를 재시작해야 한다.

Spring Cloud Config Server는 이 문제들을 Git 기반의 중앙 설정 서버로 해결한다.

Spring Cloud Config Server 아키텍처

설정 서버 구성

독립 Spring Boot 애플리케이션으로 Config Server를 만든다.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class, args);
    }
}
# config-server/application.yml
server:
  port: 8888

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/myorg/service-configs
          default-label: main
          search-paths: '{application}'   # 서비스 이름으로 된 하위 폴더 탐색
          clone-on-start: true
          timeout: 10
          # private repo면 SSH 키 또는 username/password 추가

clone-on-start: true로 설정하면 서버 기동 시 Git 저장소를 미리 클론해서 첫 요청 지연을 방지한다.

Git 저장소 구조

Config Server가 읽어갈 Git 저장소의 권장 구조다.

service-configs/
├── application.yml           # 모든 서비스 공통 설정
├── application-dev.yml       # dev 환경 공통 설정
├── application-prod.yml      # prod 환경 공통 설정
├── order-service/
│   ├── order-service.yml     # order-service 전용 설정
│   ├── order-service-dev.yml # order-service dev 환경
│   └── order-service-prod.yml
└── user-service/
    ├── user-service.yml
    └── user-service-prod.yml

Config Server는 클라이언트가 order-service + dev 프로파일로 요청하면 다음 파일들을 우선순위 높은 순으로 병합해서 반환한다.

  1. order-service/order-service-dev.yml (가장 구체적)
  2. order-service/order-service.yml
  3. application-dev.yml
  4. application.yml (가장 일반적)

더 구체적인 파일의 값이 일반 파일의 값을 덮어쓴다.

Config Server REST API

Config Server는 설정을 REST API로 노출한다.

GET /{application}/{profile}[/{label}]
GET /{application}-{profile}.yml
GET /{label}/{application}-{profile}.yml

예시 요청:

# order-service의 dev 프로파일 설정 조회
curl http://localhost:8888/order-service/dev

# YAML 형식으로 직접 조회
curl http://localhost:8888/order-service-dev.yml

# 특정 브랜치(label) 지정
curl http://localhost:8888/main/order-service-prod.yml

클라이언트 설정

각 마이크로서비스에 config client 의존성을 추가한다.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

Config Server 설정 코드 예시

Spring Boot 3.x에서는 bootstrap.yml 대신 application.ymlspring.config.import를 사용한다.

# application.yml (Spring Boot 3.x 방식)
spring:
  application:
    name: order-service           # Git 파일명과 매칭됨
  profiles:
    active: ${SPRING_PROFILES_ACTIVE:dev}
  config:
    import: "configserver:"       # Config Server에서 설정 가져오기
  cloud:
    config:
      uri: http://config-server:8888
      fail-fast: true             # 서버 연결 실패 시 기동 중단
      retry:
        max-attempts: 6
        initial-interval: 2000

fail-fast: trueretry 설정은 Config Server가 아직 기동 중일 때 클라이언트가 무한히 기다리거나 잘못된 설정으로 기동되는 것을 방지한다.

@RefreshScope: 재시작 없이 설정 갱신

Spring Cloud Config의 핵심 기능 중 하나는 동적 설정 갱신이다. Git에서 설정을 변경한 후 서비스를 재시작하지 않고 갱신하려면 @RefreshScope를 사용한다.

@RefreshScope
@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
    private String featureFlag;
    private int maxRetries;
    private String externalApiUrl;
}

설정 변경 후 해당 서비스의 /actuator/refresh 엔드포인트를 POST로 호출하면 @RefreshScope 빈이 재생성된다.

# 설정 갱신 트리거
curl -X POST http://order-service:8081/actuator/refresh
# Actuator refresh 엔드포인트 활성화
management:
  endpoints:
    web:
      exposure:
        include: refresh, health, info

Spring Cloud Bus로 일괄 갱신

서비스가 많아지면 각 서비스마다 /actuator/refresh를 호출하는 것이 번거롭다. Spring Cloud Bus는 메시지 브로커(RabbitMQ 또는 Kafka)를 통해 모든 서비스에 갱신 신호를 일괄 전파한다.

<!-- RabbitMQ 기반 Bus 사용 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

Config Server에 /actuator/busrefresh를 POST로 호출하면 모든 클라이언트 서비스가 동시에 설정을 갱신한다. GitHub Webhook을 여기에 연결하면 Git push 한 번으로 전체 설정 갱신이 자동화된다.

# Config Server + Client 모두에 RabbitMQ 설정 추가
spring:
  rabbitmq:
    host: rabbitmq
    port: 5672
    username: guest
    password: guest

민감 정보 암호화

DB 비밀번호, API 키 같은 민감 정보는 Git에 평문으로 저장하면 안 된다. Config Server는 대칭키 또는 비대칭키 암호화를 지원한다.

# Config Server 암호화 키 설정
encrypt:
  key: ${ENCRYPT_KEY}   # 환경 변수로 관리

값을 암호화하려면 Config Server의 /encrypt 엔드포인트를 사용한다.

# 값 암호화
curl -X POST http://config-server:8888/encrypt -d "mySecretPassword"
# → AQB3xyzEncryptedValue...

# 값 복호화 확인
curl -X POST http://config-server:8888/decrypt -d "AQB3xyzEncryptedValue..."
# → mySecretPassword

Git 저장소의 설정 파일에 암호화된 값을 {cipher} 접두사와 함께 저장한다.

# order-service-prod.yml (Git 저장소)
spring:
  datasource:
    password: '{cipher}AQB3xyzEncryptedValue...'
  redis:
    password: '{cipher}AQC9anotherEncryptedValue...'

Config Server가 클라이언트에 설정을 내려줄 때 자동으로 복호화한다.

운영 환경에서는 HashiCorp Vault를 백엔드로 사용하는 것이 더 안전하다. spring.cloud.config.server.vault 설정으로 통합할 수 있다.

네이티브 프로파일: 로컬 파일 백엔드

개발 환경에서 Git 없이 로컬 파일시스템을 백엔드로 사용할 수 있다.

spring:
  profiles:
    active: native
  cloud:
    config:
      server:
        native:
          search-locations:
            - classpath:/configs
            - file:${user.home}/config-repo

실무 고려사항

Config Server 고가용성: 운영 환경에서는 Config Server 2대 이상으로 클러스터를 구성하고, 클라이언트의 uri에 콤마로 구분해 나열한다.

spring:
  cloud:
    config:
      uri: http://config1:8888,http://config2:8888

Kubernetes 환경: k8s에서는 Config Server 대신 ConfigMap과 Secret을 사용하는 것이 더 자연스럽다. spring-cloud-kubernetes-config로 ConfigMap을 Spring Environment로 자동 매핑할 수 있다.

캐시 전략: Config Server는 기본적으로 Git 저장소를 일정 간격으로 refresh한다. 빠른 반영이 필요하면 force-pull: true로 설정하거나 Webhook을 활용한다.

spring:
  cloud:
    config:
      server:
        git:
          force-pull: true
          refresh-rate: 30   # 30초마다 Git pull

Config Server는 설정 파일을 코드처럼 버전 관리하는 방법을 제공한다. Git의 이력 추적, 브랜치 전략, PR 리뷰를 설정 변경에도 적용할 수 있어 운영 안정성이 높아진다.


지난 글: Spring Cloud Gateway: API 진입점 완전 정복

다음 글: Spring Cloud Resilience4j: 장애 격리와 Circuit Breaker


읽어주셔서 감사합니다. 😊