있을 유, 참 진

[Spring] 토비의 스프링:: Spring Boot Containerless 이해하기 본문

Spring

[Spring] 토비의 스프링:: Spring Boot Containerless 이해하기

U_ma 2023. 4. 18. 17:07
목표
기존의 Spring Boot main()
   - 시작 전 메인 함수 초기화
빈 컨테이너 생성하기
   - 결과 보기
Servlet 매핑하기 & Servlet Request 처리
   - 결과 보기
Servlet mapping -> Controller로 변환

   - Hello Controller 생성
   - 코드 변경
      1. 기존 코드
      2. 변경 코드

목표

💡 Spring Boot는 별도의 Web Server의 설치나 설정이 없이 개발에만 집중할 수 있게 해 준다. 기존의 Spring Boot의 main 함수 내에 직접 그것을 구현 및 정리

기존의 Spring Boot main()

💡 기존의 Spring Boot 메인, 해당 메인을 실행하면 부트 내의 내장된 톰캣이 실행되고 컨테이너를 초기화, `@SpringBootApplication` 어노테이션과 SpringApplication.run() 메서드가 이것을 가능하게 해 준다. 이 두 기능을 사용하지 않고 자바를 이용해 해당 기능을 구현한다.
@SpringBootApplication
public class HellobootApplication {
    public static void main(String[] args) {
        SpringApplication.run(HellobootApplication.class, args);
    }
}

시작 전 메인 함수 초기화

public class HellobootApplication {
    public static void main(String[] args) {
    }
}

빈 컨테이너 생성하기

💡 비어있는 Servlet Container를 올리는 단계부터 시작, 기존의 어노테이션과 run() 함수의 도움을 받던 부분을 제거하고 Tomcat 웹 서버를 구현한다.

public class HellobootApplication {
    public static void main(String[] args) {
        // Tomcat, Jetty .. 등을 추상화 해 놓음
        ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
        WebServer webServer = serverFactory.getWebServer();
        //TomcatServlet Container 동작
        webServer.start();
    }
}

결과 보기

💡 서버를 구동하고 localhost로 접근했을 때 해당 화면이 나오면 Container가 제대로 생성된 것

Servlet 매핑하기 & Servlet Request 처리

💡 생성한 컨테이너에 서블릿을 올리고 매핑. 해당 서블릿은 ‘/hello’의 주소에 매핑돼 있고. request에서 name의 이름을 가진 파라미터를 받아. ‘Hello Servlet’ + name의 파라미터 값을 반환해 준다.

public class HellobootApplication {
    public static void main(String[] args) {
        // Tomcat, Jetty .. 등을 추상화 해 놓음
        ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
        WebServer webServer = serverFactory.getWebServer(servletContext -> {

            servletContext.addServlet("hello", new HttpServlet() {
                        @Override
                        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                            //request 값 받기
                            String name = req.getParameter("name");
                            //Response 값 생성
                            resp.setStatus(HttpStatus.OK.value());
                            resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
                            resp.getWriter().println("Hello Servlet " + name);
                        }
                    })
                    //URL의 매핑
                    .addMapping("/hello");

        });
        //TomcatServlet Container 동작
        webServer.start();
    }
}

결과 보기

💡 localhost/hello?name=everyone으로 들어갔을 때 앞에서 생성했던 의도대로 Response 값을 반환한다면 성공

Servlet mapping → Controller로 변환

💡 직접적인 Servlet mapping 방식은 각 서블릿 코드에 공통적인 작업이 중복돼 나오게 됨 이를 개선하기 위해서 Controller 방식이 나옴

Hello Controller 생성

💡 기존의 @Controller 어노테이션 설정이 없는 순수한 POJO Controller 생성
public class HelloController {
    public String hello(String name) {
        return "Hello " + name;
    }
}

코드 변경

기존 코드

public class HellobootApplication {
    public static void main(String[] args) {
        // Tomcat, Jetty .. 등을 추상화 해 놓음
        ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
        WebServer webServer = serverFactory.getWebServer(servletContext -> {

            servletContext.addServlet("hello", new HttpServlet() {
                        @Override
                        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                            //request 값 받기
                            String name = req.getParameter("name");
                            //Response 값 생성
                            resp.setStatus(HttpStatus.OK.value());
                            resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
                            resp.getWriter().println("Hello Servlet " + name);
                        }
                    })
                    //URL의 매핑
                    .addMapping("/hello");

        });
        //TomcatServlet Container 동작
        webServer.start();
    }
}

변경 코드

💡 기존의 각 Servlet에 매핑하던 기존 방식 → 하나의 서블릿에서 모든 매핑의 값을 받는다(.addMapping("/*") 부분), URI의 주소가 ‘/hello’와 같고 해당 Request의 메서드가 get 방식일 때는 앞서 만들었던 Controller의 hello(String name) 메서드를 타서 해당 메서드가 반환해 주는 값을 Response에 담아서 반환한다. 이때 사용된 helloController로 매핑, req.getParameter(”name”)은 Controller에서 받는 매개변수 인자에 바인딩됨
public class HellobootApplication {

    public static void main(String[] args) {
        // Tomcat, Jetty .. 등을 추상화 해 놓음
        ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
        WebServer webServer = serverFactory.getWebServer(servletContext -> {
            //servlet mapping -> controller로 전환
            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())) {
                                /***
                                 * 웹 요청 정보를 받아서 인자값으로 넘겨주는 작업을 Binding이라고 한다.
                                 * Request의 값을 받아서 Hello Controller의 인자값으로 넘겨주는 작업을 하고 있다.
                                 */
                                //HelloController
                                HelloController helloController = new HelloController();
                                //request 값 받기
                                String name = req.getParameter("name");
                                String result = helloController.hello(name);
                                /***/

                                //Response 값 생성
                                resp.setStatus(HttpStatus.OK.value());
                                resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
                                resp.getWriter().println(result);
                            } else if(req.getRequestURI().equals("/user")) {
                                // 유저와 관련된 로직 처리
                            } else {
                                resp.setStatus(HttpStatus.NOT_FOUND.value());
                            }
                        }
                    })
                    //URL의 매핑
                    .addMapping("/*");

        });
        //TomcatServlet Container 동작
        webServer.start();
    }

}
Comments