아무나개발하자

프론트 컨트롤러 패턴 소개4 본문

Spring

프론트 컨트롤러 패턴 소개4

개발천재나천재 2022. 7. 10. 09:36

단순하고 실용적인 컨트롤러 - v4

앞서 만든 v3 컨트롤러도 서블릿 종속성을 제거하고, 뷰 경로의 중복을 제거하는 등 잘 설계된 컨트롤러이다. 하지만 또 다르게 중요한것은 "개발자가 편하게 사용 할 수있냐?? "이다. 아무리 구조가 좋고 설계가 잘된 프레임워크라도 개발자가 사용하기 불편하면 망한다. 지금 까지 spring프레임워크가 가장 사랑 받고 인기있었던 이유 중 하나도 개발자가 사용하기에 너무 편한 프레임 워크이다. 물론 spring 프레임워크는 구조와 설계도 아주 잘된 프레임워크이다.

하여튼 우리가 지금까지 구현한 v3를 보면 컨트롤러에서 ModelView 객체를 직접 생성하고 반환하는 번거로운 부분이 있었다. 이부분도 개발자 입장에서 조금더 편하게 사용할 수 있게 바꿔보도록 하자!

ControllerV4

package hello.servlet.web.frontcontroller.v4;

import java.util.Map;

public interface ControllerV4 {
    /**
     *
     * @param paramMap
     * @param model
     * @return
     */
    String process(Map<String, String> paramMap, Map<String, Object> model);

}

ControllerV4에서는 인터페이스를 반환하지 않는다. 그러면 어떻게 view경로와 데이터를 반환 할 수있을지 의문일것이다. 그래서 이번 process함수는 model 파라미터를 사용한다. 프론트 컨트롤러에서 넘겨준 model 파라미터에 데이터를 담는다, 또한 view 경로는 String으로 반환하여 프론트 컨트롤러에 전달한다. 그러면 프론트 컨트롤러는 이 값을 잘 이용해서 JSP를 render하게 된다.

FrontControllerServletV4

package hello.servlet.web.frontcontroller.v4;

import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v4.controller.MemberFormControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberListControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberSaveControllerV4;

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;

//frontController가 하는 역할이 uri 정보에 따라 controller를 매핑시켜주는 역할을 한다.
@WebServlet(name = "frontControllerServletV4", urlPatterns = "/front-controller/v4/*")
public class FrontControllerServletV4 extends HttpServlet {

    //key : url, value : Controller
    private Map<String, ControllerV4> controllerMap = new HashMap<>();

    public FrontControllerServletV4() {
        controllerMap.put("/front-controller/v4/members/new-form", new MemberFormControllerV4());
        controllerMap.put("/front-controller/v4/members/save", new MemberSaveControllerV4());
        controllerMap.put("/front-controller/v4/members", new MemberListControllerV4());
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("FrontControllerServletV4.service");

        //클라이언트로 부터 입력된 uri 정보를 받아온다.
        String requestURI = request.getRequestURI();

        //controllerMap으로 클라이언트로 부터 입력된 uri 정보를 통해 controller를 매핑.
        ControllerV4 controller = controllerMap.get(requestURI);

        //controller가 매핑되지 못했을 경우를 대비해서 만들어줌
        if(controller == null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }


        Map<String, String> paramMap = createParamMap(request);

        //모델 객체를 프론트 컨트롤러에서 생성해서 넘겨준다.
        //컨트롤러에서 모델 객체에 값을 담으면 model변수에 그대로 담겨있게 된다.
        Map<String, Object> model = new HashMap<>();

        String viewName = controller.process(paramMap, model);

        MyView view = viewResolver(viewName);

        //고대로 model을 사용(controller에서 담은 값이 그대로 있게됨)
        view.render(model, request, response);

    }

    private MyView viewResolver(String viewName) {
        return new MyView("/WEB-INF/views/" + viewName + ".jsp");
    }


    private Map<String, String> createParamMap(HttpServletRequest request) {

        Map<String, String> paramMap = new HashMap<>();

        request.getParameterNames().asIterator()
                .forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));

        return paramMap;
    }


}

구조는 v3와 동일하다 하지만 달라진것은 컨트롤러에서 process함수의 인자가 달라졌다. 그래서 프론트 컨트롤러에서는 모델 객체를 만들어서 process함수에 넘겨준다. 그러면 컨트롤러는 비즈니스 로직을 수행하고, 인자로 받은 모델에 데이터를 넣는다. 또한 view 경로로 지정해서 넘겨준다. 그러면 프론트 컨트롤러에서는 view의 논리적 경로를 절대적 경로로 바꾸고 model과 함께 JSP로 render한다.

 

정리

이번 버전의 컨트롤러는 매우 단순하고 실용적이다. 기존 구조에서 모델을 파라미터로 넘기고, 뷰의 논리 이름을 반환한다는 작은 아이디어를 적용했을 뿐인데, 컨트롤러를 구현하는 개발자 입장에서 보면 이제 군더더기 없는 코드를 작성할 수 있다. 또한 중요한 사실은 여기까지 한번에 온 것이 아니라는 점이다. 프레임워크가 점진적으로 발전하는 과정 속에서 이런 방법도 찾을 수 있었다.   - 김영한 -

 

프레임워크나 공통 기능이 수고로워야 사용하는 개발자가 편리해진다          - 김영한 -

 

코드 : refactor : 단순하고 실용적인 컨트롤러-v4

https://github.com/dlqjagml/spring/commits/master

출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1

'Spring' 카테고리의 다른 글

HTTP 메시지 컨버터2  (0) 2022.07.16
HTTP 메시지 컨버터  (0) 2022.07.14
프론트 컨트롤러 패턴 소개3  (0) 2022.07.09
프론트 컨트롤러 패턴 소개2  (0) 2022.07.08
프론트 컨트롤러 패턴 소개1  (0) 2022.07.07