ABOUT

성능과 운영 안정성을 함께 끌어올리는 개발자입니다.

92% Positional Error Reduction
79% p95 Latency Improvement
90%+ Long Tasks Reduction

2022.02 · 한국장학재단

우수 멘티

한국장학재단 사회 리더 대학생 멘토링 IT

2022.10 · 동작구청

우수 인재상

동작구청 우수 SW 인재

2025.05 · (주) 그랩

프로그래밍 우수상

(주) 그랩 우수 프로그램 개발

2025.05 · AWSKRUG

AWS한국사용자모임 발표

AI agent 스크립트 튜닝 관련 발표

ComputerScience

Development

Engineering

Trouble Shooting

GUESTBOOK

첫 마음부터
함께 나누는 온기

방명록 작성하러 가기

SUBSCRIBE

최신소식을
편하게 만나보세요.

@ConditionalOnProperty

도입

@ConditionalOnProperty 는 특정 설정값이 존재하는지와 그 값이 무엇인지에 따라 설정 클래스나 빈 등록을 켜고 끄는 조건 애노테이션이다.

Spring Boot를 쓰다 보면 같은 코드베이스 안에서도 어떤 기능은 켜고, 어떤 기능은 끄고, 어떤 자동 설정은 특정 환경에서만 적용하고 싶을 때가 많습니다.

이때 가장 자주 쓰이는 조건 중 하나가 @ConditionalOnProperty 입니다. 이름 그대로 Spring Environment 안에 들어 있는 프로퍼티 값을 보고 설정을 활성화할지 결정합니다.

그래서 이 애노테이션을 이해한다는 것은 단순히 문법을 외우는 일이 아니라, Spring Boot가 설정값을 기준으로 자동 설정과 빈 등록을 어떻게 분기하는지 이해하는 일과 거의 같습니다.

필요성

설정값 하나로 기능을 선택적으로 활성화할 수 있어야 라이브러리 자동 설정과 애플리케이션 기능 토글을 깔끔하게 분리할 수 있다

실무에서는 모든 빈을 항상 등록해 두는 방식보다, 특정 프로퍼티가 있을 때만 기능을 켜는 방식이 훨씬 자주 쓰입니다. 예를 들어 캐시, 외부 클라이언트, 커스텀 로깅, 특정 스케줄러를 필요할 때만 활성화하고 싶을 수 있습니다.

특히 라이브러리나 사내 공통 starter를 만들 때는 기본값을 유지하면서도 사용자가 설정으로 쉽게 opt-in 또는 opt-out 할 수 있어야 합니다. @ConditionalOnProperty는 이런 요구를 가장 단순한 형태로 처리하게 해 줍니다.

즉 이 애노테이션은 단순한 if문 대체제가 아니라, Spring Boot 스타일의 설정 기반 분기를 구성하는 핵심 도구에 가깝습니다.

이 애노테이션이 특히 유용한 경우
  • starter나 auto-configuration에서 기능을 설정값으로 켜고 끌 때
  • 특정 외부 시스템 연동을 opt-in 방식으로 활성화할 때
  • 개발/운영 환경에서 빈 등록 전략을 분리하고 싶을 때
  • 기본 동작은 유지하되 사용자가 프로퍼티로 세부 기능을 제어하게 하고 싶을 때

정의

지정한 프로퍼티가 Environment 안에 존재하고 기대한 값과 일치하는지 검사해 조건적으로 설정 또는 빈 등록을 적용하는 애노테이션이다

@ConditionalOnProperty@Configuration 클래스에도 붙일 수 있고, 개별 @Bean 메서드에도 붙일 수 있습니다. 즉 설정 단위 전체를 끌 수도 있고, 특정 빈 하나만 조건부로 등록할 수도 있습니다.

기본 동작은 생각보다 단순합니다. 프로퍼티가 Spring Environment에 존재하고 그 값이 문자열 기준으로 false 가 아니면 조건이 매치됩니다.

여기에 havingValue 로 원하는 값을 더 엄격하게 지정할 수 있고, 프로퍼티가 아예 없을 때도 조건을 맞춘 것으로 볼지 matchIfMissing 로 정할 수 있습니다.

핵심 메시지

"@ConditionalOnProperty 는 설정 파일을 읽는 애노테이션이 아니라

Environment 에 최종적으로 들어온 프로퍼티 값을 기준으로 빈 등록 여부를 결정하는 조건이다."

핵심 원리

핵심은 프로퍼티 파일 자체가 아니라 Environment 이고, 따라서 application.properties 뿐 아니라 환경 변수와 커맨드라인 인자처럼 Environment 로 들어오는 모든 설정 소스가 조건 평가에 영향을 준다

Spring Boot 공식 문서 기준으로 프로퍼티 값은 @Value, @ConfigurationProperties, 그리고 Spring Environment를 통해 접근됩니다. @ConditionalOnProperty 역시 결국 이 Environment를 기준으로 검사합니다.

application.properties, YAML, OS 환경 변수, Java System properties, 커맨드라인 인자, 테스트용 프로퍼티처럼 Environment에 들어오는 값이라면 모두 조건에 영향을 줄 수 있습니다.

또 Spring Boot는 property source 우선순위를 가지고 있으므로, 같은 키라도 나중 우선순위의 값이 앞의 값을 덮어쓸 수 있습니다. 그래서 @ConditionalOnProperty가 왜 예상과 다르게 동작하는지 볼 때는 파일 하나가 아니라 최종 Environment 값을 봐야 합니다.

결국 이 애노테이션은 “설정 파일을 읽는다”보다 “현재 Environment 의 최종 상태를 보고 분기한다”로 이해하는 편이 더 정확합니다.

Condition Evaluation Flow
1) Spring Boot가 여러 PropertySource를 Environment에 적재
2) 우선순위에 따라 최종 프로퍼티 값 결정
3) @ConditionalOnProperty 가 prefix + name 으로 키를 구성
4) havingValue / matchIfMissing 규칙으로 조건 평가
5) 설정 클래스 또는 @Bean 등록 여부 결정

주요 속성

prefix, name, havingValue, matchIfMissing 네 가지를 정확히 이해하면 대부분의 동작은 해석할 수 있다
속성 역할 실무 해석
prefix 프로퍼티 앞부분 경로 자동으로 점(.)이 붙으므로 app.feature처럼 적으면 됨
name 검사할 프로퍼티 이름 대시 표기(my-long-property) 권장, 여러 개면 모두 통과해야 함
value name의 별칭 짧게 쓰고 싶을 때 사용 가능하지만 실무에서는 name이 더 읽기 쉬운 편
havingValue 기대한 문자열 값 지정하지 않으면 기본 규칙은 “false가 아니면 매치”
matchIfMissing 프로퍼티가 없을 때도 매치할지 여부 기본값은 false, 즉 프로퍼티가 빠져 있으면 기본적으로 매치되지 않음

특히 prefix 는 마지막 점을 직접 붙이지 않아도 자동으로 보정됩니다. 또한 name 은 lower-case dashed notation을 권장하고, 여러 이름을 지정하면 하나라도 실패할 경우 전체 조건이 매치되지 않습니다.

기본 동작

havingValue 를 비워 두면 값이 false 가 아닌지만 보고 판단하고, 프로퍼티가 아예 없으면 matchIfMissing 이 최종 기준이 된다
프로퍼티 값 havingValue="" havingValue="true" havingValue="false" havingValue="foo"
true match match no match no match
false no match no match match no match
foo match no match no match match
프로퍼티 없음 matchIfMissing 참조 matchIfMissing 참조 matchIfMissing 참조 matchIfMissing 참조

실무에서 가장 많이 헷갈리는 부분이 바로 이것입니다. havingValue 를 비워 두면 true 만 허용하는 것이 아니라, 문자열 기준으로 false 만 아니면 매치됩니다. 따라서 명확한 boolean 토글이라면 의도를 더 분명하게 적는 편이 좋습니다.

기본 구현

가장 흔한 사용 방식은 설정 클래스 전체를 조건부로 등록하거나, 특정 Bean 메서드 하나만 프로퍼티 값에 따라 선택적으로 등록하는 것이다
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "app.feature", name = "enabled", havingValue = "true")
public class FeatureConfiguration {

    @Bean
    public FeatureService featureService() {
        return new FeatureService();
    }
}
@Configuration(proxyBeanMethods = false)
public class ClientConfiguration {

    @Bean
    @ConditionalOnProperty(prefix = "app.client", name = "mock", havingValue = "true")
    public Client mockClient() {
        return new MockClient();
    }

    @Bean
    @ConditionalOnProperty(prefix = "app.client", name = "mock", havingValue = "false", matchIfMissing = true)
    public Client realClient() {
        return new RealClient();
    }
}
실전 포인트
클래스 레벨에 붙이면 설정 전체를 한 번에 막을 수 있고, 메서드 레벨에 붙이면 같은 설정 클래스 안에서도 빈별로 더 세밀하게 분기할 수 있습니다. 기능 단위로 on/off 할 때는 클래스 레벨, 구현체 선택처럼 세부 분기일 때는 메서드 레벨이 더 자연스럽습니다.

패턴 1. 기능 토글용 enabled 플래그

starter나 공통 모듈에서는 app.feature.enabled 같은 명시적 토글 키를 두고 matchIfMissing 으로 기본 동작까지 함께 설계하는 패턴이 가장 흔하다

가장 흔한 패턴은 xxx.enabled 형태의 boolean 비슷한 플래그를 두는 것입니다. 사용자는 설정만 바꿔 기능을 켜고 끌 수 있고, 코드는 if문 없이도 빈 등록 자체를 조건부로 만들 수 있습니다.

이때 중요한 것은 기본값을 어디에 둘 것인지입니다. 프로퍼티가 없으면 기본적으로 매치되지 않으므로, 기능을 기본 on 으로 둘지 기본 off 로 둘지 matchIfMissing 까지 포함해 함께 설계해야 합니다.

@ConditionalOnProperty(
    prefix = "app.metrics",
    name = "enabled",
    havingValue = "true",
    matchIfMissing = true
)

패턴 2. 구현체 선택 분기

같은 인터페이스에 대해 mock, local, remote 같은 구현체를 프로퍼티 값으로 나누는 방식은 @ConditionalOnProperty 가 가장 직관적으로 빛나는 지점이다

이 애노테이션은 단순한 on/off 뿐 아니라 “어떤 구현을 쓸 것인가”를 나누는 데도 자주 사용됩니다. 예를 들어 개발 환경에서는 mock, 운영에서는 real, 특정 테스트에서는 noop 구현을 쓰도록 분기할 수 있습니다.

이 경우 havingValue 를 사용해 문자열 값으로 구현체를 나누면 명시성이 좋아집니다. 단, 여러 구현이 동시에 매치되지 않도록 값 설계를 분명히 해야 합니다.

@Bean
@ConditionalOnProperty(prefix = "app.storage", name = "type", havingValue = "s3")
StorageClient s3StorageClient() {
    return new S3StorageClient();
}

@Bean
@ConditionalOnProperty(prefix = "app.storage", name = "type", havingValue = "local", matchIfMissing = true)
StorageClient localStorageClient() {
    return new LocalStorageClient();
}

패턴 3. boolean 전용이면 ConditionalOnBooleanProperty 검토

최근 Spring Boot 에서는 boolean 프로퍼티 전용 애노테이션도 제공하므로, 순수한 true/false 토글이면 의도를 더 분명하게 표현할 수 있다

Spring Boot 현재 문서에는 @ConditionalOnBooleanProperty 가 따로 존재합니다. 이 애노테이션은 boolean 값 전용으로 조건을 검사하며, 기본 동작도 “프로퍼티가 존재하고 true 일 때 매치”라서 의미가 더 직접적입니다.

즉 값이 문자열로 다양하게 갈 수 있는 경우에는 @ConditionalOnProperty 가 자연스럽고, 정말 boolean 토글만 다룬다면 전용 애노테이션이 더 읽기 쉬운 선택이 될 수 있습니다.

@Bean
@ConditionalOnBooleanProperty(prefix = "app.feature", name = "enabled", matchIfMissing = true)
FeatureService featureService() {
    return new FeatureService();
}
핵심 포인트
@ConditionalOnProperty 를 boolean 용도로도 충분히 쓸 수 있지만, 기본 규칙이 “false 가 아니면 매치”라는 점 때문에 의도가 흐려질 수 있습니다. 순수한 true/false 토글이면 전용 애노테이션이 더 명확한 경우가 많습니다.

한계와 주의점

@ConditionalOnProperty 는 단순 키-값 조건에는 강하지만, 컬렉션 프로퍼티나 더 복잡한 구조를 신뢰성 있게 판단하는 용도로는 적합하지 않다

Javadoc이 명시적으로 경고하는 부분이 하나 있습니다. 이 애노테이션은 컬렉션 프로퍼티 매칭에 신뢰성 있게 사용할 수 없습니다.

예를 들어 spring.example.values 가 있을 때는 매치되더라도, 실제 환경에 spring.example.values[0] 만 잡혀 있는 경우에는 기대와 다르게 동작할 수 있습니다. 이런 경우는 커스텀 조건을 쓰는 편이 더 안전합니다.

또한 이 애노테이션은 문자열 값 기반 비교라는 점도 잊기 쉽습니다. boolean 처럼 보여도 결국 문자열 비교 규칙 위에서 돌아간다는 점을 항상 염두에 둬야 합니다.

주의해야 할 지점
  • 프로퍼티 파일이 아니라 Environment 최종값을 기준으로 평가된다는 점
  • 기본 규칙이 “true 일 때”가 아니라 “false 가 아니면”이라는 점
  • 컬렉션 프로퍼티에는 신뢰성 있게 쓰기 어렵다는 점
  • 여러 name 을 주면 OR 이 아니라 AND 로 동작한다는 점

자주 하는 실수

헷갈리는 이유는 문법이 어려워서가 아니라, 기본 비교 규칙과 Environment 우선순위를 함께 고려하지 않기 때문이다
  • havingValue 를 비워 두면 true 만 매치된다고 착각함
  • name 여러 개를 주고 OR 처럼 동작할 것이라 생각함
  • YAML 파일 값만 보고 실제 Environment 최종값은 확인하지 않음
  • matchIfMissing 를 빼먹어 기본 on/off 의도가 뒤집힘
  • 컬렉션 프로퍼티 존재 여부를 이 애노테이션 하나로 처리하려고 함
  • boolean 토글인데도 문자열 규칙을 의식하지 않고 막연히 사용함

실무 루틴

실무에서는 프로퍼티 키 이름을 명시적으로 설계하고, 기본값 정책을 matchIfMissing 으로 고정하며, 테스트로 조건 분기를 검증하는 습관이 중요하다
  1. 프로퍼티 키는 app.feature.enabled 처럼 의도가 보이게 설계한다.
  2. 기본 on/off 정책은 matchIfMissing 로 명시한다.
  3. 값 비교가 명확하지 않다면 havingValue 를 생략하지 않는다.
  4. 여러 이름을 동시에 검사할 때는 AND 조건임을 전제로 설계한다.
  5. 컬렉션이나 복잡한 구조는 커스텀 Condition 으로 분리한다.
  6. auto-configuration 테스트는 ApplicationContextRunner 로 케이스별 검증을 남긴다.

디버깅

@ConditionalOnProperty 가 기대와 다르게 동작할 때는 설정 파일을 보는 것보다 conditions report 와 최종 Environment 값을 확인하는 편이 훨씬 빠르다
1
애플리케이션을 --debug 로 실행해 조건 평가 보고서를 먼저 본다.
2
Actuator를 쓴다면 /actuator/conditions 로 어떤 조건이 왜 매치되었는지 확인한다.
3
키 이름이 실제로 prefix.name 형태로 조합된 최종 키와 일치하는지 본다.
4
설정 파일이 아니라 Environment 최종 우선순위 결과를 확인한다.
5
auto-configuration 은 ApplicationContextRunner 로 프로퍼티 케이스별 테스트를 만든다.
자주 쓰는 점검 포인트
--debug
/actuator/conditions
prefix + name 조합 확인
havingValue / matchIfMissing 재검토
Environment 우선순위 확인
ApplicationContextRunner 로 케이스 테스트

요약

@ConditionalOnProperty 의 핵심은 설정 파일을 읽는 것이 아니라 Environment 안의 최종 프로퍼티 값을 기준으로 설정 클래스와 빈 등록을 조건부로 제어하는 데 있다
  • @ConditionalOnProperty 는 Spring Environment 프로퍼티를 기준으로 조건을 평가한다.
  • ✅ 기본 규칙은 프로퍼티가 존재하고 값이 false 가 아니면 매치다.
  • havingValuematchIfMissing 로 조건을 더 정교하게 만들 수 있다.
  • prefix 는 자동으로 점을 붙이고, name 은 dashed notation을 권장한다.
  • ✅ 여러 name 을 지정하면 모두 통과해야 한다.
  • ✅ 클래스 레벨과 @Bean 메서드 레벨 모두에 적용할 수 있다.
  • ✅ 컬렉션 프로퍼티 매칭에는 신뢰성 있게 쓰기 어렵다.
  • ✅ boolean 토글이라면 @ConditionalOnBooleanProperty 도 고려할 수 있다.

 

 

728x90