본문으로 건너뛰기
김신건의 로그

[Spring Boot] Properties 외부화: PropertySource, Profile, @ConfigurationProperties

· 수정 · 📖 약 2분 · 714자/단어 #spring #spring-boot #configuration #properties #profiles #yaml
Spring Boot Properties, Externalized Configuration, application.yml, application.properties, PropertySource, @ConfigurationProperties Spring Boot, Spring Boot 환경 설정

정의

Spring Boot 는 설정값을 15가지 소스에서 우선순위로 병합. 같은 키가 여러 곳에 있으면 우선순위 높은 게 이김. 코드 변경 없이 환경별 설정 가능.

핵심 소스 (우선순위 높은 순):

  1. Devtools 글로벌 설정 ~/.config/spring-boot/
  2. @TestPropertySource (테스트)
  3. SPRING_APPLICATION_JSON (환경변수)
  4. Command-line arguments (--server.port=9090)
  5. OS 환경변수 (SERVER_PORT=9090)
  6. application-{profile}.yml (외부 → jar 안)
  7. application.yml (외부 → jar 안)
  8. @PropertySource 어노테이션
  9. default 값 (@Value("${x:fallback}"))

높은 우선순위 = 운영 환경에서 override 쉬움 패턴.

application.yml vs properties

# application.yml (선호: 계층 표현 명확)
spring:
  datasource:
    url: jdbc:postgresql://localhost/myapp
    username: app
server:
  port: 8080
  servlet:
    context-path: /api
# application.properties (단순)
spring.datasource.url=jdbc:postgresql://localhost/myapp
spring.datasource.username=app
server.port=8080
server.servlet.context-path=/api

같은 폴더에 둘 다 있으면 properties 가 우선. 보통 하나만 사용.

Profile-Specific 설정

application-{profile}.yml 명명 규칙:

src/main/resources/
├── application.yml             # 공통
├── application-dev.yml         # dev profile 활성 시
├── application-prod.yml        # prod profile 활성 시
└── application-local.yml       # 로컬 개발

활성화 방법:

# 1) JVM 인자
java -jar app.jar --spring.profiles.active=prod

# 2) 환경변수
SPRING_PROFILES_ACTIVE=prod java -jar app.jar

# 3) application.yml 안에서
spring:
  profiles:
    active: prod

여러 profile 동시: dev,debug 콤마 구분.

YAML 멀티 문서 (Spring Boot 2.4+)

한 파일 안에 profile 별 섹션:

spring:
  application:
    name: myapp
server:
  port: 8080
---
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 9090
spring:
  datasource:
    url: jdbc:postgresql://prod-db/myapp
---
spring:
  config:
    activate:
      on-profile: dev
server:
  port: 8080
spring:
  datasource:
    url: jdbc:h2:mem:dev

--- 로 구분, spring.config.activate.on-profile 로 조건. 한 파일에 모아두는 데 유용.

@ConfigurationProperties (타입 안전 바인딩)

@ConfigurationProperties(prefix = "app.mail")
@ConstructorBinding   // record / 불변
public record MailProperties(
    String host,
    int port,
    String username,
    String password,
    Duration timeout,
    Tls tls,
    List<String> recipients
) {
    public record Tls(boolean enabled, String protocol) {}
}

@SpringBootApplication
@EnableConfigurationProperties(MailProperties.class)
class App { ... }
app:
  mail:
    host: smtp.example.com
    port: 587
    username: ${MAIL_USER}
    password: ${MAIL_PASSWORD}
    timeout: 5s        # Duration 자동 파싱
    tls:
      enabled: true
      protocol: TLSv1.3
    recipients:
      - alice@example.com
      - bob@example.com

@Value 대비:

  • 타입 검증 (compile 타임)
  • IDE 자동완성 (spring-boot-configuration-processor 추가 시)
  • @Validated 와 조합 시 startup 시점 검증
  • 불변 (record 권장)

Relaxed binding

같은 property 를 여러 표기로:

app.mail.timeout-seconds     # kebab-case (yml 표준)
APP_MAIL_TIMEOUT_SECONDS     # env var (uppercase + underscore)
app.mail.timeoutSeconds      # camelCase
app.mail.timeout_seconds     # snake_case

모두 같은 키로 인식. 환경변수 (APP_MAIL_HOST) ↔ yml (app.mail.host) 자동 매핑.

@Value (단순 사용)

@Service
class GreetingService {
    @Value("${app.greeting:Hello}")
    private String greeting;

    @Value("${app.recipients}")
    private List<String> recipients;   // 콤마 구분 자동 파싱

    @Value("${app.timeout:5s}")
    private Duration timeout;
}

: 뒤 default. 없으면 startup 실패.

복잡한 구조나 nested 필드면 @ConfigurationProperties 권장. @Value 는 단발성 simple value 만.

SpEL 확장

@Value("#{T(java.lang.Math).max(1024, ${app.min-pool-size:512})}")
int poolSize;

@Value("#{systemProperties['user.timezone'] ?: 'UTC'}")
String timezone;

복잡한 식 사용 가능. 가독성 떨어지므로 자제.

환경변수 매핑

OS env var → property:

SPRING_DATASOURCE_URL          → spring.datasource.url
APP_MAIL_HOST                  → app.mail.host
APP_MAIL_TLS_PROTOCOL          → app.mail.tls.protocol
APP_RECIPIENTS_0               → app.recipients[0]    (list index)
APP_RECIPIENTS_1               → app.recipients[1]

Container / K8s 환경에서 표준. ConfigMap / Secret 을 env var 로 mount.

SPRING_APPLICATION_JSON

JSON 한 줄로 여러 property 주입:

SPRING_APPLICATION_JSON='{"server":{"port":9090},"spring":{"profiles":{"active":"prod"}}}' \
  java -jar app.jar

Heroku, Cloud Foundry 같은 PaaS 에서 자주 사용.

Command-line override

java -jar app.jar \
  --server.port=9090 \
  --spring.profiles.active=prod \
  --logging.level.root=WARN

긴급 운영 변경에 유용.

외부 파일 로드

# JAR 옆 application.yml 우선 (재배포 없이 변경)
java -jar app.jar --spring.config.location=classpath:/,file:./config/

# 또는 단일 파일
java -jar app.jar --spring.config.location=file:/etc/myapp/application.yml

K8s ConfigMap mount 패턴.

Spring Cloud Config

원격 설정 저장소 (Git, Vault, Consul) 통합:

spring:
  config:
    import: "configserver:http://config-server:8888"

여러 인스턴스 가 같은 설정 공유. spring-cloud 참고.

검증 (@Validated)

@ConfigurationProperties(prefix = "app.mail")
@Validated
public record MailProperties(
    @NotBlank String host,
    @Min(1) @Max(65535) int port,
    @NotEmpty List<@Email String> recipients
) {}

startup 시점에 검증. 잘못된 설정 = 즉시 실패 (운영 환경에서 critical).

자주 보는 패턴

시나리오 1: dev/prod 분리

# application.yml (공통)
spring:
  application:
    name: myapp

# application-dev.yml
spring:
  datasource:
    url: jdbc:h2:mem:dev
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: create-drop

# application-prod.yml
spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USER}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
logging:
  level:
    root: WARN

시나리오 2: K8s ConfigMap + Secret

apiVersion: v1
kind: ConfigMap
data:
  application.yml: |
    spring:
      profiles:
        active: prod
    server:
      port: 8080
---
apiVersion: v1
kind: Secret
data:
  DB_PASSWORD: <base64>

Pod env var 로 mount + --spring.config.location 으로 ConfigMap 파일 마운트.

시나리오 3: 외부 비밀번호 (Vault)

spring:
  config:
    import: "vault://"
  cloud:
    vault:
      authentication: TOKEN
      token: ${VAULT_TOKEN}
      uri: https://vault.internal

비밀번호 / API 키 분리.

함정과 베스트 프랙티스

  • 운영 비밀번호는 git 에 절대 commit 금지: ${ENV_VAR} 또는 Vault
  • @ConfigurationProperties 권장: @Value 는 단순한 것만
  • @Validated 로 startup 시점 검증: 운영 실수 조기 감지
  • Duration / DataSize 표기 활용: 5s, 1m, 10MB, 1GiB 자동 파싱
  • profile 별 파일 분리 vs 멀티 문서 YAML: 팀 컨벤션 정하기
  • spring.config.import 사용: Vault, Consul, Cloud Config 같은 외부 소스
  • application.yml 의 environment-specific 값 → env var override: 12-factor 원칙
  • spring-boot-configuration-processor 추가: IDE 자동완성 + IntelliJ 의 설정 키 검증

관련 위키

이 글의 용어 (5개)
[Spring Boot] 자동 구성과 @SpringBootApplicationspring
정의 Spring Boot의 핵심 가치 = 자동 구성(autoconfiguration). classpath의 라이브러리를 보고 합리적 기본 설정으로 Bean 등록. Tomcat이…
[Spring Cloud] Eureka, Config, Gateway, OpenFeignspring
정의 Spring Cloud는 Spring Boot 기반 마이크로서비스 인프라 패턴 (service discovery, config server, gateway, circuit …
[Spring] @Conditional, Profile, @ConfigurationPropertiesspring
정의 Spring Boot는 조건부 Bean 등록 메커니즘을 제공한다. classpath, property, 다른 Bean 유무 등에 따라 자동 구성을 활성화/비활성화. , 어노…
[Spring] Bean Validation: @Valid, @NotBlank, customspring
정의 Spring은 Jakarta Bean Validation (JSR-380)을 표준으로 사용. , , 등 어노테이션으로 선언적 검증. Spring MVC가 로 자동 통합. 설…
[Spring] IoC와 DI: Bean, ApplicationContext, @Autowiredspring
정의 IoC (Inversion of Control)는 객체 생성/생명주기 제어를 프레임워크가 가져가는 원칙. DI (Dependency Injection)는 그 구현 방법. S…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기