[Spring] 스프링 부트 내장 톰캣

easyone·2024년 10월 2일
0

Spring

목록 보기
10/11

WAR 배포 방식의 단점

  • 톰캣 같은 애플리케이션 서버(WAS)를 별도로 설치해야 한다.
  • 애플리케이션 코드를 WAS로 빌드해야 하며, 빌드한 파일을 WAS에 배포해야 한다.
  • 톰캣의 버전을 변경하려면 톰캣을 다시 설치해야 한다.

내장 서버와 외장 서버

이전에는 웹 애플리케이션 서버에 WAR 파일을 배포하는 방식이었는데, WAR 배포 방식의 단점을 해결하기 위해 내장 톰캣 기능을 제공한다.

내장 톰캣

public class EmbedTomcatServletMain  {
    public static void main(String[] args) throws LifecycleException {
        System.out.println("EmbedTomcatServletMain.main");
        
        // 톰캣 설정
        Tomcat tomcat = new Tomcat();
        Connector connector = new Connector();
        connector.setPort(8080);
        tomcat.setConnector(connector);

        //서블릿 등록
        Context context = tomcat.addContext("", "/");
        tomcat.addServlet("", "helloServlet", new HelloServlet());
        context.addServletMappingDecoded("/hello-servlet", "helloServlet");
        tomcat.start();
    }
  • 톰캣 설정: 내장 톰캣을 생성하고 커넥터를 사용해서 8080 포트에 연결한다.
  • 서블릿 등록: addServlet을 통해 서블릿을 등록하고, 서블릿 경로를 매핑한다.
    -> 내장 톰캣을 사용해서 복잡한 설정 없이, 톰캐 설치도 없이 메인 메서드만 실행하면 톰캣이 편리하게 실행된다.

내장 톰캣에 스프링 컨테이너 등록


 // 톰캣 설정
        Tomcat tomcat = new Tomcat();
        Connector connector = new Connector();
        connector.setPort(8080);
        tomcat.setConnector(connector);

        // 스프링 컨테이너 생성
        AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
        appContext.register(HelloConfig.class);

        // 스프링 MVC 디스패처 서블릿 생성, 스프링 컨테이너 연결
        DispatcherServlet dispatcher = new DispatcherServlet(appContext);

        // 디스패처 서블릿 등록
        Context context = tomcat.addContext("", "/");
        tomcat.addServlet("", "dispatcher", dispatcher);

        tomcat.start();

서블릿 컨테이너 초기화와 거의 유사한데, 메인 메서드를 직접 실행하는지 서블릿 컨테이너가 제공하는 초기화 메서드를 통해서 실행하는지의 차이점만 있다.

내장 톰캣 빌드 및 배포

자바의 메인 메서드를 실행해서 웹 애플리케이현을 실행하려면 jar 형식으로 빌드해야 하고, jar 안에는 'META-INF/MANIFEST.MF'파일에 실행할 메인 메서드의 클래스를 지정해줘야 한다.
build.gradle에 아래와 같이 설정을 해두면 gradle이 빌드 시 MANIFEST 파일을 자동으로 생성해준다.

//일반 Jar 생성
task buildJar(type: Jar) {
    manifest {
        attributes 'Main-Class': 'hello.embed.EmbedTomcatSpringMain'
    }
    with jar
}

그런데 jar 파일을 압축을 풀어서 보면, 라이브러리 역할을 하는 jar 파일이 없는 것을 알 수 있다. war 파일의 경우 lib 폴더에 라이브러리 역할을 하는 jar 파일이 포함되어 있었다. war는 was 위에서만 실행할 수 있으므로 war를 사용할수도 없기 때문에, 대안으로 FatJar를 사용한다.

FetJar

jar 안에는 jar를 포함할 수 없지만, 클래스를 포함할 수는 있기 때문에 라이브러리에 사용되는 jar의 압축을 풀어서 나오는 class를 jar 파일에 포함시키는 것이다.

//Fat Jar 생성
task buildFatJar(type: Jar) {
    manifest {
        attributes 'Main-Class': 'hello.embed.EmbedTomcatSpringMain' 
    }
    duplicatesStrategy = DuplicatesStrategy.WARN
    from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } // 라이브러리 jar 파일에서 class 추출
    with jar
}

장점

  • 하나의 jar 파일에 필요한 라이브러리의 클래스 파일들을 내장할 수 있게 되었다.
  • 내장 톰캣 라이브러리도 jar 내부에 내장이 가능하다.
  • jar 파일을 만들고 원하는 위치에서 실행만 하면 되기 때문에 배포 과정이 단순하다.
  • gradle에서 내장 톰캣 라이브러리의 버전만 변경하고, 빌드 후에 실행하면 된다.

단점

  • 모두 class로 풀려있기 때문에 어떤 라이브러리가 포함되어 있는지 추적하기 어렵다.
  • 클래스나 리소스명이 같은 경우 심각한 문제가 발생할 수 있다. 두 라이브러리가 서블릿 컨테이너 초기화를 시도할 경우, 둘다 해당 파일을 jar 안에 포함하면 파일명이 같기 때문에 둘 중 하나만 실행이 되는 것이다.

스프링 부트 실행 가능 Jar

FetJar의 단점을 해결하기 위해, 스프링 부트에서는 jar 내부에 jar를 포함할 수 있는 특별한 구조의 jar를 생성한다. 그리고 동시에 만든 jar를 내부 jar를 포함해서 실행할 수 있게 했다.
jar 내부에 또 jar를 포함하고 있기 때문에, 어떤 라이브러리가 포함되어 있는지 쉽게 확인할 수 있다.
a.jar, b.jar 와 같이 내부에 jar를 포함하므로 같은 경로의 파일이 있어도 둘 다 인식할 수 있다.

JarLuncher
실행 가능 Jar 내부에서는 메인메서드가 있는 BootAplication 클래스가 아니라 JarLuncher라는 전혀 다른 클래스를 실행한다. jar 내부에 jar를 읽어들이고, 특별한 구조에 맞게 클래스 정보를 읽어들이는 작업을 처리해준다. 처리 후에 Start-Class에 지정된 메인 메서드를 호출한다.

실행 과정 정리

  • java -jar .xxx.jar
  • MANIFEST.MF 인식
  • JarLauncher.main() 실행
  • Start-Class에 지정된 메인 메서드 실행
profile
백엔드 개발자 지망 대학생

0개의 댓글