SPRING/Spring Boot

[SpringBoot] 외장톰캣에서 SpringMVC 구현하기

IT록흐 2023. 4. 2. 12:05
반응형
 

[SpringBoot] 외장톰캣으로 WAS 띄우기 with IntelliJ(무료버전)

SpringBoot는 내장톰캣을 사용한다. SpringBoot가 내장톰캣을 사용하면서 간편해진 점을 알아보기 위해 우선, SpringBoot 이전에 사용했던 외장톰캣 방식에 대해서 알아보겠다. 외장톰캣 방식이란? 개발

lordofkangs.tistory.com

 

 

지난 포스팅에서 IntelliJ에서 외장톰캣을 띄어 보았다.

이제 해당 환경에서 SpringMVC를 구현해보겠다.

 

 

 

SprinMVC 구현

톰캣은 서블릿(Servlet)을 사용한다. 서블릿의 정의는 아래 포스팅을 참고바란다.

 

 

 

[ JSP ] Servlet 이란?

Servlet이란 Server Application Let의 줄임말이다. Let은 조각을 의미하니 서버의 조각을 Servlet이라고 한다. Servlet은 왜 필요할까? 우선 서버의 필요성부터 알아보자. 서버(Server)란? 서버는 'Request(요청)'

lordofkangs.tistory.com

 

 

SpringMVC 패턴에서 사용하는 서블릿은 DispatcherServlet이다. 거의 모든 비즈니스로직은 DispatcherServlet을 통해 매핑된다. 그러므로 개발자는 톰캣이 실행될 때, DispatcherServlet이 로드되도록 설정해야 한다. 

 

 

 

 

DispacherServlet은 SpringContainer를 참조하고 SpringContainer는 Bean정보를 갖는다. 그럼 DispatcherServlet을 톰캣의 서블릿 컨테이너에 등록하고 톰캣이 실행될 때, 로드되도록 설정해야한다. 

 

과거에는 이 과정을 web.xml이 담당했으나 이제는 SpringServletContainerInitializer가 담당한다.

 

SpringServletContainerInitializer 인터페이스는 톰캣이 실행되기 전, 설정을 초기화하는 기능을 제공한다. 초기화하고 싶은 설정은 개발자가 구현체를 만들어 구현한다.

 

/*
ServletContainerInitializer 구현체
*/

@HandlesTypes(AppInit.class) // 초기화정보가 담긴 파일목록 설정 
public class MyContainerInitV2  implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        
        for(Class<?> appInitClass : c){ // c에 등록된 초기화파일목록 루핑
            try{        
                AppInit appInit = (AppInit)appInitClass.getDeclaredConstructor().newInstance();
                appInit.onStartup(ctx);// ServletContext 주소 넘기기
            }catch(Exception e){
                throw new RuntimeException(e);
            }
        }
    }
}

 

 

구현체는 onStartup(Set<Class<?>> c, ServletContext ctx)를 Override한다. 

 

c는 @HandlesTypes에 설정된 초기화설정 관련 파일목록이 스캔되어 저장된다. @HandlesTypes(AppInit.class)에서 AppInit.class는 인터페이스인데 인터페이스 구현체들은 모두 c에 자동으로 등록된다.

 

ctx는 ServletContext로 c에 등록된 초기화 로직들이 ServletContext에 접근가능하도록 한다. appInit.onStartup(ctx);에서 ctx를 파라미터로 넘기는데, 이는 초기화로직에서 생성된 서블릿을 ServletContext에 등록이 가능하도록 한다.

 

 

 

 

 

 

 

이렇게 @HadlesTypes를 통해, 여러 개의 설정초기화 클래스 접근할 수 있다. 

 

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 dispatcher = new DispatcherServlet(appContext);

        //디스패처 서블릿을 서블릿 컨테이너에 등록 
        ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcherV2",dispatcher);

        // /spring/* 요청이 디스패처 서블릿을 통하도록 설절
        servlet.addMapping("/spring/*");
    }
}

 

그리고 실질적으로 서블릿은 설정초기화클래스에서 생성한다. 위 코드는 Bean설정정보가 담긴 클래스를 SpringContainer에 등록하고 DispatcherServlet에 해당 컨테이너를 등록하는 코드이다. 그리고 DispatcheServlet은 파라미터로 받은 서블릿컨테이너에 추가된다.

 

그렇다면 

 

최초의 시작인 SpringServletContainerInitializer의 구현체는 누가 실행시킬까? 톰캣이 실행시킨다. 그러므로 '정해진 위치'에  SpringServletContainerInitializer 구현체 정보를 담아야 한다.

 

 

 

 

META-INF > services > jakarta.servlet.ServletContainerInitializer 파일에 아래와 같이 구현체경로를 넣어준다.

 

hello.container.MyContainerInitV1
hello.container.MyContainerInitV2

 

META-INF > services > jakarta.servlet.ServletContainerInitializer 는 약속된 위치이기 때문에 디렉토리 및 파일의 이름과 경로가 틀려서는 안된다. 톰캣은 실행하면서 해당 위치의 파일을 읽어 ServletContainer 초기화 구현체 클래스 위치를 파악하고 실행시킨다.

 

 

 

 

 

 

 

정리하면, 톰캣은 ServletContainer를 설정하기위해 초기화 과정을 거친다. 이때 약속된 위치의 파일을 읽어 ServletContainerInitializer 구현체를 실행한다. 구현체는 @HandlesType에 정의된 클래스들을 스캔하여 설정로직이 담긴 클래스들을 실행한다. 이때 ServletContext도 파라미터로 넘겨 설정과정에서 생성된 서블릿을 등록하게 한다. 설정초기화클래스는 DispacherServlet을 생성하고 이를, 파라미터로 받은 ServletContext에 등록한다. 

 

DispacherServlet은 SpringContainer를 등록하고 SpringContainer는 Bean설정클래스를 등록한다. Bean설정클래스는 Controller와 같은, SpringMVC패턴에 필요한 객체를 Bean으로 등록한다. 

 

 

그럼 이제 WAS를 실행시켜 보자.

 

 

URL : http://localhost:8080/spring/hello-spring

 

위에서 url이 localhost:8080/spring/ 으로 들어오는 요청은 모두 DispacherServlet을 통하도록 설정했다.

> servlet.addMapping("/spring/*");

 

그리고 DispacherServlet은 hello-spring 경로로 들어온 요청을 HelloController와 매핑한다.

 

Bean설정클래스 ( HelloConfig.java ) 

@Configuration
public class HelloConfig {
    @Bean // HelloController 등록
    public HelloController helloController(){
        return new HelloController();      
    }
}

HelloController.java

@RestController
public class HelloController {
    @GetMapping("hello-spring") // 매핑정보
    public String hello(){
        System.out.println("HelloController.hello");
        return "hello spring!!!!!!!!!!";
    }
}

 

 

 

 

여기까지 외장톰캣 환경에서

SpringMVC를 구현해보았다.

 

다음 포스팅에서는

내장톰캣환경에서 SpringMVC를 구현해보겠다.

 

 

 


 

 

 

참고자료

 

스프링 부트 - 핵심 원리와 활용 - 인프런 | 강의

실무에 필요한 스프링 부트는 이 강의 하나로 모두 정리해드립니다., - 강의 소개 | 인프런

www.inflearn.com

 

 

 

 

 

 

반응형