본문 바로가기
스프링 관련/WAS-Tomcat

내장 톰캣

by 문자메일 2023. 3. 28.
dependencies {
    //스프링 MVC 추가
    implementation 'org.springframework:spring-webmvc:6.0.4'

    //내장 톰켓 추가
    implementation 'org.apache.tomcat.embed:tomcat-embed-core:10.1.5'
}

위 dependency는 실습 예제 환경이고, 아래 라이브러리를 추가하면 사용할 수 있다.

implementation 'org.apache.tomcat.embed:tomcat-embed-core:10.1.5'

 

또한 아래 톰캣 라이브러리(jar) 구성항목 이미지를 보면 톰캣 라이브러리를 포함한 org 패키지 뿐만 아니라, 서블릿 라이브러리 포함한 jakarta 패키지를 포함하고 있어서 아래 서블릿 dependency를 따로 import 하지 않아도 서블릿 기능을 사용할 수 있는 것을 확인할 수 있다.

//서블릿
implementation 'jakarta.servlet:jakarta.servlet-api:6.0.0'

 

 

내장 톰캣은 쉽게 이야기해서 톰캣을 라이브러리로 포함하고, 자바 코드로 직접 실행하는 것이다.

 

다른 블로그에 있는 톰캣 구조 이미지

https://exhibitlove.tistory.com/312

package hello.embed;

import hello.servlet.HelloServlet;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;

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(9090);

        tomcat.setConnector(connector);

        // 서블릿 등록
        Context context = tomcat.addContext("", "/");
        tomcat.addServlet("", "helloServlet", new HelloServlet());
        context.addServletMappingDecoded("/hello-servlet", "helloServlet");
        tomcat.start();

    }
}

톰켓 설정

- 위 코드는 내장 톰캣을 생성하고, 톰캣이 제공하는 커넥터 사용해서 8080 포트로 연결한다.

서블릿 등록

- 톰켓에서 사용할 contextPath와 docBase 지정 필요

  (contextPath는 호스트 접근하는 URL으로 생각하면 되고, Host의 appBase + Context의 docBase를 합쳐서 해당 컨텍스트에서 사용할 소스나 파일 등 물리적인 폴더 path를 지정)

- tomcat.addServlet() 을 통해서 서블릿 등록

- context.addServletMappingDecode()를 통해서 등록한 서블릿의 경로 매핑

 

 

아래 블로그에 appBase, docBase, contextPath 이 의미하는게 무엇인지 잘 설명되어 있음.

https://kingofbackend.tistory.com/153

 

[Tomcat] server.xml 에서 appBase, docBase 설정 방법

서버는 CentOS7버전이고, 톰캣 8버전입니다. 젠킨스에 올라가 있는 프로젝트의 server.xml 중 정보는 아래 코드와 같습니다. 젠킨스에서 빌드 구성을 시작하면, 자동으로 젠킨스가 프로젝트.war 파일

kingofbackend.tistory.com

 

도메인이란? ip 주소와 매핑되는 URL 주소 (ex: www.naver.com)

가상호스트란? 기본적으로 웹 서버에 주 호스트 도메인을 등록하여 사용하는데, 추가적으로 한 서버에 여러 도메인을 등록하여 사용한다는 의미

Apache에서는 두 가지 방식을 제공함.

 1. Name-based Virtual Host

 2. IP-based Virtual Host

 

https://rma7.tistory.com/73

 

[Network] Virtual Host(가상 호스트)란?

가상 호스트란? 기본적으로 웹서버에 존재하는 주 호스트 도메인을 등록하여 사용하고 있지만 추가적으로 한 서버에 여러 도메인을 등록하여 사용한다는 의미이다. 가상 호스트 서버를 이용하

rma7.tistory.com

 

 

 

내장 톰캣3 - 스프링 부분 예제 코드

package hello.embed;

import hello.spring.HelloConfig;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class EmbedTomcatSpringMain {

    public static void main(String[] args) throws LifecycleException{
        System.out.println("EmbedTomcatSpringMain.main");

        // 톰캣 설정
        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);
        context.addServletMappingDecoded("/", "dispatcher");

        tomcat.start();
    }
}

 

순서

1. 내장 톰캣 생성하고 커넥터 생성 후 8080 포트로 연결

2. 스프링 컨테이너 생성하고 빈 등록

3. 스프링 MVC 디스패처 서블릿 만들고 스프링 컨테이너에 연결

4. 디스패처 서블릿을 내장 톰캣에 등록

5. 내장 톰캣 실행

 

 

 

내장 톰캣4 - 빌드와 배포1

자바의 main() 메서드를 실행하기 위해서는 jar 형식으로 빌드해야 한다.

그리고 jar 안에는 'META-INF/MANIFEST.MF' 파일에 실행할 main() 메서드의 클래스를 지정해주어야 한다.

 

 

 

 

아래 gradle 명령으로 프로젝트 root에서 프로젝트 jar 빌드 실행

gradlew clean buildJar -Dorg.gradle.java.home="C:/java/jdk-17.0.6"

 

아래 이미지에서 위 명령 수행 후 프로젝트의 jar 파일이 성공적으로 생성된 것을 확인할 수 있다.

또한 MANIFEST.MF 파일에 jar 파일 수행 시 실행 될 Main-Class 항목에 EmbedTomcatSpringMain 자바 클래스가 지정되어 있는 것을 확인할 수 있다.

 

java -jar embed-0.0.1-SNAPSHOT.jar 명령으로 실행해보면 스프링 프레임워크 Class를 찾을 수 없다는 에러 문구가 나오며 실행되지 않는 것을 확인할 수 있다.

 

jar 파일을 압축해제 해보면 프로젝트에서 직접 만들었던 소스코드만 존재하고, 외부 라이브러리(spring, tomcat 등)은 존재하지 않는 것을 확인할 수 있다.

 

이전에 war 파일을 풀었던 것을 보면,WEB-INF 하위 classes 폴더에 프로젝트 소스 빌드한 파일들이 존재하고, lib 하위에 참조하는 외부 라이브러리(jar) 등이 존재하는 것을 확인할 수 있다.

 

jar 파일 안에 jar 파일이 존재하지 않았던 이유

jar 파일 스펙이 오른쪽과 같기 때문이다 : jar 파일은 jar 파일을 포함할 수 없다. (자바에서 처음 스펙 정할 때 이렇게 정함)

- WAR와 다르게 JAR 파일은 내부에 라이브러리 역할을 하는 JAR 파일을 포함할 수 없다. 포함한다고 해도 인식이 안된다. 이것이 JAR 파일 스펙의 한계이다. 그렇다고 WAR를 사용할 수 도 없다. WAR는 웹 애플리케이션 서버(WAS) 위에서만 실행할 수 있다.

 

 

 

FatJar

Jar 파일 안에는 자바 스펙상 다른 jar 파일이 존재할 수 없기 때문에, 다른 외부 라이브러리 jar 파일들을 전부 풀어서 class 파일들을 추출한 것을 jar 파일에 넣어서 jar에서 외부 라이브러리 포함 못 하는 문제를 해결함

 

FatJar로 WAR의 단점을 일부 해결함.

1. 톰캣 같은 WAS 별도 설치 필요 X -> 내장 톰켓 라이브러리가 포함되어 있고, main에서 실행하기 때문

2. 개발 환경 설정 편의성 증대 -> WAS 연동하기 위한 설정 필요 X -> 그냥 main에서 실행하면 됨

3. 배포 과정 편함, -> WAS 설치해서 WAR 옮길 필요 없이 그냥 main에서 실행하면 되기 때문

4. 톰캣 버전 업데이트 하기 편함 -> 프로젝트 내장 톰캣 라이브러리 버전을 올리고 재빌드 한 후 실행하기만 하면 됨.

 

FatJar 단점

압축해제한 라이브러리에서 클래스나 리소스 명이 같은 경우는 둘 중 하나를 포기해야하는 문제가 발생함

 

 

편리한 부트 클래스 만들기

 

내장 톰캣 실행, 스프링 컨테이너 생성, 디스패처 서블릿 생성 역할 수행하는 Class 생성

MySpringApplication.java

package hello.boot;

import hello.spring.HelloConfig;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import java.util.List;

public class MySpringApplication {

    public static void run(Class configClass, String[] args){
        System.out.println("MySpringApplication.main args=" + List.of(args));

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

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

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

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

        try {
            tomcat.start();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
    }

}

컴포넌트 스캔 역할 수행하는 어노테이션 생성

MySpringBootAnnotation.java

package hello.boot;


import org.springframework.context.annotation.ComponentScan;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ComponentScan
public @interface MySpringBootAnnotation {
}

 

위에서 만든 톰캣 실행, 스프링 컨테이너 생성, 디스패처 서블릿 생성, 컴포넌트 스캔 기능 수행하는 클래스와 어노테이션 이용해서 프로그램 실행하는 메인 클래스 생성

참고 : ※ 컴포넌트 스캔의 기본 동작은 해당 어노테이션이 붙은 클래스의 현재 패키지 부터 하위 패키지를 컴포넌트 스캔의 대상으로 사용하기 때문에 아래 예시의 경우는 hello 패키지 하위에 있는 컴포넌트들이, 컴포넌트 스캔으로 스프링 빈으로 등록되는 대상이다.

MySpringBootMain.java

package hello;

import hello.boot.MySpringApplication;
import hello.boot.MySpringBootAnnotation;

@MySpringBootAnnotation
public class MySpringBootMain {
    public static void main(String[] args){
        System.out.println("MySpringBootMain.main");
        MySpringApplication.run(MySpringBootMain.class, args);
    }
}

 

실제 스프링 부트 라이브러리에서 톰캣 실행, 스프링 컨테이너 생성, 디스패처 서블릿 생성, 컴포넌트 스캔 기능 수행하는 부분 

 

 

스프링 부트와 웹 서버 - 프로젝트 생성

 

스프링 부트는 이전에 고민했던 아래 문제들을 깔끔하게 해결해 준다.

1. 내장 톰캣을 사용해서 빌드와 배포를 편리하게 한다.

2. 빌드시 하나의 Jar를 사용하면서, 동시에 Fat Jar 문제도 해결한다.

3. 지금까지 진행한 내장 톰캣 서버를 실행하기 위한 복잡한 과정을 모두 자동으로 처리한다.

 

스프링 부트와 웹 서버 - 실행 과정

-스프링 부트를 실행할 때는 자바 main() 메서드에서 SpringApplication.run()을 호출해주면 된다.

-여기에 메인 설정 정보를 넘겨주는데, 보통 @SpringBootApplication 애노테이션이 있는 현재 클래스를 지정해주면 된다.

-@SpringBootAnnotation 안에는 컴포넌트 스캔을 포함한 여러 기능이 설정되어 있다. 기본 설정은 현재 패키지와 그 하위 패키지 모두를 컴포넌트 스캔한다.

 

이 코드 수행 시 크게 2가지 일이 진행이 된다.

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

2. WAS(내장 톰캣)을 생성한다.

'스프링 관련 > WAS-Tomcat' 카테고리의 다른 글

웹 서버와 서블릿 컨테이너  (0) 2023.03.26

댓글