
어머, 내 설명이 조금이나마 도움이 됐다니 정말 기쁘다! 😊 강의 노트까지 보여주니 내가 더 신나서 자세히 설명해 줄 수 있겠어! 클로이 녀석이 짠 FrontController를 DispatcherServlet으로 바꾸는 작업까지 함께 해보자! 차근차근 따라오면 분명히 이해할 수 있을 거야.
자, 먼저 네가 준 강의 노트를 꼼꼼히 살펴보자.
이미지를 보면 MVC 패턴의 흐름이 좀 더 자세하게 나와 있네! Presentation Layer, Business Logic Layer, Persistence Layer로 나누어서 각 계층의 역할을 보여주고 있어.
service.XXX())하고, 그 결과에 따라 성공 시에는 OK View로, 실패 시에는 Error View로 이동하는 것을 보여주고 있어.dao.insert())하고, 그 결과를 바탕으로 예외를 처리하거나 다음 단계로 진행하는 것을 보여주고 있어.insert() 메서드를 통해 데이터베이스에 데이터를 삽입하고, SQL Exception이 발생할 수 있음을 보여주고 있어.이 흐름을 DispatcherServlet 기반 MVC 구조와 연결해서 다시 한번 생각해 보자.
이제 클로이가 짜준 FrontController를 DispatcherServlet으로 바꿔보자!
DispatcherServlet은 서블릿 컨테이너 (예: Tomcat)가 제공하는 기능을 활용하기 때문에, 네가 직접 HttpServlet을 상속받아서 모든 요청을 처리하는 FrontController를 만들 필요가 없어. 대신 Spring MVC와 같은 프레임워크에서 제공하는 DispatcherServlet을 설정하고, HandlerMapping, Controller, ViewResolver 등을 설정해서 네 애플리케이션의 흐름을 제어하게 돼.
하지만 지금 네 코드 구조에서는 DispatcherServlet을 바로 적용하기는 어렵기 때문에, DispatcherServlet의 핵심 아이디어를 네 FrontController에 녹여보는 방식으로 개선해 볼게.
현재 FrontController는 모든 요청을 받아서 직접 URI를 분석하고, 조건문을 이용해서 각 Controller의 process 메서드를 호출하는 방식으로 동작하고 있어. 이걸 좀 더 유연하게 만들기 위해서 다음과 같은 변경을 고려해 볼 수 있어.
HandlerMapping 도입: URI와 Controller를 매핑하는 HandlerMapping 인터페이스와 이를 구현하는 클래스를 만들어서, URI에 따라 어떤 Controller를 실행할지 결정하도록 분리하는 거야. 지금처럼 if-else if 문을 사용하는 대신, Map과 같은 자료구조를 이용해서 URI와 Controller를 관리하면 더 깔끔해지겠지?
Controller 인터페이스 통일: 현재 UserController, AccommodationController 등의 Controller들은 process라는 메서드를 가지고 있지만, 인터페이스를 구현하도록 통일하면 DispatcherServlet처럼 다양한 Controller를 일관된 방식으로 처리할 수 있게 돼.
ModelAndView 도입: Controller에서 처리 결과 데이터와 View 이름을 함께 담아서 반환하는 ModelAndView 객체를 사용하는 거야. 이렇게 하면 FrontController는 Controller의 처리 결과와 보여줄 View 정보를 명확하게 알 수 있고, View를 선택하고 응답을 생성하는 역할을 분리할 수 있어.
개선된 FrontController의 대략적인 구조는 다음과 같을 거야.
package web.mvc;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
// HandlerMapping 인터페이스 (URI와 Controller를 매핑)
interface HandlerMapping {
Object getHandler(String uri);
}
// 간단한 HandlerMapping 구현
class SimpleHandlerMapping implements HandlerMapping {
private Map<String, Object> handlerMap = new HashMap<>();
public SimpleHandlerMapping() {
handlerMap.put("/user", new UserController()); // 예시
handlerMap.put("/accommodation", new AccommodationController()); // 예시
handlerMap.put("/room", new RoomController()); // 예시
handlerMap.put("/reservation", new ReservationController()); // 예시
}
@Override
public Object getHandler(String uri) {
// URI 패턴에 따라 적절한 Controller를 반환
if (uri.startsWith("/user")) {
return handlerMap.get("/user");
} else if (uri.startsWith("/accommodation")) {
return handlerMap.get("/accommodation");
} else if (uri.startsWith("/room")) {
return handlerMap.get("/room");
} else if (uri.startsWith("/reservation")) {
return handlerMap.get("/reservation");
}
return null;
}
}
// Controller 인터페이스 (모든 Controller는 이 인터페이스를 구현)
interface Controller {
ModelAndView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
// ModelAndView 클래스 (처리 결과 데이터와 View 이름 저장)
class ModelAndView {
private String viewName;
private Map<String, Object> model = new HashMap<>();
public ModelAndView(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return viewName;
}
public Map<String, Object> getModel() {
return model;
}
public void addObject(String attributeName, Object attributeValue) {
this.model.put(attributeName, attributeValue);
}
}
// 기존의 Controller들을 Controller 인터페이스를 구현하도록 수정해야 함
class UserController implements Controller {
public ModelAndView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 사용자 관련 로직 처리
// ...
ModelAndView mv = new ModelAndView("/user/list.jsp"); // 예시 View 이름
mv.addObject("users", /* 사용자 목록 데이터 */ null);
return mv;
}
}
// ... 다른 Controller들도 마찬가지로 수정 ...
@WebServlet("*.do")
public class FrontController extends HttpServlet {
private static final long serialVersionUID = 1L;
private HandlerMapping handlerMapping;
@Override
public void init() throws ServletException {
handlerMapping = new SimpleHandlerMapping();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
private void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String command = uri.substring(contextPath.length());
// HandlerMapping을 통해 처리할 Controller를 찾음
Controller controller = (Controller) handlerMapping.getHandler(command);
if (controller != null) {
ModelAndView mv = controller.process(request, response);
if (mv != null) {
String viewName = mv.getViewName();
// ViewResolver를 통해 실제 View 경로를 찾고 forward 또는 redirect
if (viewName.startsWith("redirect:")) {
response.sendRedirect(request.getContextPath() + viewName.substring("redirect:".length()));
} else {
jakarta.servlet.RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views" + viewName); // 예시 View 경로
dispatcher.forward(request, response);
}
}
} else {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
}
}
위 코드는 DispatcherServlet의 기본적인 아이디어를 반영한 개선된 FrontController의 예시야.
HandlerMapping 인터페이스와 SimpleHandlerMapping 클래스를 도입해서 URI와 Controller를 매핑하는 역할을 분리했어.Controller 인터페이스를 정의하고, 각 Controller가 이 인터페이스를 구현하도록 변경해야 해.ModelAndView 클래스를 도입해서 Controller가 처리 결과 데이터와 View 이름을 함께 반환하도록 했어.FrontController의 processRequest 메서드에서는 HandlerMapping을 이용해서 Controller를 찾고, Controller의 process 메서드를 호출해서 ModelAndView를 받아서 View를 처리하도록 변경했어.주의: 위 코드는 기본적인 개념을 보여주기 위한 예시이며, 실제 DispatcherServlet처럼 다양한 기능 (interceptor, exception resolver 등)을 포함하고 있지는 않아.
이 정도면 클로이가 짠 FrontController와 DispatcherServlet의 차이점, 그리고 DispatcherServlet의 핵심 아이디어를 조금이나마 이해하는 데 도움이 되었기를 바라! 😊 궁금한 점이 있다면 언제든지 다시 물어봐!