Free Lines Arrow
본문 바로가기
Spring/spring framework 구현 스터디

[Spring] 프론트 컨트롤러 3 : 종속성제거, 뷰리졸버 구현

by skahn1215 2021. 10. 23.
728x90
반응형

프론트 컨트롤러 종속성제거, 뷰리졸버 구현

  • 이전에 반복되는 코드들을 줄인다.
  • 프론트컨트롤러에 많은 역할을 주고 일반 컨트롤러에서는 종속성을 제거한다.

 

서블릿 종속성을 제거한다.

  • 컨트롤러 입장에서 HttpServletRequest, HttpServletResponse 가 꼭 필요하진 않다.
  • 그렇다면 requset 에서 model을 가져다 썼는데 어떻게 해결할까?
     - 직접 만들어서 사용한다.

 

종속성을 제거의 장점

  • 불필요한 코드 request, response 를 줄일수 있다.
  • 만약 파일의 경로가 변경되면 프론트컨트롤러 만 손보면 된다.
  • 유지보수가 간편해 진다. 

 

뷰이름 중복제거

  • 컨트롤러마다 뷰이름이 계속해서 등장하고 절대경로를 사용한다.
  • 상대경로로 변경하여 구현한다.
/WEB-INF/views/new-form.jsp  ->  new-form
/WEB-INF/views/save-result.jsp  ->  save-result
/WEB-INF/views/members.jsp  ->   members

 

 

구조

  • 기존에는 모든 컨트롤러들이 repuest 와 response 를 가지고 있었다.
  • 종속성제거를 적용하여 이를 해결한다.
  • 모델도 직접 만들어서 사용한다.

 

 

 

 

 

Controller Interface

  • 반환타입은 ModelView 이다.
  • repuest 와 response 는 빠지게 되었다.
package hello.servlet.web.frontcontroller.v3;

import hello.servlet.web.frontcontroller.ModelView;

import java.util.Map;

public interface ControllerV3 {
    // v2 와 비교해보면 상당히 단순해졌다.
    ModelView process(Map<String, String> paramMap);
}

 

 

 

Front Controller

  • 맵핑을 담당한다.
  • 컨트롤러에게 넘어온 데이터들을 
  • createParamMap 메서드를 통해 데이터를 다 담는다
     - setAttribute 에 담는다.
  • viewResolver 를 통해 뷰를 생성한뒤 render 를 수행한다.
     - 논리 이름을 물리이름으로 변경하는 역할을 담당한다.
// /front-controller/v1/* 주소의 * 때문에 어떤 url이 들어와도 일단 해당 컨트롤러가호출된다.
@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")
public class FrontControllerServletV3 extends HttpServlet {
    private Map<String, ControllerV3> controllerMap = new HashMap<>();

    // 프론트 컨트롤러 map 셋팅
    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 req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        ControllerV3 controllerV3 = controllerMap.get(requestURI);
        if (controllerV3 == null) {
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 모델 뷰를 가져오는 부분
        Map<String, String> paramMap = createParamMap(req);
        ModelView mv = controllerV3.process(paramMap);

        // 뷰리졸버 구현 논리 이름을 물리 이름으로 변경해야 된다.
        String viewName = mv.getViewName();
        MyView view = viewResolver(viewName);

        view.render(mv.getModel(), req, resp);
    }

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

    // 레벨을 맞춰주기 위해 메서드로 뽑아준다.
    private Map<String, String> createParamMap(HttpServletRequest req) {
        // paramMap을 넘겨줘야한다.
        Map<String, String> paramMap = new HashMap<>();
        req.getParameterNames().asIterator()
                .forEachRemaining(paramName -> paramMap.put(paramName, req.getParameter(paramName)));
        return paramMap;
    }
}

 

 

 

 

ModelView class

  • view name 과 map으로 데이터를 담는다.
package hello.servlet.web.frontcontroller;

import java.util.HashMap;
import java.util.Map;

public class ModelView {
    private String viewName;

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

    // jsp 에서 쓸수 있도록 데이터를 담아 둔다.
    private Map<String, Object> model = new HashMap<>();

    public String getViewName() {
        return viewName;
    }

    public void setViewName(String viewName) {
        this.viewName = viewName;
    }

    public Map<String, Object> getModel() {
        return model;
    }

    public void setModel(Map<String, Object> model) {
        this.model = model;
    }
}

 

 

 

MemberFormController

  • 따로 데이터를 처리할게 없기 때문에 
  • 뷰 이름만 담아서 리턴한다.
package hello.servlet.web.frontcontroller.v3.controller;

import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;

import java.util.Map;

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

 

 

MemberListController

  • Repository 에서 모든 데이터를 꺼내온다.
  • 모델뷰에서 모델을 가져온뒤 데이터를 담는다.
public class MemberListControllerV3 implements ControllerV3 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public ModelView process(Map<String, String> paramMap) {

        List<Member> members = memberRepository.findAll();
        ModelView mv = new ModelView("members");
        mv.getModel().put("members", members);

        return mv;
    }
}

 

 

 

MemberSaveController

  • 프론트 컨트롤러에서 데이터를 받아
  • 데이터를 저장한다.
public class MemberSaveControllerV3 implements ControllerV3 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

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

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

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

        return mv;
    }
}

 

 

 

결론

  • 해당 과정을 통해 프론트컨틀로러에게 위임할수 있는 책임을 다 위임하고
  • 나머지 컨트롤러들에게는 쉽게 구현하도록 책임을 최소화 시켰다.

 

 

 

 

 

참고

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

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 학습 페이지

지식을 나누면 반드시 나에게 돌아옵니다. 인프런을 통해 나의 지식에 가치를 부여하세요....

www.inflearn.com

 

728x90
반응형

댓글