Spring/SpringMVC

View 분리 - v2

느리지만 꾸준하게 2022. 4. 24. 22:33

컨트롤러 모든 부분에서 뷰로 이동하는 부분에 중복이 있고, 깔끔하지 않았다.

String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);

 

 

이 부분을 분리하기 위해 뷰를 별도로 처리해서 객체를 만들자.

 

  • 컨트롤러가 JSP 포워딩에 대해서 고민하지 않고 MyView만 생성해서 호출만 해주면 된다.

 

MyView를 만들어보자.

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;

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

 

 

 

ControllerV2도 만들어주자. ControllerV1 형식과 비슷하지만 반환은 MyView를 반환한다.

package hello.setvlet.web.frontcontroller.v2;

import hello.setvlet.web.frontcontroller.MyView;

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

public interface ControllerV2 {

    MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}

 

회원등록 폼을 만들어주자. v2 - controller package를 만들어주고 MemberFormControlerV2 클래스를 만든다음 ControllerV2를 implements 해준다.

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

import hello.setvlet.web.frontcontroller.MyView;
import hello.setvlet.web.frontcontroller.v2.ControllerV2;

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

public class MemberFormControllerV2 implements ControllerV2 {
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        return null;
    }
}

 

기존의 MemberFormControllerV1 같은 경우는

아래와 같이 지정을 해주었다.

@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String viewPath = "/WEB-INF/views/new-form.jsp";
    RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
    dispatcher.forward(request, response);
}

 

MemberFormControllerV2 같은 경우는 변수 받고

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

import hello.setvlet.web.frontcontroller.MyView;
import hello.setvlet.web.frontcontroller.v2.ControllerV2;

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

public class MemberFormControllerV2 implements ControllerV2 {
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 변수 받고
        MyView myView = new MyView("/WEB-INF/views/new-form.jsp");
        return myView;
    }
}

 

 

 Inline Variable로 간단하게 지정해줄 수 있다.(ctrl + T)

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

import hello.setvlet.web.frontcontroller.MyView;
import hello.setvlet.web.frontcontroller.v2.ControllerV2;

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

public class MemberFormControllerV2 implements ControllerV2 {
    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 변수 받고
        // Inline으로 간단하게 지정
        return new MyView("/WEB-INF/views/new-form.jsp");
    }
}

 

 

 

MemberSaveControllerV2도 만들어주자.

// MemberSaveControllerV2

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

import hello.setvlet.domain.member.Member;
import hello.setvlet.domain.member.MemberRepository;
import hello.setvlet.web.frontcontroller.MyView;
import hello.setvlet.web.frontcontroller.v2.ControllerV2;

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

public class MemberSaveControllerV2 implements ControllerV2 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

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


        // 모델에 데이터를 보관한다.
        request.setAttribute("member", member);

        // 다 지워버릴 수 있다.
//
//        String viewPath = "/WEB-INF/views/save-result.jsp";
//        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
//        dispatcher.forward(request, response);

        return new MyView("/WEB-INF/views/save-result.jsp");
    }
}

 

 

MemberListControllerV2도 만들어주자.

// MemberListControllerV2

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

import hello.setvlet.domain.member.Member;
import hello.setvlet.domain.member.MemberRepository;
import hello.setvlet.web.frontcontroller.MyView;
import hello.setvlet.web.frontcontroller.v2.ControllerV2;

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.List;

public class MemberListControllerV2 implements ControllerV2 {

    // ControllerV1 인터페이스를 구현받고
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();
        request.setAttribute("members", members);
        return new MyView("/WEB-INF/views/members.jsp");
    }
}

 

 

자 FromControllerServletV2도 만들어주자.

// FrontControllerServletV2 Class

package hello.setvlet.web.frontcontroller.v2;

import hello.setvlet.web.frontcontroller.MyView;
import hello.setvlet.web.frontcontroller.v2.controller.MemberFormControllerV2;
import hello.setvlet.web.frontcontroller.v2.controller.MemberListControllerV2;
import hello.setvlet.web.frontcontroller.v2.controller.MemberSaveControllerV2;

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;

// v2/ 하위에 어떤 url이 들어와도 Servlet은 호출이 된다.
@WebServlet(name = "frontControllerServletV2", urlPatterns = "/front-controller/v2/*")
public class FrontControllerServletV2 extends HttpServlet {

    // 스트링으로 들어오면 ControllerV2로 찾는다.
    private Map<String, ControllerV2> controllerMap = new HashMap<>();

    // 값을 넣어주게 될 것이다.
    public FrontControllerServletV2() {
        controllerMap.put("/front-controller/v2/members/new-form", new MemberFormControllerV2());
        controllerMap.put("/front-controller/v2/members/save", new MemberSaveControllerV2());
        controllerMap.put("/front-controller/v2/members", new MemberListControllerV2());
    }

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


        String requestURI = request.getRequestURI();

        ControllerV2 controller = controllerMap.get(requestURI);
        if (controller == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        MyView view = controller.process(request, response);
        view.render(request, response);
    }
}

 

 

localhost:8080에서 확인을 해주자. 정상적으로 출력이 잘된다.

 

전체 프로세스는 회원가입을 하면 아래가 호출이 되고

 

이 형식에서

/front-controller/v2/*

 

front-controller에서 찾고

/front-controller/v2/members/new-form

 

컨트롤러에서 처리과정을 거쳐서

ControllerV2 controller = controllerMap.get(requestURI);
if (controller == null) {
    response.setStatus(HttpServletResponse.SC_NOT_FOUND);
    return;
}

process에서 가져오게 된다.

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

 

그러면 MemberControllerV2에서 new MyView를 생성해서 넘겨주게 된다.

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

 

그리고 FrontControllerServletV2에 그대로 넘어오고 render를 호출하면

// new MyView("/WEB-INF/views/new-form.jsp");
MyView view = controller.process(request, response);
view.render(request, response);

 

MyView에서 viewPath값이 이렇게 넘어올 수 있다.

Save도 마찬가지이다. 즉 Front Controller에서 공통로직을 다 처리하게 된다.

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

 

 

정리를 해보자.

V2 구조에서 클라이언트가 HTTP 요청을 하게 되면 매핑 정보 꺼내서 돌리고 Controller를 찾아서 Controller에 직접 호출하고 

Controller는 MyView를 생성만 하고 반환만 하고 Front Controller에서 MyView로 render를 호출하고 

JSP forward가 되고 HTML이 응답된다.

  • 즉 프론트 컨트롤러의 도입으로 `MyView` 객체의 render()를 호출하는 부분을 모두 일관되게 처리할 수 있고
  • 컨트롤러는 각각 MyView 객체를 생성만 해서 반환하면 된다.

 

 

 

 

 

<출처 김영한: 스프링 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' 카테고리의 다른 글

단순하고 실용적인 컨트롤러 - v4  (0) 2022.04.25
Model 추가 - v3  (0) 2022.04.24
프론트 컨트롤러 도입 - v1  (0) 2022.04.24
MVC 프레임워크 만들기  (0) 2022.04.23
MVC 패턴 - 한계  (0) 2022.04.23