본문으로 바로가기
반응형

 

 

 

● 프론트 컨트롤러(Front Controller)  

 

 

Spring MVC 에서 프론트 컨트롤러가 도입되고 나서 컨트롤러의 호출 이전에 공통 처리 기능이 가능해졌다.

 

 

 

프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받고,

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

 

즉 모든 요청의 입구를 하나로 통일하면서 공통 처리 기능이 가능해지고,

프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 되었다.

 

 

 

그럼 프론트 컨트롤러를 도입해보고자 한다.

 

 

 

 


● 프론트 컨트롤러 도입

 

 

 

프론트 컨트롤러를 단계적으로 도입해보고자 한다.

 

 

 

 

우선 프론트 컨트롤러의 동작 구조는 아래와 같이 간단하게 설명이 된다.

 

1. 요청받은 url 주소를 토대로 프론트 컨트롤러에서 매핑된 정보(컨트롤러)를 반환

 

2. 해당 컨트롤러 로직 실행

 

3. 컨트롤러 수행 후 JSP로 forwading

 

 

 

해당 순서에 맞게 동작하기 위한 컨트롤러(프론트 컨트롤러 포함)를 작성해보자.

 

 

 

 

 

○ 컨트롤러 구현

 

 

 - ControllerV1(인터페이스)

package hello.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;
}

 

우선 모든 컨트롤러를 ControllerV1이라는 인터페이스를 통해 구현하도록 한다.

 

이유는 프론트 컨트롤러에서 요청을 처리할 컨트롤러를 찾고자 할 텐데,

이때 모든 컨트롤러를 수용하기 위한 컨트롤러를 만들기 위함이다.

 

 

 

 

 

- MemberListControllerV1(회원 목록을 보기 위한 컨트롤러)

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

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.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);
    }
}

 

위와 같은 방법으로 ControllerV1 인터페이스를 상속받아 

로직을 실행하는 process() 함수를 오버라이딩 하여 작성한다.

(편의상 하나의 다른 컨트롤러 작성은 생략)

 

 

 

 

- FrontControllerServletV1(프론트 컨트롤러)

package hello.servlet.web.frontcontroller.v1;

import hello.servlet.web.frontcontroller.v1.controller.MemberFormControllerV1;
import hello.servlet.web.frontcontroller.v1.controller.MemberListControllerV1;
import hello.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;

// v1 하위에 어떤 url이 들어와도 일단 이 서블릿이 호출됨
@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")
public class FrontControllerServletV1 extends HttpServlet {

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

    public FrontControllerServletV1() {
        // 매개변수 1 url 요청이 오면 , MemberFormControllerV1 생성
        controllerV1Map.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
        controllerV1Map.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
        controllerV1Map.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();// 들어온 URL을 얻을 수 있다.

        ControllerV1 controller = controllerV1Map.get(requestURI); // 요청 온 URI에 의해 Controller 객체가 다르게 호출
        if(controller == null){ // 읽어온 URI에 매치되는 Controller 객체가 없다면
            response.setStatus(HttpServletResponse.SC_NOT_FOUND); // 404 Not Found
            return;
        }

        controller.process(request, response);

    }
}

 

 

애노테이션을 이용해 공통된 처리 기능을 요하는 url 매핑 주소를 가진 컨트롤러들을 실행하기 전에

작성된 프론트 컨트롤러를 무조건 거치도록 한다.(/* 을 통해 모든 하위 url 이전에 수행되게끔)

 

그런 다음 요청 url에 맞게 map에서 value(컨트롤러)를 꺼내서 부모 인터페이스로 받은 다음(다형성)

process() 함수를 실행한다.(여기선 오버라이딩에 의해 호출된 구현체 컨트롤러의 process() 실행)

 

 


 

 

이런 식으로 프론트 컨트롤러를 통해서 공통된 url을 가진 컨트롤러들을 수행하기 전

프론트 컨트롤러를 거치도록 할 수 있다.

 

 

하지만, 프론트 컨트롤러로 분리했음에도 코드가 간결해지지 않은 느낌이다.

 

 

아래 코드를 보면

 

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

 

모든 컨트롤러는 위와 같은 공통 코드를 포함하고 있다.

 

 

 

 

다음 포스팅에선 이러한 View 처리 코드를 분리해보고자 한다.

 

 

 

 

 

반응형