[Spring MVC] #2 프레임워크 만들기 (3) Model 추가

Jaeyoo (유재형)·2022년 2월 2일
1

SpringMVC

목록 보기
4/12
post-thumbnail
post-custom-banner

이전에 view를 분리한 구조에서 각 컨트롤러에 불필요한 request, response가 있었다.
Model을 추가해서 서블릿 종속성을 제거하자. 그리고 추가로 뷰 이름의 중복도 제거해보자


수정해야 할 부분


1. 서블릿 종속성 제거

  • 컨트롤러 입장에서는 HttpServletRequest, HttpServletResponse가 꼭 필요없다.
  • 요청 파라미터 정보를 Map으로 넘기면 서블릿 기술 없이 동작가능.
  • request 객체를 Model로 사용하는 대신 별도의 Model 객체를 만들어 반환하자

2. 뷰 이름 중복 제거

  • 컨트롤러는 뷰의 논리 이름을 반환하고, 실제 물리위치의 이름은 프론트 컨트롤러에서 처리하도록 단순화 하자.

➡️ 이렇게 하면 나중에 뷰의 폴더 위치가 변경되어도 프론트 컨트롤러 부분만 수정하면 된다.


Model 추가


구조

ModelView

public class ModelView {
    private String viewName;
    private Map<String, Object> model = new HashMap<>();

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

    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;
    }
}
  • 서블릿 종속성을 제거하기위해 Model을 직접만들고 View의 이름까지 전달하는 객체 생성
  • ModelView 클래스의 멤버변수로는 view의 논리적 이름과 Model 역할의 Map 이있다.

ControllerV3

public interface ControllerV3 {

    ModelView process(Map<String, String> paramMap);
}
  • 인터페이스
  • HttpServletRequest, HttpServletResponse 없이 간단하다.

회원 등록 컨트롤러

public class MemberFormControllerV3 implements ControllerV3 {

    @Override
    public ModelView process(Map<String, String> paramMap) {
        return new ModelView("new-form");
    }
}
  • view의 논리적 이름만 넣는다.

회원 저장 컨트롤러

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;
    }
}
  • 요청 파라미터의 값을 Map으로부터 가져온다.
  • member 객체를 ModelView의 model에 저장한다.

회원 목록 컨트롤러

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

프론트 컨트롤러 V3

@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")
public class FrontControllerServletV3 extends HttpServlet {

    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();
        MyView view = viewResolver(viewName);
        view.render(mv.getModel(), 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;
    }
}
  • paramMap에 request에 담긴 정보들을 넣어주어야한다.
  • viewResolver에서 뷰의 논리 이름을 물리적 이름으로 변경해준다.
  • render함수에 model 매개변수를 추가해주어야해서 MyView에 render 함수를 오버로딩한다.
    ⚠️ 함수의 시그니처가 다르기때문에 오버라이딩 아님
    • render 함수에서는 model에 있는 데이터를 전부 request로 넣어준다.
      ➡️ request의 생명주기가 response될때 까지이므로 뷰에 전달해줄때 model의 역할을 해준다.
  • createParamMap에서는 요청 파라미터의 정보를 꺼내서 Map으로 변환
  • 메서드 추출을 하고 내부에서만 사용하기때문에 private 처리해준다.

더 개선 해야될 부분

  • 각 컨트롤러에서 ModelView를 생성해서 반환해야하는 부분이 불편
profile
기록과 반복
post-custom-banner

0개의 댓글