[Spring Boot] 웹 서버와 서블릿 컨테이너

Jimin Lim·2023년 6월 25일
0

Spring

목록 보기
5/18
post-thumbnail
post-custom-banner

✅ 스프링 부트

  1. WAS: 내장 웹 서버
  2. 라이브러리 관리: jpa 관련 라이브러리 가져와 줌, 스프링과 외부 라이브러리 버전 자동 관리
  3. 자동 구성: 스프링, 외부 라이브러리의 빈 자동 등록
  4. 외부 설정: 환경에 따라 달라져야 하는 외부 설정 공통화
  5. 프로덕션 준비: 메트릭, 상태 확인 기능 제공

✅ 톰캣 & 빌드

🔗 외장 서버 vs 내장 서버

외장 서버
서버에 톰캣같은 WAS를 설치한 후, WAS에서 동작하도록 서블릿 스펙에 맞춰 코드를 작성한 후 war 파일을 WAS를 전달해 배포하였다.

내장 서버
애플리케이션 코드 내에 WAS가 라이브러리로 내장되어 있다. 개발자는 코드 작성 후 jar로 빌드한 후 해당 jar를 원하는 위치에서 실행하면 WAS도 함께 실행된다.

🔗 jar vs war

jar
여러 클래스와 소스를 묶어서 jar(java archive)라는 압축 파일을 만들 수 있다. jvm위에서 직접 실행되거나 다른 곳에서 라이브러리로 사용할 수 있다.

war
jar는 jvm위에서 실행된다면, war는 웹 애플리케이션 서버 위에서 실행된다. 또한 아래와 같은 구조를 지켜야 한다.

  • WEB-INF
    • classes : 실행 클래스 모음
    • lib : 라이브러리 모음
    • web.xml : 웹 서버 배치 설정 파일(생략 가능)
  • index.html : 정적 리소스

🔗 외장서버로 배포

  1. war로 빌드 (./gradlew build)
  2. tomcat의 webapps에 war파일 추가
  3. tomcat의 bin에서 서버 실행(./startup.sh)

✅ 초기화

🔗 서블릿 컨테이너 초기화

서블릿은 ServletContainerInitializer로 서블릿 컨테이너를 초기화 한다.

@HandlesTypes(AppInit.class)
public class MyContainerInitV2 implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        System.out.println("MyContainerInitV2.onStartup");
        System.out.println("c = " + c + ", ctx = " + ctx);

        for (Class<?> appInitClass : c) {
            try {
                //new AppInitV1Servlet()과 같은 코드
                AppInit appInit = (AppInit) appInitClass.getDeclaredConstructor().newInstance();
                appInit.onStartup(ctx);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        }
    }
}

애플리케이션 초기화 과정

  1. @HandlesType 으로 애플리케이션 초기화 인터페이스 지정
  2. ServletContainerInitializer는 파라미터로 넘어오는 Set<Class<?>> c에 인터페이스 구현체들을 모두 찾아 클래스로 전달한다. @HandlesTypes(AppInit.class) 이므로 AppInit 구현체들을 넘김
  3. appInitClass.getDeclaredConstructor().newInstance();로 객체를 생성한다.
  4. appInit.onStartup(ctx); 서블릿 컨테이너 정보가 담긴 ctx를 전달하며 초기화 코드를 실행한다.

실행 과정

  1. 서블릿 컨테이너 초기화 실행
  • ServletContainerInitializer구현해서 초기화 코드 만듦
  • resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
  1. 애플리케이션 초기화 실행
  • @HandlesTypes(AppInit.class)

🔗 스프링 컨테이너 등록

public class AppInitV2Spring implements AppInit{
    @Override
    public void onStartup(ServletContext servletContext) {
        System.out.println("AppInitV2Spring.onStartup");

        //스프링 컨테이너 생성
        AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
        appContext.register(HelloConfig.class);
        
        //스프링 MVC 디스패처 서블릿 생성, 스프링 컨테이너 연결
        DispatcherServlet dispatcherServlet = new DispatcherServlet(appContext);
        
        //디스패처 서블릿을 서블릿 컨테이너에 등록
        ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcherV2", dispatcherServlet);
        
        // /spring/* 요청이 디스패처 서블릿을 통하도록 
        servlet.addMapping("/spring/*");

    }
}
  • 디스패처 서블릿에 HTTP요청이 오면 디스패처 서블릿은 해당 스프링 컨테이너에 들어있는 컨트롤러 빈들을 호출한다.
  • AppInit대신 WebApplicationInitializer 을 구현하면 애플리케이션 초기화를 알아서 해준다.

✅ 정리

서블릿 컨테이너 초기화를 통해 필요한 서블릿 등록하고, 스프링 컨테이너도 생성해서 등록하였다. 또한 스프링 mvc가 동작하도록 디스패처 서블릿도 중간에 연결했다.
이 방식은 서블릿 컨테이너 위에서 동작하는 것으로, 항상 톰캣 같은 서블릿 컨테이너에 배포를 해야만 동작하는 방식이다. 스프링 부트는 내장 톰캣을 사용하여 이런 부분이 바뀌게 된다.

profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️
post-custom-banner

0개의 댓글