서블릿

웹 서버 프로그래밍을 하기 위한 자바 코드라고 할 수 있습니다.

 

  • 서블릿의 형태 : HttpServlet 클래스를 상속한 클래스
  • 서블릿의 관리 : Servlet Container 에 의해 관리,실행됩니다.

 

개발자는 서블릿을 가지고 무엇을 하는가?
  • 웹 서버 역할 : HTTP Server + Servlet Container 를 통해 웹 서버 역할에 필요한 부분을 구현
  • 개발자의 역할 : 서블릿을 만들어 HTTP 요청을 처리하고 받는 부분을 구현

 

 

대표적인 웹서버 : Tomcat

웹 애플리케이션 서버 ( WAS ) 중 하나인 톰캣은 개발자가 작성한 서블릿을 관리해주는 Servlet Container 입니다.

 

  • 여기서 "서블릿을 관리" 해준다는 의미는?

클라이언트가 어떠한 요청을 했을 때, 어떤 서블릿을 실행할 것인지 제어해주는 것을 의미합니다.

 

  • Tomcat 의 주요 구성인 web.xml 이란?

Servlet에 대한 정보를 쓰는 파일입니다. 최근에는 대부분 java config 파일로 자바 소스 설정을 합니다.

 

  • 서버에서 수행하는 작업

1. 서버 TCP/IP 연결

2. HTTP 요청 메시지 파싱해서 읽기

3. POST 방식으로 URL 읽기

4. Content-Type 확인

5. HTTP Message-body 내용 파싱

6. 비지니스 로직 실행

7. HTTP Response Message 생성

8. TCP/IP에 응답 후 소켓 종료

 

위의 1~8번 중 6번을 제외한 부분을 WAS 가 대신 해줍니다.

 

 

 

대표적인 서블릿

1. DispatcherServlet

 - HTTP 프로토콜로 들어오는 모든 요청을 받아, 적합한 컨트롤러에 위임해주는 프론트 컨트롤러

 - 처리 과정

: 클라이언트의 요청 -> 서블릿 컨테이너가 요청 받음 ( ex. 톰캣 ) ->

  DispatcherServlet -> [ 공통적인 작업을 먼저 처리한 후 해당 요청을 처리해야 하는 컨트롤러를 찾아 작업 위임 ]

 

2. Servlet Filter

- 서블릿 실행 전, 후에 어떤 작업을 하고자 할 때 사용됩니다.

 

3. Servlet Context

- 서블릿 단위로 생성되는 컨텍스트입니다.

- DispatcherServlet 과 같은 서블릿을 등록하면 해당 서블릿이 갖는 하나의 작은 컨테이너 역할을 하는 객체

 

4. Application Context

- Root Context로 스프링에 의해 생성되는 Bean에 대한 Spring IoC Container  입니다.

- BeanFactory 를 상속받는 Context

- 여러 서블릿에서 공통으로 사용할 Bean을 등록하는 Context

 

5. WebApplicationInitializer

- Servlet Context 를 프로그래밍적으로 설정하기 위한 인터페이스 ( web.xml 을 대체하기 위함 )

 

6. ContextLoaderListener

- 서블릿 컨테이너의 시작과 종료시에 발생하는 이벤트를 처리하는 리스너를 등록하기 위한 

   ServletContextListener 인터페이스의 구현체


7. AnnotationConfigWebApplicationContext

- Component  class( @Configuration, @Inject, @Component ) 를 입력값으로 받는

WebApplicationContext 인터페이스의 구현체

 

서블릿의 형태

@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class helloServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("helloServlet.service");
        System.out.println("request = " + request);
        System.out.println("response = " + response);

        String username = request.getParameter("username");
        System.out.println("username : " + username);

        response.setContentType("text/plain");
        response.setCharacterEncoding("utf-8");
        response.getWriter().write("hello" + username);

    }
}

- 동작 

1. "/hello" 라는 url 이 들어오면, service에 있는 코드를 실행시켜 줍니다.

2. HTTP 요청 정보 사용하기 : HttpServletRequest

3. HTTP 요청 정보 제공하기 : HttpServletResponse ( 개발자들은 여기에 데이터를 넣어준다. )

4. requset.getParameter('username") 

 : hello?username=park 했을 때, 입력되는 park 를 받아온다.

 

5. reponse에 응답 코드를 작성

6. 포스트맨으로 테스트

 

 

로그인 동작 예시
<form action="/request-param" method="post">
    username: <input type="text" name="username" />
    password: <input type="password" name="password" /> <button type="submit"> 전송 </button>
</form>

이러한 Form 형식이 있을 때,

버튼을 누르면 "/requset-param" 이라는 url 로 POST 를 보내게 됩니다.

 

@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("단일 파라미터 조회 - start");
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        System.out.println("username = " + username);
        System.out.println("password = " + password);
        System.out.println("단일 파라미터 조회 - end");

        if(username.equals("kim") && password.equals("1234")){
            response.getWriter().write("ok");
        }
    }
}

- 결과 : ok 가 출력되며 정상 동작함을 알 수 있습니다.

 

 

위의 서블릿으로 구현한 코드를 Spring MVC 로 구현한다면?

 

1. HttpServletRequest 대신 @RequestParam

위의  service 코드를 controller에 @RequestParam 을 통해 간단하게 나타낼 수 있습니다.

@PostMapping("/save")
public String process(
            @RequestParam String username,
            @RequestParam String password,
            Model model
    ) {
        Member member = new Member(username, password);
        memberRepository.save(member);

        model.addAttribute("member", member);
        return "save-result";
}

 

2. 타임리프를 활용한 동적인 뷰 템플릿 구성

서블릿에 작성한 HTML 코드는 정적인 코드로 여러 문제점을 가지고 있습니다.

 

[ 기존코드 ]

@WebServlet(name = "memberSaveServlet", urlPatterns = "/save")
public class memberSaveServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void  service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("MemberSaveServlet.service");

        // 요청 데이터
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 저장
        Member member = new Member(username, password);
        memberRepository.save(member);

        // 응답 메시지
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
        PrintWriter writer = response.getWriter();
        writer.write("<html>\n" +
                "<head>\n" +
                " <meta charset=\"UTF-8\">\n" + "</head>\n" +
                "<body>\n" +
                "성공\n" +
                "<ul>\n" +
                "    <li>id="+member.getId()+"</li>\n" +
                "    <li>username="+member.getUsername()+"</li>\n" +
                " <li>password="+member.getPassword()+"</li>\n" + "</ul>\n" +
                "<a href=\"/index.html\">메인</a>\n" + "</body>\n" +
                "</html>");
    }
}

 

위의 write 부분에 작성한 html 을 아래의 타임리프를 활용한 코드로 정적으로 사용자 데이터를 관리할 수 있습니다.

<html lang="en" xmlns:th="http://www.thymeleaf.org">
<main class="form-signin">
  <form th:action="${/member/save}" method="post">
    <h1 class="h3 mb-3 fw-normal">Sign in</h1>
    <div class="form-floating">
      <input type="text" class="form-control" id="floatingInput" name="username" placeholder="아이디를 입력하세요.">
      <label for="floatingInput">ID</label>
    </div>
    <div class="form-floating">
      <input type="password" class="form-control" id="floatingPassword" name="password" placeholder="비밀번호를 입력하세요.">
      <label for="floatingPassword">Password</label>
    </div>

    <button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
  </form>
</main>
  </body>
</html>

타임리프의 action 코드를 통해 POST 로 사용자가 정보를 입력 후 버튼을 누르면

POST로 "member/save" 의 url 에 사용자 정보를 보냅니다.

그러면 위에 작성한 POST의 process 메서드로 비지니스 로직을 구현할 수 있게 됩니다.

 

이를 통해 기존에 서블릿 부분에 작성한 코드는 작성할 필요가 없게 됩니다.

'스터디' 카테고리의 다른 글

Swagger Annotation  (0) 2023.09.13
CSRF  (0) 2022.12.31
세션과 쿠키, JWT  (0) 2022.12.31
로그인 페이지는 GET ? POST ? (2)  (0) 2022.12.04
로그인 페이지는 GET ? POST ? (1)  (0) 2022.12.01

+ Recent posts