Spring/SpringMVC

Model 추가 - v3

느리지만 꾸준하게 2022. 4. 24. 23:40

서블릿 종속성을 제거하고 구현 코드를 단순화 해보자.

요청 파라미터 정보는 자바의 Map으로 대신 넘기도록 하면 지금 구조에서 컨트롤러가 서블릿 기술을 몰라도 동작할 수 있다.

또 request 객체를 Model로 사용하는 대신 별도의 Model 객체를 만들어서 반환하면 된다.

public MyView process(HttpServletRequest request, HttpServletResponse response)

request, response 코드가 없어도 되는 것이다.

구현하는 컨트롤러가 서블릿 기술을 사용하지 않도록 변경 해보면 구현코드와 테스트 코드 둘 다 작성이 쉬워진다.

HttpServletRequest request, HttpServletResponse response

 

뷰 이름 중복 제거도 해주는데

컨트롤러에서 지정하는 뷰 이름에 중복이 있다. 뷰의 논리 이름을 반환하고 실제 위치는 프론트 컨트롤러에서 처리하도록 하면

뷰의 폴더 위치가 함께 이동해도 프론트 컨트롤러만 고치면 된다.

return new MyView("/WEB-INF/views/new-form.jsp");

 

아래의 3개만 집어넣도록 하자.

new-form
members
save-result

 

 

프론트 컨트롤러에서 이렇게 한번만 바꿔주면 될 것이다. 즉 변경의 지점을 하나로 만들 수 있다.

 

V3 구조에서 viewResolver이 물리이름을 바꿔주고 MyView반환을 FrontController로 해줄 것이다.

그리고 render를 호출해주면 된다.

 

ModelView

  • 컨트롤러에서 서블릿에 종속적인 HttpServletRequest를 사용했는데 Model도 request.setAttribute()를 통해
  • 데이터를 저장하고 뷰에 전달했다.
  • 서블릿의 종속성을 제거하기 위해 Model을 직접 만들고, 추가로 View 이름까지 전달하는 객체를 만들자.

     

     

     

     

     

ModelView를 만들어 준 다음

// ModelView를 만들어주고

package hello.setvlet.web.frontcontroller;

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

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

 

ControllerV3도 만들어준다.

// ControllerV3
package hello.setvlet.web.frontcontroller.v3;

import hello.setvlet.web.frontcontroller.ModelView;

import java.util.Map;

public interface ControllerV3 {

    // Servlet 기술x
    ModelView process(Map<String, String> paramMap);
}

 

 

구현을 해주자. MemberFormControllerV3를 만들어주고

package hello.setvlet.web.frontcontroller.v3.controller;

import hello.setvlet.web.frontcontroller.ModelView;
import hello.setvlet.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");
    }
}

 

 

MemberSaveControllerV3도 만들고

// MemberSaveControllerV3 Class

package hello.setvlet.web.frontcontroller.v3.controller;

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

import java.util.Map;

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

 

 

MemberListControllerV3도 만들어준다.

package hello.setvlet.web.frontcontroller.v3.controller;

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

import java.util.List;
import java.util.Map;

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

 

 

FrontControllerServletV3 Class를 만들어 준다.

createParamMap을 이용하기 위해 (Command + option + m을 이용)

package hello.setvlet.web.frontcontroller.v3;

import hello.setvlet.web.frontcontroller.ModelView;
import hello.setvlet.web.frontcontroller.MyView;
import hello.setvlet.web.frontcontroller.v3.controller.MemberFormControllerV3;
import hello.setvlet.web.frontcontroller.v3.controller.MemberListControllerV3;
import hello.setvlet.web.frontcontroller.v3.controller.MemberSaveControllerV3;

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;

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

        Map<String, String> paramMap = createParamMap(request);
        ModelView mv = controller.process(paramMap);
        view.render(request, response);
    }

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

 

 

실제 view를 찾아주는 기능인 viewResolver라는 기능도 만들어주자.

 

이 부분도 (command+option+m)을 이용해서 viewResolver로 뽑아주자.

MyView view = viewResolver(viewName);
package hello.setvlet.web.frontcontroller.v3;

import hello.setvlet.web.frontcontroller.ModelView;
import hello.setvlet.web.frontcontroller.MyView;
import hello.setvlet.web.frontcontroller.v3.controller.MemberFormControllerV3;
import hello.setvlet.web.frontcontroller.v3.controller.MemberListControllerV3;
import hello.setvlet.web.frontcontroller.v3.controller.MemberSaveControllerV3;

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;

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

        Map<String, String> paramMap = createParamMap(request);
        ModelView mv = controller.process(paramMap);

        String viewName = mv.getViewName();// 논리이름 new-form
        MyView view = viewResolver(viewName);

        view.render(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;
    }
}

 

이 부분도 create method로 해주자.

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

 

 

MyView에서도  아래와 같이 만들어준다.

command + option +m 을 이용해서 modelToRequestAttribute로 뽑아주고 만들자.

public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    modelToRequestAttribute(model, request);
    RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
    dispatcher.forward(request, response);
}

private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
    model.forEach((key, value) -> request.setAttribute(key, value));
}
package hello.setvlet.web.frontcontroller;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

public class MyView {
    private String viewPath; // new MyView("/WEB-INF/views/new-form.jsp");

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

    public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        modelToRequestAttribute(model, request);
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }

    private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
        model.forEach((key, value) -> request.setAttribute(key, value));
    }
}

 

결과값이 잘 나온다.

 

 

프론트 컨트롤러 Servlet V3에 있는 경로에서

@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")

 

 

얘가 호출이 될 것이고 

controllerMap.put("/front-controller/v3/members/new-form", new MemberFormControllerV3());

 

 

createParamMap을 가지고 map을 다 뽑는다.

그리고 paramMap을 만들어서 반환을 해준다.

Map<String, String> paramMap = createParamMap(request);
ModelView mv = controller.process(paramMap);

 

MemberFormControllerV3에서는 논리이름만 넣게된다.(viewResolver가 논리이름을 물리이름으로 바꿔줌)

@Override
public ModelView process(Map<String, String> paramMap) {
    // 논리적인 이름을 넣어주자
    return new ModelView("new-form");
}

 

 

그리고 MyView를 반환을 해준다.

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

 

 

그리고 model에 담긴데이터를 HttpServletRequesrt에 setAttribute로 넣어서 담고  RequestDispatcher dispatcher로 해서 JSP로 이동을 해준다.

그 다음 JSP로 렌더링이 될 것이다.

public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    modelToRequestAttribute(model, request);
    RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
    dispatcher.forward(request, response);
}

private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
    model.forEach((key, value) -> request.setAttribute(key, value));
}

 

 

그래서 아래 controller 코드 3가지를 보게되면 Servlet 종속성 제거하고

뷰 이름의 중복을 다 제거해주었다.

//MemberControllerV3

package hello.setvlet.web.frontcontroller.v3.controller;

import hello.setvlet.web.frontcontroller.ModelView;
import hello.setvlet.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");
    }
}

 

// MemberListControllerV3

package hello.setvlet.web.frontcontroller.v3.controller;

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

import java.util.List;
import java.util.Map;

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

 

// // MemberSaveControllerV3

package hello.setvlet.web.frontcontroller.v3.controller;

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

import java.util.Map;

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

 

Model 추가 - V3 코드를 처음부터 다시한번 살펴보면서 학습하자. 굉장히 어렵다.

 

 

 

 

 

 

<출처 김영한: 스프링 MVC 1편 - 벡앤드 웹 개발 핵심 기술>

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

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -

www.inflearn.com

 

'Spring > SpringMVC' 카테고리의 다른 글

유연한 컨트롤러1 - v5  (0) 2022.04.25
단순하고 실용적인 컨트롤러 - v4  (0) 2022.04.25
View 분리 - v2  (0) 2022.04.24
프론트 컨트롤러 도입 - v1  (0) 2022.04.24
MVC 프레임워크 만들기  (0) 2022.04.23