Free Lines Arrow
본문 바로가기
Spring/spring framework 구현 스터디

[Spring] 프론트 컨트롤러 2 : 적용, 뷰 분리

by skahn1215 2021. 10. 21.
728x90
반응형

프론트 컨트롤러 적용 과 뷰 분리

프론트 컨트롤러를 직접 만들어 보자.

 

프론트 컨트롤러 적용

 

 

전체적인 구조

 

 

 

ControllerV1 interface

인터페이스로 만든 이유
 - 다형성으로 필요한 컨트롤러들을 구현한다.

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

 

 

MemberFormContrellerV1

  • 앞서 구현한 기존로직과 동일하다.
  • 다른점은 ControllerV1 을 상속받아 구현을 하였다.
public class MemberFormControllerV1 implements ControllerV1 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public void process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();

        req.setAttribute("members", members);

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

 

 

MemberSaveControllerV1

  • 역시 동일하다.
public class MemberSaveControllerV1 implements ControllerV1 {

    private MemberRepository memberRepository = MemberRepository.getInstance();

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

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

        //Model 데이터 보관
        req.setAttribute("member",member);

        String viewPath = "/WEB-INF/views/save-result.jsp";
        RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
        dispatcher.forward(req,resp);
    }
}

 

 

MemberLIstControllerV1

public class MemberLIstControllerV1 implements ControllerV1 {

    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

 

 

 

FrontControllerServletv1 핵심이다.

  • 여기서는 서블릿을 상속받아 FrontController 를 구현하였다.
  • urlpatterns 의 마지막 * 은 해당 URI 계층의 모든 요청을 처리하기 위해서이다.
  • Map 으로 Key 는 urlpattern, value 는 다형성을 이용하여 요청에 맞는 컨트롤러를 호출한다.
// /front-controller/v1/* 주소의 * 때문에 어떤 url이 들어와도 일단 해당 컨트롤러가호출된다.
@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 req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("FrontControllerServletV1.service");
        String requestURI = req.getRequestURI();
        ControllerV1 controllerV1 = controllerMap.get(requestURI);
        if (controllerV1 == null) {
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        controllerV1.process(req, resp);
    }
}

 

 

뷰 분리

코드를 보면 뷰 즉 JSP 를 호출 foward 하는 부분이 중복적으로 발생한다.

이것을 하나의 객체에서 관리해보자.

 

 

MyView class

  • dispatcher 를 가져오는 로직과
  • forward 로직을 여기서 호출한다.
public class MyView {
    private String viewPath;

    public MyView(String viewPath) {
        this.viewPath = viewPath;
    }

    public void render(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
        dispatcher.forward(req, resp);
    }
}

 

 

Controller Interface

  • 위예제와 동일하지만 다른점은 반환타입이 MyView 이다.
// 기존과 동일하지만 반환을 MyView로 한다.
public interface ControllerV2 {
    MyView process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
}

 

 

FrontControllerServletV2

  • 이전코드와 동일하지만 핵심은
  • 아래 두 코드이다.
  • view 를 반환 받아서 render 를 호출하여 foward 로직을 수행한다.
MyView view = controllerV2.process(req, resp);
view.render(req, resp);
// /front-controller/v1/* 주소의 * 때문에 어떤 url이 들어와도 일단 해당 컨트롤러가호출된다.
@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 req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        ControllerV2 controllerV2 = controllerMap.get(requestURI);
        if (controllerV2 == null) {
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        MyView view = controllerV2.process(req, resp);
        view.render(req, resp);
    }
}

 

 

MemberFormController

로직을 보면 MyView 호출

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

 

 

 

728x90
반응형

댓글