MVC 프레임워크 만들기

이성준·2022년 2월 20일
0

스프링 MVC

목록 보기
4/10

프론트 컨트롤러 패턴이란? 공통적인걸 앞에서 처리하는 패턴



단순하게 매핑 정보만 넣어보자

ControllerV1

public interface ControllerV1 {
    void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;  
}

먼저 컨트롤러 인터페이스를 도입, 각 컨트롤러는 이걸 구현한것 그리고 기존에 했던 서블릿을 그대로 구현해 준다.

FrontControllerServletV1

@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")
//하위에 들어오는건 어떤것이던지 호출
public class FrontControllerServletV1 extends HttpServlet {

    private Map<String, ControllerV1> controllerMap = new HashMap<>();

    public FrontControllerServletV1() {
        controllerMap.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
        controllerMap.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
        controllerMap.put("/front-controller/v1/members", new MemberListControllerV1());
    }

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

        String requestURI = request.getRequestURI();
        //localhost:8080/ 여기서부터 받아옴
        ControllerV1 controller = controllerMap.get(requestURI);
        if(controller ==null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        controller.process(request,response);

co
frontcontroller호출
팁: 리팩토링할때 구조적인걸 개선하고 세밀한걸 개선

매핑정보에서 컨트롤러를 찾아와서 컨트롤러 호출 v1에서는 컨트롤러가 jsp를 직접 forward했지만
V2는 MyView가 대신 jsp를 호출해준다
http://localhost:8080/front-controller/v2/members 입력 -> 그럼 FrontControllerServlet2가 호출됨

FrontControllerServlet2

 MyView view = controller.process(request, response);
        view.render(request,response);
    }

process 호출 그다음 MemberFormControllerV2로 가서 new Myview를 넘겨줌
그다음 render를 호출

MemberFormControllerV2

public class MyView {
    private String viewPath;

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

jsp 호출
v2 의의: 화면을 렌더링하기위한 myview객체가 생성됨



기존에는 controller에서 view를 반환했다 이번에는 modelview를 반환 그리고 논리이름을 viewresolver가 물리이름으로 바꿔서 반환해준다
v3의의 : 컨트롤러가 서블릿 기술을 몰라도 동작할수 있게 하자, 뷰 이름에 중복이 있으니까 물리위치의 이름을 프론트 컨트롤러에서 처리하도록 하자

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

모델 객체 생성으로 인해 controller에서 서블릿 종속성을 제거

ModelSaveControllerV3

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

v2에서는 request.setAttribute에서 파라미터정보를 관리했지만 v3에서는 modelview 객체에 member를 담아준다.

frontControllerServletV3

@WebServlet(name = "frontControllerServletV3", urlPatterns = "/frontcontroller/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();
 MyView view = viewResolver(viewName);
 view.render(mv.getModel(), 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;
 }
 private MyView viewResolver(String viewName) {
 return new MyView("/WEB-INF/views/" + viewName + ".jsp");
 }
}

createParamap에서 파라미터 정보를 꺼내 Map으로 변환한다 그리고 viewresolver가 물리 경로를 반환

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


ModelView를 객체를 생성하고 반환해야 하는 부분이 살짝 번거롭다
그래서 컨트롤러가 modelview를 반환안하고 viewName만 반환한다.

public interface ControllerV4 {
    String process(Map<String, String> paramMap, Map<String, Object> model);
}

modelview를 반환안함 => model 객체가 그냥 파라미터로 전달 되기 때문

MemberFormControllerV4

public class MemberFormControllerV4 implements ControllerV4 {
 @Override
 public String process(Map<String, String> paramMap, Map<String, Object>
model) {
 return "new-form";
 }
}

new-form 이라는 논리 이름만 반환함

FrontControllerServletV4 는 이전과 거의 동일

Map<String, Object> model = new HashMap<>();
String viewName = controller.process(paramMap, model);
MyView view = viewResolver(viewName);

모델 객체를 프론트 컨트롤러에서 생성해서 넘겨주고, 모델 객체에 값을 담으면 여기에 그대로 담김

0개의 댓글

Powered by GraphCDN, the GraphQL CDN