자바 백엔드 입문 보강 — Spring Boot 자동 구성

2026-05-26자바 백엔드 입문

자바 백엔드 입문 보강편. 12편에서 만든 첫 프로젝트가 설정 한 줄 없이 톰캣을 띄우고 DB에 붙는 그 '알아서 됨'의 정체 — Spring Boot 자동 구성을 @SpringBootApplication부터 @ConditionalOnClass·AutoConfiguration.imports·디버깅까지 풀어쓴 학습 노트.

📚 자바 백엔드 입문 · 보강편 — Spring Boot 자동 구성

이 글은 자바 백엔드 입문 시리즈의 보강편이에요. 12편에서 start.spring.io로 첫 프로젝트를 만들고 ▶ Run 한 번에 톰캣이 8080에 떴죠. 그때 슬쩍 넘어간 게 하나 있어요. 나는 설정 파일에 아무것도 안 썼는데, 톰캣은 어떻게 떴고 JSON 응답은 누가 만들어 줬을까. 그 "알아서 됨"의 정체가 바로 Spring Boot의 자동 구성(Auto Configuration)입니다. 12편과 13편 application.yml 사이에 놓고 읽으면 딱 맞아요.

자동 구성이 처음엔 마법처럼 느껴지는 이유

처음 Spring Boot를 만지면 이 부분이 제일 얼떨떨해요. 이유는 두 가지예요.

첫째, 내가 안 만든 Bean이 어디서 왔는지가 안 보여요. 옛날 Spring은 톰캣 연결, DataSource, 메시지 컨버터를 XML에 일일이 적었거든요. Boot는 그걸 다 지워버렸는데, 지운 설정이 "사라진" 게 아니라 "보이지 않는 곳에서 자동으로 채워진" 거라 더 헷갈려요.

둘째, "그럼 내가 직접 설정하면 충돌 나는 거 아냐?" 라는 불안. 결론부터 말하면 안 나요. 자동 구성은 항상 내가 만든 걸 먼저 존중하거든요. 이게 핵심인데, 뒤에서 @ConditionalOnMissingBean으로 풀게요.

비유 한 줄 — 자동 구성은 신입사원 책상 세팅 같은 거예요. 입사하면(=의존성을 추가하면) 총무팀이 알아서 책상·노트북·사번을 깔아 둬요. 단, 내가 노트북을 직접 들고 왔으면 총무팀은 그건 안 건드리고 비켜 줍니다. 이 글은 그 "총무팀"이 정확히 누구이고 무슨 규칙으로 움직이는지를 풀어 가요.

@SpringBootApplication 한 줄에 숨은 세 가지

12편 메인 클래스 기억나죠. 딱 이 한 줄이 출발점이에요.

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

@SpringBootApplication은 사실 어노테이션 세 개를 합친 합성 어노테이션이에요. 소스를 열어 보면 이렇게 생겼어요.

@SpringBootConfiguration   // 사실상 @Configuration — 이 클래스도 설정 클래스
@ComponentScan             // 이 패키지부터 아래로 @Component 다 찾아 등록
@EnableAutoConfiguration   // 나머지는 알아서 채워 줘
public @interface SpringBootApplication { ... }

19편 @Configuration·@Bean18편 @Component·@Autowired에서 다룬 게 앞의 둘이에요. @ComponentScan은 내가 짠 코드(컨트롤러·서비스·리포지토리)를 훑어 컨테이너에 올리는 역할이고요. 그러니까 내 코드를 등록하는 건 @ComponentScan, 나머지 인프라를 채우는 건 @EnableAutoConfiguration — 이 분업이 자동 구성을 이해하는 첫 갈림길이에요.

여기서 시험 함정이 하나 있어요. @ComponentScan메인 클래스가 놓인 패키지부터 그 하위만 훑어요. 그래서 컨트롤러를 메인 클래스보다 바깥 패키지에 두면 "분명 @RestController 붙였는데 404가 뜨는" 상황이 생깁니다. 자동 구성 문제가 아니라 스캔 범위 문제예요.

자동 구성 후보는 어디서 오나 — AutoConfiguration.imports

@EnableAutoConfiguration이 켜지면 Boot가 가장 먼저 하는 일은 후보 목록 읽기예요. 클래스패스에 깔린 모든 라이브러리(jar) 안을 뒤져서 이 파일을 찾아요.

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

이름이 길죠. 예전 Boot 2.6 이하에서는 META-INF/spring.factories라는 파일에 적혀 있었는데, 2.7부터 위 .imports 파일로 바뀌었어요. 둘 다 하는 일은 똑같아요 — "이 jar에는 이런 자동 구성 클래스들이 들어 있습니다" 라고 적어 둔 명단이에요. 한 줄에 클래스 하나씩.

org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
... (spring-boot-autoconfigure jar 하나에만 수백 개)

Boot는 클래스패스에 있는 모든 jar의 이 명단을 싹 모아요. 그러면 후보 자동 구성 클래스가 수백 개 쌓여요.

여기까지 따라오셨다면 한 가지 의문이 들 거예요. "후보가 수백 개면, 그게 다 적용돼서 안 쓰는 Bean까지 잔뜩 생기는 거 아냐?" 아니에요. 후보일 뿐이고, 실제로 켜지는 건 조건을 통과한 것만입니다. 그 조건이 다음 이야기예요.

@Conditional — 조건이 맞을 때만 켜진다

자동 구성 클래스 하나하나에는 "이런 상황일 때만 나를 적용해" 라는 조건표가 붙어 있어요. 이게 @Conditional 계열 어노테이션이에요. 자주 보는 둘만 짚으면 충분해요.

  • @ConditionalOnClass(...) — 클래스패스에 특정 클래스가 있을 때만 켜져요. 그 라이브러리를 안 썼으면 조용히 건너뜀.
  • @ConditionalOnMissingBean(...) — 같은 타입의 Bean을 내가 직접 안 만들었을 때만 켜져요. 내가 만들었으면 비켜 줌.

실제 DataSourceAutoConfiguration을 단순화하면 이런 모양이에요.

@AutoConfiguration
@ConditionalOnClass(DataSource.class)   // JDBC 의존성이 있을 때만
public class DataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean             // 내가 DataSource를 안 만들었을 때만
    public DataSource dataSource(...) {
        // HikariCP 같은 기본 커넥션 풀로 DataSource 생성
    }
}

읽어 보면 규칙이 그대로 보여요. JDBC 관련 클래스가 클래스패스에 있고(=spring-boot-starter-data-jpa 같은 걸 추가했고), 내가 DataSource를 직접 정의하지 않았을 때만 Boot가 기본 DataSource를 만들어 줘요. 둘 중 하나라도 어긋나면 이 자동 구성은 켜지지 않아요.

💡 시험 핵심

@ConditionalOnMissingBean 한 줄이 "내가 만들면 내 게 이긴다"는 규칙의 정체예요. 자동 구성은 언제나 사용자 정의에 양보합니다. 그래서 첫 단락의 "내가 직접 설정하면 충돌 나나?" 걱정은 안 해도 돼요 — Boot가 알아서 물러나요.

스타터 하나가 자동 구성을 통째로 켠다

그럼 클래스패스에 라이브러리를 어떻게 깔길래 조건이 맞춰지는 걸까요. 답이 스타터(starter)예요. spring-boot-starter-web 한 줄을 build.gradle에 적으면, 그 안에 톰캣·Spring MVC·Jackson이 한 묶음으로 딸려 와요. 라이브러리가 깔리니까 @ConditionalOnClass 조건이 줄줄이 만족되고, 관련 자동 구성이 도미노처럼 켜지는 거예요.

스타터는 직접 코드를 갖고 있지 않아요. "이 기능을 쓰려면 필요한 라이브러리들" 을 한데 묶어 둔 의존성 꾸러미일 뿐이에요. 그래서 스타터 하나 추가 = "이 기능 쓸 거니까 관련 자동 구성 다 켜 줘"라는 신호가 됩니다.

스타터깔리는 것자동으로 켜지는 자동 구성(예시)
spring-boot-starter-web톰캣, Spring MVC, JacksonDispatcherServlet·내장 톰캣·JSON 메시지 컨버터
spring-boot-starter-data-jpaHibernate, Spring Data JPADataSource·EntityManager·트랜잭션 매니저
spring-boot-starter-securitySpring Security기본 로그인 폼·시큐리티 필터 체인

12편에서 의존성으로 골랐던 Spring Web이 바로 이 starter-web이에요. 그거 하나 체크한 덕분에 톰캣이 8080에 떴던 거죠. 마법이 아니라 조건이 맞아서 켜진 자동 구성이었어요.

자동 구성을 눈으로 확인하는 법

여기까지 읽고 나면 자연스레 궁금해져요. "그래서 지금 내 앱에서 뭐가 켜졌고 뭐가 안 켜졌는데?" Boot는 이걸 보여주는 리포트를 갖고 있어요. application.yml에 한 줄만 켜면 돼요.

debug: true

또는 실행할 때 --debug 플래그를 붙여도 같아요.

java -jar my-shop.jar --debug

그러면 부팅 로그에 ConditionEvaluationReport(조건 평가 리포트)가 찍혀요. 모양이 이래요.

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

Positive matches:        ← 조건을 통과해 "켜진" 자동 구성
-----------------
   DispatcherServletAutoConfiguration matched:
      - @ConditionalOnClass found required class 'DispatcherServlet'

Negative matches:        ← 조건이 안 맞아 "꺼진" 자동 구성
-----------------
   DataSourceAutoConfiguration:
      - @ConditionalOnClass did not find required class 'javax.sql.DataSource'

Positive matches는 켜진 것, Negative matches는 안 켜진 것과 왜 안 켜졌는지 이유까지 보여줘요. "DB 설정을 했는데 DataSource가 안 만들어진다" 싶을 때 이 리포트를 보면 "JDBC 의존성을 안 넣어서 조건 자체가 불발됐다" 가 한눈에 잡혀요. 디버깅의 출발점이에요.

57편 Actuator를 붙였다면 /actuator/conditions 엔드포인트로 같은 정보를 JSON으로도 받을 수 있어요. 로그를 뒤지는 것보다 깔끔하죠.

자주 막히는 함정

입문자가 자동 구성 때문에 실제로 자주 멈추는 지점 세 개만 정리할게요.

컴포넌트가 안 잡혀요. 90%는 메인 클래스보다 바깥 패키지에 클래스를 둔 경우예요. @ComponentScan은 메인 클래스 패키지 하위만 본다는 걸 떠올리면 돼요. 메인 클래스를 최상위 패키지에 두는 게 안전합니다.

특정 자동 구성을 끄고 싶어요. 기본 DataSource 같은 걸 내 손으로만 관리하고 싶을 때가 있어요. 그럴 땐 명시적으로 제외해요.

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MyShopApplication { ... }

DataSource를 직접 만들었더니 둘이 충돌하는 것 같아요. 사실 충돌은 안 나요. 내가 만든 순간 @ConditionalOnMissingBean이 자동 구성을 꺼버리니까. 다만 DataSource를 여러 개 직접 만들면 Boot가 "어느 걸 기본으로?" 하고 헷갈려해요. 그땐 하나에 @Primary를 붙여 주면 됩니다.

시험·면접 직전 압축 노트 — 자동 구성

  • 자동 구성 = "설정 안 써도 Boot가 알아서 인프라 Bean을 채워 주는" 메커니즘
  • 시작점은 @SpringBootApplication 한 줄
  • @SpringBootApplication = @SpringBootConfiguration + @ComponentScan + @EnableAutoConfiguration
  • @ComponentScan = 내 코드(컨트롤러·서비스) 등록 / @EnableAutoConfiguration = 나머지 인프라 채움
  • @ComponentScan메인 클래스 패키지 하위만 스캔 — 바깥에 두면 안 잡힘
  • 자동 구성 후보 명단 = jar 안의 META-INF/spring/...AutoConfiguration.imports
  • Boot 2.7 이전엔 META-INF/spring.factories에 적혀 있었음 (하는 일은 동일)
  • 후보는 수백 개지만 조건을 통과한 것만 실제로 켜짐
  • @ConditionalOnClass = 클래스패스에 특정 클래스 있을 때만 켜짐
  • @ConditionalOnMissingBean = 내가 같은 Bean을 안 만들었을 때만 켜짐
  • 핵심 한 줄 — 내가 만들면 내 Bean이 이긴다 (자동 구성은 사용자 정의에 양보)
  • 스타터(starter) = 코드가 아니라 의존성 묶음 — 라이브러리를 깔아 조건을 충족시킴
  • starter-web 하나가 톰캣·MVC·Jackson 자동 구성을 통째로 켬
  • 켜진/꺼진 자동 구성 확인 = application.ymldebug: true 또는 --debug
  • 리포트의 Positive matches = 켜진 것 / Negative matches = 안 켜진 것 + 이유
  • Actuator 붙이면 /actuator/conditions로 같은 정보 JSON 확인 가능
  • 특정 자동 구성 끄기 = @SpringBootApplication(exclude = { ... })
  • DataSource 여러 개 직접 정의 시 하나에 @Primary 지정

공식 문서: Spring Boot — Auto-configurationCondition Annotations에서 더 깊은 사양을 확인할 수 있어요.

시리즈 다른 편

개념상 이 자리 (12편과 13편 사이):

같이 읽으면 좋은 글:

답글 남기기

error: Content is protected !!