쌓고 쌓다

프론트 컨트롤러 개념과 구현 본문

프로그래밍/JSP & Servlet

프론트 컨트롤러 개념과 구현

승민아 2023. 7. 30. 13:49

인프런 - 김영한

기존의 MVC 패턴에서 공통 로직이 필요하면 공통 로직을 실행하고 별도의 로직을 수행하게 해야한다.

공통된 입구가 없기에 모든 입구마다 공통 로직을 깔아줬음.

 

 

프론트 컨트롤러 패턴

인프런 - 김영한

프론트 컨트롤러 패턴으로 입구를 하나로 만들어 공통 로직을 수행할 수 있다.

또한 프론트 컨트롤러의 뒤에 있는 나머지 컨트롤러는 요청에 맞는 컨트롤러를 서블릿을 사용하지 않고 호출할 수 있다.

스프링 웹 MVC의 핵심 "DispatcherServlet"도 FrontController 패턴으로 만들어져 있다고 한다.

 

프론트 컨트롤러 V1

만들어볼 프론트 컨트롤러 구조는 위와 같다.

프론트 컨트롤러로 들어온 요청 URL에 따라 컨트롤러를 호출하여 응답한다.

 

Controller 인터페이스

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

프론트 컨트롤러에서 인터페이스를 호출하여 구현체와 상관없이 각각의 컨트롤러의 process를 수행할 수 있게 인터페이스를 만든다.

 

1. 컨트롤러

컨트롤러는 인터페이스를 오버라이딩하여 각각 목적에 따라 로직(process)을 수행한다.

 

MemberFormController

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

이 컨트롤러로 들어온 요청은 Form 페이지를 응답한다.

 

MemberSaveController

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);
        request.setAttribute("member", member);

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

사용자에게 입력을 받는 Form 페이지에서 form 태그의 action으로 이 컨트롤러와 매핑하며, 넘어온 데이터를 처리하는 컨트롤러이다.

Repository에 저장한 후 결과 페이지를 응답한다.

 

memberListController

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

저장된 Member 리스트를 모델에 담아 뷰에 넘겨 응답한다.

 

2. 프론트 컨트롤러

 

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

사용자가 요청한 URL에 따라 수행할 process를 달리하기 위해

URL과 일치하는 Key를 찾아 수행할 Controller 구현체를 반환 받기 위함이다.

 

@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	String requestURI = request.getRequestURI();// URL, URI 차이점 학습 필요!!!!!!
	ControllerV1 controller = controllerMap.get(requestURI);
	if(controller == null) {
	    response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
	}

	controller.process(request, response);
}

요청한 URL을 가져와 Map에서 일치하는 구현체를 꺼낸다.

만약 구현체가 없다면 Controller가 없는걸로 404 Not Found 에러를 응답한다.

존재한다면 그 컨트롤러의 process를 수행하면 된다.

 

그러면 이 프론트 컨트롤러는 어떤 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 request, HttpServletResponse response) throws ServletException, IOException {
        String requestURI = request.getRequestURI();// URL, URI 차이점 학습 필요!!!!!!
        ControllerV1 controller = controllerMap.get(requestURI);
        if(controller == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        controller.process(request, response);
    }
}

 

*urlPatterns = "/front-controller/v1/*"는 /front-controller/v1 하위의 모든 요청에 대해 이 서블릿을 수행하게 만든다.

 

'프로그래밍 > JSP & Servlet' 카테고리의 다른 글

서블릿, JSP로 MVC 패턴 만들기  (0) 2023.07.29
JSP 애플리케이션과 MVC 패턴  (0) 2023.07.25
서블릿 애플리케이션과 템플릿 엔진  (0) 2023.07.24
HTTP 응답 데이터  (0) 2023.07.22
HTTP 요청 데이터  (0) 2023.07.20
Comments