스프링 부트(핵심 원리와 활용)

Ch02. 스프링 부트와 내장 톰캣 - 스프링 부트와 웹 서버

webmaster 2023. 3. 5. 14:33
728x90

스프링 부트는 지금까지 고민한 문제를 깔끔하게 해결해 준다.

  • 내장 톰캣을 사용해서 빌드와 배포를 편리하게 한다.
  • 빌드시 하나의 Jar를 사용하면서, 동시에 Fat Jar 문제도 해결한다.
  • 지금까지 진행한 내장 톰캣 서버를 실행하기 위한 복잡한 과정을 모두 자동으로 처리한다.

프로젝트 생성

build.gradle

plugins {
   id 'java'
   id 'org.springframework.boot' version '3.0.2'
   id 'io.spring.dependency-management' version '1.1.0'
}

group = 'hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
   mavenCentral()
}

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-web'
   testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
   useJUnitPlatform()
}
  • 스프링 부트를 사용하면 라이브러리 뒤에 버전 정보가 없는 것을 확인할 수 있다.
  • 스프링 부트는 현재 부트 버전에 가장 적절한 외부 라이브러리 버전을 자동으로 선택해준다. (이 부분에 대한 자세한 내용은 뒤에서 다룬다.)

HelloController

@RestController
public class HelloController {
  @GetMapping("hello-spring")
  public String hell(){
    System.out.println("HelloController.hell");
    return "hello spring!";
  }
}

내장 톰켓 의존성 확인

tomcat 의존성
gradle에 추가되어 있는 의존성

  • 라이브러리 의존관계를 따라가보면 내장 톰캣( tomcat-embed-core )이 포함된 것을 확인할 수 있다.

실행 과정

@SpringBootApplication
public class BootApplication {

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

}
  • 스프링 부트를 실행할 때는 자바 main() 메서드에서 SpringApplication.run() 을 호출해주면 된다.
  • 여기에 메인 설정 정보를 넘겨주는데, 보통 @SpringBootApplication 애노테이션이 있는 현재 클래스를 지정해주면 된다.
  • 참고로 현재 클래스에는 @SpringBootApplication 애노테이션이 있는데, 이 애노테이션 안에는 컴포넌트 스캔을 포함한 여러 기능이 설정되어 있다. 기본 설정은 현재 패키지와 그 하위 패키지 모두를 컴포넌트 스캔한다.

"SpringApplication.run(BootApplication.class, args);" 한 줄에 코드에 수많은 일들이 동작하지만 핵심은 크게 2가지이다.

스프링 컨테이너를 생성한다.

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContextFactory
private ConfigurableApplicationContext createContext() {
 if (!AotDetector.useGeneratedArtifacts()) {
  return new AnnotationConfigServletWebServerApplicationContext();
 }
 return new ServletWebServerApplicationContext();
}
  • new AnnotationConfigServletWebServerApplicationContext() 이 부분이 바로 스프링 부트가 생성하는 스프링 컨테이너이다.
  • 이름 그대로 애노테이션 기반 설정이 가능하고, 서블릿 웹 서버를 지원하는 스프링 컨테이너이다.

WAS(내장 톰캣)를 생성한다

org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
 if (this.disableMBeanRegistry) {
  Registry.disableRegistry();
 }
 Tomcat tomcat = new Tomcat();
 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
 tomcat.setBaseDir(baseDir.getAbsolutePath());
 for (LifecycleListener listener : this.serverLifecycleListeners) {
  tomcat.getServer().addLifecycleListener(listener);
 }
 Connector connector = new Connector(this.protocol);
 connector.setThrowOnFailure(true);
 tomcat.getService().addConnector(connector);
 customizeConnector(connector);
 tomcat.setConnector(connector);
 tomcat.getHost().setAutoDeploy(false);
 configureEngine(tomcat.getEngine());
 for (Connector additionalConnector : this.additionalTomcatConnectors) {
  tomcat.getService().addConnector(additionalConnector);
 }
 prepareContext(tomcat.getHost(), initializers);
 return getTomcatWebServer(tomcat);
}
  • Tomcat tomcat = new Tomcat() 으로 내장 톰캣을 생성한다.

스프링 부트도 우리가 앞서 내장 톰캣에서 진행했던 것과 동일한 방식으로 스프링 컨테이너를 만들고, 내장 톰캣을 생성하고 그 둘을 연결하는 과정을 진행한다.

참고

스프링 부트는 너무 큰 라이브러리이기 때문에 스프링 부트를 이해하기 위해 모든 코드를 하나하나 파보는 것은 추천하지 않는다.

스프링 부트가 어떤 식으로 동작하는지 개념을 이해하고, 꼭 필요한 부분의 코드를 확인하자.

빌드와 배포

jar 빌드

./gradlew clean build

build/libs/boot-0.0.1-SNAPSHOT.jar 실행

java -jar boot-0.0.1-SNAPSHOT.jar

스프링 부트 jar 분석

boot-0.0.1-SNAPSHOT.jar 파일 크기를 보면 대략 18M 정도 된다. 참고로 버전에 따라서 용량은 변할 수 있다.

FatJar과 비슷한 방식으로 만들어진 것으로 생각되니 jarv파일 압축을 풀어보자

jar 압축 풀기

jar -xvf boot-0.0.1-SNAPSHOT.jar

JAR를 푼 결과

JAR 를 푼 결과

  • JAR를 푼 결과를 보면 Fat Jar가 아니라 처음보는 새로운 구조로 만들어져 있다.
  • 심지어 jar 내부에 jar를 담아서 인식하는 것이 불가능한데, jar가 포함되어 있고, 인식까지 되었다.

참고

빌드 결과를 보면 boot-0.0.1-SNAPSHOT-plain.jar 파일도 보이는데, 이것은 우리가 개발한 코드만 순수한 jar로 빌드한 것이다. 무시하면 된다.

728x90