8 min read

<Spring> 우리는 왜 MVC를 사용할까?

MVC의 등장 배경

자바의 JSP와 서블릿만을 이용해서 웹서비스를 개발해보신 경험이 있으신가요?JSP의 경우 아래와 같은 코드 구조를 가지게 됩니다.굉장히 간단한 코드임에도 자바 코드와 함께 HTML 코드가 복잡하게 얽혀 있는것을 확인 할 수 있습니다.심지어 해당 코드의 UI는 간단함을 넘어 조금은 성의없어 보이기까지 합니다.하지만 진짜 JSP 코드의 문제는 따로 있습니다.그건 바로 변경 생명 주기가 서로 다른 요소들이 결합(뷰+비즈니스 로직)되어 있다는 점입니다.쉽게 설명하자면 UI가 변경되어야 하는 시점에 비지니스 로직도 함께 변경되어야 합니다.즉,하나의 파일에 너무 많은 역할을 부여하여 해당 파일을 유지보수하기 굉장히 어렵다는 것입니다.(물론 HTML을 String에 직접 넣어야하는 Servlet만 사용하는 것보다는 JSP가 발전된 형태입니다)

위와 같은 이유들로부터 이를 타파하고자 생성된 개념이 MVC 패턴입니다.

<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    MemberRepository memberRepository = MemberRepository.getInstance();
    List<Member> members = memberRepository.findAll();
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="/index.html">메인</a>
<table>
    <thead>
    <th>id</th>
    <th>username</th>
    <th>age</th>
    </thead>
    <tbody> <%
        for (Member member : members) {
            out.write("    <tr>");
            out.write("         <td>" + member.getId() + "</td>");
            out.write("         <td>" + member.getUsername() + "</td>");
            out.write("         <td>" + member.getAge() + "</td>");
            out.write("    </tr>");
        }
        %>
    </tbody>
</table>
</body>
</html>

MVC 패턴이 뭐지?

지금까지 어떤 이유에서 MVC 패턴이 등장했는지 알아보았습니다.본격적으로 MVC패턴 자체에 대해서 알아보겠습니다.MVC는 ModelViewController의 약자로 합쳐져있던 view-logicbusiness-logic을 아래의 세가지 요소를 통해 나누어 관리하는 기능을 수행합니다.각각 요소들의 정의부터 내리겠습니다.

  • Model:뷰에 출력할 데이터를 담아둡니다.뷰가 필요한 데이터를 모두 모델에 담아서 전달해주기 때문에 뷰는 비즈니스 로직 및 데이터 접근에 대한 책임을 지지 않고 화면을 렌더링하는 일에만 책임을 가지게 됩니다.
  • View: 모델에 담겨있는 데이터를 받고 해당 데이터를 활용해서 사용자에게 뿌려질 화면을 그리는 일에 집중합니다.주로 HTML을 생성하는 일을 한다고 보면 됩니다.
  • Controller:HTTP 요청을 받아서 파라미터를 검증하고,비지니스 로직을 실행합니다.그리고 뷰에 전달할 결과 데이터를 모델에 담아줍니다.

아래의 그림을 통해 MVC 패턴이 적용된 모델과 그렇지 않은 모델을 비교해 봅시다.

위의 그림은 비지니스 로직과 뷰로직이 서로 붙어 있는 형태입니다.앞서 저희가 만든 jsp파일의 형태가 위와 같습니다.이를 MVC를 적용시키면 아래와 같은 모델로 바뀝니다.(참고로 MVC 패턴 1이라고 합니다)

클라이언트의 호출이 Controller를 통해 들어오고 들어온 호출에 대한 비지니스 로직을 수행한 결과를 Model에 담아서 Veiw로직으로 전달해줍니다.이후 View에서는 해당 Model을 통해 응답을 클라이언트에게 내려주는 형태입니다.

여기서 좀 더 발달된 형태의 MVC 2 패턴이 아래와 같이 생겨납니다.이는 요청을 받는 역할을 하는 컨트롤러의 부담을 줄이기 위한 구조(정확히는 Controller가 져야할 책임을 줄이기 위해서입니다)로 비지니스 로직을 서비스라는 새로운 계층으로 옮겨 처리합니다.정리하자면 굉장히 익숙하게 봐왔던 Controller, Service, Repository 3계층은 MVC2 패턴에서 발생했으며,Controller의 책임을 줄이기 위한 구조라고 이해하시면 됩니다.

MVC 패턴 적용해보기

앞서 JSP만으로 모든 기능을 수행하는 간단한 코드를 보셨을 것입니다.이제 MVC1패턴을 적용해서 구조를 변경 시켜 보겠습니다.Servlet에 Controller를 적용시키고 서블릿내에서 비지니스 로직을 수행하게 만든 뒤,결과를 Model에 담아 View로 넘기는 구조로 바꾸겠습니다.

@WebServlet(name = "mvcMemberListServlet",urlPatterns = "/servlet-mvc/members")
public class MvcMemberListServlet extends HttpServlet {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();
        request.setAttribute("members", members);
        String viewPath = "/WEB-INF/views/members.jsp";
        RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
        requestDispatcher.forward(request, response);
    }
}

MvcMemberListServlet 서블릿은 Controller 역할을 수행합니다.해당 구조는 Controller가 비지니스로직도 동시에 수행하는 MVC1패턴입니다.요청이 들어오면 비지니스 로직을 수행하고 해당 결과를 model에 심어서 RequestDispatcher를 통해서 view에게 넘겨주고 있습니다. RequestDispatcher은 forward라는 메서드를 통해 인자로 받은 viewPath 경로의 jsp파일로 request와 response를 옮기는 기능을 수행합니다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="/index.html">메인</a>
<table>
    <thead>
    <th>id</th>
    <th>username</th>
    <th>age</th>
    </thead>
    <tbody>
    <c:forEach var="item" items="${members}">
        <tr>
            <td>${item.id}</td>
            <td>${item.username}</td>
            <td>${item.age}</td>
        </tr>
    </c:forEach>
    </tbody>
</table>
</body>
</html>

나머지 jsp 파일은 view의 역할,즉 화면을 그리는 책임만 가지고 있습니다.사실 jsp의 경우 거의 사용되지 않기에 간략하게 소개만 하고 넘어가겠습니다.실제로 자바 진영의 SSR언어는 주로 thymeleaf가 많이 사용됩니다.

MVC 패턴의 한계

아래의 그림을 보시죠.클라이언트에서 들어오는 호출을 할때매다 각각의 Controller (저희가 배운걸로는 서블릿)들이 제 역할을 충실히 해주고 있습니다.그런데 과연 아래와 같이 Controller가 가지는 공통된 부분을 매번 코드를 작성하고 호출해야하는게 효율적일까요? 공통된 부분이 있으면 이를 한번에 처리해주는 일종의 수문장 역할을 하는 무언가 있으면 좋을 것 같다는 생각이 드시지 않나요?

위한 같은 한계점을 돌파하기 위해 스프링 MVC는 Dispatcher Servlet라는 존재를 도입하게 됩니다.저희는 앞으로 진행되는 포스팅을 통해 직접 위와 같은 문제점을 가지는 MVC 패턴에서 스프링의 MVC 패턴까지 프레임워크를 하나하나 만들어가며 배워봅시다.

Ref : 김영한 - 스프링 MVC 1편