현의 개발 블로그

[Servlet, JSP, MVC 패턴] MVC로 웹 애플리케이션 만들기 본문

스프링부트 이론/MVC

[Servlet, JSP, MVC 패턴] MVC로 웹 애플리케이션 만들기

hyun2371 2023. 6. 21. 15:43

서블릿을 컨트롤러로 사용하고, JSP를 뷰로 사용해서 MVC 패턴을 적용하자.

Model은  HttpServletRequest 객체를 사용한다.

 

request는 내부에 데이터 저장소를 가지고 있다.

request.setAttribute()로 데이터를 보관하고, request.getAttribute()로 데이터를 조회할 수 있다.

 

회원 등록 폼

컨트롤러

String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response); //servlet에서 JSP를 호출 (제어권 넘김)

 

dispatcher.forward()

다른 servlet이나 JSP로 이동할 수 있는 기능이다.

서버 내부에서 다시 호출이 발생한다.

servlet을 호출하면 JSP가 호출되고, JSP는클라이언트에 응답값인 뷰를 반환한다.

 

 

redirect VS forward

redirect는 클라이언트에 redirect 경로를 응답을 해주면, 클라이언트가 해당 경로로 다시 요청한다.

클라이언트는 서버에 두 번 요청한다.

따라서 클라이언트가 인지할 수 있고, 실제로 URL 경로도 변경된다.

 

forward는 서버 내부에서 일어나는 호출이기 때문에 클라이언트가 인지하지 못한다.

 

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
...
<!-- 상대경로 사용, [현재 URL이 속한 계층 경로 + /save] -->
<form action="save" method="post">
  username: <input type="text" name="username" />
  age: <input type="text" name="age" />
  <button type="submit">전송</button>
</form>
...

상대 경로 사용

form의 action을 보면 절대 경로가 아니라 상대 경로이다.

폼 전송 시 현재 속한 URL에 속한 계층 경로 + save가 호출된다.

- 현재 계층 경로: /servlet-mvc/members/

- 결과: /servlet-mvc/members/save

 

/WEB-INF

해당 경로 안에 JSP가 있으면, 외부에서 JSP를 직접 호출할 수 없다.

컨트롤러에서만 JSP를 호출할 수 있게 해준다.

 

 

 

회원 저장

컨트롤러

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);

Model인 http.request에 데이터를 보관한다.

 

<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
...
성공
<ul>
  <li>id=${member.id}</li>
  <li>username=${member.username}</li>
  <li>age=${member.age}</li>
</ul>
<a href="/index.html">메인</a>

 

<%=request.getAttribute("member")%> 로 모델에 저장한 Member 객체를 꺼낼 수 있지만 복잡하다.

 

JSP의 ${} 문법을 사용하면 모델에 담긴 데이터를 쉽게 조회할 수 있다.

 

회원 목록 조회

컨트롤러

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);

 

members에서 member를 순서대로 꺼내서 item 변수에 담고, 출력하는 과정을 반복한다.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
..
  </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>

 

 

 


한계

컨트롤러와 뷰 로직을 분리하였다. 향후 화면에 수정이 필요하면 뷰 로직만 변경하면 된다.

 

뷰는 모델에서 필요한 데이터를 꺼내고 화면을 만든다.

뷰는 괜찮지만, 현재 작성한 컨트롤러는 중복과 불필요한 코드가 많다.

 

현재 컨트롤러의 문제점들은 아래와 같다.

 

포워드 중복

View로 이동하는 코드가 항상 중복된다.

이 부분을 메서드로 추출해도 되지만, 해당 메서드도 결국 직접 호출해야 한다.

RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);

 

ViewPath 중복

만약 jsp가 아닌 thymeleaf 같은 다른 뷰로 변경한다면, 해당 코드를 다 변경해야 한다.

String viewPath = "/WEB-INF/views/new-form.jsp";

 

사용하지 않는 코드

아래 코드를 사용하지 않을 때도 있다.

HttpServletRequest request, HttpServletResponse response

response는 현재 코드에서 사용되지 않는다.

그리고 HttpServletRequest, HtpServletResponse를 사용하는 코드는 테스트 케이스를 작성하기 어렵다.

 

 

해결책

기능이 복잡해질수록 컨트롤러에서 공통으로 처리해야 하는 부분이 많이 증가할 것이다.

 

서블릿이 호출되기 전에 공통 기능을 처리해야 한다.

프론트 컨트롤러 패턴을 도입하면 앞의 문제들을 해결 할 수 있다. (입구를 하나로)

스프링 MVC 핵심도 프론트 컨트롤러이다.

 

 

 


Reference

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -

www.inflearn.com

 

Comments