본문 바로가기
Java , Spring/Java

[Java] 서블릿과 스프링에서의 MVC

by 방배킹 2024. 1. 18.

HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러

 

서블릿 이란?

서블릿이란 동적 웹페이지를 만들때 사용되는 자바 기반의 웹 어플리케이션 프로그래밍 기술이다.

 

urlPatterns의 URL이 호출되면 서블릿 코드가 실행된다. 

HTTP 요청 정보를 사용할 수 있는 HttpServletRequest와 응답 정보를 제공하는 HttpServletResponse 객체가 있다,

개발자는 HTTP 스펙을 매우 편리하게 사용할 수 있다.

 

 

  1. 웹 브라우저에서 HTTP 요청메시지를 보낸다.
  2. WAS에서 해당 요청메시지를 기반으로 request와 response 객체를 만들어서 servlet 객체를 호출한다.
  3. 호출한 servlet 객체에 request, response 객체를 같이 넘겨준다.
  4. request 객체를 이용해서 servlet의 비즈니스로직(service 메서드)을 실행한다.
  5. 응답 결과를 response 객체에 담은후 servlet 컨테이너에 전달한다.
  6. servlet 컨테이너가 HTTP 응답 메시지를 생성해 클라이언트에게 전달한다.

servlet과 싱글톤

  • 클라이언트의 요청은 항상 다르기 떄문에 request, response 객체는 요청이 올때마다 새로 만들어야한다. 하지만 helloServlet은 새로 만들 이유가 없다. ⭢ 최초 로딩시점에 만들어서 싱글톤으로 사용
  • 즉, 모든 고객의 요청은 동일한 서블릿 객체 인스턴스에 접근한다. ⭢ (싱글톤은 항상 공유 변수 사용 주의하자)
  • 서블릿 컨테이너가 종료시 서블릿도 같이 종료된다.
  • 동시 요청을 위한 멀티 쓰레드 처리를 지원해준다.

 

servlet 멀티 쓰레드

 

쓰레드 하나를 사용하는데 다중 요청이 들어오면 문제가 생길 수 도있다.

 

그러면 요청마다 쓰레드를 생성해주면 요청 처리중 장애가 발생해도 신규 쓰레드를 생성해서 할당하면 되기 떄문에 문제가 되지 않는다.

 

하지만 신규 요청이 올때마다 쓰레드를 생성하는 방법은 몇가지 문제점이 있다.

  1. 쓰레드 생성비용이 매우 비싸기 때문에 요청이 올때마다 쓰레드를 생성하면 응답속도가 늦어진다.
  2. 쓰레드 컨텍스트 스위칭 비용이 발생하는데 이또한 비용이 높아 문제가 발생할 수 도있다.
  3. 쓰레드 생성 제한이 없어 고객 요청이 너무 많이 와서 쓰레드를 너무 많이 만들면 서버가 죽을 수 도있다.

 

위 문제점을 해결하기 위해 쓰레드 풀을 사용한다.

 

쓰레드 풀은 필요한 쓰레드를 미리 만들어 쓰레드 풀어 보관하고 요청이 오면 쓰레드 풀에서 쓰레드를 꺼내 할당한다.

쓰레드 사용이 끝나면 쓰레드 풀에 반납한다. 모든 쓰레드가 사용중이라면 대기하도록 설정할 수 있다.

 

쓰레드 풀 장점

  1. 쓰레드가 미리 생성되어 있으므로, 쓰레드를 생성하고 종료하는 비용이 절약되고, 응답 시간이 빠르다.
  2. 생성 가능한 쓰레드의 최대치가 있으므로 너무 많은 요청이 와도 기존 요청을 안전하게 처리할 수 있다.

 

servlet의 문제점

초기 servlet은 service 메서드를 구현해서 사용했는데 웹서버 요청, 응답에 대한 처리를 직접해야했으며, HTML을 생성하는것이 불편했다.

 

@WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/newform")
public class MemberFormServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
        PrintWriter w = response.getWriter();
        w.write("<!DOCTYPE html>\n" +
                "<html>\n" +
                "<head>\n" +
                " <meta charset=\"UTF-8\">\n" +
                " <title>Title</title>\n" +
                "</head>\n" +
                "<body>\n" +
                "<form action=\"/servlet/members/save\" method=\"post\">\n" +
                " username: <input type=\"text\" name=\"username\" />\n" +
                " age: <input type=\"text\" name=\"age\" />\n" +
                " <button type=\"submit\">전송</button>\n" +
                "</form>\n" +
                "</body>\n" +
                "</html>\n");
    }
}

이처럼 HTML 코드를 작성하기가 너무 불편했으며, 직접 response 객체에 값을 넣어줘야 했다.

 

JSP

서블릿의 단점을 보완하기 위해 생겼다.

(뷰) HTML 파일을 서블릿에서 분리하여 JSP파일에 작성하였다.

하지만 여전히 JSP 파일은 뷰의 역할만 하는것이 아닌 비즈니스 로직을 포함하고있다.

main/webapp/jsp/members/new-form.jsp
```html
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>Title</title>
</head>
<body>
<form action="/jsp/members/save.jsp" method="post">
 username: <input type="text" name="username" />
 age: <input type="text" name="age" />
 <button type="submit">전송</button>
</form>
</body>
</html>

MVC ( Model, View, Controller )

  • Controller: HTTP 요청을 받아서 파라미터 검증, 비즈니스 로직 실행, 뷰에 전달할 데이터를 조회해서 모델에 담는다.
  • Model: (뷰와 컨트롤러 사이에서 데이터를 주고 받기 위한 DTO) 뷰에 출력할 데이터를 담아둔다, 뷰가 필요한 데이터를 모두 모델에 담아서 전달해줘서 뷰는 비즈니스 로직 이나 데이터 접근없이 화면을 렌더링 하는것에 집중할 수 있다.
  • View: 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에만 집중한다. (HTML 생성)

 

 

  1. 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다.
  2. 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.
  3. 핸들러 어댑터 실행: 핸들러 어댑터를 실행한다.
  4. 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행한다.
  5. ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환한다.
  6. viewResolver 호출: 뷰 리졸버를 찾고 실행한다.
    JSP의 경우: InternalResourceViewResolver 가 자동 등록되고, 사용된다.
  7. View 반환: 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다.
    JSP의 경우 InternalResourceView(JstlView) 를 반환하는데, 내부에 forward() 로직이 있다.
  8. 뷰 렌더링: 뷰를 통해서 뷰를 렌더링 한다.

댓글