이전 포스팅에서 프론트 컨트롤러의 개념에 대해 알아보았다.
https://healthdevelop.tistory.com/entry/spring49
[Spring MVC] 프론트 컨트롤러 패턴 도입 | 프론트 컨트롤러 패턴 적용하기 - 회원 관리 예제(FrontContr
● 프론트 컨트롤러(Front Controller) Spring MVC 에서 프론트 컨트롤러가 도입되고 나서 컨트롤러의 호출 이전에 공통 처리 기능이 가능해졌다. 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청
healthdevelop.tistory.com
프론트 컨트롤러를 통해 요청에 맞는 컨트롤러 매핑을 하여 컨트롤러를 실행시켰다.
하지만 아직까지는 깔끔하게 코드가 간결화되지 않고 중복되는 부분이 남아있다.
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
바로 view 화면으로 이동하기 위한 forward() 하는 과정이다.
이 부분을 깔끔하게 분리해보고자 한다.
● MyView 생성
별도로 뷰를 처리하는 객체를 만들어서 view 화면으로의 이동 역할을 단독으로 수행하게끔 하고자 한다.
구조는 아래와 같다.
기존에 작성했던 코드에서 Front Controller 컨트롤러를 호출 후,
해당 컨트롤러에서 직접 forward(view로 이동)하기까지 했다.
이번 포스팅에선 MyView 객체를 생성하고,
MyView에서는 컨트롤러에서 반환받은 viewPath를 통해 JSP forwarding을 한다.
컨트롤러는 비즈니스 로직 수행, MyView는 rendering 만의 역할을 하는 것이다.
MyView 객체를 만들어보자.
MyView
ackage hello.servlet.web.frontcontroller;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
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를 가지고 있다.
대강 구조를 익히자면 Controller에서 MyView를 생성할 때 viewPath를 주어 생성하고
MyView가 호출되면 넘겨받은 viewPath를 통해 render() 함수를 실행한다.
이제 컨트롤러를 작성해보자.
(여기서의 컨트롤러는, MyView를 Front Controller에 반환하는 컨트롤러)
ControllerV2 인터페이스
package hello.servlet.web.frontcontroller.v2;
import hello.servlet.web.frontcontroller.MyView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public interface ControllerV2 {
MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
비즈니스 로직을 수행하는 process 함수이지만 반환 타입을 보면 MyView이다.
말로 설명보다는 실제 컨트롤러를 구현해보는 것이 이해하는데에 수월하기에
바로 컨트롤러 구현체를 만들어보자.
MemberFormControllerV2
package hello.servlet.web.frontcontroller.v2.controller;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v2.ControllerV2;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MemberFormControllerV2 implements ControllerV2 {
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// String viewPath = "/WEB-INF/views/new-form.jsp";
// RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);// controller에서 view로 이동할 때 사용됨
// dispatcher.forward(request, response); // 다른 서블릿이나 JSP로 이동할 수 있는 기능이다. 서버 내부에서 다시 호출이 발생한다.
// 위와 동일
return new MyView("/WEB-INF/views/new-form.jsp");
}
}
이전에 작성했던 코드를 단 한줄로 줄일 수 있게 되었다.
컨트롤러에선 단지 비즈니스 로직만을 수행하고
viewpath가 담긴 MyView 객체를 프론트 컨트롤러에 넘겨주기만 하면 된다.
FrontControllerServletV2
package hello.servlet.web.frontcontroller.v2;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v1.ControllerV1;
import hello.servlet.web.frontcontroller.v2.controller.MemberFormControllerV2;
import hello.servlet.web.frontcontroller.v2.controller.MemberListControllerV2;
import hello.servlet.web.frontcontroller.v2.controller.MemberSaveControllerV2;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
// v1 하위에 어떤 url이 들어와도 일단 이 서블릿이 호출됨
@WebServlet(name = "frontControllerServletV2", urlPatterns = "/front-controller/v2/*")
public class FrontControllerServletV2 extends HttpServlet {
private Map<String, ControllerV2> controllerV2Map = new HashMap<>();
public FrontControllerServletV2() {
// 매개변수 1 url 요청이 오면 , MemberFormControllerV1 생성
controllerV2Map.put("/front-controller/v2/members/new-form", new MemberFormControllerV2());
controllerV2Map.put("/front-controller/v2/members/save", new MemberSaveControllerV2());
controllerV2Map.put("/front-controller/v2/members", new MemberListControllerV2());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();// 들어온 URL을 얻을 수 있다.
ControllerV2 controller = controllerV2Map.get(requestURI); // 요청 온 URI에 의해 Controller 객체가 다르게 호출
if(controller == null){ // 읽어온 URI에 매치되는 Controller 객체가 없다면
response.setStatus(HttpServletResponse.SC_NOT_FOUND); // 404 Not Found
return;
}
MyView view = controller.process(request, response);
view.render(request, response);
}
}
이전 포스팅과 비슷한 프론트 컨트롤러 패턴 구조이다.
"/front-controller/v2/*" 형태로 오는 모든 url로 요청이 오면 해당 프론트 컨트롤러(서블릿)가 호출된다.
이제 하위 요청 url에 따라 가져오는 컨트롤러가 달라지고
해당 컨트롤러를 실행하면(process() 함수 실행) MyView가 반환된다.
우리는 이 MyView의 render() 함수를 실행함으로써 JSP 페이지로 포워딩을 할 수 있게 된다.
하지만 아직도 중복되는 부분이 많다.
/WEB-INF/views/new-form.jsp
/WEB-INF/views/save-result.jsp
위와 같이 "/WEB-INF/views/" 부분이 겹치기도 하고
작성했던 컨트롤러에서의 역할은 MyView 반환 뿐이지만,
인터페이스를 구현할 때 HttpServletRequest, HttpServletResponse을 항상 매개변수로 받아온다.
다음 포스팅에서는 Model 추가를 통해 이 문제를 해결해보고자 한다.
'Java > Spring' 카테고리의 다른 글
[Spring MVC] 프론트 컨트롤러 패턴 | 구조 단순화(논리뷰 반환) | 회원 관리 예제(FrontController) (0) | 2022.06.01 |
---|---|
[Spring MVC] 프론트 컨트롤러 패턴 | 모델(Model) 추가 | 회원 관리 예제(FrontController) (0) | 2022.05.31 |
[Spring MVC] 프론트 컨트롤러 패턴 도입 | 프론트 컨트롤러 패턴 적용하기 - 회원 관리 예제(FrontController) (0) | 2022.05.16 |
[Spring] 웹 스코프(Prototype Scope) | 웹 스코프 특징 | 웹 스코프 종류 | 빈 스코프 (0) | 2022.01.30 |
[Spring] 프로토타입 스코프(Prototype Scope) | 빈 스코프 (0) | 2022.01.28 |