서블릿 MVC
이전글
Servlet과 JSP
애플리케이션 요구사항 회원 정보 이름(username) 나이(age) 기능 요구사항 회원 저장 회원 조회 회원 도메인 @Getter @Setter @NoArgsConstructor public class Member { private Long id; private String username; private int age;
pressky99.tistory.com
MVC 패턴
여기서는 request 객체의 내부 저장소를 모델로 사용한다.
회원 등록 폼 - 컨트롤러
@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);
}
}
/servlet-mvc/members/new-form을 호출하면 /WEB-INF/views/new-form.jsp로 forward한다.
/WEB-INF에 있는 JSP는 외부에서 직접 호출할 수 없고 컨트롤러를 통해서 호출할 수 있다.
회원 등록 폼 - 뷰
<!-- main/webapp/WEB-INF/views/new-form.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--
상대경로 사용, [현재 URL이 속한 계층 경로 + /save]
/servlet-mvc/members/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>
forward를 했기 때문에 현재 URL은 servlet-mvc/members/new-form이다.
회원 저장 - 컨트롤러
@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);
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);
}
}
HttpServletRequest 객체를 Model 객체로 사용한다. request는 내부에 데이터 저장소가 있어서 request.setAttribute(), request.getAttribute()를 사용하여 데이터를 보관하고 조회할 수 있다.
회원 저장 - 뷰
<!-- main/webapp/WEB-INF/views/save-result.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
성공
<ul>
<!-- <%= request.getAttribute("member") %> -->
<li>id=${member.id}</li>
<li>username=${member.username}</li>
<li>age=${member.age}</li>
</ul>
</body>
</html>
request.getAttribute() 대신 JSP의 ${}를 사용해서 더 깔끔하게 조회할 수 있다.
회원 목록 - 컨트롤러
@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 dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
회원 목록 - 뷰
<!-- main/webapp/WEB-INF/views/members.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<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가 제공하는 taglib 기능을 사용해서 members를 출력한다.
이전 글에서 서블릿이나 JSP만으로 작성한 코드를 서블릿(컨트롤러)과 JSP(뷰)로 MVC 패턴을 적용했다.
그러나 컨트롤러에서 같은 코드가 중복되는 문제가 있다.
// 중복 코드
String viewPath = "...";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
프론트 컨트롤러를 도입하여 이를 해결할 수 있다.
MyView
public class MyView {
private String viewPath;
public MyView(String viewPath) {
this.viewPath = viewPath;
}
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
기존 코드에서 중복되는 부분인 포워드를 MyView 객체가 처리한다.
넘어온 viewPath로 render() 메서드가 포워드한다.
Controller
public interface Controller {
MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
프론트 컨트롤러에서 여러 컨트롤러를 모두 다룰 수 있도록 다형성을 위해 인터페이스로 작성한다.
회원 등록 - 컨트롤러
public class MemberFormController implements Controller {
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
return new MyView("/WEB-INF/views/new-form.jsp");
}
}
회원 저장 - 컨트롤러
public class MemberSaveController implements Controller {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public MyView process(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);
memberRepository.save(member);
request.setAttribute("member", member);
return new MyView("/WEB-INF/views/save-result.jsp");
}
}
회원 목록 - 컨트롤러
public class MemberListController implements Controller {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
request.setAttribute("members", members);
return new MyView("/WEB-INF/views/members.jsp");
}
}
프론트 컨트롤러
@WebServlet(name = "frontControllerServlet", urlPatterns = "/front-controller/*")
public class FrontControllerServlet extends HttpServlet {
private Map<String, Controller> controllerMap = new HashMap<>();
public FrontControllerServlet() {
controllerMap.put("/front-controller/members/new-form", new MemberFormController());
controllerMap.put("/front-controller/members/save", new MemberSaveController());
controllerMap.put("/front-controller/members", new MemberListController());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
Controller controller = controllerMap.get(requestURI);
if (controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
MyView view = controller.process(request, response);
view.render(request, response);
}
}