스프링 MVC - 1편 - MVC 패턴 적용
인프런 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술편을 학습하고 정리한 내용 입니다.
서블릿을 컨트롤러로 사용하고, JSP를 뷰로 사용해서 MVC 패턴을 적용해 보자.
Model은 HttpServletRequest 객체를 사용한다. request는 내부에 데이터 저장소를 가지고 있는데, request.setAttribute(), reqeuest.getAttribute()를 사용하면 데이터를 보관하고, 조회할 수 있다.
회원 등록
hello.servlet.web.servletmvc.MvcMemberFormServlet 다음과 같이 패키지와 서블릿 클래스를 만들자.
1
2
3
4
5
6
7
8
9
10
@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를 호출할 수 없다. 우리가 기대하는 것은 항상 컨트롤러를 통해서 JSP를 호출하는 것이다.
redirect vs forward 리다이렉트는 실제 클라이언트(웹 브라우저)에 응답이 나갔다가, 클라이언트가 redirect경로로 다시 요청한다. 따라서 클라이언트가 인지할 수 있고, URL 경로도 실제로 변경 된다. 반면에 포워드는 서버 내부에서 일어나는 호출이기 때문에 클라이언트가 전혀 인지하지 못한다.
회원 등록 폼
webapp/WEB-INF/views/new-form.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ 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>
여기서 form action에 보면 절대경로가 아니라 /가 빠진 상대 경로다.
이렇게 하면 현재 URL이 속한 계층 경로 + /save 가 호출된다.
현재 계층 경로 : /servlet-mvc/members/
결과 : /servlet-mvc/members/save


url 바뀐 것만 확인하자.
회원 저장
hello.servlet.web.servletmvc.MvcMemberSaveServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@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);
}
}
기존에 했던 것처럼
1
2
3
4
5
6
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
멤버정보 가져와서 저장하고 추가된게
1
2
3
4
5
6
// Model에 데이터를 보관한다.
request.setAttribute("member", member);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
뷰로 이동 시키는 거다.
이제 뷰를 만들자.
webapp/WEB-INF/views/save-result.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</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>
<%= request.getAttribute("member")%> 뭐 이런 식으로 member를 꺼낼 수도 있지만, 너무 복잡해 진다.
JSP는 ${ } 문법을 제공하는데, 이 문법을 사용하면 request의 attribute에 담긴 데이터를 편리하게 조회할 수 있다.


잘 된다.
회원 리스트
hello.servlet.web.servletmvc.MvcMemberListServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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);
}
}
이전처럼
1
2
List<Member> members = memberRepository.findAll();
request.setAttribute("members", members);
멤버 리스트 가져와서 세팅하고
1
2
3
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
forward 시킨다. 뷰페이지 만들어 보자.
webapp/WEB-INF/views/members.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<%@ 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>
<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>
모델에 담아둔 members를 JSP가 제공하는 taglib기능을 사용해서 반복하면서 출력했다.
jstl 기능을 써서 간단하게 forEach 태그를 사용했다.
이 때 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 이걸 선언해 줘야 사용할 수 있다.
1
2
3
4
5
6
7
<c:forEach var="item" items="${members}">
<tr>
<td>${item.id}</td>
<td>${item.username}</td>
<td>${item.age}</td>
</tr>
</c:forEach>
다음과 같이 forEach 돌아서 출력한 것 이다.

몇 개 넣어보고 출력해 보니 잘 된다.
MVC 덕분에 컨트롤러 로직과 뷰 로직을 확실하게 분리한 것을 확인할 수 있다. 향후 화면에 수정이 발생하면 뷰 로직만 변경하면 된다.
아무튼 보면 계속 뭔가가 반복되고, 중복 되는 거 같다.. forward 시키는 구문이라던가..
이런 점을 최적화 하다 보면 결국 스프링 프레임워크에 가까워지는 거다..
댓글남기기