build.gradle
//JSP 추가 시작
//스프링부트 3.0 이상
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'jakarta.servlet:jakarta.servlet-api'
implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api'
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl'
//JSP 추가 끝텍스트
JSP는 자바 코드를 그대로 사용 가능하며, JSP 문서는 다음과 같이 시작한다.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="xxx.xxx.xxx.xxx.xxx" %>
: 자바의 import문과 같다:
<% ~~ %>
: 자바 코드 입력
<%= ~~ %>
: 자바 코드 출력
서블릿으로 개발할 때는 뷰(View)화면을 위한 HTML을 만드는 작업이 자바 코드에 섞여 복잡했지만, JSP 덕분에 뷰를 생성하는 HTML 작업을 깔끔하게 가져가고, 동적으로 변경이 필요한 부분에만 자바 코드를 적용했다.
하지만 다양한 비즈니스 로직을 구현하는 코드가 모두 JSP에 노출됨으로 JSP가 많은 역할을 담당하고 있음을 알 수 있다. 다시 말해, 유지보수가 힘들다는 단점을 가지고 있다.
비즈니스 로직은 서블릿처럼 다른곳에서 처리하고, JSP는 HTML로 화면(View)을 그리도록 하는 MVC 패턴이 등장했다.
Model - View - Controller의 약자인 MVC 패턴을 알아보자.
컨트롤러
: HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행 후에 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.
모델
: 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아서 전달해주는 덕분에 뷰는 화면을 렌더링 하는 일에 집중할 수 있다.
뷰
: 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다. 여기서는 HTML을 생성하는 부분을 말한다.
MVC 패턴의 본질적인 목표는 관심사 분리 !
- 컴포넌트 간의 역할 분리로 서로 간의 결합도 ↓
- 코드의 재사용성 및 확장성 ↑
- 테스트 용이 및 유지보수 ↑
이번 예제에선 서블릿을 컨트롤러로 사용하고, JSP를 뷰로 사용, HttpServletRequest 객체를 모델로 사용
request
는 내부에 데이터 저장소를 가지고 있어 데이터를 보관 / 조회 가능request.setAttribute()
request.getAttribute()
dispatcher.forward()
: 다른 서블릿이나 JSP로 이동할 수 있는 기능
: 서버안에서 내부적으로 다시 호출이 발생 (리다이렉트가 아님)
/WEB-INF
: 이 경로안에 JSP가 있으면 외부에서 직접 JSP를 호출할 수 없다.
@WebServlet(name = "mvcMemberListServlet", urlPatterns = "/servlet-mvc/members")
public class MvcMemberListServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// 비즈니스 로직 구현
List<Member> members = memberRepository.findAll();
// request의 내부 저장소에 저장
request.setAttribute("members", members);
// 요청/응답 객체를 가지고 JSP 호출
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
❗
redirect
vsforward
redirect
는 실제 클라이언트(웹 브라우저)에 응답이 나갔다가, 클라이언트가 redirect 경로로 다시 요청한다. 따라서 클라이언트가 인지할 수 있고, URL 경로도 실제로 변경된다.
반면에forward
는 서버내부에서 일어나는 호출이기 때문에 클라이언트가 전혀 인지하지 못한다.
예제 학습을 하면서 중복되는 코드 그리고 공통 처리가 어렵다는 단점을 가지고 있다.
forward
, ViewPath
중복이 문제를 해결하려면 컨트롤러 호출 전에 먼저 공통 기능을 처리해야 해야한다.
이를 프론트 컨트롤러(Front Controller) 패턴을 도입하여 해결이 가능하다 !
📌 본 포스트는 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 통해 학습한 내용을 요약 및 정리한 것입니다.