minOS

MVC 프레임워크 만들기 - 프론트 컨트롤러 Ver 3 본문

TIL/김영한의 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

MVC 프레임워크 만들기 - 프론트 컨트롤러 Ver 3

minOE 2024. 7. 1. 18:45
728x90

Model 추가 - ver3


서블릿 종속성 제거
컨트롤러 입장에서 HttpServletRequest, HttpServletResponse이 꼭 필요할까?
1) 요청 파라미터 정보를 자바의 Map으로 대신 넘기도록 하면 지금 구조에서는 컨트롤러가 서블릿 기술을 몰라도 동작할 수 있다. 2) request 객체를 Model로 사용하는 대신에 별도의 Model 객체를 만들어서 반환하면 된다. 우리가 구현하는 컨트롤러가 서블릿 기술을 전혀 사용하지 않도록 변경해보자. 이렇게 하면 구현 코드도 매우 단순해지고, 테스트 코드 작성이 쉽다.


뷰 이름 중복 제거

컨트롤러에서 지정하는 뷰 이름에 중복이 있는 것을 확인할 수 있다.
컨트롤러는 뷰의 논리 이름 을 반환하고, 실제 물리 위치의 이름은 프론트 컨트롤러에서 처리하도록 단순화 하자. 이렇게 해두면 향후 뷰의 폴더 위치가 함께 이동해도 프론트 컨트롤러만 고치면 된다.

ex)

/WEB-INF/views/new-form.jsp -> new-form`
/WEB-INF/views/save-result.jsp -> save-result

 

VER 3 구조


ModelView

지금까지 컨트롤러에서 서블릿에 종속적인 HttpServletRequest를 사용했다. 그리고 Model`request.setAttribute()` 를 통해 데이터를 저장하고 뷰에 전달했다.

서블릿의 종속성을 제거하기 위해 Model을 직접 만들고, 추가로 View 이름까지 전달하는 객체를 만들어보자.

@Getter @Setter
public class ModelView {
    private String viewName;
    private Map<String,Object> Model = new HashMap<>();

    public ModelView(String viewName) {
        this.viewName = viewName;
    }


}

뷰의 이름과 뷰를 렌더링할 때 필요한 model 객체를 가지고 있다. model 은 map으로 되어 있으므로 컨트롤러에서 뷰에 필요한 데이터를 key ,value로 넣어주면 된다.


Controller Ver3

public interface ControllerV3 {

    ModelView process(Map<String,String> paramMap);  //서블릿에 종속적이지 않음
}

 이 컨트롤러는  서블릿 기술을 전혀 사용하지 않는다. (ver2 는 사용) 따라서 구현이 매우 단순해지고 , 테스트 코드 작성이 쉽다.

 HttpServletRequest가 제공하는 파라미터는 프론트 컨트롤러가 paramMap에 담아서 호출해주면 된다.
응답 결과로 뷰 이름과 뷰에 전달할 Model 데이터를 포함하는 ModelView 객체를 반환한다.

MemberFormControllerVer3 - 회원 등록 폼

public class MemberFormControllerV3 implements ControllerV3 {
    @Override
    public ModelView process(Map<String, String> paramMap) {
        return new ModelView("new-form");
    }
}

ModelView 를 생성할때 "new-form"이라는 viewName을 지정한다.(논리 이름)
실질적인 물리적인 이름은 프론트 컨트롤러에서 처리한다. 모델 뷰 반환


MemberSaveControllerV3 - 회원 저장  

public class MemberSaveControllerV3 implements ControllerV3 {

    private MemberRepository memberRepository = MemberRepository.getInstance();
    @Override
    public ModelView process(Map<String, String> paramMap) {
        String username = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));


        Member member= new Member(username,age);
        memberRepository.save(member);

        ModelView mv = new ModelView("save-result");
        mv.getModel().put("member",member);
        return mv;

    }
}

 

paramMap.get("username");
- 파라미터 정보는 map에 담겨있다. map에서 필요한 요청 파라미터를 조회하면 된다.

mv.getModel().put("member", member);
-모델은 단순한 map이므로 모델에 뷰에서 필요한 "member" 객체를 담고 모델 뷰 반환한다.


FrontControllerServletV3

 

@WebServlet(name="frontControllerServletV3",urlPatterns = "/front-controller/v3/*")
public class FrontControllerServletV3 extends HttpServlet {
    //key  == url , value = controller
    private Map<String, ControllerV3> controllerMap = new HashMap<>();

    public FrontControllerServletV3() {
        controllerMap.put("/front-controller/v3/members/new-form", new MemberFormControllerV3());
        controllerMap.put("/front-controller/v3/members/save", new MemberSaveControllerV3());
        controllerMap.put("/front-controller/v3/members", new MemberListControllerV3());
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String requestURI = request.getRequestURI();
        ControllerV3 controller = controllerMap.get(requestURI);
        if (controller == null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        //paramMap 넘겨줘야함
        Map<String, String> paramMap = createParamMap(request);


       ModelView mv = controller.process(paramMap);
        String viewName = mv.getViewName();// 논리이름 -> new-form
        MyView view = viewResolve(viewName);

        view.render(mv.getModel(),request,response);


    }

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

    private static 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;
    }
}


createParamMap()

HttpServletRequest에서 파라미터 정보를 꺼내서 Map으로 변환한다. 그리고 해당 Map( `paramMap` )을 컨트롤러 에 전달하면서 호출한다.



뷰 리졸버

MyView view = viewResolver(viewName)

 

컨트롤러가 반환한 논리 뷰 이름을 실제 물리 뷰 경로로 변경한다. 그리고 실제 물리 경로가 있는 MyView 객체를 반환 한다.

 

view.render(mv.getModel(), request, response)`
- 뷰 객체를 통해서 HTML 화면을 렌더링 한다.
- 뷰 객체의 `render()` 는 모델 정보도 함께 받는다.
- JSP
`request.getAttribute()` 로 데이터를 조회하기 때문에, 모델의 데이터를 꺼내서

-  request.setAttribute()` 로 담아둔다. JSP로 포워드 해서 JSP를 렌더링 한다.

 

728x90