서블릿 컨테이너를 직접 생성해 보자.
스프링 구조

스프링은 위의 그림처럼 서블릿 컨테이너와 Bean 컨테이너 2가지 구조로 이루어져 있다. 오늘 실행할 실습은 서블릿 컨테이너를 직접 생성하고 등록하는 예제를 진행할 것이다.
스프링 부트 초기 설정
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HellobootApplication {
public static void main(String[] args) {
SpringApplication.run(HellobootApplication.class, args);
}
}
Spring Boot를 초기에 main메서드를 실행하면 알아서 Spring Boot 애플리케이션이 실행된다. 이것 대신에 직접 내장된 Tomcat을 실행하는 예제 코드로 바꿀 것이다. Tomcat도 Java로 작성되어 있기 때문에 실행할 수 있다.
TomcatServletWebServerFactory를 사용하여 내장 Tomcat서버를 설정할 수 있다. 이 서버는 서블릿을 등록하는 역할을 담당하며, WebServer 인터페이스를 구현하는 객체를 생성한다.
TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer( ...
서블릿을 등록하기 위해 getWebServer() 매개변수로 익명 클래스를 등록해야 한다. 내가 작성한 예제에서는 람다로 대체했다.
서블릿 컨테이너 등록
servletContext.addServlet("frontcontroller", new HttpServlet() {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//인증, 보안, 다국어, 공통 기능
if(req.getRequestURI().equals("/hello") && req.getMethod().equals(HttpMethod.GET.name())) {
String name = req.getParameter("name");
String returnValue = helloController.hello(name);
resp.setStatus(HttpStatus.OK.value());
resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
resp.getWriter().println(returnValue);
}
else if(req.getRequestURI().equals("user")) {
// logic
}
else {
resp.setStatus(HttpStatus.NOT_FOUND.value());
}
}
}).addMapping("/*");
});
addServlet 메서드를 활용하여 Servlet을 직접 등록할 수 있다. 위에 코드에서는 "frontcontroller"라는 이름으로 서블릿을 등록했다. 각각의 요청에 따라 다른 서블릿을 만드는 것이 아닌 모든 요청을 1개의 서블릿으로 매핑시킨다. addMapping메서드 안에 "/*"를 설정함으로 써 모든 요청은 frontcontroller 서블릿으로 오며, dispatcher servlet역할을 담당하게 된다.
요청에 담긴 url을 frontcontroller 서블릿이 확인 후 조건에 맞는 handler를 소환하는 방식으로 예제 코드가 작성됐다. service안에 첫 번째 if문을 보면 url이 "/hello"이고 GET메서드일 때 조건문 안에 들어가서 HelloController를 실행한다. 요청 url과 메서드에 따라서 if 조건문을 수정하고, 요청을 처리할 handler를 소환하면 된다.
public class HelloController {
public String hello(String name) {
return "Hello " + name;
}
}
HelloController에서 String값을 반환하고 그것을 response에 담아서 반환하는 예제이다.
매핑과 바인딩
여기서 알아야 할 중요한 개념은 매핑과 바인딩이다.
- 매핑(Mapping)
- 매핑은 클라이언트의 요청 URL과 컨트롤러와의 연결을 설정하는 것을 의미한다.
- 위에 예제 코드에서 매핑은 if 조건문을 통해 이루어지고 있다.
- 바인딩(Binding)
- 웹 요청 정보를 추출하고 의미 있는 오브젝트에 담아서 전달하는 작업을 바인딩이라고 한다.
- frontcontroller의 주 기능은 매핑과 바인딩이다.
전체 코드
public class HellobootApplication {
public static void main(String[] args) {
//SpringApplication.run(HellobootApplication.class, args);
TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer(servletContext -> {
HelloController helloController = new HelloController();
servletContext.addServlet("frontcontroller", new HttpServlet() {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//인증, 보안, 다국어, 공통 기능
if(req.getRequestURI().equals("/hello") && req.getMethod().equals(HttpMethod.GET.name())) {
String name = req.getParameter("name");
String returnValue = helloController.hello(name);
resp.setStatus(HttpStatus.OK.value());
resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
resp.getWriter().println(returnValue);
}
else if(req.getRequestURI().equals("user")) {
// logic
}
else {
resp.setStatus(HttpStatus.NOT_FOUND.value());
}
}
}).addMapping("/*");
});
webServer.start();
}
}
'Spring' 카테고리의 다른 글
| [스프링부트] 조건부 자동 구성 (0) | 2024.04.19 |
|---|---|
| [스프링부트] 자동 구성 기반 애플리케이션 (0) | 2024.04.17 |
| [스프링부트] Bean Container와 Proxy (0) | 2024.04.16 |
| [스프링부트] 빈 컨테이너 생성 (0) | 2024.04.08 |
| [스프링부트] 스프링부트 살펴보기 (0) | 2024.04.04 |