서블릿으로 MVC 구조를 직접 구현하다 보면
코드는 돌아가지만, 구조적으로 계속 찜찜한 지점들이 생긴다.
이번 글에서는 그중에서도 View 이동과 관련된 중복 문제를 중심으로
왜 프론트 컨트롤러가 등장했는지 정리해본다.
① View로 이동하는 코드의 중복 (forward 중복)
컨트롤러 역할을 하는 모든 서블릿에서
JSP로 이동할 때마다 아래 코드가 반복된다.
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
회원 목록, 회원 저장, 회원 폼 등
모든 서블릿에서 동일한 코드가 계속 등장한다.
• 메서드로 분리할 수는 있음
• 하지만 결국 매번 직접 호출해야 함
• 컨트롤러의 핵심 로직이 아닌 코드가 계속 눈에 띔
-컨트롤러가 비즈니스 흐름 제어에만 집중하지 못한다.-
② View 경로 문자열 중복 (ViewPath 중복)
String viewPath = "/WEB-INF/views/new-form.jsp";
여기에는 두 가지 문제가 숨어 있다.
✔ prefix / suffix가 하드코딩됨
• prefix: /WEB-INF/views/
• suffix: .jsp
모든 컨트롤러에서 이 문자열을 직접 작성해야 한다.
✔ View 기술 변경에 취약
만약 View를 JSP → Thymeleaf로 바꾸면?
// 기존
"/WEB-INF/views/new-form.jsp"
// 변경 후
"/templates/new-form.html"
모든 컨트롤러 코드를 수정해야 함
즉,
• 컨트롤러가 View 기술(JSP, Thymeleaf)에 강하게 결합
• 변경에 매우 취약한 구조
현재 구조에서는
• 요청 분기
• 비즈니스 로직 호출
• View 선택
• forward 처리
이 모든 책임이 각 서블릿마다 흩어져 있음, 컨트롤러가 너무 많은 일을 함.
여기서 자연스럽게 나오는 질문:
“이 공통 코드를 한 곳에서 처리할 수는 없을까?”
그래서 등장하는 개념이 바로 프론트 컨트롤러(Front Controller) 이다.
모든 요청을 단 하나의 컨트롤러가 먼저 받는 구조
클라이언트 요청
↓
[Front Controller]
↓
각 기능별 컨트롤러
↓
View
프론트 컨트롤러가 해결해주는 것들
1) forward 로직을 한 곳에서 처리
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
이 코드는 프론트 컨트롤러에 단 한 번만 존재한다.
개별 컨트롤러에서는
“어떤 뷰를 쓸지”만 반환하면 된다.
2) ViewPath 조립 책임 분리
개별 컨트롤러:
return "new-form";
프론트 컨트롤러:
String viewPath = "/WEB-INF/views/" + viewName + ".jsp";
prefix / suffix가 한 곳에만 존재
View 기술 변경 시 수정 포인트 단 하나
3) 컨트롤러의 역할이 명확해짐
컨트롤러는 이제 딱 이것만 한다.
• 요청 파라미터 처리
• 비즈니스 로직 호출
• 논리적인 View 이름 반환
Controller = 요청 처리 + 흐름 결정
View 처리 = Front Controller