본문 바로가기
빌드노트 검색

빌드노트

@Profile에 dev와 local을 넣었는데 dev만 켜도 빈이 생긴다

@Profile({"dev", "local"})는 두 프로필 중 하나만 활성화되어도 빈을 등록한다. Spring 6.2.12의 새 컨텍스트 네 개에서 배열과 "dev & local"의 등록 결과를 비교하고, 필요한 조합에 맞춰 조건을 고치는 방법을 설명한다.

@Profile({"dev", "local"})를 붙인 빈이 dev 하나만 활성화해도 등록된다면, 먼저 배열의 의미를 확인해야 한다. 이 배열은 두 프로필이 모두 필요하다는 뜻이 아니다. dev 또는 local 중 하나라도 활성화되면 등록 조건을 만족한다. 둘 다 필요한 빈이라면 해당 @Bean 메서드나 구성 클래스의 조건을 @Profile("dev & local")로 바꾼다.

local이 dev의 하위 환경처럼 들려도 이름만으로 그런 관계가 생기지는 않는다. 두 이름을 배열에 나란히 쓰면 선택 가능한 조건 두 개가 된다. Spring의 @Profile API 문서 (새 탭에서 열림)도 배열에 지정한 이름 중 하나가 활성화되면 조건이 성립한다고 설명한다.

같은 타입의 빈으로 두 조건을 비교했다

별도 최소 컨텍스트에 문자열 빈 두 개를 선언했다. arrayBean은 이름 배열을, andBean은 AND 표현식을 사용한다. 아래 코드를 비교하면 바뀌는 부분이 반환 타입이나 빈 생성 코드가 아니라 @Profile의 값이라는 것을 확인할 수 있다.

@Configuration(proxyBeanMethods = false)
class DemoConfig {
    @Bean
    @Profile({"dev", "local"})
    String arrayBean() { return "array"; }

    @Bean
    @Profile("dev & local")
    String andBean() { return "and"; }
}

두 빈은 비교를 위해 함께 둔 것이며, 실제 코드에서는 필요한 조건 하나를 대상 빈에 적용하면 된다. 문자열 하나 안의 &가 두 프로필을 AND로 연결한다. 배열의 원소를 두 개에서 하나로 줄였다는 사실보다, 그 한 문자열이 어떤 조건을 표현하는지가 중요하다. 지원하는 연산자는 Profiles API 문서 (새 탭에서 열림)에서 확인할 수 있다.

실행 환경은 Windows 11, OpenJDK 17.0.2, Spring Framework 6.2.12였다. 활성 프로필 없음, dev, local, 둘 다의 네 경우마다 새 AnnotationConfigApplicationContext를 만들었다. 각 경우에 프로필을 먼저 지정하고 위 설정만 등록한 뒤 빈의 존재를 조회했으며, 조회 후 컨텍스트를 닫았다.

직접 비교하려면 Spring Context 의존성이 있는 별도 Java 코드에서 위 설정과 다음 코드를 사용한다. 예제의 애너테이션과 컨텍스트 클래스는 모두 org.springframework.context.annotation 패키지에 있다. 다음 블록은 메서드 안에서 dev 하나만 활성화했을 때의 등록 여부를 확인한다.

try (var ctx = new AnnotationConfigApplicationContext()) {
    ctx.getEnvironment().setActiveProfiles("dev");
    ctx.register(DemoConfig.class);
    ctx.refresh();
    System.out.printf("arrayBean=%s andBean=%s%n",
        ctx.containsBean("arrayBean"), ctx.containsBean("andBean"));
}

이 입력에서는 arrayBean=true andBean=false가 나왔다. setActiveProfiles의 인수를 없음, "dev", "local", "dev", "local"로 바꾸며 블록 전체를 매번 새로 실행하면 네 경우를 비교할 수 있다. 이 비교는 외부 spring.profiles.active와 spring.profiles.default 설정의 영향을 받지 않는 최소 컨텍스트 기준이다. 특히 인수 없는 경우를 확인할 때는 해당 외부 설정이 없는지 확인해야 한다.

실제 네 번의 실행에서 관찰한 등록 여부를 정리하면 다음과 같다. true는 그 이름의 빈이 존재한다는 뜻이다.

활성 프로필        arrayBean    andBean
없음               false        false
dev                true         false
local              true         false
dev, local         true         true

차이를 드러내는 것은 dev 또는 local 하나만 활성화한 두 행이다. 배열 조건은 이미 만족하지만 AND 조건은 나머지 프로필이 없어서 만족하지 않는다. 둘을 모두 켠 경우에는 두 조건이 모두 참이므로, 그 경우만 확인해서는 배열을 AND로 오해한 것을 발견하기 어렵다.

여기서 ‘없음’은 명시적으로 활성화한 프로필이 없다는 뜻이다. Spring에는 이때 적용되는 기본 프로필이 있으며 기본 이름은 default다. 따라서 프로필 조건이 붙은 모든 빈이 사라진다는 뜻은 아니다. 이번 조건에 쓰인 이름은 dev와 local이므로 기본 프로필 default가 조건을 대신 만족시키지는 않는다. 기본 프로필의 동작은 Environment 공식 설명 (새 탭에서 열림)을 참고할 수 있다.

등록 조건과 활성화할 이름을 구별한다

둘 다 필요한 조건을 만들 때 바꿀 곳은 빈 선언의 @Profile("dev & local")다. 활성화할 때는 여전히 setActiveProfiles("dev", "local")처럼 두 이름을 각각 전달한다. "dev & local"은 조건 표현식이지 활성화할 프로필 이름 목록이 아니다.

수정 후에도 예상과 다르다면 실제 컨텍스트의 getEnvironment().getActiveProfiles()로 활성 목록을 확인한다. dev만 켰다고 생각했는데 목록에 local도 있다면 AND 조건의 빈이 생기는 것은 자연스럽다. 반대로 둘 다 활성화했는데 빈이 없다면 해당 구성 클래스가 등록됐는지, 같은 빈 선언에 다른 조건이 있는지 확인한다. 최소 예제처럼 프로필 지정은 설정 등록과 refresh()보다 앞에 둔다. 이 순서는 Environment의 프로필 활성화 예제 (새 탭에서 열림)에도 나와 있다.

이번 실행은 @Bean 메서드의 두 프로필 조건과 빈 존재 여부를 비교한 것이다. 실제 애플리케이션 설정이나 운영 빈은 불러오지 않았으며, Spring Boot의 프로필 그룹이나 설정 파일에서 최종 활성 목록이 만들어지는 과정은 검증하지 않았다. 선택 기준은 필요한 조합에 맞추면 된다. 어느 한 환경에서 쓸 빈은 배열로, 두 환경 조건이 함께 필요할 때는 단일 AND 표현식으로 선언한다.

← 전체 글 보기