본문 바로가기
쿤즈 Dev/Spring Boot

스프링 면접 질문 완전정복 ② — Spring Boot 내부 동작 & 성능 8선 (오토컨피규레이션·@Conditional·커스텀 스타터·Virtual Threads)

by Koonz:) 2026. 10. 26.
728x90
요약: Spring Boot가 "알아서 설정해 주는" 내부 원리와 성능 최적화를 면접 관점으로 정리합니다. 다루는 질문 — Q11 오토컨피규레이션 내부 동작 · Q12 조건부 애너테이션(@Conditional)과 @ConditionalOnMissingBean · Q13 커스텀 스타터 만들기 · Q14 기동 시간 최적화 · Q15 Actuator 프로덕션 활용 · Q16 Actuator 엔드포인트 보안 · Q17 가상 스레드(Virtual Threads) · Q18 멀티 환경 설정 관리. 핵심 한 줄: "자동 설정은 마법이 아니라 .imports 파일 + @Conditional 평가의 결과다."

스프링부트 면접 25선 2편 오토컨피규레이션 Conditional 커스텀스타터 Virtual Threads

 

Spring Boot 면접에서 가장 자주 갈리는 지점은 "부트를 써봤는가"가 아니라 "부트가 내부적으로 무슨 일을 하는지 설명할 수 있는가" 입니다. 오토컨피규레이션, 조건부 빈 등록, 기동 성능, 가상 스레드 같은 주제는 미들급 이상에서 반드시 나옵니다. 이 글은 스프링부트 면접 핵심 원리를 코드 예제와 함께 정리합니다. (1편 Spring Core & AOP, 3편 Transactions & JPA와 함께 보세요.)

 

Q11. 스프링부트 오토컨피규레이션(Auto-Configuration) 내부 동작

 

① 정의 — 클래스패스·프로퍼티·기존 빈 상태를 보고 필요한 빈을 조건부로 자동 등록해 주는 메커니즘입니다. @SpringBootApplication 안의 @EnableAutoConfiguration이 진입점입니다.

 

② 왜 면접에 나오나 — "부트가 어떻게 DataSource를, Tomcat을 알아서 띄우는가?"는 부트 이해도를 가늠하는 대표 질문입니다. 단순히 "자동으로 된다"가 아니라 로딩 파일 → 조건 평가 → 등록 흐름을 말할 수 있어야 합니다.

 

③ 상세 설명

 

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

 

@SpringBootApplication
// = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

 

동작 순서는 다음과 같습니다.

 

  1. @EnableAutoConfiguration이 AutoConfigurationImportSelector를 임포트합니다.
  2. 이 셀렉터가 클래스패스의 모든 jar에서 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일을 읽어 자동 설정 클래스 목록을 모읍니다.

 

  • Spring Boot 2.7 미만에서는 META-INF/spring.factories의 EnableAutoConfiguration 키를 사용했습니다. 2.7에서 .imports 방식이 도입되고 3.0부터 spring.factories의 자동설정 등록은 제거되었습니다. (면접 단골 비교 포인트)

 

  1. 각 자동 설정 클래스의 @Conditional 조건을 평가해 통과한 것만 실제로 빈을 등록합니다.
  2. @AutoConfiguration(before/after) 또는 @AutoConfigureBefore/@AutoConfigureAfter로 설정 간 순서가 정해집니다.

 

실제 자동 설정 클래스는 이렇게 생겼습니다(스프링 내부 축약 예).

 

@AutoConfiguration
@ConditionalOnClass(DataSource.class)                 // 클래스패스에 DataSource가 있을 때만
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean                          // 사용자가 직접 만든 빈이 없을 때만
    public DataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().build();
    }
}

 

④ 디버깅 팁 — 무엇이 적용/탈락됐는지 --debug 플래그로 Condition Evaluation Report를 출력할 수 있습니다.

 

java -jar app.jar --debug
# 또는 application.yml: debug: true
# 출력: Positive matches(적용) / Negative matches(탈락) / Exclusions

 

스프링부트 오토컨피규레이션 내부 동작 EnableAutoConfiguration ImportSelector imports Conditional Bean 등록 흐름

 

면접 답변 포인트
@SpringBootApplication 안의 @EnableAutoConfiguration이 AutoConfigurationImportSelector를 통해 각 jar의 META-INF/spring/...AutoConfiguration.imports를 읽어 자동 설정 후보를 모으고, 각 클래스의 @Conditional을 평가해 통과한 것만 빈으로 등록합니다. (2.7부터 .imports, 3.0에서 spring.factories 방식 제거.) 적용/탈락은 --debug의 Condition Evaluation Report로 확인한다고 덧붙이면 좋습니다.

 

Q12. 조건부 애너테이션(@Conditional)과 @ConditionalOnMissingBean 동작

 

① 정의 — 특정 조건이 만족될 때만 빈/설정을 등록하도록 하는 애너테이션군입니다. 오토컨피규레이션의 "자동"을 가능케 하는 핵심 장치입니다.

 

② 왜 면접에 나오나 — "내가 직접 만든 빈이 자동 설정보다 우선되는 이유"를 설명하려면 @ConditionalOnMissingBean의 평가 시점을 알아야 합니다.

 

③ 상세 설명 — 주요 조건 애너테이션

 

애너테이션 등록 조건
@ConditionalOnClass 지정 클래스가 클래스패스에 존재할 때
@ConditionalOnMissingClass 지정 클래스가 없을 때
@ConditionalOnBean 지정 빈이 이미 등록돼 있을 때
@ConditionalOnMissingBean 지정 타입 빈이 아직 없을 때
@ConditionalOnProperty 프로퍼티 값이 조건에 맞을 때
@ConditionalOnWebApplication 웹 애플리케이션일 때
@ConditionalOnExpression SpEL 표현식이 true일 때

 

④ 동작 코드 예제

 

@AutoConfiguration
public class GreetingAutoConfiguration {

    // (1) 클래스패스 조건
    @Bean
    @ConditionalOnClass(name = "com.acme.GreetingClient")
    public GreetingClient greetingClient() {
        return new GreetingClient();
    }

    // (2) 프로퍼티 조건: greeting.enabled=true 일 때만 (없으면 matchIfMissing=true로 기본 on)
    @Bean
    @ConditionalOnProperty(prefix = "greeting", name = "enabled",
                           havingValue = "true", matchIfMissing = true)
    public GreetingService greetingService() {
        return new GreetingService("안녕하세요");
    }

    // (3) 사용자가 같은 타입 빈을 안 만들었을 때만 기본 빈 제공
    @Bean
    @ConditionalOnMissingBean(MessageFormatter.class)
    public MessageFormatter defaultFormatter() {
        return new SimpleMessageFormatter();
    }
}

 

@ConditionalOnMissingBean이 왜 "사용자 빈 우선"으로 동작하는가? 핵심은 평가 순서입니다. 스프링은 사용자 정의 설정(@Configuration, @Component)을 먼저 처리하고, 자동 설정은 가장 마지막에 처리합니다. 따라서 자동 설정이 평가될 시점에는 이미 사용자 빈이 컨테이너에 있으므로 @ConditionalOnMissingBean 조건이 false가 되어 기본 빈이 건너뛰어집니다.

 

@Configuration
public class MyConfig {
    @Bean
    public MessageFormatter customFormatter() {   // 내가 만든 빈이 자동 기본 빈을 "이김"
        return new JsonMessageFormatter();
    }
}

 

⚠️ 주의: 여러 빈 메서드가 서로의 존재에 의존할 때 @ConditionalOnMissingBean은 같은 설정 클래스 내 선언 순서의 영향을 받습니다. 자동 설정 클래스 안에서는 조건부 빈을 신중히 배치해야 합니다.

 

스프링 Conditional OnClass OnProperty OnMissingBean 세 관문 매칭 플로우차트

 

면접 답변 포인트

 

@Conditional류는 "조건 만족 시에만 빈 등록"이고, @ConditionalOnMissingBean이 사용자 빈을 우선하게 만드는 이유는 자동 설정이 사용자 설정보다 나중에 평가되기 때문입니다. 그래서 내가 같은 타입 빈을 정의하면 자동 기본 빈은 조건 불충족으로 skip됩니다. matchIfMissing, havingValue 같은 옵션도 함께 언급하면 좋습니다.

 

Q13. 커스텀 스프링부트 스타터(Custom Starter) 만들기

 

① 정의 — 특정 기능(라이브러리 + 자동 설정 + 기본 프로퍼티)을 의존성 하나 추가만으로 쓰게 묶은 모듈입니다.

 

② 왜 면접에 나오나 — 사내 공통 모듈(로깅, 인증, 메시징 래퍼)을 스타터로 배포한 경험은 "부트를 깊이 이해했다"는 강한 신호입니다.

 

③ 상세 설명 — 2개 모듈 구조

 

관례상 스타터는 두 모듈로 나눕니다.

 

모듈 역할
acme-spring-boot-autoconfigure 실제 자동 설정 클래스 + @ConfigurationProperties + .imports 파일
acme-spring-boot-starter 의존성 묶음(autoconfigure + 필요한 서드파티 라이브러리). 코드는 거의 없음

 

⚠️ 네이밍 규칙: spring-boot로 시작하는 이름(spring-boot-starter-xxx)은 스프링 공식 전용입니다. 서드파티는 xxx-spring-boot-starter 형식을 써야 합니다.

 

④ 동작 코드 예제

 

(1) 프로퍼티 바인딩 클래스

 

@ConfigurationProperties(prefix = "acme")
public class AcmeProperties {
    private boolean enabled = true;
    private String apiKey;
    // getter/setter 생략
}

 

(2) 자동 설정 클래스 (autoconfigure 모듈)

 

@AutoConfiguration
@ConditionalOnClass(AcmeClient.class)
@EnableConfigurationProperties(AcmeProperties.class)
public class AcmeAutoConfiguration {

    @Bean
    @ConditionalOnProperty(prefix = "acme", name = "enabled", havingValue = "true", matchIfMissing = true)
    @ConditionalOnMissingBean
    public AcmeClient acmeClient(AcmeProperties properties) {
        return new AcmeClient(properties.getApiKey());
    }
}

 

(3) 등록 파일 — autoconfigure 모듈의 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

 

com.acme.autoconfigure.AcmeAutoConfiguration

 

(4) 스타터 모듈 build.gradle (코드 없이 의존성만 묶음)

 

// acme-spring-boot-starter/build.gradle
dependencies {
    api project(":acme-spring-boot-autoconfigure")
    api "com.acme:acme-client:1.2.0"
}

 

(5) 사용 측 — 의존성 한 줄 + 프로퍼티

 

dependencies { implementation "com.acme:acme-spring-boot-starter:1.0.0" }

 

acme:
  enabled: true
  api-key: "my-secret-key"

 

💡 보너스: spring-boot-configuration-processor를 autoconfigure 모듈에 넣으면 IDE 자동완성용 메타데이터(spring-configuration-metadata.json)가 생성됩니다.

 

면접 답변 포인트

 

스타터는 autoconfigure 모듈(자동 설정 + @ConfigurationProperties + AutoConfiguration.imports)과 starter 모듈(의존성 묶음)로 분리합니다. 자동 설정은 @ConditionalOnClass/@ConditionalOnMissingBean으로 "있으면 쓰고 사용자 빈이 있으면 양보"하게 설계합니다. 네이밍은 xxx-spring-boot-starter, 등록은 spring.factories가 아니라 .imports 파일이라는 점이 최신 포인트입니다.

 

Q14. 스프링부트 기동 시간(Startup Time) 최적화

 

① 정의 — 애플리케이션이 요청을 받을 준비가 되기까지의 시간을 줄이는 기법 모음입니다.

 

② 왜 면접에 나오나 — 서버리스/오토스케일링/배포 빈도가 높은 환경에서 콜드 스타트는 직접적인 비용·지연입니다. "무엇을 손대면 기동이 빨라지는가"를 수치 감각과 함께 답할 수 있어야 합니다.

 

③ 상세 설명 — 최적화 레버

 

  1. 지연 초기화(Lazy Initialization) — 모든 빈을 기동 시 만들지 않고 최초 사용 시 생성.
  2. 컴포넌트 스캔 범위 축소 — 넓은 @ComponentScan은 클래스패스 스캔 비용↑. 패키지를 좁히거나 명시적 @Import 사용.
  3. 불필요한 오토컨피규레이션 제외 — 안 쓰는 자동 설정 평가 비용 제거.
  4. spring-context-indexer(@Indexed) — 컴파일 타임 인덱스로 런타임 스캔 대체.
  5. JVM 플래그 — 개발 중 -XX:TieredStopAtLevel=1(C2 생략), CDS/AppCDS로 클래스 로딩 단축.
  6. Spring AOT + GraalVM Native Image — 반사·프록시를 빌드 타임에 처리해 네이티브 실행(기동 수십 ms 수준). Spring Boot 3+.

 

④ 동작 코드/설정 예제

 

# application.yml
spring:
  main:
    lazy-initialization: true        # 지연 초기화
  autoconfigure:
    exclude:                         # 안 쓰는 자동 설정 제외
      - org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

 

// 컴포넌트 스캔 범위를 명시적으로 좁힘
@SpringBootApplication(scanBasePackages = "com.acme.core")
public class MyApplication { }

 

# (1) 개발 기동 가속 JVM 플래그
java -XX:TieredStopAtLevel=1 -jar app.jar

# (2) Spring Boot 3.3+ CDS: 학습 실행으로 아카이브 생성 후 재사용
java -XX:ArchiveClassesAtExit=app.jsa -Dspring.context.exit=onRefresh -jar app.jar
java -XX:SharedArchiveFile=app.jsa -jar app.jar

# (3) GraalVM 네이티브 이미지 빌드 (수십 ms 기동)
./gradlew nativeCompile

 

스프링부트 기동 시간 최적화 체크리스트 lazy init 컴포넌트 스캔 축소 exclude Indexed CDS AOT Native

 

⚠️ lazy-initialization=true는 기동은 빨라지지만 첫 요청 지연과 설정 오류 발견이 늦어지는 트레이드오프가 있습니다. 운영 전면 적용 전 검증 필요.

 

면접 답변 포인트

 

레버는 세 층입니다 — 애플리케이션 층(lazy init, 스캔 축소, 불필요 auto-config exclude, @Indexed), JVM 층(-XX:TieredStopAtLevel=1, CDS/AppCDS), 빌드 층(Spring AOT + GraalVM Native). 네이티브는 기동이 극적으로 빨라지지만 리플렉션 힌트·빌드 복잡도 비용이 있고, lazy init은 첫 요청 지연 트레이드오프가 있다고 균형 있게 답하면 좋습니다.

 

Q15. 스프링부트 Actuator와 프로덕션 활용

 

① 정의 — 운영 중 애플리케이션의 상태(health)·지표(metrics)·정보(info) 등을 HTTP/JMX 엔드포인트로 노출하는 모듈입니다.

 

② 왜 면접에 나오나 — "운영 가시성을 어떻게 확보했는가", "쿠버네티스 헬스체크를 어떻게 붙였는가"는 프로덕션 경험을 가늠하는 질문입니다.

 

③ 상세 설명 — 주요 엔드포인트

 

엔드포인트 용도
/actuator/health 애플리케이션/하위 컴포넌트(DB, Redis 등) 상태
/actuator/metrics Micrometer 지표(JVM, HTTP, 커스텀)
/actuator/info 빌드/버전 등 정보
/actuator/prometheus Prometheus 스크랩용 지표(의존성 추가 시)
/actuator/env, /actuator/loggers 환경/로그 레벨 조회·변경

 

④ 동작 설정 예제

 

의존성:

 

implementation "org.springframework.boot:spring-boot-starter-actuator"
implementation "io.micrometer:micrometer-registry-prometheus"   // Prometheus 연동 시

 

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus   # 노출 엔드포인트 명시
  endpoint:
    health:
      show-details: when-authorized                # health 상세는 인증 시에만
      probes:
        enabled: true                              # k8s liveness/readiness 활성화
  info:
    env:
      enabled: true

 

커스텀 HealthIndicator로 하위 시스템 상태를 추가할 수 있습니다.

 

@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
    private final PaymentClient client;
    public PaymentGatewayHealthIndicator(PaymentClient client) { this.client = client; }

    @Override
    public Health health() {
        return client.ping()
            ? Health.up().withDetail("latencyMs", client.lastLatency()).build()
            : Health.down().withDetail("reason", "gateway unreachable").build();
    }
}

 

쿠버네티스는 /actuator/health/liveness, /actuator/health/readiness를 프로브로 사용합니다. 커스텀 지표는 Micrometer로 기록합니다.

 

@Service
public class OrderService {
    private final Counter orderCounter;
    public OrderService(MeterRegistry registry) {
        this.orderCounter = registry.counter("orders.created");
    }
    public void create(Order o) { /* ... */ orderCounter.increment(); }
}

 

면접 답변 포인트

 

Actuator는 health/metrics/info를 운영 가시성으로 노출하고, Micrometer가 지표 파사드로서 Prometheus 등 모니터링 백엔드와 연결됩니다. 쿠버네티스에서는 probes.enabled=true로 liveness/readiness 프로브를, 하위 시스템은 커스텀 HealthIndicator로 묶는다고 답하면 실무 깊이가 드러납니다.

 

Q16. Actuator 엔드포인트 보안

 

① 정의 — Actuator가 노출하는 운영 정보가 외부에 새지 않도록 노출 범위 제한 + 인증/인가 + 네트워크 분리로 보호하는 것입니다.

 

② 왜 면접에 나오나 — /actuator/env, /actuator/heapdump 같은 엔드포인트는 민감 정보 유출·공격 표면이 됩니다. 보안 설정 경험은 사고 예방 역량의 신호입니다.

 

③ 상세 설명 — 3단 방어

 

  1. 노출 최소화 — 기본적으로 웹에는 health만 노출됩니다. 필요한 것만 exposure.include에 추가하고, 민감한 것은 exclude.
  2. 인증/인가 — Spring Security로 Actuator 경로를 보호. EndpointRequest.toAnyEndpoint()로 매칭.
  3. 포트 분리 — management.server.port로 관리 엔드포인트를 별도 포트에 띄워 외부 노출을 막고, 내부망/방화벽으로 접근 제한.

 

④ 동작 코드 예제

 

(1) 노출 최소화 + 포트 분리

 

management:
  server:
    port: 9000                      # 서비스(8080)와 분리된 관리 포트
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
        exclude: env,beans,heapdump # 민감 엔드포인트 차단

 

(2) Spring Security로 Actuator 인가

 

@Configuration
public class ActuatorSecurityConfig {

    @Bean
    public SecurityFilterChain actuatorChain(HttpSecurity http) throws Exception {
        http
            .securityMatcher(EndpointRequest.toAnyEndpoint())   // 모든 actuator 경로
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(EndpointRequest.to("health", "info")).permitAll() // 공개
                .anyRequest().hasRole("ADMIN"))                 // 나머지는 ADMIN만
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

 

⚠️ show-details: always는 DB 접속 정보 등 세부를 노출할 수 있으니 운영에서는 when-authorized/never를 권장합니다. heapdump·threaddump는 외부 노출 금지.

 

면접 답변 포인트

 

세 겹으로 막습니다 — 노출 최소화(exposure.include/exclude, 기본 health만), Spring Security 인가(EndpointRequest.toAnyEndpoint()로 ADMIN 제한), 관리 포트 분리(management.server.port). health 상세는 when-authorized, heapdump 류는 외부 차단이라고 덧붙이면 좋습니다.

 

Q17. 가상 스레드(Virtual Threads)와 스프링부트 사용법

 

① 정의 — JDK 21에서 정식 도입된 경량 스레드(Project Loom). OS 스레드(캐리어)에 다대일로 매핑되어, 블로킹 시 캐리어를 반납하므로 수십만 개를 띄울 수 있습니다.

 

② 왜 면접에 나오나 — I/O 바운드 서버의 동시성 모델을 "스레드풀 튜닝" 없이 바꾸는 최신 주제. 장점만 외우면 pinning 함정에서 걸립니다.

 

③ 상세 설명 — 기존 플랫폼 스레드는 요청당 OS 스레드를 점유해 블로킹 I/O 동안 자원이 놀았습니다. 가상 스레드는 블로킹 지점에서 캐리어 스레드를 반납하고 다른 가상 스레드가 그 위에서 실행됩니다. 스프링부트 3.2+에서는 플래그 하나로 Tomcat 요청 처리, @Async, RestClient 등에 가상 스레드를 적용합니다.

 

④ 동작 설정/코드 예제

 

# JDK 21+ 필요
spring:
  threads:
    virtual:
      enabled: true     # Tomcat 요청 처리 + @Async executor가 가상 스레드 사용

 

// 직접 실행자가 필요할 때 (수동)
@Configuration
public class ExecutorConfig {
    @Bean
    public ExecutorService appTaskExecutor() {
        return Executors.newVirtualThreadPerTaskExecutor(); // 요청마다 새 가상 스레드
    }
}

 

주의점(면접에서 꼭 언급)

 

  • pinning(고정) — synchronized 블록 안에서 블로킹하거나 네이티브 호출 시 가상 스레드가 캐리어에 고정되어 반납되지 않습니다. synchronized → ReentrantLock으로 바꾸는 것이 권장됩니다. (JDK 21 기준 이슈이며, 후속 JDK에서 상당 부분 개선되었습니다.)
  • 풀링 금지 — 가상 스레드는 생성 비용이 싸므로 풀에 담지 않고 작업마다 새로 만듭니다.
  • CPU 바운드엔 이점 없음 — 블로킹 I/O 동시성에 유리하고, 연산 집약 작업은 플랫폼 스레드가 적합합니다.
  • ThreadLocal — 대량의 가상 스레드가 ThreadLocal을 많이 쓰면 메모리 압박. ScopedValue(프리뷰) 등을 고려.

 

가상 스레드 플랫폼 스레드 비교 JDK21 Loom 캐리어 반납 pinning 풀링금지

 

면접 답변 포인트

 

가상 스레드는 JDK 21 Loom 산물로, 블로킹 시 캐리어 스레드를 반납해 I/O 바운드 동시성을 스레드풀 없이 끌어올립니다. 스프링부트 3.2+는 spring.threads.virtual.enabled=true 한 줄로 적용합니다. 다만 synchronized/네이티브 호출 pinning, 풀링 금지, CPU 바운드엔 무의미라는 주의점을 함께 말해야 깊이가 드러납니다.

 

Q18. 멀티 환경(Multi-Environment) 설정 관리

 

① 정의 — dev/stage/prod 등 환경별로 설정을 분리·주입하고, 민감 정보를 안전하게 공급하는 방식입니다.

 

② 왜 면접에 나오나 — "환경별 DB URL/시크릿을 어떻게 관리했나"는 실무 설정 역량의 직접 지표입니다.

 

③ 상세 설명 — 네 가지 축

 

  1. 프로파일별 파일 — application-{profile}.yml을 spring.profiles.active로 선택.
  2. @Profile — 환경별로 다른 빈 등록.
  3. spring.config.import — 외부 설정(파일/Config Server/Vault/configtree) 가져오기.
  4. 환경변수·시크릿 — 비밀은 파일에 하드코딩하지 않고 환경변수/시크릿 매니저로 주입.

 

④ 동작 설정/코드 예제

 

(1) 프로파일별 파일 + 활성화

 

# application.yml (공통)
spring:
  profiles:
    active: ${APP_ENV:dev}        # 환경변수 APP_ENV, 없으면 dev
---
# application-prod.yml
spring:
  datasource:
    url: ${DB_URL}                # 시크릿은 환경변수로 주입
    username: ${DB_USER}
    password: ${DB_PASSWORD}

 

(2) 한 파일에 프로파일 구간 (spring.config.activate.on-profile)

 

spring:
  config:
    activate:
      on-profile: prod
logging:
  level:
    root: WARN

 

(3) @Profile로 환경별 빈

 

@Configuration
public class MailConfig {
    @Bean @Profile("prod")
    public MailSender realMailSender() { return new SmtpMailSender(); }

    @Bean @Profile({"dev", "test"})
    public MailSender fakeMailSender() { return new LoggingMailSender(); }
}

 

(4) 외부 설정 가져오기 + 쿠버네티스 시크릿(configtree)

 

spring:
  config:
    import:
      - "optional:configtree:/etc/secrets/"   # k8s 시크릿 마운트 디렉터리
      - "optional:configserver:http://config-server:8888"

 

# 실행 시 프로파일/시크릿 주입
APP_ENV=prod DB_PASSWORD=*** java -jar app.jar
# 또는
java -jar app.jar --spring.profiles.active=prod

 

⚠️ 설정 우선순위: 커맨드라인 인자 > 환경변수/시스템 프로퍼티 > application-{profile}.yml > application.yml. 같은 키는 더 높은 우선순위가 덮어씁니다. 비밀번호는 절대 소스/깃에 커밋하지 말 것.

 

면접 답변 포인트

 

application-{profile}.yml + spring.profiles.active로 환경을 분리하고, 환경별 빈은 @Profile, 외부/민감 설정은 spring.config.import(Config Server·Vault·configtree)로 가져옵니다. 시크릿은 파일이 아니라 환경변수/시크릿 매니저로 주입하고, 설정은 커맨드라인 > 환경변수 > 프로파일 파일 > 공통 파일 순으로 오버라이드된다는 우선순위를 말하면 완성도가 높습니다.

 

한눈 정리

 

# 질문 한 줄 핵심
Q11 오토컨피규레이션 .imports 로딩 → @Conditional 평가 → 통과분만 등록 (3.0에서 spring.factories 제거)
Q12 @Conditional / OnMissingBean 자동 설정이 나중에 평가돼 사용자 빈이 우선
Q13 커스텀 스타터 autoconfigure 모듈 + starter 모듈, 이름은 xxx-spring-boot-starter
Q14 기동 최적화 앱층(lazy·scan·exclude·@Indexed)+JVM층(CDS·플래그)+빌드층(AOT·Native)
Q15 Actuator health/metrics/info + Micrometer, k8s probes
Q16 Actuator 보안 노출 최소화 + Spring Security 인가 + 관리 포트 분리
Q17 Virtual Threads spring.threads.virtual.enabled=true, pinning·풀링금지·CPU바운드 주의
Q18 멀티 환경 profile 파일 + @Profile + config.import + 시크릿은 환경변수

 

✅ 셀프 체크리스트

 

  • [ ] 오토컨피규레이션 로딩 파일이 .imports(2.7+)로 바뀐 이유를 말할 수 있다
  • [ ] @ConditionalOnMissingBean이 사용자 빈을 우선하는 "평가 순서"를 설명할 수 있다
  • [ ] 커스텀 스타터를 두 모듈로 나누는 이유를 안다
  • [ ] 기동 최적화 레버를 앱/JVM/빌드 세 층으로 구분해 말할 수 있다
  • [ ] 가상 스레드의 pinning 함정과 풀링 금지 원칙을 안다

 

📚 스프링 면접 질문 완전정복 시리즈 (발행 시 URL 연결)

 

  • [스프링 면접 ①편 — 코어 & AOP 10선]
  • [스프링 면접 ②편 — 스프링부트 내부 동작 & 성능 8선] ← 현재 글
  • [링크: 스프링 면접 ③편 — 트랜잭션 & JPA 5선] (다음 글)
  • [링크: 스프링 면접 ④편 — 프로덕션 & MSA 2선]

 

🔗 함께 보면 좋은 글

 

댓글