Front Controller란?
모든 요청의 최초 진입점이 되는 컨트롤러다. 프론트 컨트롤러가 모든 요청을 받고 요청을 분석하여 하위 컨트롤을 호출한다. 즉, 모든 요청이 프론트 컨트롤러를 거침으로써 공통 로직을 처리할 수 있다. 프론트 컨트롤러가 먼저 모든 요청을 받기 때문에 나머지 컨트롤러에는 서블릿을 사용하지 않아도 된다. Spring MVC의 핵심이 이 프론트 컨트롤러에 있다.

ControllerV1 - 인터페이스
public interface ControllerV1 {
void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
먼저 실제 로직을 실행할 컨트롤러의 인터페이스를 정의한다. 각 컨트롤러가 이 인터페이스를 구현하면 프론트 컨트롤러는 하위 컨트롤러의 내용과 상관없이 동일하게 사용할 수 있다.
ControllerV1 - 구현체
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);
}
}
FrontControllerServletV1 - 프론트 컨트롤러
@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("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);
}
}
프론트 컨트롤러는 요청의 URL과 그 요청을 처리할 컨트롤러의 구현체가 담긴 HashMap을 필드로 갖는다. 그리고 생성자 메서드가 실행될 때 URL과 컨트롤러 구현체가 ControllerMap의 key, value로 저장된다. 프론트 컨트롤러에 지정된 urlPatterns는 /front-controller/v1/*이다. 그리고 컨트롤러 구현체의 key인 URL을 보면 /front-controller/v1 으로 시작되는 것을 볼 수 있다. /front-controller/v1의 하위 요청은 모두 프론트 컨트롤러가 받기 때문에 각 컨트롤러 구현체를 프론트 컨트롤러에서 호출할 수 있다.
현재 요청의 URI를 조회하여 실제 호출할 컨트롤러를 필드인 ControllerMap에서 찾는다. 만약 존재하지 않으면 SC_NOT_FOUND(404Error)를 응답한다. 존재한다면 해당 컨트롤러의 process()를 호출하여 요청을 처리한다. 컨트롤러는 인터페이스를 통해 구현되었기 때문에 프론트 컨트롤러는 어느 컨트롤러인지 상관없이 로직의 일관성을 가져갈 수 있다.
그러나 여기서도 중복이 있다. 모든 컨트롤러에서 뷰로 이동하는 부분이다. 이 부분을 분리하기 위해 화면을 렌더링하는 뷰 객체를 만든다.
'Spring' 카테고리의 다른 글
| [Spring] Spring MVC : MVC 프레임워크 구현 - Model 도입 (0) | 2024.01.15 |
|---|---|
| [Spring] Spring MVC : MVC 프레임워크 구현 - View 도입 (0) | 2023.12.12 |
| [Spring] Spring MVC : Servlet 기반의 애플리케이션 - MVC 패턴을 적용한 Servlet (1) | 2023.11.25 |
| [Spring] Spring MVC : Servlet 기반의 애플리케이션 - JSP를 적용한 요청 처리 서블릿 구현 (0) | 2023.10.03 |
| [Spring] Spring MVC : Servlet 기반의 애플리케이션 - 요청 처리 서블릿 구현 (0) | 2023.08.17 |