오봉이와 함께하는 개발 블로그

스프링 MVC 1 - View 분리 v2 본문

BE/Servlet

스프링 MVC 1 - View 분리 v2

오봉봉이 2022. 8. 11. 20:57
728x90

View 분리 - v2

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

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

이 부분을 깔끔하게 분리하기 위해 별도로 뷰를 처리하는 객체를 만들어보자.

v2 구조

  1. 클라이언트가 서버에 요청
  2. 프론트 컨트롤러가 매핑 정보를 조회해서 기능이 있는 컨트롤러를 호출
  3. 기능 컨트롤러는 MyView 객체를 프론트 컨트롤러에 반환
  4. 프론트 컨트롤러는 MyView의 render()호출
  5. JSP forward

MyView

// package hello.servlet.web.frontcontroller;

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

// package hello.servlet.web.frontcontroller.v2;

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

MemberFormControllerV2 - 회원 등록 폼

// package hello.servlet.web.frontcontroller.v2.controller;

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

각 컨트롤러는 복잡한 dispatcher.forward()를 직접 작성해서 호출하지 않아도 된다.
단순히 MyView 객체를 생성하고 거기에 뷰 이름만 반환하면 된다.

MemberSaveControllerV2 - 회원 저장

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

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

MemberListControllerV2 - 회원 목록

public class MemberListControllerV2 implements ControllerV2 {

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

프론트 컨트롤러 V2

// package hello.servlet.web.frontcontroller.v2;

@WebServlet(name = "frontControllerServletV2", urlPatterns = "/front-controller/v2/*")
public class FrontControllerServletV2 extends HttpServlet {

    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 {
        System.out.println("FrontControllerServletV2.service");
        String requestURI = request.getRequestURI(); // 클라이언트 URL에서 controllerMap에 있는 URL중 하나를 찾아서 클래스를 생성해준다.

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

ControllerV2의 반환 타입이 MyView이기 때문에 프론트 컨트롤러는 컨트롤러 호출 결과로 MyView를 반환 받는다.
그리고 view.render()를 호출하면 forward 로직을 호출해서 JSP가 실행된다.

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

출처 : 인프런 김영한 지식 공유자님 강의 - 스프링 MVC 1편 백엔드 웹 개발 핵심 기술
728x90
Comments