토비의 스프링 부트 - 독립 실행형 서블릿 애플리케이션

2023. 4. 9. 21:30·공부/Spring
반응형

출처 : 인프런

Containerless 개발 준비

내장형 톰캣을 실행하지 않도록 HellobootApplication.java에서 일부 코드를 삭제한다.

package tobyspring.helloboot;

public class HellobootApplication {

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

 

 

서블릿 컨테이너 띄우기

톰캣은 설치를 해서 사용할 수도 있지만 내장형 톰캣도 따로 있어 설치하지 않고 임베디드 형식으로 사용할 수 있게 개발되었다. 스프링 부트는 https://start.spring.io/ 에서 프로젝트를 생성하면 프로젝트에 자동으로 임베디드 톰캣 라이브러리가 포함된다. 그럼 프로젝트에서 내장형 톰캣을 불러와 사용해보자.

톰캣 라이브러리를 직접 가져오는게 아니라 스프링에서 제공해주는 TomcatServletWebServerFactory 클래스를 사용한다. 톰캣 라이브러리를 직접 가져오게 되면 복잡한 생성, 수많은 설정들을 해줘야하는 번거로움이 있는데 스프링에서 제공하는 클래스를 사용하면 보다 쉽게 톰캣을 사용할 수 있다.

package tobyspring.helloboot;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServer;

public class HellobootApplication {

   public static void main(String[] args) {
      TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
      WebServer webServer = serverFactory.getWebServer();
      webServer.start();
   }
}

위 코드에서 TomcatServletWebServerFactory로 WebServer 객체를 만든다. WebServer 객체는 단순히 '톰캣'만을 위한 클래스는 아니며 톰캣 같은 서블릿 컨테이너를 구동시켜주는 프로그램들을 위한 클래스이다.  즉, 추상화가 되어있는 클래스다. 만약 톰캣 외 Jetty 서버를 사용하고 싶다면 TomcatServletServerFactory 객체를 JettyServletServerFactory로 변경 후 WebServer 클래스를 사용하면 된다.

 

 

서블릿 등록

getWebServer() 메서드 안에 익명 클래스를 생성한다.

WebServer webServer = serverFactory.getWebServer(new ServletContextInitializer() {
   @Override
   public void onStartup(ServletContext servletContext) throws ServletException {

   }
});

ServletContextInitializer()는 Functional Interface이기 때문에 람다식으로 간단히 작성할 수 있다.

WebServer webServer = serverFactory.getWebServer(servletContext -> {

});

 

addServlet()을 이용해 서블릿을 등록한다. HttpServlet의 여러 메서드 중 service()를 오버라이딩한다.

WebServer webServer = serverFactory.getWebServer(servletContext -> {
   servletContext.addServlet("hello", new HttpServlet() {
      @Override
      protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         super.service(req, resp);
      }
   });
});

 

이제 HTTP 응답에 필수 요소인 header, status code, body를 작성하고, 요청이 들어오면 서블릿에서 어느 서비스로 보내줘야하는 mapping 관련 정보도 작성해준다.

WebServer webServer = serverFactory.getWebServer(servletContext -> {
   servletContext.addServlet("hello", new HttpServlet() {
      @Override
      protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         resp.setStatus(200);
         resp.setHeader("Content-Type", "text/plain");
         resp.getWriter().println("Hello Servlet");
      }
   }).addMapping("/hello");
});

 

서버 재실행 후 테스트

 

 

서블릿 요청 처리

위에서 작성한 코드는 값들을 문자열로 넣어주었다. 이런식으로 개발자가 직접 작성하면 오타의 위험성이 높기 때문에 스프링에서 제공하는 enum 값을 사용하면 더 안전하게 코드를 작성할 수 있다.

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   resp.setStatus(HttpStatus.OK.value());
   resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
   resp.getWriter().println("Hello Servlet");
}

 

또한 HelloController에서 작성했던 것 처럼 요청 메세지에서 파라미터를 가져와 출력해보자.

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   String name = req.getParameter("name");

   resp.setStatus(HttpStatus.OK.value());
   resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
   resp.getWriter().println("Hello " + name);
}

요청 메세지 객체인 req에서 getParmeter() 메서드를 사용하면 파라미터 값을 가져올 수 있다.

 

서버 재실행 후 테스트

 

 

프론트 컨트롤러 - 전환

프론트 컨트롤러는 제일 앞단에서 모든 요청을 받아들이는 웹 컴포넌트이다. frontController에서 먼저 요청을 받은 후, 알맞은 서블릿과 매핑되도록 코드를 작성하면 유지보수 및 관리가 더욱 쉬워진다는 장점이 있다. 또한 공통적인 부분을 한 번에 처리할 수 있어 편리하다.

WebServer webServer = serverFactory.getWebServer(servletContext -> {
   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");

            resp.setStatus(HttpStatus.OK.value());
            resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
            resp.getWriter().println("Hello " + name);
         }
         else if(req.getRequestURI().equals("/user")) {
            //
         }

         else {
            resp.setStatus(HttpStatus.NOT_FOUND.value());
         }

      }
   }).addMapping("/*");
});

원래 기존에 addMaping()값인 "/hello"를 지우고 모든 요청을 받아들이겠다는 의미인 "/*"를 기재한다. 그리고 나서 요청의 uri 값을 가져와 if문으로 분기처리를 한다. uri 값으로만 분기처리할 수도 있지만, HTTP Method의 종류에 따라서도 분기처리가 가능하다. HelloController에 기재된 hello() 메서드는 GET Method이니 요청 uri이 'hello'이면서 HTTP Method가 'GET'인 요청만 받아서 처리하도록 위의 코드를 작성했다.

서버 재실행 후 테스트

GET 메서드 외 다른 HTTP Method를 사용하면 404 에러가 떨어진다.

 

 

Hello 컨트롤러 매핑과 바인딩

현재 작성한 코드들은 frontController에서 로직 처리를 하는 코드이다. 원래 이런 식으로 frontController에서 로직 처리하는게 아니라 분리하는게 원칙이다. 로직 처리 부분을 분리해보자.

일단 HelloController에 작성한 hello() 메서드를 재활용하기 위해 코드 일부분을 삭제한다.

public class HelloController {
    public String hello(String name) {
        return "hello " + name;
    }
}

 

그리고 frontController에서 HelloController 인스턴스를 만들어 사용하자.

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 ret = helloController.hello(name);

            resp.setStatus(HttpStatus.OK.value());
            resp.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
            resp.getWriter().println(ret);
         }
         else if(req.getRequestURI().equals("/user")) {
            //
         }

         else {
            resp.setStatus(HttpStatus.NOT_FOUND.value());
         }

      }
   }).addMapping("/*");
});

 

서버 재실행 후 테스트

 

 

 

 


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

반응형
저작자표시 비영리 변경금지 (새창열림)
'공부/Spring' 카테고리의 다른 글
  • 토비의 스프링 부트 - 독립 실행형 스프링 애플리케이션-2
  • 토비의 스프링 부트 - 독립 실행형 스프링 애플리케이션-1
  • 토비의 스프링 부트 - 스프링 부트 시작하기
  • 토비의 스프링 부트 - 스프링 부트 살펴보기
데부한
데부한
어차피 할 거면 긍정적으로 하고 싶은 개발자
    반응형
  • 데부한
    동동이개발바닥
    데부한
  • 전체
    오늘
    어제
    • 분류 전체보기 (307)
      • 방통대 컴퓨터과학과 (27)
        • 잡담 (9)
        • 3학년1학기 (17)
      • 프로젝트 및 컨퍼런스 회고 (1)
        • 프로젝트 (4)
        • 한이음 프로젝트 (0)
        • 회고 (3)
      • 공부 (165)
        • Spring (37)
        • JPA (71)
        • 인프런 워밍업 클럽_BE (10)
        • Java (6)
        • React.js (27)
        • 넥사크로 (11)
        • 기타 (3)
      • 알고리즘 (85)
        • 알고리즘 유형 (10)
        • 알고리즘 풀이 (57)
        • SQL 풀이 (18)
      • 에러 해결 (13)
      • 잡담 (7)
        • 국비교육 (2)
        • 구매후기 (5)
        • 진짜 잡담 (0)
  • 블로그 메뉴

    • Github
    • Linkedin
    • 홈
    • 방명록
    • 글쓰기
    • 관리
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    토이프로젝트
    RESTful
    react
    토비의스프링부트
    egov
    넥사크로
    IT
    스프링부트
    방통대
    기출문제
    백준
    개발자
    운영체제
    코딩테스트
    프론트엔드
    에러해결
    springboot
    알고리즘
    QueryDSL
    oracle
    Spring
    전자정부프레임워크
    인프런
    SpringBoot를 이용한 RESTful Web Service 개발
    자바스크립트
    SQL
    프로그래머스
    Java
    JPA
    MSA
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
데부한
토비의 스프링 부트 - 독립 실행형 서블릿 애플리케이션
상단으로

티스토리툴바