spring

Front Controller (1)

Beencle 2023. 3. 16. 20:11

이전까지 사용했던 MVC 방식도 이전에 Servlet이나 JSP방식으로 개발했을때보다는 훨씬 수월하고 편리하게 개발을 할 수 있었지만 역시 한계가 존재한다. 매번 매핑된 주소로 들어가기 위해 처리하는 공통처리 부분이 꼭 필요하다는 부분이 아직 우리에게는 불편하게 느껴진다. 혹여나 공통처리 부분을 따로 뺀 뒤에 메소드로 만들어 그 메소드만 호출을 한다고 해도 매번 호출할때마다 그 메소드 역시 반복해서 호출해야하므로 별로 나아진게 없다. 그러므로 우리는 이제부터 모든 처리를 하기 앞서서 Front Controller라는 기능을 만들어 수문장을 시키게하여 반복되는 코드를 줄이려고 한다.

출처:김영한 MVC 1

이 FrontController는 클라이언트에서 요청이 들어오면 각각에 맞는 컨트롤러로 배당을 시켜줄것이고 해당 컨트롤러에는 Servlet을 사용하지않아도 된다.


먼저 여러 처리가 들어올때 받을 공통 인터페이스를 만들어보자

public interface ControllerV1 {
    void process(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, 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);
    }
}

 

- 폼에서 입력한 정보를 가져와 레파지토리에 저장할 컨트롤러

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

 

- 레퍼런스에 저장된 회원정보들을 리스트로 보여주는 컨트롤러

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

각자의 컨트롤러를 만들었으니 이 컨트롤러로 가기전에 클라이언트에 요청에 알맞게 이동할 FrontConroller를 만들어보자.

@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 {
        System.out.println("frontControllerServletVdd1.service");

        String requestURI = request.getRequestURI();

        ControllerV1 controller = controllerMap.get(requestURI); // 해당 키 값의 value 추출
        if(controller == null){
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        controller.process(request,response);
    }
}

해당 FrontController를 분석해보면 먼저 앞서와 같이 매핑주소를 만들었는데 위의 3가지 매핑 주소를 받아야하므로 뒤에 /*를 사용했다. 그리고 호출이 됨과 동시에 3개의 매핑주소를 만들어야하므로 Map으로 주소값과 주소값에 맞는 기능에 맞는 컨트롤러가 매칭된 모습이다. ( 주소, 컨트롤러 )의 모습. 실행될 service부분을 보게되면 requestURI라는게 보이는데 이는 현재 요청이 들어오는 주소인데 우리는 요청을 http://localhost:8080/front-controller/v1/members/new-form 이런 모습으로 할것이다. 이때 reqeustURI는 8080부터 뒤에 입력값을 받을 수 있으므로 /front-controller/v1/member/new-form이 된다. 우리는 conroller라는 Map에 주소를 키값으로 만들고 value로 컨트롤러를 넣어주었다.

그러므로 ControllerV1 controller에는 현재 주소에 맞는 컨트롤러가 매칭이 되게 된다.

쉽게 설명하면 http://localhost:8080/front-controller/v1/members/new-form로 들어가게 되면

ControllerV1 controller = new MemberFromControllerV1(); 이런 형태라고 생각하면 된다. 

이제 우리가 갈 컨트롤러는 정해졋으니 해당 컨트롤러의 기능인 process를 호출하면 해당 컨트롤러는 인터페이스를 상속받았던 기능이 실행되게 된다. ( 혹시나 해당 컨트롤러에 대한 정보가 없다면 이전 글들을 참고하면 모두 나와있다. )