You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
애플리케이션 초기화(AppInit) 인터페이스를 구현해서 실제 동작하는 코드를 만들어보자.
AppInitV1Servlet
packagehello.container;
importhello.servlet.HelloServlet;
importjakarta.servlet.ServletContext;
importjakarta.servlet.ServletRegistration;
/** * http://localhost:8080/hello-servlet */publicclassAppInitV1ServletimplementsAppInit {
@OverridepublicvoidonStartup(ServletContextservletContext) {
System.out.println("AppInitV1Servlet.onStartup");
//== 순수 서블릿 코드 등록 ==////서블릿 객체를 서블릿 컨테이너에 넣는다.ServletRegistration.DynamichelloServlet = servletContext.addServlet("helloServlet", newHelloServlet());
//경로 매핑을 해준다.helloServlet.addMapping("/hello-servlet");
}
}
여기서는 프로그래밍 방식으로 HelloServlet 서블릿을 서블릿 컨테이너에 직접 등록한다.
HTTP로 /hello-servlet 를 호출하면 HelloServlet 서블릿이 실행된다.
참고 - 서블릿 등록시 @WebServlet 이 아닌 프로그래밍 방식을 사용하는 이유
@WebServlet 애노테이션 하나로 서블릿을 편리하게 등록할 수 있기는하지만, 애노테이션 방식은 유연하게 변경이 어렵다. 가령 @WebServlet 은 정해진 프로퍼티에 url을 직접 넣어야지만 프로그래밍 방식은 여러 방법으로 넣어줄 수 있다. 또한 분기처리도 가능하다. 참고 -@WebServlet애노테이션 방식 예시
리플렉션을 사용해서 객체를 생성한다. 참고로 이 코드는 new AppInitV1Servlet() 과 같다 생각하면 된다.
appInit.onStartup(ctx)
애플리케이션 초기화 코드를 직접 실행하면서 서블릿 컨테이너 정보가 담긴 ctx 도 함께 전달한다.
서블릿 컨테이너 초기화만 있어도 될 것 같은데, 왜 이렇게 복잡하게 애플리케이션 초기화라는 개념을 만들었을까?
편리함
서블릿 컨테이너를 초기화하려면 ServletContainerInitializer 인터페이스를 구현한 코드를 만들어야 한다. 추가로 META-INF/services/jakarta.servlet.ServletContainerInitializer 파일에 해당 코드를 직접 지정해야한다.
애플리케이션 초기화는 특정 인터페이스만 구현하면 된다.
의존성
애플리케이션 초기화는 서블릿 컨테이너에 상관없이 원하는 모양으로 인터페이스를 만들 수 있다. 이를 통해 애플리케이션 초기화 코드가 서블릿 컨테이너에 대한 의존을 줄일 수 있다. 특히 ServletContext ctx 가 필요없는 애플리케이션 초기화 코드라면 의존을 완전히 제거할 수도 있다.
${\textsf{\color{orange}2.7. 스프링 컨테이너 등록 및 WAS 통합}}$
이제 WAS와 스프링 컨테이너를 통합해보자.
앞서 배운 서블릿 컨테이너 초기화와 애플리케이션 초기화를 활용하면 된다.
AppInit 인터페이스 구현체 : AppInitV1Servlet ⇒ AppInitV2Spring
다음과 같은 과정이 필요할 것이다. (애플리케이션 초기화 코드 AppInitV2Spring 에서 이루어짐)
스프링 컨테이너 만들기
스프링 MVC 컨트롤러를 스프링 컨테이너에 빈으로 등록
이제는 서블릿을 등록하는 대신 편하게 스프링 MVC 컨트롤러를 등록하자
스프링 MVC를 사용하는데 필요한 디스패처 서블릿을 서블릿 컨테이너에 등록
현재 라이브러리에는 스프링 관련 라이브러리가 전혀 없다. 스프링 관련 라이브러리를 추가하자.
build.gradle : spring-webmvc 추가
dependencies {
//서블릿
implementation 'jakarta.servlet:jakarta.servlet-api:6.0.0'//스프링 MVC 추가
implementation 'org.springframework:spring-webmvc:6.0.4'
}
spring-webmvc 라이브러리를 추가하면 스프링 MVC 뿐만 아니라 spring-core 를 포함한 스프링
핵심 라이브러리들도 함께 포함된다.
WebApplicationInitializer 인터페이스를 구현한 부분을 제외하고는 이전의 AppInitV2Spring 과 거의 같은 코드이다.
WebApplicationInitializer 는 스프링이 이미 만들어둔 애플리케이션 초기화 인터페이스이다.
여기서도 디스패처 서블릿을 새로 만들어서 등록하는데, 이전 코드에서는 dispatcherV2 라고 했고, 여기선 dispatcherV3 라고 해주었다. 참고로 이름이 같은 서블릿을 등록하면 오류가 발생한다.
servlet.addMapping("/") 코드를 통해 모든 요청이 해당 서블릿을 타도록 했다.
따라서 다음과 같이 요청하면 해당 디스패처 서블릿을 통해 /hello-spring 이 매핑된 컨트롤러 메서드가 호출된다.
(1) 정리
현재 등록된 서블릿 다음과 같다.
/ = dispatcherV3
/spring/* = dispatcherV2
/hello-servlet = helloServlet
/test = TestServlet
이런 경우 우선순위는 더 구체적인 것이 먼저 실행된다.
참고
여기서는 이해를 돕기 위해 디스패처 서블릿도 2개 만들고, 스프링 컨테이너도 2개 만들었다.
일반적으로는 스프링 컨테이너를 하나 만들고, 디스패처 서블릿도 하나만 만든다. 그리고 디스패처 서블릿의 경로 매핑도 / 로 해서 하나의 디스패처 서블릿을 통해서 모든 것을 처리하도록 한다
(2) 스프링 MVC가 제공하는 서블릿 컨테이너 초기화 분석
WebApplicationInitializer 인터페이스 하나로 애플리케이션 초기화가 가능한 이유가 뭘까?
스프링도 결국 서블릿 컨테이너에서 요구하는 부분을 모두 구현해야 한다.
spring-web 라이브러리를 열어보면 서블릿 컨테이너 초기화를 위한 등록 파일을 확인할 수 있다. 그리고 이곳에 서블릿 컨테이너 초기화 클래스가 등록되어 있다.
/META-INF/services/jakarta.servlet.ServletContainerInitializer 코드를 확인해보자
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
WAS를 실행하는 시점에 다음과 같이 초기화 작업이 필요하다.
WAS가 제공하는 초기화 기능 사용 시 WAS 실행 시점에 위 같은 초기화 과정을 진행할 수 있다.
과거에는 web.xml 을 사용해서 초기화 했지만 지금은 서블릿 스펙에서 자바 코드를 사용한 초기화도 같이 지원한다.
지금부터 서블릿 컨테이너의 초기화 기능을 알아보고 이어서 이 초기화 기능을 활용해 스프링을 만들고 연결해보자.
서블릿 컨테이너 초기화 개발
서블릿은
ServletContainerInitializer라는 초기화 인터페이스를 제공한다. 이름 그대로 서블릿 컨테이너를 초기화 하는 기능을 제공한다.서블릿 컨테이너는 실행 시점에 초기화 메서드
onStartup()을 호출해준다. 여기서 애플리케이션에 필요한 기능들을 초기화 하거나 등록할 수 있다.ServletContainerInitializerSet<Class<?>> c: 조금 더 유연한 초기화를 기능을 제공한다.@HandlesTypes애노테이션과 함께 사용한다. 이후에 코드로 설명한다.ServletContext ctx: 서블릿 컨테이너 자체의 기능을 제공한다. 이 객체를 통해 필터나 서블릿을 등록할 수 있다. ⇒ 서블릿 컨테이너 라고 봐도 됨방금 본 서블릿 컨테이너 초기화 인터페이스를 간단히 구현해서 실제 동작하는지 확인해보자.
이것이 끝이 아니다. 추가로 서블릿 컨테이너(WAS)에게 실행할 초기화 클래스가 뭔지 알려줘야 한다.


다음 경로에 파일을 생성하자.
ServletContainerInitializer인터페이스를 구현하여 서블릿 컨테이너 초기화이 파일에 방금 만든 MyContainerInitV1 클래스를 패키지 경로를 포함해서 지정해주었다.
이렇게 하면 WAS를 실행할 때 해당 클래스를 초기화 클래스로 인식하고 로딩 시점에 실행한다.
WAS를 실행해보자.
실행 결과 로그

WAS 실행 시점에 해당 초기화 클래스 onStartup 메서드가 실행된 것을 확인할 수 있다.
서블릿 컨테이너 초기화를 조금 더 자세히 알아보자.
여기서는 HelloServlet 이라는 서블릿을 서블릿 컨테이너 초기화 시점에 프로그래밍 방식으로 직접 등록해줄 것이다.
(1) 서블릿을 등록하는 2가지 방법
@WebServlet애노테이션이 서블릿을 등록하고 실행하면 다음과 같은 결과가 나온다.
HelloServlet.servicehello servlet!이제 다음 내용을 통해서 서블릿을 등록해보자.
(2) 애플리케이션 초기화
애플리케이션 초기화(AppInit) 인터페이스를 구현해서 실제 동작하는 코드를 만들어보자.
AppInitV1Servlet/hello-servlet를 호출하면 HelloServlet 서블릿이 실행된다.서블릿 컨테이너 초기화(ServletContainerInitializer)는 앞서 알아보았다. 그런데 애플리케이션 초기화(AppInit)는 어떻게 실행되는 것일까? 다음 코드를 만들어 보자.
MyContainerInitV2MyContainerInitV2 등록
MyContainerInitV2 를 실행하려면 서블릿 컨테이너에게 알려주어야 한다. 설정을 추가하자.
resources/META-INF/services/jakarta.servlet.ServletContainerInitializerWAS를 실행해보자.
서블릿을 실행해보자. ⇒ http://localhost:8080/hello-servlet
(3) 정리: 초기화 과정
resources/META-INF/services/jakarta.servlet.ServletContainerInitializer@HandlesTypes애노테이션에 애플리케이션 초기화 인터페이스AppInit.class를 지정해둔다.Set<Class<?>> c에 애플리케이션 초기화 인터페이스의 구현체들을 모두 찾아서 클래스 정보로 전달한다.@HandlesTypes(AppInit.class)를 지정했으므로AppInit.class의 구현체인AppInitV1Servlet.class정보가 전달된다.appInitClass.getDeclaredConstructor().newInstance()new AppInitV1Servlet()과 같다 생각하면 된다.appInit.onStartup(ctx)서블릿 컨테이너 초기화만 있어도 될 것 같은데, 왜 이렇게 복잡하게 애플리케이션 초기화라는 개념을 만들었을까?
편리함
ServletContainerInitializer인터페이스를 구현한 코드를 만들어야 한다. 추가로META-INF/services/jakarta.servlet.ServletContainerInitializer파일에 해당 코드를 직접 지정해야한다.의존성
이제 WAS와 스프링 컨테이너를 통합해보자.
앞서 배운 서블릿 컨테이너 초기화와 애플리케이션 초기화를 활용하면 된다.
AppInit인터페이스 구현체 :AppInitV1Servlet⇒AppInitV2Spring다음과 같은 과정이 필요할 것이다. (애플리케이션 초기화 코드
AppInitV2Spring에서 이루어짐)현재 라이브러리에는 스프링 관련 라이브러리가 전혀 없다. 스프링 관련 라이브러리를 추가하자.
build.gradle: spring-webmvc 추가dependencies { //서블릿 implementation 'jakarta.servlet:jakarta.servlet-api:6.0.0' //스프링 MVC 추가 implementation 'org.springframework:spring-webmvc:6.0.4' }spring-webmvc라이브러리를 추가하면 스프링 MVC 뿐만 아니라 spring-core 를 포함한 스프링핵심 라이브러리들도 함께 포함된다.
HelloControllerHelloConfig이제 애플리케이션 초기화를 사용해서 서블릿 컨테이너에 스프링 컨테이너를 생성하고 등록하자.
AppInitV2SpringAnnotationConfigWebApplicationContext가 바로 스프링 컨테이너이다.AnnotationConfigWebApplicationContext부모를 따라가 보면ApplicationContext인터페이스를 확인할 수 있다.appContext.register(HelloConfig.class)new DispatcherServlet(appContext)servletContext.addServlet("dispatcherV2", dispatcher)/spring/*요청이 디스패처 서블릿을 통하도록 설정servlet.addMapping("/spring/*");/spring/*이렇게 경로를 지정하면/spring과 그 하위 요청은 모두 해당 서블릿을 통하게 된다./spring/hello-spring/spring/hello/go실행 및 결과 → http://localhost:8080/spring/hello-spring
실행 과정 정리
/spring/hello-spring/spring/*패턴으로 호출했기 때문에 다음과 같이 동작한다.dispatcherV2디스패처 서블릿이 실행된다. (/spring)/spring을 제외한,/hello-spring가 매핑된 컨트롤러HelloController의 메서드를 찾아서 실행한다.0x03. 스프링 MVC 가 서블릿 컨테이너 초기화 지원
지금까지의 과정을 보면 서블릿 컨테이너를 초기화 하기 위해 다음과 같은 복잡한 과정을 진행했다.
ServletContainerInitializer인터페이스 구현한 서블릿 컨테이너 객체 생성 그 안에서 서블릿 초기화 코드 구현@HandlesTypes애노테이션으로 애플리케이션 초기화할 인터페이스 명시META-INF/services/{패키지 경로 + 서블릿 컨테이너 구현체}파일에 서블릿 컨테이너 초기화 클래스 경로를 등록서블릿 컨테이너 초기화 과정은 상당히 번거롭고 반복되는 작업이다. 스프링 MVC는 이러한 서블릿 컨테이너 초기화 작업을 이미 만들어두었다. 덕분에 개발자는 서블릿 컨테이너 초기화 과정은 생략하고, 애플리케이션 초기화 코드만 작성하면 된다.
스프링이 지원하는 애플리케이션 초기화를 사용하려면 다음 인터페이스만 구현하면 된다.
WebApplicationInitializer스프링이 지원하는 애플리케이션 초기화 코드를 사용해보자
AppInitV3SpringMvc
(1) 정리
현재 등록된 서블릿 다음과 같다.
/=dispatcherV3/spring/*=dispatcherV2/hello-servlet=helloServlet/test=TestServlet(2) 스프링 MVC가 제공하는 서블릿 컨테이너 초기화 분석
WebApplicationInitializer인터페이스 하나로 애플리케이션 초기화가 가능한 이유가 뭘까?스프링도 결국 서블릿 컨테이너에서 요구하는 부분을 모두 구현해야 한다.
spring-web 라이브러리를 열어보면 서블릿 컨테이너 초기화를 위한 등록 파일을 확인할 수 있다. 그리고 이곳에 서블릿 컨테이너 초기화 클래스가 등록되어 있다.
/META-INF/services/jakarta.servlet.ServletContainerInitializer코드를 확인해보자SpringServletContainerInitializer
생성하고 실행하는 것을 확인할 수 있다. 우리는 앞서 이 인터페이스를 구현했다.
AppInitV3SpringMvc 에서 본 것 처럼 편리하게 애플리케이션 초기화를 사용할 수 있다.
Beta Was this translation helpful? Give feedback.
All reactions