[Spring] 멀티모듈 구조 적용하기 2편 - 환경변수 설정

Inung_92·2025년 8월 11일
1

Spring

목록 보기
19/20
post-thumbnail

이전 포스팅에 이어 멀티모듈 구조 적용하기 2편을 시작하겠습니다!🫡
혹시 이전 포스팅을 못보신 분들은 이전글을 참고하세요👏


⚙️ 외부 환경 변수 파일

해당 포스팅은 application.yml에 대한 내용이 아닌 외부 환경 변수 파일을 추가적으로 설정하여, 프로퍼티를 적용하는 방법에 대한 내용입니다.💡

Springboot의 초기화 순서

1. application.yml 로드 (Spring Boot Configuration Processing)2. Spring Boot Auto Configuration 실행
   - DataSourceAutoConfiguration
   - JpaAutoConfiguration  
   - LoggingAutoConfiguration
   등등...
   ↓
3. Spring Context 생성 시작
   ↓
4. @Configuration 클래스들 처리
   ↓
5. @PropertySource 로드 ← 이미 너무 늦음!

위 흐름에 따르면 JPA와 DataSource 등의 설정은 @Configuration 클래스를 이용하여 처리하면 이미 구성이 완료된 상태로 수정이 되지 않기 때문에 아래 로드 방법 중 spring.profiles.config.import 방식을 사용하는 것이 가장 안전합니다.

# module-syste/resources/application.yml
spring:
  profiles:
    active: dev
  config:
    import:
      - classpath:application-common.yml
      - classpath:application-common-${spring.profiles.active}.yml

 로드 방법

springboot에서 환경 변수 및 외부 설정 파일을 로드하는 방법은 다음과 같습니다.

spring.config.import

정보를 상속받고 싶은 경우에 특정 모듈에 정의된.yml 경로를 지정하여 사용할 수 있으며, 추가되는 설정 파일 만큼 경로를 계속 지정해야 한다는 번거로움이 존재할 수 있고, 오타로 인해 예상치 못한 오류가 발생 할 수 있습니다.

spring:
  config:
    import:
      - classpath:spring-config-import.yml

spring.profiles.include

spring.profiles.active 또는 spring.profiles.include되는 프로파일에 따라 설정 파일을 로드하는 원리를 이용하여 특정 프로파일을 명시하여 포함되도록 하는 방식입니다.

spring:
  profiles:
    include: spring-profiles-include

주의사항은 include에 작성된 프로파일은 application-프로파일명.yml로 작성된 파일의 프로파일명에 해당합니다. 이러한 특성으로 네이밍 컨벤션에 대한 숙지가 정확하지 않으면 의도한대로 적용되기가 쉽지 않습니다.

@PropertySource

외부 설정 파일의 내용을 로드하여 애플리케이션의 다양한 환경 설정 값에 접근할 수 있도록 돕는 어노테이션으로 @Configuration이 적용된 클래스에 주로 사용됩니다.

@Configuration
@Profile("!dev && !prod") // dev와 prod가 아닌 모든 프로파일(default)
@PropertySource(value = "classpath:/external-config-common.yml")
public class DefaultConfig {}

@Profile 어노테이션과 함께 사용하여 프로파일에 맞는 설정 파일 적용도 가능합니다.

저는 설정 파일의 추가 등을 고려하여 확장성을 보장하고, 코드 기반으로 설정을 하는 것이 휴먼 에러를 방지할 수 있다고 판단했기 때문에

@PropertySource@Profile을 적용하여 환경 변수 파일을 구성하겠습니다.

작성 예시

환경 변수 파일 적용

springboot에서 환경 변수를 읽는 순서는 다음과 같습니다.

  1. 커맨드 라인 인수: --server.port=8080
  2. 기본 설정 파일: application.yml
  3. 프로파일별 설정 파일: application-dev.yml 등
  4. @Configuration 클래스: 외부 환경 변수 파일

환경 변수를 읽어들이는 순서를 고려하여 다음과 같은 시나리오로 환경 변수를 구성하겠습니다.

  • module-common에 기본적이고, 공통으로 사용되는 환경 변수 작성
  • module-system, module-customer는 module-common을 의존하여 환경 변수 정보를 전달 받음
  • 환경 변수의 오버라이드가 필요한 경우 module-system, module-customer에서 application.yml작성
  • @Configuration 클래스를 정의하여 외부 환경 변수 추가

위 시나리오에서 핵심은 module-common에서 환경 변수 파일을 application-common.yml로 정의하여 기본 설정 파일의 우선 순위를 보장하고 Config 클래스를 통한 외부 환경 변수 파일 로드를 수행합니다. 이후 필요에 따라 특정 모듈에서 기본 설정 파일인 application.yml을 작성하여 오버라이드 하는 방식으로 동작을 처리합니다.

@PropertySource의 동작 방식

  • 프로퍼티 파일 지정 : Value 속성에 로드할 프로퍼티 파일의 경로 지정
    • ex) classpath:external-config-common.yml
  • Environment 등록 : 스프링이 위 설정을 감지하여 지정된 프로퍼티 파일의 내용을 Environment 인터페이스의 PropertySource구현체로 등록
  • 값 접근 : 등록된 PropertySource를 통해 프로퍼티 값을 획득
    • @Value어노테이션을 주로 사용
    • Environment객체를 통해서도 접근 가능
  • YAML 파일 지원 구성 : .yml 파일의 경우 별도의 설정이 필요
    • YamlPropertySourceFactory를 직접 구현
    • PropertySourceFactory를 구현하는 구현체로서 동작

Environment객체가 생성되는 시점은 AbstractApplicationContext가 refresh()를 수행하면 내부에서 prepareRefresh()가 호출되는데 해당 위치에서 객체를 생성하고, 검증하며 prepareBeanFactory()메서드에서 생성된 객체를 빈으로 등록합니다.

Config 클래스 선언

@Configuration
@Profile("!dev && !prod")
@PropertySource(
		value = "classpath:/external-config-common.yml",
		factory = YamlPropertySourceFactory.class
)
public class DefaultConfig {}

@Configuration
@Profile("dev")
@PropertySource(
		value = "classpath:/external-config-common-dev.yml",
		factory = YamlPropertySourceFactory.class
)
class DevConfig {}

@Configuration
@Profile("prod")
@PropertySource(
		value = "classpath:/external-config-prod.yml",
		factory = YamlPropertySourceFactory.class
)
class ProductionConfig {}

dev, prod, default프로파일에 대한 Config 클래스들을 정의합니다.

  • value : 기준이 되는 모듈의 /src/main/resources하위에 존재하는 환경 변수 파일명을 작성
  • factory : properties 파일의 경우 불필요하며, yml파일의 정보 로드 시 별도의 Factory Class 정의

Factory class

public class YamlPropertySourceFactory implements PropertySourceFactory {
	@Override
	public PropertySource<?> createPropertySource (String name, EncodedResource resource) {
		YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
		factory.setResources(resource.getResource());
		Properties properties = factory.getObject();
		return new PropertiesPropertySource(resource.getResource().getFilename(), properties);
	}
}

프로파일별 application.yml 정의

경로는 module-common/src/main/resources/ 하위에 작성 합니다.

-project-root
 |-build
 |-module-app
 |-module-common
   |-src
     |-main
       |-java
       |-resources
         |-application-common.yml      # default
         |-application-common-dev.yml  # 개발환경
         |-application-common-prod.yml # 운영환경
         |-external-config-common.yml
         |-external-config-common-dev.yml
         |-...
 |-...기타모듈

이렇게 작성하면 Config 클래스에서 파일을 로드할 준비가 완료됩니다.

application.yml 오버라이드

-project-root
 |-build
 |-module-app
 |-module-common
 |-module-customer
   |-src
     |-main
       |-java
       |-resources
         |-application.yml      # default
         |-application-dev.yml  # 개발환경
         |-application-prod.yml # 운영환경
 |-...기타모듈

springboot에서 인식 가능한 기본 설정 파일 포맷으로 환경 변수 파일을 작성하면 module-common 이 먼저 로드되고나서 해당 모듈의 springboot가 기본 설정 파일의 정보를 읽고, 설정 정보를 덮어 씌웁니다.

module-customer에서 아래과 같이 application.yml을 정의하고, 같은 경로에 application-dev.yml을 작성한다면 module-common에서 정의했던 환경 변수가 module-customer에서 정의한 dev프로파일의 환경 변수 파일로 덮어씌워집니다.

spring:
  profiles:
    active: dev
  config:
  	import:
    	- classpath:application-common.yml
        - classpath:application-common-${spring.profiles.active}.yml

여기까지 완료를 하셨다면 @SpringBootApplication 어노테이션이 명시된 클래스의 main 메서드를 실행했을 경우 평소와 같이 정상적으로 애플리케이션이 실행되어야 합니다.

또한, 마지막 예시에서 active를 dev로 설정한 결과가 로그에 아래와 같이 출력되면 됩니다!👏

: The following 1 profiles are active: "dev"
profile
개발감자🥔

0개의 댓글