본문 바로가기
스프링/스프링 웹

[Spring] 프론트 컨트롤러 패턴(Front Controller Pattern) 개념과 적용

by drCode 2023. 5. 24.
728x90
반응형

여러 서블릿을 통해서 HTTP 요청을 처리하는 기존의 서블릿 방식과 달리,

 

프론트 컨트롤러를 도입함으로써 모든 요청을 하나의 컨트롤러로 모이게 할 수 있다.

 

프론트 컨트롤러 도입 하기 전 모델

프론트 컨트롤러 도입 하기 전 모델

 

프론트 컨트롤러를 도입한 후 모델

프론트 컨트롤러를 도입한 후 모델

 

프론트 컨트롤러 패턴의 특징은

1) 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받는다.

2) 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출한다.

3) 입구를 하나로 통일한다.

4) 공통 처리 기능을 한다.

5) 프론트 컨트롤러를 제외한 나머지 컼ㄴ트롤러는 서블릿을 사용하지 않아도 된다.

 

스프링 웹 MVC 와 프론트 컨트롤러

스프링 웹 MVC의 핵심도 바로 FrontController이고,

스프링 웹 MVC의 DistpatcherServlet이 FrontController 패턴으로 구현되어 있다.

 

프론트 컨트롤러 도입 - V1

 

프론트 컨트롤러를 단계적으로 도입해본다.

 

V1

Front Controller V1

ControllerV1.java

package helloMVC.servlet.web.frontcontroller.v1;

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

public interface ControllerV1 {

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

 

서블릿과 비슷한 모양의 컨트롤러 인터페이스를 도입한다.

 

각 컨트롤러들은 이 인터페이스를 구현하면 된다.

 

프론트 컨틀로러는 이 인터페이스를 호출해서 구현과 관계없이 로직의 일관성을 가져갈 수 있다.

 

이제 이 인터페이스를 구현한 컨트롤러를 만들어보자.

 

지금 단계에서는 기존 로직을 최대한 유지하는게 핵심이다.

 

MemberFormControllerV1 - 회원 등록 컨트롤러

package helloMVC.servlet.web.frontcontroller.v1.controller;

import helloMVC.servlet.web.frontcontroller.v1.ControllerV1;

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

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 - 회원 저장 컨트롤러

package helloMVC.servlet.web.frontcontroller.v1.controller;

import helloMVC.servlet.domain.member.Member;
import helloMVC.servlet.domain.member.MemberRepository;
import helloMVC.servlet.web.frontcontroller.v1.ControllerV1;

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

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);
        System.out.println("member = " + member);
        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 - 회원 목록 컨트롤러

package helloMVC.servlet.web.frontcontroller.v1.controller;

import helloMVC.servlet.domain.member.Member;
import helloMVC.servlet.domain.member.MemberRepository;
import helloMVC.servlet.web.frontcontroller.v1.ControllerV1;

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

 

내부 로직은 기존 서블릿과 거의 같다.

 

이제 프론트 컨트롤러를 만들어본다.

 

FrontControllerServletV1 - 프론트 컨트롤러

package helloMVC.servlet.web.frontcontroller.v1;

import helloMVC.servlet.web.frontcontroller.v1.controller.MemberFormControllerV1;
import helloMVC.servlet.web.frontcontroller.v1.controller.MemberListControllerV1;
import helloMVC.servlet.web.frontcontroller.v1.controller.MemberSaveControllerV1;

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;

@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")
public class FrontControllerServletV1 extends HttpServlet {

    private Map<String, ControllerV1> controllerMap = new HashMap<>();

    // Alt + Insert => None Select
    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 {
        System.out.println("FrontControllerServletV1.service");

        String requestURI = request.getRequestURI();
        ControllerV1 controllerV1 = controllerMap.get(requestURI);
        if(controllerV1 == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        controllerV1.process(request, response);
    }
}

 

프론트 컨트롤러 분석

 

1) urlPatterns

  : urlPatterns = "/front-controller/v1/*" → /front-controller/v1를 포함한 하위 모든 요청은 이 서블릿에서 받아들인다

  : 예) /front-controller/v1, /front-controller/v1/a,  /front-controller/v1/a/b

 

2) controllerMap

 : key - 매핑 URL

 : value : 호출될 컨트롤러

 

3) service()

 : 먼저 requestURI를 조회해서 실제 호출할 컨트롤러를 controllerMap에서 찾는다. 만약 없다면 404(SC_NOT_FOUND) 상태 코드를 반환한다.

 : 컨트롤러를 찾고 controller.process(request, response); 을 호출해서 해당 컨트롤러를 실행한다.

 

4) JSP

 : jsp는 이전에 MVC 에서 사용했던 것을 그대로 사용한다.

 

 

※ URI 개념은 아래를 참고하시면 좋습니다.

https://drcode-devblog.tistory.com/386

 

[HTTP] URI(Uniform Resource Identifier)

scheme://[userinfo@]host[:port][/path][?query][#fragment] https://www.google.com/search?q=hello&hl=ko URI(Uniform Resource Identifier) 리소스를 식별하는 통합된 방법 URI? URL? URN? URI는 로케이터(locator), 이름(name) 또는 둘다 추가

drcode-devblog.tistory.com

https://drcode-devblog.tistory.com/393

 

[HTTP] HTTP API를 만들어보자.

요구사항 회원 정보 관리 API를 만들어라. 회원 목록 조회 회원 조회 회원 등록 회원 수정 회원 삭제 API URI 설계 URI(Uniform Resource Identifier) 회원 목록 조회 / read-member-list 회원 조회 / read-member-by-id

drcode-devblog.tistory.com

 

실행

 : http://localhost:8080/front-controller/v1/members/new-form

http://localhost:8080/front-controller/v1/members/new-form
http://localhost:8080/front-controller/v1/members/save

  :

http://localhost:8080/front-controller/v1/members

 

기존 서블릿, JSP로 만든 MVC와 동일하게 실행 되는 것을 확인할 수 있다

 

지금까지 프론트 컨트롤러 도입에 대해 알아봤다.

 

단일 서블릿을 여러개 구성한 구조를 앞단 프론트 컨트롤러를 통해서 각각의 컨트롤러를 처리한 방식인데,

 

구조만 바뀐 형식이다.

 

구조를 건드릴 땐, 구조만 바꾸면 된다.

 

같은 레벨, 뎁스까지만 개선하고 차근차근히 바꿔나가야 한다.

 

구조를 바꿨는데 문제가 없다면, 점차 세부적인 것을 개선해야 한다.

728x90
반응형

댓글