MVC 패턴

YH·2023년 4월 18일
0

✅ 들어가기전,

✔️ 서블릿과 JSP의 한계

  • 서블릿으로 개발할 때는 View 화면을 위한 HTML 코드를 자바 코드에 섞여있어서 지저분하고 복잡하다.
  • JSP를 사용했을 때는 View 생성하는 HTML 부분이 서블릿 보다 깔끔하고 중간에 자바 코드가 필요한 부분만 적용할 수 있어 좀 더 편리해졌다.
  • 그러나, JSP 안에 비지니스 로직과 View 영역이 모두 포함되어 있고, 비지니스 로직에 있는 자바 코드가 모두 JSP에 노출되어 있다. JSP가 너무 많은 역할을 하므로 코드가 많아질수록 복잡해지고 유지보수는 더더욱 힘들어진다.
  • 이러한 문제를 해결하기 위해 MVC 패턴이 등장했다.

✅ MVC 패턴 - 개요

  • 너무 많은 역할
    • 서블릿이나 JSP만으로 비지니스 로직과 뷰 렌더링까지 모두 처리하면 너무 많은 역할을 하게 되고 유지보수가 어려워진다.
  • 변경의 라이프 사이클
    • UI를 수정하는 일과 비지니스 로직을 수정하는 일은 각각 다르게 발생할 가능성이 높고 서로 영향을 주지 않는다. 서로 다른 라이프 사이클을 가진 부분을 하나의 코드로 관리하는 것은 유지보수하기 좋지 않다.
  • 기능 특화
    • JSP와 같은 뷰 템플릿은 화면 렌더링에 특화되어 있으므로, 특화된 업무만 담당하는 것이 효과적이다.

MVC (Model View Controller)

  • MVC 패턴은 컨트롤러와 뷰라는 영역을 나누어 서로 역할을 분리하여 담당하도록 하는 패턴이다.
  • Controller : HTTP 요청을 받아서 파라미터를 검증하고, 비지니스 로직을 실행한다. 그리고 뷰에 전달할 데이터를 조회해서 Model에 담는다.
  • Model : View에 출력할 데이터를 담아둔다. 필요한 데이터를 모델에 담아둠으로써 뷰는 비지니스 로직이나 데이터 접근에 신경쓸 필요가 없다.
  • View : 모델에 담겨있는 데이터를 사용해서 화면을 렌더링하는 부분. HTML을 생성한다.



✅ MVC 패턴 - 적용

  • 서블릿을 컨트롤러로 사용하고, JSP를 뷰로 사용해서 MVC 패턴을 적용해본다.
  • Model은 HttpServletRequest 객체를 사용한다. request는 내부에 데이터 저장소를 가지고 있는데, request.setAttribute(), request.getAttribute()를 사용하면 데이터를 보관 및 조회가 가능하다.

✔️ 회원 등록 폼 - 컨트롤러

@WebServlet(name = "mvcMemberFormServlet", urlPatterns = "/servlet-mvc/members/new-form")
public class MvcMemberFormServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String viewPath = "/WEB-INF/views/new-form.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}
  • dispatcher.forward() : 다른 서블릿이나 JSP로 이동할 수 있는 기능. 서버 내부에서 다시 호출이 발생한다.
  • /WEB-INF : 해당 경로에 JSP가 있으면 외부에서 직접 JSP를 호출할 수 없다.
  • redirect vs forward
    • redirect실제 클라이언트에 응답이 나갔다가, 클라이언트가 redirect 경로로 다시 요청한다. 따라서, 클라이언트가 인지할 수 있고 URL 경로도 실제 변경된다.
    • forward서버 내부에서 일어나는 호출이기 때문에 클라이언트가 전혀 인지하지 못한다.

✔️ 회원 저장 - 컨트롤러

@WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
public class MvcMemberSaveServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(username, age);
        System.out.println("member = " + member);
        memberRepository.save(member);

        //Model에 데이터를 보관한다.
        request.setAttribute("member", member);

        String viewPath = "/WEB-INF/views/save-result.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}
  • HttpServlerRequest를 Model로 사용한다.
  • request.setAttribute()를 사용하면 request 객체에 데이터를 보관해서 뷰에 전달할 수 있다.
  • 뷰에서는 request.getAttribute()를 사용해서 데이터를 꺼낼 수 있다.

✔️ 회원 목록 조회 - 컨트롤러

/**
 * 회원 목록 조회 컨트롤러
 */
@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 {
        System.out.println("MvcMemberListServlet.service");
        List<Member> members = memberRepository.findAll();

        //Model에 저장
        request.setAttribute("members", members);
        
        String viewPath = "/WEB-INF/views/members.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}

✅ MVC 패턴 - 한계

✔️ MVC 컨트롤러의 단점

  • 포워드 중복
    View로 이동하는 코드가 중복 호출된다. 메소드로 공통화 할 수 있지만, 아래 메소드도 직접 호출해줘야 한다.
    RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
     dispatcher.forward(request, response);
  • ViewPath 중복
    String viewPath = "/WEB-INF/views/new-form.jsp";
  • 사용하지 않는 코드
    • HttpServletRequest request, HttpServletResponse response 를 사용할 때도 있고 안할 때도 있다.
    • HttpServletRequest, HttpServletResponse를 사용하는 코드는 테스트 케이스 작성도 어렵다.
  • 공통 처리가 어렵다.
    • 기능이 복잡해질 수록 컨트롤러에서 공통으로 처리해야 하는 부분이 점점 많아진다. 공통 메소드를 항상 호출 해야하고 중복된다.
    • 이러한 공통 처리 문제를 해결하기 위해 프론트 컨트롤러 패턴으로 해결할 수 있다. (스프링 MVC의 핵심도 프론터 컨트롤러 이다.)

참고 Reference

  • 인프런 강의
profile
하루하루 꾸준히 포기하지 말고

0개의 댓글