NYO_O

파편화된 설정 정보의 중앙 집중화: Spring Cloud Config 아키텍처 본문

Architecture/MSA

파편화된 설정 정보의 중앙 집중화: Spring Cloud Config 아키텍처

NYO_O 2026. 5. 27. 07:00
반응형

수십 개의 application.yml이 만드는 배포 병목 현상

지금까지 우리는 마이크로서비스 아키텍처(MSA)를 구성하는 핵심 통신 인프라들에 대해 알아보았습니다. 서비스들은 서비스 디스커버리를 통해 서로의 위치를 찾고, 로드 밸런서를 통해 부하를 분산하며, 서킷 브레이커를 통해 연쇄 장애를 차단합니다. 

2026.05.26 - [Tech/MSA] - 연쇄 장애 방지: 서킷 브레이커(Circuit Breaker) 아키텍처

 

연쇄 장애 방지: 서킷 브레이커(Circuit Breaker) 아키텍처

연쇄 장애(Cascading Failure)이전 포스팅들을 통해 서비스 디스커버리를 활용하여 타겟 인스턴스의 IP를 찾고, 로드 밸런싱을 통해 트래픽을 골고루 분산시키는 아키텍처를 구축했습니다. 2026.05.26 -

ddangnyo.tistory.com

하지만 서비스의 개수가 20개, 30개로 늘어나면서 '설정 관리'의 늪에 빠지게 됩니다. 모든 Spring Boot 프로젝트는 데이터베이스 접속 정보, 서킷 브레이커의 타임아웃 임계치, 외부 API의 인증 키 등을 담고 있는 application.yml 파일을 내부에 가지고 있습니다.

만약 보안상의 이유로 공통 데이터베이스의 패스워드를 변경해야 하거나, 트래픽 폭주에 대응하기 위해 전체 서비스의 타임아웃 설정을 3초에서 5초로 늘려야 한다면 어떻게 될까요? 개발자는 30개의 레포지토리를 일일이 열어 코드를 수정하고, 30번의 커밋(Commit)과 30번의 CI/CD 파이프라인 빌드를 거쳐, 전체 시스템을 재시작해야만 합니다. 이는 비즈니스의 민첩성을 위해 도입한 MSA가 오히려 유지보수의 거대한 병목으로 전락하는 순간입니다.

설정의 외부화(Externalization)

현대적인 클라우드 애플리케이션 개발의 지침이 되는 '12-Factor App' 방법론에서는 설정 관리의 핵심 원칙을 '설정의 외부화(Externalization)'로 정의합니다. 애플리케이션 코드와 설정 정보는 라이프사이클이 완전히 다르기 때문에, 소스 코드 내부에 설정을 하드코딩하거나 패키징하지 말고 런타임 환경에서 읽어오도록 분리해야 한다는 것입니다.

모놀리식 환경에서는 단순히 OS의 환경 변수를 주입하는 것만으로도 이 원칙을 지킬 수 있었습니다. 하지만 수십 대의 컨테이너가 수시로 생성되고 소멸하는 MSA 환경에서는 개별 인스턴스에 환경 변수를 일일이 주입하는 방식은 관리가 불가능에 가깝습니다. 따라서 분산된 서비스들이 공통으로 바라보고 참조할 수 있는 단일 진실 공급원(SSOT, Single Source of Truth), 즉 중앙 집중형 설정 서버가 필요해집니다.

Spring Cloud Config의 중앙 집중식 관리 메커니즘

이러한 인프라적 요구사항을 해결하기 위해 스프링 진영에서 제공하는 솔루션이 바로 Spring Cloud Config입니다.

Spring Cloud Config는 아키텍처 상으로 'Config Server'와 각 마이크로서비스에 탑재되는 'Config Client'로 나뉩니다. 동작 메커니즘은 매우 직관적입니다.

  1. 관리자는 Git 레포지토리(또는 Vault, 파일 시스템)를 하나 생성하여 모든 마이크로서비스의 application.yml 파일들을 중앙에 모아 업로드합니다.
  2. Config Server는 이 Git 레포지토리를 바라보도록 연동되어 구동됩니다.
  3. 개별 마이크로서비스(Client)들은 부팅되는 시점에 자신의 로컬 설정 파일을 읽기 전에, 중앙의 Config Server로 HTTP 요청을 보내 "내 이름(Application Name)과 현재 프로필(dev/prod)에 맞는 설정 정보를 다오"라고 질의합니다.
  4. Config Server는 Git에서 최신 설정 파일을 읽어와 클라이언트에게 응답하고, 클라이언트는 이 정보를 바탕으로 애플리케이션 컨텍스트를 구성하여 띄워집니다.

이 아키텍처를 도입하면 설정이 변경되었을 때 개발자는 오직 중앙의 Git 레포지토리 정보만 수정하면 됩니다. 설정의 버저닝(Versioning)과 히스토리 관리까지 Git의 강력한 기능을 그대로 활용할 수 있다는 것이 엄청난 이점입니다.

무중단 설정 동기화: @RefreshScope와 Spring Cloud Bus

중앙에서 설정을 관리하게 되었지만 여전히 한 가지 과제가 남습니다. Git 레포지토리의 설정 파일이 수정되었더라도, 이미 부팅되어 메모리에 설정값을 올려둔 마이크로서비스들은 스스로 재시작되기 전까지는 변경된 값을 알지 못합니다.

Spring은 애플리케이션의 재부팅 없이 런타임에 설정을 동적으로 갱신하기 위해 @RefreshScope 어노테이션과 Actuator의 /actuator/refresh 엔드포인트를 제공합니다. 해당 엔드포인트를 호출하면 서비스는 Config Server에서 최신 설정을 다시 받아와 메모리의 빈(Bean)을 새로고침합니다.

하지만 인스턴스가 100대라면 /refresh API를 100번 호출해야 할까요? 이를 우아하게 해결하는 것이 Spring Cloud Bus입니다. Spring Cloud Bus는 RabbitMQ나 Kafka 같은 메시지 브로커를 활용하여 모든 마이크로서비스를 하나의 이벤트 파이프라인으로 연결합니다. 관리자가 단 하나의 인스턴스에만 설정 갱신 요청(/actuator/busrefresh)을 보내면, 해당 이벤트가 메시지 큐를 타고 모든 인스턴스에 즉각적으로 전파(Broadcast)되어 수백 대의 컨테이너가 런타임에 일제히 새로운 설정을 적용하게 됩니다.

Kubernetes ConfigMap과의 관계

서비스 디스커버리 포스팅에서 언급했듯, 최근의 인프라 생태계는 쿠버네티스(Kubernetes)를 중심으로 재편되고 있습니다. 설정 관리 역시 예외는 아닙니다.

쿠버네티스 환경에서는 인프라 레벨에서 제공하는 ConfigMapSecret 리소스를 활용하여 설정의 외부화와 중앙 관리를 훌륭하게 수행할 수 있습니다. 때문에 순수하게 인프라 플랫폼에 라우팅과 설정을 위임하는 조직의 경우 별도의 Spring Cloud Config Server를 구축하지 않기도 합니다.

하지만 Spring Cloud Config는 여전히 강력한 무기입니다. 프로파일(Profile) 계층 구조에 따른 복잡한 설정 오버라이딩이나 Spring 특유의 @RefreshScope를 활용한 정교한 런타임 빈(Bean) 새로고침 등, 프레임워크 애플리케이션 계층에 특화된 기능을 다룰 때는 Kubernetes 네이티브 리소스만으로는 완벽히 대체하기 어려운 디테일을 제공합니다. 실무에서는 조직의 인프라 성숙도와 요구사항에 따라 두 가지를 적절히 혼합하거나 취사선택하여 아키텍처를 설계합니다.

마무리

오늘은 MSA 환경에서 필연적으로 발생하는 설정 파일의 파편화 문제를 방지하고, Git과 메시지 브로커를 결합하여 수백 대의 서버 설정을 한 번에 동적으로 통제하는 Spring Cloud Config의 아키텍처에 대해 알아보았습니다.

지금까지 우리는 백엔드 내부 깊숙한 곳에서 마이크로서비스들이 서로 어떻게 통신하고, 설정 정보를 공유하며, 연쇄 장애를 막아내는지에 집중했습니다. 하지만 백엔드 서버들이 아무리 유기적으로 소통한다고 한들, 최종적으로 웹 브라우저나 모바일 앱(클라이언트)이 이 서비스들을 호출해야만 비즈니스가 완성됩니다.

클라이언트는 회원 조회를 위해 '회원 서비스 IP'를 알아야 하고, 주문을 위해 '주문 서비스 IP'를 따로 알아야 할까요? 만약 내부 서비스가 50개라면 클라이언트는 50개의 IP를 전부 하드코딩하고 관리해야 할까요?

다음 포스팅에서는 클라이언트와 복잡한 백엔드 마이크로서비스들 사이에 서서, 트래픽의 단일 진입점 역할을 수행하며 인증과 라우팅을 통제하는 'API 게이트웨이(API Gateway)'에 대해 상세히 다루어 보겠습니다.

반응형