Skip to main content

Command Palette

Search for a command to run...

Spring Boot Auto-configuration 동작 원리 — @SpringBootApplication이 켜는 100개의 스위치

Updated
11 min read

Spring Boot의 가장 큰 매력은 "별것 안 했는데 알아서 동작한다"는 점입니다. 그러나 그 알아서 동작하는 부분에 사고가 생기면, 어디서부터 봐야 할지 막막해집니다. 이 글은 @SpringBootApplication 한 줄이 어떻게 수십 개의 자동 설정을 켜고, 그 중 어떤 것이 살아남고 어떤 것이 조용히 사라지는지를 Spring Boot 3.4 기준 내부 코드와 함께 정리합니다. 처음 Spring Boot에 입문한 분, 그리고 Bean이 왜 안 만들어지는지 한참을 헤매 본 분을 대상으로 합니다.

처음 Spring Boot 프로젝트를 만들었을 때, 저는 어리둥절했습니다. pom.xmlspring-boot-starter-web을 추가했더니 톰캣이 떠 있고, application.yml에 데이터소스 URL을 적었더니 HikariCP 풀이 만들어져 있었습니다. 분명히 어떤 코드도 작성하지 않았는데 말입니다.

@SpringBootApplication 어노테이션 한 줄에 그 모든 것이 들어 있다는 건 알았지만, 정확히 어떤 메커니즘으로 그것들이 켜지는지는 한참 후에야 알게 되었습니다. 그리고 그 시점은 보통 자동 설정이 꺼졌을 때입니다. 분명히 클래스패스에 라이브러리가 있는데도 빈이 안 만들어지거나, 내가 만든 @Configuration을 무시하고 자동 설정이 우선 적용되는 식이죠.

이 글은 그 블랙박스를 풀어 헤치고, 자동 설정이 클래스패스 스캔 → 후보 로딩 → 필터링 → 정렬 → 적용으로 이어지는 5단계를 따라가며 정리합니다.


1. 출발점: @SpringBootApplication이 감추는 세 어노테이션

@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

@SpringBootApplication은 사실 세 어노테이션의 합성입니다.

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(...)
public @interface SpringBootApplication { ... }
  • @SpringBootConfiguration — 그냥 @Configuration의 별명. 의미상 "이 클래스가 부트 애플리케이션의 루트 설정"
  • @ComponentScan — 자기 패키지부터 하위 패키지까지 @Component 계열 빈 등록
  • @EnableAutoConfiguration — 이 글의 주인공. 자동 설정을 켭니다

@EnableAutoConfiguration의 본체

@Target(TYPE)
@Retention(RUNTIME)
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

핵심은 @Import(AutoConfigurationImportSelector.class) 한 줄입니다. **자동 설정이라는 화려한 이름과 달리, 그 본질은 평범한 @Import**입니다. 단지 import할 클래스의 목록을 런타임에 동적으로 결정할 뿐입니다.

@AutoConfigurationPackage는 보너스입니다. @SpringBootApplication이 붙은 클래스가 속한 패키지를 "기본 패키지"로 등록해, 이후 @EntityScan이나 @MapperScan이 생략되어도 동작하게 합니다.


2. AutoConfigurationImportSelector — 마지막에 일하는 선택자

AutoConfigurationImportSelector는 단순한 ImportSelector가 아니라 DeferredImportSelector를 구현합니다.

public class AutoConfigurationImportSelector implements DeferredImportSelector,
        BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware,
        EnvironmentAware, Ordered {

    static final int ORDER = Ordered.LOWEST_PRECEDENCE - 1;
    ...
}

DeferredImportSelector라는 이름에서 짐작되듯, 이 셀렉터는 다른 모든 @Configuration 처리가 끝난 뒤에야 실행됩니다. 이유는 명확합니다.

@ConditionalOnMissingBean이 동작하려면 사용자가 정의한 빈이 먼저 등록되어 있어야 합니다. 만약 자동 설정이 사용자 설정보다 먼저 적용되면, "사용자가 빈을 정의하지 않았을 때만 우리가 제공한다"는 규칙을 지킬 수 없습니다.

flowchart TD
    A["Application start"] --> B["Process @SpringBootApplication"]
    B --> C["Process @ComponentScan beans"]
    C --> D["Process user @Configuration"]
    D --> E["DeferredImportSelector.process()"]
    E --> F["AutoConfigurationImportSelector"]
    F --> G["Auto-config classes registered LAST"]
    G --> H["Conditions evaluated against current bean state"]

이 순서가 "사용자 설정이 자동 설정을 항상 이긴다"는 Spring Boot의 황금률을 만듭니다.

셀렉터의 6단계

AutoConfigurationImportSelector.getAutoConfigurationEntry()가 자동 설정을 결정하는 핵심 메서드입니다. 실제 코드를 보면 6단계로 정리됩니다.

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) return EMPTY_ENTRY;
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}
  1. isEnabledspring.boot.enableautoconfiguration=false면 빈 엔트리 반환
  2. getCandidateConfigurationsMETA-INF/spring/...AutoConfiguration.imports에서 후보 로드
  3. removeDuplicatesLinkedHashSet으로 중복 제거 (여러 jar에 같은 클래스가 들어 있을 수 있음)
  4. getExclusions / checkExcludedClassesexclude 속성과 spring.autoconfigure.exclude 프로퍼티 적용
  5. filterAutoConfigurationImportFilter 체인이 클래스패스/빈 조건을 빠르게 걸러 냄
  6. fireAutoConfigurationImportEventsAutoConfigurationImportListener들에게 결과 통지 (대표적으로 ConditionEvaluationReport)

각 단계를 차례로 풀어 보겠습니다.


3. 후보 로딩 — AutoConfiguration.imports라는 텍스트 파일

자동 설정의 후보 목록은 어디서 올까요. Spring Boot 3는 ImportCandidates라는 클래스가 책임집니다.

public final class ImportCandidates implements Iterable<String> {
    private static final String LOCATION = "META-INF/spring/%s.imports";
    private static final String COMMENT_START = "#";

    public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
        String location = String.format(LOCATION, annotation.getName());
        Enumeration<URL> urls = classLoader.getResources(location);
        ...
    }
}

@EnableAutoConfiguration이 사용하는 @AutoConfiguration 어노테이션의 FQN을 파일명에 박아 넣습니다. 결과적으로 모든 jar 안의 다음 경로에 있는 파일이 합쳐집니다.

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

파일 형식은 한 줄에 하나의 클래스 FQN, #로 시작하는 주석 허용. Spring Boot 3.4의 spring-boot-autoconfigure jar 안에는 이런 식으로 백 줄 가까이 들어 있습니다.

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
...
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

spring.factories의 죽음

Spring Boot 2.7 이전에는 META-INF/spring.factories 파일에 다음과 같이 등록했습니다.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
...

이 방식은 두 가지 문제가 있었습니다.

  • spring.factories는 다양한 SpringFactoriesLoader 키가 한 파일에 섞여 있어, 자동 설정만 추리려면 키 매칭이 필요했습니다
  • 백슬래시 라인 컨티뉴에이션과 콤마 구분으로 파싱이 깨지기 쉬웠습니다 (특히 GraalVM 네이티브 이미지에서 메타데이터 분석 시 부담)

Spring Boot 2.7에서 META-INF/spring/...AutoConfiguration.imports 방식이 도입되고 3.0에서 spring.factories의 자동 설정 키는 완전히 제거되었습니다 (다른 SPI 키는 여전히 spring.factories를 사용).

패키지가 다른 이유

흥미로운 점 하나. AutoConfigurationImportSelectororg.springframework.boot.autoconfigure 패키지에 있지만, ImportCandidatesorg.springframework.boot.context.annotation에 있습니다.

ImportCandidates는 자동 설정 전용 도구가 아니기 때문입니다. 같은 메커니즘이 @ImportAutoConfiguration (테스트 슬라이스에서 사용), Spring Cloud의 @EnableConfigurationServer 등 다른 어노테이션에서도 재사용됩니다. 어노테이션마다 자신의 FQN이 박힌 .imports 파일을 갖는 구조입니다.


4. 필터 — 클래스패스 단계에서 빠르게 잘라 내기

후보 백 개를 모두 @Configuration으로 파싱한 뒤 @Conditional을 평가하면 시간이 너무 오래 걸립니다. Spring Boot는 그 전에 메타데이터 기반 필터로 명백히 동작 불가능한 후보를 제거합니다.

private static class ConfigurationClassFilter {
    private final AutoConfigurationMetadata autoConfigurationMetadata;
    private final List<AutoConfigurationImportFilter> filters;

    List<String> filter(List<String> configurations) {
        String[] candidates = StringUtils.toStringArray(configurations);
        for (AutoConfigurationImportFilter filter : this.filters) {
            boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
            for (int i = 0; i < match.length; i++) {
                if (!match[i]) candidates[i] = null;
            }
        }
        ...
    }
}

빌드 시점에 만들어지는 메타데이터

AutoConfigurationMetadata는 빌드 시점에 생성된 META-INF/spring-autoconfigure-metadata.properties를 읽습니다. 이 파일에는 각 자동 설정 클래스에 붙은 @ConditionalOnClass 같은 어노테이션의 인자가 키-값 형태로 들어 있습니다.

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration=
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.ConditionalOnClass=\
javax.servlet.Servlet,org.springframework.web.servlet.DispatcherServlet
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.AutoConfigureOrder=...

AutoConfigurationImportFilter의 구현체들은 이 properties 파일만 읽고, 실제 @Configuration 클래스를 로드하지 않고 후보를 솎아 냅니다. 즉 클래스 자체를 메모리에 올리지 않고도 @ConditionalOnClass(Servlet.class) 같은 조건을 사전 평가할 수 있습니다.

세 가지 필터

AutoConfigurationImportSelectorSpringFactoriesLoaderAutoConfigurationImportFilter 구현체들을 로드합니다. 표준 구현은 세 개입니다.

  • OnClassCondition@ConditionalOnClass의 클래스가 클래스패스에 없으면 후보 탈락. Class.forName으로 시도해 ClassNotFoundException이면 결정
  • OnBeanCondition@ConditionalOnBean/@ConditionalOnMissingBean의 일부 케이스를 사전 평가
  • OnWebApplicationCondition@ConditionalOnWebApplication 평가 (Servlet, Reactive, Any)

이 단계에서 떨어진 후보는 이후 @Configuration 파싱 자체가 일어나지 않습니다. 시작 시간을 크게 줄이는 핵심 최적화입니다.

flowchart LR
    A["100+ candidates from .imports"] --> B["OnClassCondition filter"]
    B --> C["OnBeanCondition filter"]
    C --> D["OnWebApplicationCondition filter"]
    D --> E["Surviving candidates parsed as @Configuration"]
    E --> F["Per-method @Conditional re-evaluated"]

5. 조건 어노테이션 — 살아남은 후보를 다시 검사

필터를 통과한 클래스는 일반 @Configuration처럼 파싱되고, 그 시점에 모든 @Conditional 어노테이션이 다시 평가됩니다. 자주 쓰이는 조건 어노테이션과 그 평가자를 정리하면 다음과 같습니다.

어노테이션 평가자 의미
@ConditionalOnClass OnClassCondition 지정된 클래스가 클래스패스에 있을 때
@ConditionalOnMissingClass OnClassCondition 지정된 클래스가 클래스패스에 없을 때
@ConditionalOnBean OnBeanCondition BeanFactory에 지정된 빈이 이미 등록돼 있을 때
@ConditionalOnMissingBean OnBeanCondition 지정된 빈이 등록돼 있지 않을 때
@ConditionalOnProperty OnPropertyCondition 프로퍼티가 특정 값과 매칭될 때
@ConditionalOnResource OnResourceCondition 클래스패스 리소스가 존재할 때
@ConditionalOnWebApplication OnWebApplicationCondition Web/Reactive/Any 환경일 때
@ConditionalOnExpression OnExpressionCondition SpEL 식이 true일 때

전형적인 자동 설정의 형태는 두 조건을 함께 쓰는 것입니다.

@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().build();
    }
}

읽어 보면 의미가 그대로 드러납니다.

  • 클래스패스에 javax.sql.DataSource가 있을 때만 (@ConditionalOnClass)
  • 사용자가 DataSource 빈을 직접 등록하지 않았을 때만 (@ConditionalOnMissingBean)
  • 자동 설정이 기본 DataSource를 만들어 준다

@ConditionalOnMissingBean이 자기 클래스 안의 빈을 못 보는 함정

자주 부딪히는 함정 하나입니다. 같은 @Configuration 클래스 안에서 @Bean 메서드끼리는 @ConditionalOnMissingBean으로 서로를 가리지 못합니다. 메서드 평가 순서가 보장되지 않기 때문입니다 (Spring Boot 이슈 #30508 참조).

해결책은 둘 중 하나입니다. 별도의 @Configuration 클래스로 분리하거나, 빈 정의의 의존 관계를 명시적으로 만들거나.


6. 정렬 — @AutoConfigureBefore/After/Order

후보가 모두 결정된 뒤에는 적용 순서를 정해야 합니다. AutoConfigurationGroup.selectImports()에서 AutoConfigurationSorter.getInPriorityOrder()로 위임됩니다.

세 가지 어노테이션이 정렬을 제어합니다.

  • @AutoConfigureBefore(X.class) — 이 클래스가 X보다 먼저 적용되도록
  • @AutoConfigureAfter(X.class) — 이 클래스가 X보다 나중에 적용되도록
  • @AutoConfigureOrder(N) — 직접 관계가 없는 자동 설정들 사이의 우선순위 (낮은 값이 먼저)

@AutoConfigureBefore/After는 의존 관계가 명확할 때, @AutoConfigureOrder는 서로 모르는 자동 설정들 사이에서 사용합니다.

주의: 일반 빈에 쓰이는 @Order는 자동 설정 순서에 영향을 주지 않습니다. 자동 설정에는 반드시 @AutoConfigureOrder를 써야 합니다.

Spring Boot 2.7 이후의 단축 문법

@AutoConfiguration은 Spring Boot 2.7에 도입되었습니다. 자동 설정 클래스에는 항상 같은 어노테이션 묶음이 붙기 때문에 이를 한곳에 모은 메타 어노테이션입니다.

@Target(TYPE)
@Retention(RUNTIME)
@Documented
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore
@AutoConfigureAfter
public @interface AutoConfiguration {
    @AliasFor(annotation = Configuration.class)
    String value() default "";

    @AliasFor(annotation = AutoConfigureBefore.class, attribute = "value")
    Class<?>[] before() default {};

    @AliasFor(annotation = AutoConfigureAfter.class, attribute = "value")
    Class<?>[] after() default {};
    ...
}

세 가지 차이점이 있습니다.

  1. @Configuration(proxyBeanMethods = false)를 강제 — CGLIB 프록시 비용 회피. 자동 설정은 일반적으로 @Bean 메서드 사이의 직접 호출이 없어야 합니다
  2. before/after 속성이 어노테이션 본체에 통합 — 별도 @AutoConfigureBefore 줄을 추가할 필요가 없음
  3. 의도가 명시적 — "이 클래스는 자동 설정이지 일반 @Configuration이 아니다"

권장사항은 @AutoConfiguration을 쓰는 것입니다. @Configuration도 동작은 하지만, 의도를 흐리고 proxyBeanMethods=true로 동작하는 부작용이 있습니다.


7. 진단 — CONDITIONS EVALUATION REPORT

자동 설정이 어떻게 결정되었는지를 보고 싶을 때 가장 쉬운 방법은 --debug 플래그입니다.

java -jar myapp.jar --debug

ConditionEvaluationReportLoggingListenerApplicationContextInitializer로 등록되어 있어, 컨텍스트 초기화가 끝나면 다음과 같은 보고서를 DEBUG 레벨로 출력합니다.

============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:
-----------------
   AopAutoConfiguration matched:
      - @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
      - @ConditionalOnClass found required class 'org.aspectj.lang.annotation.Aspect' (OnClassCondition)

   DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required class 'javax.sql.DataSource' (OnClassCondition)

Negative matches:
-----------------
   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class
           'jakarta.jms.ConnectionFactory' (OnClassCondition)

Exclusions:
-----------
   None

Unconditional classes:
----------------------
   org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

세 섹션이 보입니다.

  • Positive matches — 적용된 자동 설정과 그 근거
  • Negative matches — 후보였지만 조건을 만족하지 못해 탈락한 자동 설정과 사유
  • Exclusionsexclude 속성으로 명시적으로 배제된 항목
  • Unconditional classes — 조건 없이 항상 적용되는 자동 설정

운영 환경에서 actuator를 사용한다면 /actuator/conditions 엔드포인트로 같은 정보를 JSON으로 받을 수 있습니다.


8. 직접 만드는 starter

자동 설정을 직접 만드는 것은 의외로 단순합니다. 사내 라이브러리에 자동 설정을 넣어 보겠습니다.

자동 설정 클래스

package com.acme.greeting.autoconfigure;

@AutoConfiguration
@ConditionalOnClass(GreetingService.class)
@ConditionalOnProperty(prefix = "acme.greeting", name = "enabled",
                      havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public GreetingService greetingService(GreetingProperties properties) {
        return new GreetingService(properties.getMessage());
    }
}

프로퍼티 클래스

@ConfigurationProperties(prefix = "acme.greeting")
public class GreetingProperties {
    private String message = "Hello";
    // getters/setters
}

.imports 파일

src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

com.acme.greeting.autoconfigure.GreetingAutoConfiguration

이 jar를 의존성으로 추가하면 사용자 측에서는 다음만 하면 됩니다.

acme:
  greeting:
    message: "안녕하세요"

GreetingService 빈이 자동으로 등록되고, 사용자가 @Bean GreetingService를 직접 만들면 자동 설정은 양보합니다 (@ConditionalOnMissingBean 덕분에).

메타데이터 자동 생성

spring-autoconfigure-metadata.properties 파일은 손으로 작성하지 않습니다. org.springframework.boot:spring-boot-autoconfigure-processorannotationProcessor 의존성으로 추가하면 컴파일 시점에 자동 생성됩니다.

dependencies {
    annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor")
}

이 프로세서가 있어야 4단계의 사전 필터가 우리가 만든 자동 설정에도 적용됩니다.


9. 운영 함정 다섯 가지

(1) excludeName vs exclude

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

exclude는 컴파일 의존성을 요구합니다. 자동 설정 클래스가 클래스패스에 있어야 합니다. 사이드카로 들어온 자동 설정처럼 컴파일 시 알 수 없는 경우는 excludeName이나 프로퍼티를 사용해야 합니다.

spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

(2) "있어야 할 자동 설정이 없다"

99%의 경우 두 가지 중 하나입니다.

  • 클래스패스에 필요한 라이브러리가 없음 (보통 starter 누락)
  • 사용자가 어딘가에서 같은 타입의 빈을 직접 등록함 (@ConditionalOnMissingBean이 양보)

--debug로 negative matches를 보면 둘 중 어느 쪽인지 즉시 드러납니다.

(3) @TestConfiguration이 자동 설정을 가린다

테스트에서 @TestConfiguration으로 빈을 정의하면 @ConditionalOnMissingBean이 양보합니다. 반대로 자동 설정이 동작해야 하는 경우라면 테스트에서 빈을 직접 등록하면 안 됩니다. 둘이 의도적으로 충돌하는 시나리오를 만들면 디버깅이 어려워집니다.

(4) proxyBeanMethods = false의 함의

@AutoConfiguration은 항상 proxyBeanMethods = false입니다. 같은 @Configuration 안에서 @Bean 메서드를 직접 호출해서 빈 인스턴스를 가져오는 패턴은 동작하지 않습니다.

@AutoConfiguration
public class MyAutoConfiguration {

    @Bean
    public A a() { return new A(); }

    @Bean
    public B b() {
        return new B(a());  // CGLIB 프록시 없음 → 새 A 인스턴스 생성됨
    }
}

해결: b(A a)로 파라미터 주입을 받습니다. 이것이 권장 패턴입니다.

(5) @AutoConfigureBefore/After는 빈 생성 순서가 아니다

@AutoConfiguration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MyAutoConfiguration { ... }

이 어노테이션은 자동 설정 클래스가 파싱되는 순서만 결정합니다. 실제 빈이 만들어지는 순서는 의존성 그래프와 @DependsOn이 결정합니다. 빈 생성 순서가 중요하다면 @DependsOn을 추가로 사용해야 합니다.


10. 정리

@SpringBootApplication 한 줄에서 시작해 자동 설정이 적용되기까지 거치는 길은 다음과 같습니다.

flowchart TD
    A["@SpringBootApplication"] --> B["@EnableAutoConfiguration"]
    B --> C["@Import(AutoConfigurationImportSelector.class)"]
    C --> D["DeferredImportSelector deferred until last"]
    D --> E["ImportCandidates.load()"]
    E --> F["META-INF/spring/...AutoConfiguration.imports"]
    F --> G["AutoConfigurationImportFilter chain"]
    G --> H["OnClassCondition / OnBeanCondition / OnWebApplicationCondition"]
    H --> I["Surviving candidates parsed as @Configuration"]
    I --> J["@AutoConfigureBefore/After/Order sorting"]
    J --> K["Per-method @Conditional re-evaluation"]
    K --> L["Beans registered into BeanFactory"]

이 흐름을 머리에 넣어 두면 두 가지 능력이 생깁니다.

  • 사라진 자동 설정의 원인 추적--debug의 negative matches를 어디부터 읽을지가 명확해집니다
  • 자체 starter 설계 — 어떤 조건으로, 어떤 순서로 적용될지를 의도대로 설계할 수 있습니다

처음에는 마법처럼 보이는 자동 설정도, 결국 텍스트 파일 한 줄 한 줄을 읽고 클래스패스를 한 클래스씩 확인하는 차분한 절차의 합입니다. 마법은 없습니다. 잘 설계된 컨벤션과, 그 컨벤션을 충실히 실행하는 백 줄짜리 셀렉터 한 개가 있을 뿐입니다.


참고자료

More from this blog

JVM은 컨테이너의 CPU와 메모리 한계를 어떻게 알아낼까

8코어 노드에 컨테이너를 띄웠는데 ForkJoinPool이 스레드를 한두 개만 만들어요. 메모리는 넉넉히 줬는데 컨테이너가 자꾸 OOMKilled로 죽고요. 분명히 같은 JAR인데 로컬에서는 멀쩡하다가 쿠버네티스에만 올리면 이상해져요. 이 글은 "왜 컨테이너 속 JVM은 다르게 행동하는가"를 cgroup이라는 진짜 경계선과, JVM이 그 경계를 읽어내는 내

May 21, 202615 min read

ThreadPoolExecutor는 언제 스레드를 새로 만들까 — execute()의 3단계

Executors.newFixedThreadPool(10) 한 줄을 쓰면서도, 11번째 작업이 오면 스레드가 11개로 늘어날 거라고 막연히 기대해 본 적 없으신가요. 실제로는 큐가 먼저 무한히 쌓이고 스레드는 영원히 10개에 머물러요. 이 글은 ThreadPoolExecutor가 작업을 받았을 때 "스레드를 새로 만들지, 큐에 넣을지, 거부할지"를 결정하는

May 21, 202617 min read

자바 synchronized는 어떻게 동작할까 — 모니터, 락 인플레이션, 그리고 사라진 biased locking

synchronized 키워드 하나로 스레드 안전을 얻는 동안, JVM 안에서는 객체 헤더의 비트를 뒤집고, 스택에 락 레코드를 쌓고, 경합이 생기면 네이티브 모니터로 승격하는 일이 벌어져요. 이 글은 그 한 번의 잠금이 객체 헤더부터 ObjectMonitor까지 어떤 경로를 거치는지, 그리고 한때 있었다가 JDK 18에서 사라진 biased locking

May 19, 202616 min read

JVM 객체 할당의 비밀 — TLAB, Bump-the-Pointer, 그리고 할당이 거의 공짜인 이유

Java에서 new를 호출하면 무슨 일이 벌어질까요? "힙에 메모리를 잡는다"는 한 문장 뒤에는 스레드마다 자기만의 분양 구역을 나눠 갖는 정교한 설계가 숨어 있어요. 이 글은 HotSpot JVM이 객체 할당을 어떻게 "거의 공짜"로 만드는지 그 내부를 따라가 보려는 글이에요. JVM 메모리 동작 원리에 관심 있는 분께 권해요. 자바를 쓰다 보면 객체를

May 15, 202614 min read

Java Zero-Copy — FileChannel.transferTo, sendfile, 그리고 Kafka가 디스크를 네트워크로 흘려보내는 방법

"파일을 읽어서 소켓으로 보낸다." 한 줄짜리 요구사항이에요. 그런데 이 한 줄 뒤에서 데이터는 메모리를 네 번이나 복사하고, CPU는 커널과 유저 공간을 네 번이나 들락거려요. Kafka처럼 초당 수십만 건을 흘려보내야 하는 시스템에서 이 비용은 그냥 넘길 수가 없어요. 이 글은 그 복사를 한 겹씩 벗겨내는 zero-copy의 동작 원리를 따라가요. 전통

May 15, 202617 min read

끄적끄적 테크 블로그

165 posts

물류 회사에 다니고 있는 개발자 블로그입니다. 개발을 너무 좋아해서 정신없이 작업하다가 중간에 끄적거리며 내용들을 몇개 적어봅니다 ㅎㅎ