본문으로 바로가기
반응형

 

 

이전 포스팅에서 프론트 컨트롤러의 개념에 대해 알아보았다.

 

 

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 화면으로의 이동 역할을 단독으로 수행하게끔 하고자 한다.

 

 

 

구조는 아래와 같다.

출처: 스프링 MVC 1편(김영한)

 

 

기존에 작성했던 코드에서 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 추가를 통해 이 문제를 해결해보고자 한다.

 

 

 

 

 

반응형