Spring Boot 애플리케이션에서 프로필을 사용하여 환경을 구분할 수 있습니다:
spring:
profiles:
active: dev
❗ 실시간 구성 변경을 반영하는 방법에는 여러 가지가 있습니다.
Spring Cloud Bus를 사용하는 방법, 수동으로/actuator/refresh엔드포인트를 호출하는 방법, Spring Boot DevTools를 사용하는 방법, 그리고 Git 저장소를 사용하는 방법이 있습니다. 각 방법은 상황에 따라 적절히 선택하여 사용할 수 있습니다. Spring Cloud Bus는 메시징 시스템을 통해 실시간으로 설정 변경 사항을 전파하는 데 매우 유용하며, Git 저장소를 사용하면 설정 파일의 버전 관리를 쉽게 할 수 있습니다. Spring Boot DevTools는 주로 개발 환경에서 유용하게 사용됩니다.
/actuator/refresh 엔드포인트 사용
/actuator/refresh 엔드포인트를 사용할 수 있습니다.설정 갱신 절차
Config 서버에서 설정 파일을 변경합니다.
클라이언트 애플리케이션의 /actuator/refresh 엔드포인트를 POST 요청으로 호출하여 변경된 설정을 반영합니다.
이 방법은 간단하지만, 각 클라이언트 애플리케이션에서 수동으로 엔드포인트를 호출해야 합니다.
Spring Boot DevTools 사용

Config 서버를 생성하고, port와 간단한 message를 동적으로 변경해보고 실제 갱신되는 모습을 확인한다. 수동으로
/actuator/refresh엔드포인트 사용해서 변경을 확인.
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-config-server'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
다음과 같은 dependency를 적용한다. config-server 라도 eureka를 통해 통신해야 하기에 관련 설정도 추가해준다.
또한 @EnableConfigServer 도 꼭 붙여줘야 한다!

다음 계층 형태로 yml 파일을 작성한다.
application.yml
server:
port: 18080
spring:
profiles:
active: native
application:
name: config-server
cloud:
config:
server:
native:
search-locations: classpath:/config-repo # 리소스 폴더의 디렉토리 경로
eureka:
client:
service-url:
defaultZone: http://localhost:19090/eureka/
product-service.yml
server:
port: 19093
message: "product-service message"
product-service-local.yml
server:
port: 19083
message: "product-service-local message"
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
서버와 달리 config 를 dependency에 추가한다.
application.yml
server:
port: 0 # 임시 포트, 이후 Config 서버 설정으로 덮어씌움
spring:
profiles:
active: local
application:
name: product-service
config:
import: "configserver:"
cloud:
config:
discovery:
enabled: true
service-id: config-server
management:
endpoints:
web:
exposure:
include: refresh
eureka:
client:
service-url:
defaultZone: http://localhost:19090/eureka/
message: "default message"
product-service 서버에서는 profile 설정을 local로 설정했고,
config.import를 통해 configserver 에서 설정을 가져옴을 설정했고,
cloud.config.discovery.enabled: true: 를 통해 eureka 서버로 부터 가져올 수 있게끔 설정했다.
또한
actuator 설정으로 refresh 엔드포인트를 노출시켰다.
ProductController.java
/**
* @RefreshScope 애노테이션은 Spring 애플리케이션의 빈이 설정 변경을 반영할 수 있도록 하는 역할을 합니다.
* 기본적으로 Spring 애플리케이션의 빈은 애플리케이션이 시작될 때 초기화되고, 설정 값이 변경되더라도 해당 빈은 갱신되지 않습니다.
* 이 애노테이션을 사용하면 /actuator/refresh 엔드포인트를 호출하여 설정 변경 사항을 동적으로 반영할 수 있습니다.
*/
@RefreshScope
@RestController
@RequestMapping("/product")
public class ProductController {
@Value("${server.port}")
private String serverPort;
@Value("${message}")
private String message;
@GetMapping
public String getProduct() {
return "Product detail from PORT : " + serverPort + " and message : " + this.message ;
}
}
또한 중요하게 봐야할 것은 @RefreshScope 이다. 해당 어노테이션을 붙여놔야지 @Value 어노테이션으로 yml에 있는 정보가 바뀐다면 동적으로 반영할 수 있다.
서버를 모두 띄우고 테스트 해본다.

먼저
localhost:19083/product 로 접근해 보면, product 서버의 포트인 0 번과 default 메시지가 아니라 config 서버에서 지정해준 19083 포트에 product-service-local message 가 뜨는 것을 확인할 수 있다.
profiles:active: local 설정을 없앤다면 product-service message가 뜬다.

다음은 자동 업데이트를 테스트하기 위해 config-server의 product-service-local.yml 의 내용을 변경하고, 서버를 재시작 한다.
단지 서버를 재시작 한다고 해서 자동으로 변경되지 않고,

해당 엔드포인트로 POST 메서드를 통해 요청해주면 message 의 값이 변경되었다는 의미를 200 상태코드와 함께 반환해준다.

변경된 사항이 없으면 빈 결과값을 내뱉는다.

여튼 다시 localhost:19083/product 엔드포인트로 접근해보면, product-service 서버를 다시 재시작 하지 않고도, yml 파일의 값 변경을 확인하고 적용하는 모습을 보여준다.

로그에서도 확인할 수 있는데, product-service.yml 이 2번 뜨는 이유는 아직 정확히는 모르겠다..
MSA 에서는 Config 설정 또한 하나의 서버를 통해 공통적인 관리를 해주는 것이 신기하기도 복잡하기도 했다.
생각해보면 계층형 아키텍처에서는 서버가 하나이기에 크게 고민할 필요 없었던 문제지만, MSA 환경에서는 여러 서버에서의 공통적인 Config 설정 또한 중요한 포인트이다.
실시간으로 구성을 변경하는 방법도 다양했는데, 특히 Git 저장소도 활용할 수 있으니 정말 환경이 잘 구성되어 있다고 생각한다. 정말 실시간이 중요하고, 개발 환경에 메시징 시스템을 사용하고 있는 경우라면 Spring Cloud Bus + 메시징 시스템을 사용하는 편이 좋아 보인다.
acutator를 많이 사용해보진 못했는데, refresh 엔드포인트의 역할을 하나 알게되었다. 이처럼 수동 갱신은 간편하지만, 각 client 애플리케이션에서 수동으로 호출해줘야 한다는 번거로움이 존재하기에 도입한다면 고려해볼 만한 문제인 것 같다.
앞에 내용들은 많이 들어본 내용이였는데, config 쪽은 생소해서 재밌기도하고 어렵기도 했다.