Spring

Spring 지원 없이 Servlet을 통해 Front Controller 구현

꾸준함. 2023. 3. 2. 13:22

개요

앞서 작성한 Spring Boot 개요 게시글에서 스프링 부트는 스프링 애플리케이션 개발에 요구되는 Servlet Container의 설치, WAR 폴더 구조, web.xml, WAR 빌드, 컨테이너로 배치, 포트 바인딩, 클래스로더, 로깅 등과 같은 필요하지만 애플리케이션 개발의 핵심이 아닌 단순 반복 작업을 제거해 주는 개발 도구와 아키텍처를 지원한다고 언급했습니다.
https://jaimemin.tistory.com/2254

Spring Boot 개요

개요 그동안 많은 스프링 부트 프로젝트를 진행했지만 부끄럽게도 스프링과 스프링 부트의 차이점을 완벽히 이해하지 못한 채 개발을 진행했습니다. 저는 단순하게 "스프링 부트는 내장 톰캣

jaimemin.tistory.com

 
이번 게시글에서는 Spring Boot의 지원을 최소화하고 서블릿을 통해서만 Front Controller를 구현해 볼 것이며 이를 통해 Spring Boot의 Containerless 특성이 없다면 간단한 컨트롤러 구현에도 얼마나 많은 공수가 드는지를 체감할 수 있을 것입니다.

 

구현하고 싶은 애플리케이션

구현하고 싶은 애플리케이션은 간단합니다.
클라이언트가 [도메인 Url]/hello?name={name}으로 GET 요청을 보냈을 때 응답으로 "Hello {name}" 내려주는 간단한 애플리케이션입니다.
Spring Boot 프로젝트인 경우 아래와 같이 Hello Controller만 구현해 주면 됩니다.
 

@RestController
public HelloController {
	
    @GetMapping("/hello")
    public String hello(@RequestParam String name) {
    	return "Hello " + name;
    }
}

 
하지만, 스프링의 지원이 없을 경우 구현하는데 조금 고생할 수 있습니다.
 

초기 설정

Spring Initialzr를 통해 스프링 프로젝트를 생성하면 아래와 같이 *Application 클래스에 @SpringBootApplication 어노테이션과 main 메서드가 생성됩니다.
 

 
앞서 개요에서 언급한 단순 반복 작업을 main 메서드에서 처리해 주기 때문에 Spring Initialzr에서 구성해 준 그대로 앱을 띄우면 tomcat 서버가 8080 포트로 뜨는 것을 확인할 수 있습니다.
이번에는 스프링 지원 없이 컨트롤러를 구현할 것이기 때문에 아래와 같이 어노테이션과 main 메서드 내용을 모두 지워줍니다.
 

 
당연한 얘기지만 위 상태에서 main 메서드를 실행하면 tomcat 서버가 뜨지 않는 것을 확인할 수 있습니다.

 

서블릿 컨테이터 띄우기 및 서블릿 등록

스프링 부트 프로젝트를 만들 때 Spring Web 모듈을 추가했을 경우 아래와 같이 3개의 내장형 톰캣 라이브러리가 추가됩니다.

 
제목에는 Spring 지원 없이 구현을 한다고 했지만 사실 내장형 톰캣의 초기화 및 간편 설정을 지원하는 TomcatServletWebServerFactory 클래스를 사용하기 위해서는 Spring Boot의 지원이 필요합니다.
톰캣 웹 서버를 실행하는 코드는 아래와 같이 구성하면 됩니다.
 

public static void main(String[] args) {
    // Tomcat 말고 Jetty를 사용하고 싶을 경우 new JettyServerWebServerFactory();
    ServletWebServerFactory serverFactory = new TomcatServerWebServerFactory();
    WebServer webServer = serverFactory.getWebServer();
    webServer.start();
}

 
위와 같이 코드를 작성하고 main 메서드를 실행하면 tomcat 서버가 뜨는 것을 확인할 수 있습니다.
톰캣은 Servlet Container이고 클라이언트로 요청을 받았을 때 비즈니스 로직을 처리하고 응답을 내려줄 서블릿을 등록 해줘야 합니다.
코드 내에서 서블릿을 등록하기 위해서는 ServletContext 객체가 필요하며 이를 위해 ServletContextInitializer 인터페이스를 구현한 오브젝트를 getWebServer() 메서드에 매개변수로 전달해야 합니다.
앞서 구현한 HelloController와 동일한 동작을 하기 위해서는 코드를 아래와 같이 작성해야 합니다.


 
스프링 부트 프로젝트에서는 간단하게 작성할 수 있는 코드가 서블릿 하나 추가했다고 벌써 복잡해진 것을 확인할 수 있습니다.

 

프런트 컨트롤러 패턴 도입

프런트 컨트롤러 패턴은 요약하자면 모든 서블릿이 공통적으로 처리할 로직을 앞단에 존재하는 컨트롤러라는 Object에서 공통적인 작업을 처리하고 요청에 맞는 Object에 위임합니다.
여기서 공통적으로 처리할 로직이란 인증, 인가, 보안, 다국어 처리를 예로 들 수 있습니다.
프런트 컨트롤러에 대해 더 자세하게 알고 싶다면 아래 링크 참고 바랍니다.
https://jaimemin.tistory.com/1817

Front Controller 패턴

복습 기존의 Servlet, JSP를 통한 MVC Pattern의 한계점을 복습해봅시다. (https://jaimemin.tistory.com/1815) 뷰 렌더링과 컨트롤러 역할을 분리한 건 좋지만 페이지가 늘어남에 따라 컨트롤러 내 중복 코드 다

jaimemin.tistory.com

 
앞서 작성한 코드는 URL이 늘어날 때마다 서블릿을 추가로 등록하는 코드를 작성해야 하는 반면 위 패턴을 도입할 경우 프런트 컨트롤러라는 서블릿을 하나만 등록하고 요청 URI에 맞게 처리하는 코드를 서블릿 내에 작성해 주면 됩니다.
따라서, 프론트 컨트롤러가 모든 URL을 다 처리할 수 있도록 서블릿 바인딩을 wildcard로 변경하고 서블릿 내에서 HTTP 요청 정보를 이용해 각 요청을 분리하여 로직을 처리하면 됩니다.
코드는 아래와 같습니다.
 

 
 
스프링 MVC의 경우 DispatcherServlet이 내장되어 있으므로 개발자가 별도로 구현하지 않더라도 자동으로 프런트 컨트롤러 패턴이 도입되어 있는 반면 스프링 지원 없이 코드를 작성하다 보니 톰캣을 띄우는 코드, 서블릿을 등록하는 코드, 패턴을 도입하는 코드 모두 작성해야 한다는 것을 알 수 있었습니다.

 

정리

위 예제 코드를 작성하면서 평소 스프링 프로젝트를 개발할 때 Spring Boot의 도움을 많이 받고 있다는 것을 다시 한번 깨달을 수 있었습니다.
자동으로 생성되는 어노테이션과 main 메서드가 없을 경우 톰캣을 띄우는 코드부터 톰캣에 서블릿을 등록하는 코드까지 모두 개발자가 구현해야한다는 애로사항이 생겼습니다.
이로써 스프링 부트의 containerless 특성이 얼마나 개발자를 편하게 해주는지 알 수 있었습니다.

 

출처

인프런 토비의 스프링 부트 - 이해와 원리

반응형