새소식

Springboot

[Springboot] Springboot의 내부 작동 원리(With Log)

  • -

서론

Spring Boot 애플리케이션을 시작할 때 작성하는 단 한 줄의 코드, SpringApplication.run(MyApplication.class, args)에는 사실 복잡하고 체계적인 과정들이 숨겨져 있다. 이 글에서는 이 한 줄의 코드가 어떻게 완전한 애플리케이션으로 변하는지 단계별로 살펴본다.
Spring Boot 애플리케이션을 시작하면 다음과 같은 로그가 출력된다:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.4.5)

2025-05-10T18:48:00.456+09:00  INFO 29464 --- [test] [  restartedMain] com.test.test.TestApplication            : Starting TestApplication using Java 17.0.11 with PID 29464 (A:\Spring\test\build\classes\java\main started by ... in A:\Spring\test)
2025-05-10T18:48:00.458+09:00  INFO 29464 --- [test] [  restartedMain] com.test.test.TestApplication            : No active profile set, falling back to 1 default profile: "default"

이 로그에서 확인할 수 있듯이, Spring Boot 애플리케이션이 시작되면 가장 먼저 Spring Boot 배너가 출력되고, 애플리케이션 시작 정보(Java 버전, PID, 실행 경로 등)와 활성화된 프로파일 정보가 표시된다.

 

1. SpringApplication 객체 생성

애플리케이션 시작 과정의 첫 단계는 SpringApplication 객체를 생성하는 것이다. 이 객체는 전체 애플리케이션의 구동 과정을 조율하는 지휘자 역할을 한다.

세부 과정

  • SpringApplication.run() 메서드 호출: 내부적으로 new SpringApplication(...) 코드가 실행된다.
  • 실행 환경(WebApplicationType) 판단: 클래스패스를 분석하여 애플리케이션이 웹 환경(Servlet 또는 Reactive)인지, 비-웹 환경(NONE)인지를 자동으로 감지한다.
  • ApplicationContext 타입 결정: 결정된 환경에 따라 적절한 ApplicationContext 구현체 타입을 선택한다.
  • 초기 리스너(ApplicationListener) 등록: 애플리케이션 구동 초기 단계에 필요한 내/외부 이벤트 리스너들을 등록한다.

💡 알아두면 좋은 점: ApplicationContext는 Spring Framework의 핵심 컨테이너이며, Bean 객체들을 생성, 관리, 주입하고 생명주기를 관리하는 중심 시스템이다. Spring 애플리케이션의 IoC(Inversion of Control)와 DI(Dependency Injection)를 실제로 실행하는 주체이다.

2025-05-10T18:48:00.456+09:00  INFO 29464 --- [test] [  restartedMain] com.test.test.TestApplication            : Starting TestApplication using Java 17.0.11 with PID 29464 (A:\Spring\test\build\classes\java\main started by ... in A:\Spring\test)
2025-05-10T18:48:00.458+09:00  INFO 29464 --- [test] [  restartedMain] com.test.test.TestApplication            : No active profile set, falling back to 1 default profile: "default"

이 단계에서 애플리케이션 이름, Java 버전, 프로세스 ID, 실행 경로 등의 기본 정보와 활성화된 프로파일 정보가 로그에 출력된다.
여기서 "restartedMain" 스레드는 메인 애플리케이션 스레드로, Spring Boot DevTools가 활성화된 경우 재시작 기능을 지원하기 위해 이런 이름을 사용한다.

 

2. 리소스 초기화 및 실행 준비

두 번째 단계에서는 ApplicationContext를 구성하기 전에 애플리케이션 실행에 필요한 기본 자원과 환경 정보를 준비한다.

세부 과정

  • Banner 출력: 콘솔에 출력되는 Spring Boot의 시작 배너를 출력한다.
  • 환경 정보(Environment) 생성: application.properties, 시스템 환경 변수, 커맨드라인 인자 등 다양한 소스의 설정 값을 통합하는 Environment 객체를 생성한다.
  • 커맨드라인 파라미터 바인딩: 실행 시 전달된 -key=value 형태의 인자를 파싱하여 Environment에 추가한다.
  • ApplicationContext 생성: 첫 단계에서 결정된 타입의 ApplicationContext 객체를 인스턴스화한다. 이 시점에서는 빈이 등록되지 않은 빈 컨테이너만 존재한다.
    2025-05-10T18:48:00.499+09:00  INFO 29464 --- [test] [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
    2025-05-10T18:48:00.499+09:00  INFO 29464 --- [test] [  restartedMain] .s.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
    2025-05-10T18:48:00.499+09:00  INFO 29464 --- [test] [  restartedMain] .s.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
    이 단계의 로그에서는 DevTools 관련 설정 메시지와 기타 Spring 모듈(예: Spring Data JPA)의 초기화 관련 로그가 출력된다. 개발자 도구 속성 기본값이 활성화되었다는 메시지와 추가 웹 관련 로깅을 위한 팁이 표시된다.

 

3. Context 구성 및 Bean 등록

세 번째 단계는 Spring IoC 컨테이너의 핵심 작업이 이루어지는 부분으로, 생성된 ApplicationContext에 애플리케이션의 모든 컴포넌트(Bean)를 등록하고 설정한다.

세부 과정

  • @ComponentScan 처리: @SpringBootApplication이 있는 기본 패키지부터 스캔하여 @Component, @Service, @Repository, @Controller 등의 어노테이션이 붙은 클래스를 찾아 빈으로 등록한다.
  • @Configuration 처리: @Configuration 클래스를 찾아내고, 그 안의 @Bean 메서드를 실행하여 반환되는 객체들을 빈으로 등록한다.
  • @EnableAutoConfiguration 처리: 클래스패스에 있는 라이브러리와 환경 설정을 기반으로 Spring Boot가 자동으로 필요한 설정(웹 서버, 데이터베이스, JPA 등)을 수행하고 관련 빈들을 등록한다.
  • 다른 @Enable 어노테이션 처리: @EnableCaching, @EnableScheduling 등 다양한 기능을 활성화하는 어노테이션에 따라 관련 빈들이 등록된다.
  • Context Refresh: 모든 빈 정의가 등록되면 컨텍스트를 새로고침한다. 이 단계에서 빈 객체가 실제로 생성되고, 의존성 주입이 이루어지며, @PostConstruct 같은 초기화 메서드가 실행된다.
2025-05-10T18:48:01.097+09:00  INFO 29464 --- [test] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2025-05-10T18:48:01.107+09:00  INFO 29464 --- [test] [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2025-05-10T18:48:01.107+09:00  INFO 29464 --- [test] [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.40]
2025-05-10T18:48:01.142+09:00  INFO 29464 --- [test] [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2025-05-10T18:48:01.143+09:00  INFO 29464 --- [test] [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 652 ms
2025-05-10T18:48:01.368+09:00  INFO 29464 --- [test] [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729

이 단계의 로그에서는 내장 Tomcat 서버의 초기화, 서블릿 엔진 시작, 웹 애플리케이션 컨텍스트 초기화 등의 메시지가 출력된다.
특히 Root WebApplicationContext: initialization completed in 652 ms 메시지는 메인 애플리케이션 컨텍스트 초기화가 완료되었음을 나타내며, 이때 모든 빈이 생성되고 의존성이 주입된다.
개발 모드에서는 코드 변경 시 자동으로 애플리케이션을 재시작해주는 LiveReload 서버도 실행된다.

 

4. ApplicationRunner / CommandLineRunner 실행

네 번째 단계에서는 ApplicationContext가 완전히 로드된 후 실행되어야 하는 사용자 정의 초기화 로직을 수행한다.
해당 부분은 사용자가 별도로 log를 남기지 않는다면 별도 출력이 없다.

세부 과정

  • 컨텍스트에서 ApplicationRunner 또는 CommandLineRunner 인터페이스를 구현한 빈들을 찾는다.
  • 찾아낸 Runner 빈들의 run() 메서드를 실행한다.
  • 여러 Runner 빈이 있다면 @Order 등으로 실행 순서를 제어할 수 있다.

💡 팁: 애플리케이션 시작 시 특정 초기화 작업이 필요하다면 CommandLineRunner나 ApplicationRunner를 구현하는 것이 좋다. 데이터베이스 초기 데이터 로드나 외부 시스템 연결 체크 등의 작업에 유용하다.

 

5. 내장 서버 실행 (웹 애플리케이션인 경우)

웹 애플리케이션인 경우, 외부의 HTTP 요청을 수신하기 위해 내장 웹 서버를 시작한다.

세부 과정

  • if(첫 단계에서 WebApplicationType이 SERVLET 또는 REACTIVE로 판단된 경우)에만 실행된다.
  • 클래스패스에 있는 내장 서버 라이브러리(Tomcat, Jetty, Undertow, Netty 등)를 감지하고, 웹 관련 ApplicationContext를 사용하여 서버를 설정하고 구동한다.
  • 서버는 지정된 포트에서 요청을 대기하며, 수신된 요청은 ApplicationContext의 컨트롤러 등으로 전달된다.
2025-05-10T18:48:01.394+09:00  INFO 29464 --- [test] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''

이 단계의 로그에서는 내장 Tomcat 서버가 지정된 포트(8080)에서 시작되었음을 확인할 수 있다. 여기서 context path ''는 루트 경로('/')에서 애플리케이션이 제공됨을 의미한다.

[브라우저] ──HTTP 요청──▶ [Tomcat (내장 서버)]
                       └─▶ DispatcherServlet (Spring)
                             └─▶ Controller, Service, Repository 등
                       ◀── HTTP 응답 ──┘

 

6. 완전한 ApplicationContext 반환 및 실행 완료

마지막 단계에서는 애플리케이션 시작 과정이 성공적으로 완료되었음을 알리고, 구동된 ApplicationContext 객체를 반환한다.

세부 과정

  • SpringApplication.run() 메서드가 모든 과정이 완료된 ApplicationContext 객체를 반환한다.
  • 웹 애플리케이션의 경우, 내장 서버가 백그라운드에서 실행을 유지하므로 main 스레드는 대기 상태로 들어간다.
  • 비-웹 애플리케이션의 경우, Runner 실행 후 main 메서드가 종료될 수 있다.
  • 애플리케이션 시작 완료를 알리는 이벤트가 발행된다.
2025-05-10T18:48:01.422+09:00  INFO 29464 --- [test] [  restartedMain] com.test.test.TestApplication            : Started TestApplication in 1.225 seconds (process running for 1.883)

이 단계의 로그에서는 애플리케이션이 성공적으로 시작되었음을 알리는 메시지가 출력된다.
여기서 "1.225 seconds"는 애플리케이션 초기화에 걸린 시간이고, "process running for 1.883"은 JVM 프로세스가 시작된 후 경과한 시간을 의미한다.

 

종합적인 시작 로그 분석

Spring Boot 애플리케이션의 전체 시작 로그를 시간순으로 정리하면 앞서 설명한 6단계 과정이 잘 드러난다:

이 로그를 통해 다음과 같은 주요 단계들을 확인할 수 있다:

  • Spring Boot 배너 출력과 애플리케이션 시작 알림 (1단계)
  • 환경 설정 및 프로파일 정보 로드 (2단계)
  • DevTools 설정과 개발 환경 준비 (2단계)
  • 내장 Tomcat 서버 초기화 및 웹 애플리케이션 컨텍스트 준비 (3단계)
  • 웹 애플리케이션 컨텍스트 초기화 완료 (3단계)
  • LiveReload 서버 실행 (개발 모드에서 코드 변경 감지용) (3단계)
  • Tomcat 서버 시작 완료 (5단계)
  • 애플리케이션 시작 완료 알림 (6단계)

이런 내부 과정을 이해하는 것은 문제 상황에서 디버깅하거나, Spring Boot의 다양한 커스터마이징 포인트를 활용하는 데 큰 도움이 된다. 앞으로 Spring Boot 애플리케이션을 개발할 때, 이 글에서 설명한 시작 과정의 각 단계를 떠올려 보면 좋다.
다음 포스팅에서는 Spring Boot의 자동 구성(Auto-Configuration) 메커니즘에 대해 더 자세히 다룰 예정이다.

'Springboot' 카테고리의 다른 글

[Spring] 서블릿과 톰캣의 내부 구조  (0) 2025.05.17
백엔드를 시작하며...  (0) 2024.07.08
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.