
예전에는 개발 할 때 하나의 서블릿이나 JSP만으로 비즈니스 로직과 뷰 렌더링까지 모두 처리했습니다.
하지만 이렇게 되면 너무 많은 역할은 한 곳에서 하게 되고 유지보수가 어렵고, 복잡해집니다.
이러한 문제점을 해결하기 위해서 MVC 패턴을 사용하게 되었습니다.

MVC 패턴은 하나의 서블릿, 또는 JSP로 처리하던 이전 방법들과 다르게 컨트롤러와 뷰라는 영역으로 서로 역할을 나눕니다.

서비스 : 비즈니스 로직 처리
컨트롤러 : 서비스 호출
모델 : 뷰에 출력할 데이터를 담아두고, 뷰에게 전달해줌
뷰 : 모델에 담겨있는 데이터를 사용해서 화면에 보여줌
서블릿을 컨트롤러, JSP를 뷰로 사용해서 MVC 패턴을 적용해 보았습니다.
모델은 HttpServletRequest 객체를 통해서, setAttribute(), 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를 호출 불가, 항상 컨트롤러를 통해 호출해야합니다.
회원 등록 폼 - 뷰
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 상대경로 사용, [현재 URL이 속한 계층 경로 + /save] -->
<form action="save" method="post">
username: <input type="text" name="username" />
age: <input type="text" name="age" />
<button type="submit">전송</button>
</form>
</body>
</html>
여기서 action을 보면 절대 경로(/로 시작)이 아니라 상대경로(/로 시작 아님)입니다.
이렇게 상대경로를 사용하면 폼 전송시 현재 URL + action의 값이 호출됩니다.
현재 계층 경로: /servlet-mvc/members/
결과: /servlet-mvc/members/save
회원 저장 - 뷰
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
성공
<ul>
<li>id=${member.id}</li>
<li>username=${member.username}</li>
<li>age=${member.age}</li>
</ul>
<a href="/index.html">메인</a>
</body>
</html>
<%=((Member)request.getAttribute("member")).getId()%> 이 방법으로 모델에서 member 객체를 꺼낼 수 있지만, 너무 복잡합니다.
JSP에서 제공하는 ${} 문법을 사용하면 편리하게 데이터를 조회할 수 있습니다.
MVC 패턴에서 컨트롤러 코드에 중복이 많고, 필요하지 않는 코드들도 종종 보입니다.
포워드 중복
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
위의 코드와 같이 View로 이동하는 코드가 항상 중복으로 호출됩니다.
ViewPath에 중복
String viewPath = "/WEB-INF/views/new-form.jsp";
/WEB-INF/views/, jsp 같은 부분들이 중복으로 호출됩니다.
사용하지 않는 코드
HttpServletRequest request, HttpServletResponse response
위의 코드에서 response, request 같은 부분을 사용하지 않는 경우도 있습니다.
공통 처리 문제
단순하게 공통 기능을 메서드로 뽑으면 될 것 같지만, 결과적으로 해당 메서드도 항상 호출해야 하고, 호출하는 것 자체도 중복입니다.
해결 방안
이러한 문제를 처리하기 위해 컨트롤러 호출 전에 먼저 공통 기능을 처리해야 합니다.
프론트 컨트롤러(Front Controller) 패턴을 도입하면 이런 문제를 깔끔하게 해결할 수 있습니다.