스프링 MVC의 프론트 컨트롤러 패턴 - V1

최지환·2022년 3월 26일
0

스프링

목록 보기
1/12
post-thumbnail
post-custom-banner

프론트 컨트롤러 패턴

프론트 컨트롤러 패턴이란?

  • REST AP를 구현할 때, 한개의 URL에 하나의 서블릿을 대응해야만 했던 구조에서 코드의 중복을 줄이기 위해 고안된 패턴

  • 따라서 프론트 컨트롤러를 여러 서블릿 컨트롤러들의 앞단에 두어 일종의 ‘수문장’ 역할을 하게 둠

    → 이를 통해 컨트롤러 공통영역을 처리해 줄 수 있도록 함.

프론트 컨트롤러 도입 전의 관계도

프론트 컨트롤러 도입 후의 관계도

Front Controller의 특징

Front Controller 패턴의 특징을 살펴보자

  • 프론트 컨트롤러 서블릿 하나로 클라이언트의 모든 요청을 수행한다.
  • 해당 요청을 수행할 컨트롤러를 찾아서 호출한다.
  • 공통 로직을 처리 가능(코드의 중복 제거)

Front controller 이 공통 로직을 처리하고, 각 상황에 따라 해당 역할을 수행할 컨트롤러를 실행한다.

그렇다면 프론트 컨트롤러를 회원 등록을 하고 회원 리스트를 볼 수 있는 코드에 적용해보자.

우선 현재는 클라이언트로 이름와 나이를 입력 받아 그 정보로 회원을 등록하기 위한 서블릿, 입력 받은 정보를 전달받아 회원을 저장하는 서블릿,모든 회원 목록 리스트를 반환하는 서블릿, MvcMemberFormServlet,MemberSaveServlet,MemberListServlet이렇게 세가지가 있다.

MvcMemberFormServlet 코드

@WebServlet(name = "mvcMemberFormServlet",urlPatterns ="/servlet-mvc/members/new-form")
public class MvcMemberFormServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String viewPath ="/WEB-INF/views/new-form.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request,response); 
    }
}

MvcMemberSaveServlet 코드

@WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
public class MvcMemberSaveServlet extends HttpServlet {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(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);

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

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

MvcMemberSaveServlet 코드

@WebServlet(name = "mvcMemberListServlet",urlPatterns = "/servlet-mvc/members")
public class MvcMemberListServlet extends HttpServlet {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();
        request.setAttribute("members",members);
        String viewPath = "/WEB-INF/views/members.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request,response);
    }
}

Front Controller 로 구조 변경

우선 인터페이스를 만들어준다 → 각 컨트롤러들은 이 인터페이스를 통해 구현한다. 프론트 컨트롤러에서 이 인터페이스를 호출해 각 구현체들을 컨트롤러로써 사용한다. → 이는 로직의 일관성을 가져갈 수 있다.

ControllerV1

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

ForntControllerServletV1

@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 {
        String requestURI = request.getRequestURI();
        ControllerV1 controller = controllerMap.get(requestURI);
        if (controller == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        controller.process(request, response);
    }
}
  • Map을 이용하여 요청이 들어온 url에 따라 해당하는 컨트롤러가 호출 될 수 있도록 한다.
  • /front-controller/v1/ 을 통해 /front-controller/v1 의 하위 모든 경로에 대한 요청을 받는다.
    는 모든 것을 포함한다는 의미

MemberFormControllerV1

public class MemberFormControllerV1 implements ControllerV1 {
    @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);
    }
}

MemberSaveControllerV1

public class MemberSaveControllerV1 implements ControllerV1 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public void 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);

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

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

MemberListControllerV1

public class MemberListControllerV1 implements ControllerV1 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();
        request.setAttribute("members", members);
        String viewPath = "/WEB-INF/views/members.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}

위의 프런트 컨트롤러를 적용함으로써

Front controller 이 공통 로직을 처리하고, 각 상황에 따라 해당 역할을 수행할 컨트롤러를 실행할 수 있게 되었다.

또한 맵핑 되어 있지 않은 url을 통한 접근을 404에러로 처리할 수 있게 되었다.

post-custom-banner

0개의 댓글