자바 백엔드 웹 기술의 진화과정(3) - 웹 프론트 컨트롤러 v4, v5
프론트 컨트롤러 특징
- 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
- 요청에 맞는 컨트롤러를 찾아서 호출
- 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 된다.
- 단계적으로 도입
- v1 : 프론트 컨트롤러 패턴의 도입
- v2 : view render를 처리해주는 ****MyView 도입
- v3 : 서블릿 종속성 제거 및 뷰의 물리경로를 가지는 뷰 리졸버의 사용
- v4 : v3코드에서 컨트롤러가 직접 뷰의 논리 이름을 반환하도록 변경
- v5 : 어댑터(Adapter) 패턴의 도입 → 다양한 종류의 컨트롤러 처리
V4 - 컨트롤러의 ViewName반환
- 기존 V3구조에서 Controller가
ModelView
객체가 아닌 String인viewName
을 반환하도록 변경- 매번 ModelView객체를 return하지 않아서 편리해짐
ModelView
를 사용하지 않으므로 model정보를 저장하는Map<String, Object>
model을 받음
ControllerV4 인터페이스
public interface ControllerV4 {
/**
* @param paramMap
* @param model
* @return viewName
*/
String process(Map<String, String> paramMap, Map<String, Object> model);
}
MemberFormControllerV4
public class MemberFormControllerV4 implements ControllerV4 {
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
return "new-form"; //단순하게 new-form이라는 논리 이름만 반환
}
}
MemberListControllerV4
public class MemberListControllerV4 implements ControllerV4 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
List<Member> members = memberRepository.findALl();
model.put("members", members); //Map에 members를 저장
return "members"; //String인 members를 반환
}
}
FrontControllerServletV4
@WebServlet(name = "FrontControllerServletV4", urlPatterns = "/front-controller/v4/*")
public class FrontControllerServletV4 extends HttpServlet {
private Map<String, ControllerV4> controllerMap = new HashMap<>();
public FrontControllerServletV4() {
controllerMap.put("/front-controller/v4/members/new-form", new MemberFormControllerV4());
controllerMap.put("/front-controller/v4/members/save", new MemberSaveControllerV4());
controllerMap.put("/front-controller/v4/members", new MemberListControllerV4());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
ControllerV4 controller = controllerMap.get(requestURI);
if (controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
Map<String, String> paramMap = createParamMap(request);
Map<String, Object> model = new HashMap<>(); //추가된 부분
String viewName = controller.process(paramMap, model);
MyView view = viewResolver(viewName);
view.render(model, request, response);
}
private MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
private Map<String, String> createParamMap(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
return paramMap;
}
}
Map<String, Object> model = new HashMap<>()
: 모델객체를 프론트 컨트롤러에서 생성해서 넘겨준다. 컨트롤러가 여기에 값을 담게 된다.
V5 - 핸들러 어댑터의 추가
- 여러가지 인터페이스를 사용하기위해 어댑터(Adapter)의 사용
- 다른 인터페이스인
ControllerV3
,ControllerV4
를 같이 사용 할 수 있다!
MyHandlerAdapter - 어탭터용 인터페이스
public interface MyHandlerAdapter {
boolean supports(Object handler);
ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler);
}
boolean supports(Object handler)
: 어댑터에서 해당 컨트롤러를 처리 할 수 있는지 판단해주는 메서드ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
: 어댑터에서 실제 컨트롤러의 로직을 호출 해주는 메서드
FrontControllerServletV5
@WebServlet(name = "frontControllerServletV5", urlPatterns = "/front-controller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {
//맵에서 value값이 Controller에서 Object로 변경 되었다.
private final Map<String, Object> handlerMappingMap = new HashMap<>();
private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>();
//생성자에서 핸들러와 어댑처를 초기화 한다.
public FrontControllerServletV5() {
initHandlerMappingMap();
initHandlerAdapters();
}
private void initHandlerMappingMap() {
handlerMappingMap.put("/front-controller/v5/v3/members/new-form", new MemberFormControllerV3());
handlerMappingMap.put("/front-controller/v5/v3/members/save", new MemberSaveControllerV3());
handlerMappingMap.put("/front-controller/v5/v3/members", new MemberListControllerV3());
handlerMappingMap.put("/front-controller/v5/v4/members/new-form", new MemberFormControllerV4());
handlerMappingMap.put("/front-controller/v5/v4/members/save", new MemberSaveControllerV4());
handlerMappingMap.put("/front-controller/v5/v4/members", new MemberListControllerV4());
}
private void initHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
handlerAdapters.add(new ControllerV4HandlerAdapter());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
Object handler = getHandler(request);
if (handler == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
MyHandlerAdapter adapter = getHandlerAdapter(handler);
ModelView mv = adapter.handle(request, response, handler);
MyView view = viewResolver(mv.getViewName());
view.render(mv.getModel(), request, response);
}
private Object getHandler(HttpServletRequest request) {
String requestURI = request.getRequestURI();
return handlerMappingMap.get(requestURI);
}
//Adapter를 가져올때는 supports 메서드를 이용해서 메서드를 찾는다.
private MyHandlerAdapter getHandlerAdapter(Object handler) {
for (MyHandlerAdapter adapter : handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
throw new IllegalArgumentException("handler adapter를 찾을 수 없습니다. handler=" + handler);
}
private MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
}
- 매핑의 값이 Controller에서 Object로 변경되었다.
ControllerV3HandlerAdapter
public class ControllerV3HandlerAdapter implements MyHandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof ControllerV3);
}
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
ControllerV3 controller = (ControllerV3) handler;
Map<String, String> paramMap = createParamMap(request);
ModelView mv = controller.process(paramMap);
return mv;
}
private Map<String, String> createParamMap(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
return paramMap;
}
}
supports()
: handler의 클래스가 ControllerV3인지 판단해줌 → handler가 V3일 경우에만 처리ModelView()
: handler를 V3로 변환한 후 V3인터페이스에 맞게 호출
ControllerV4HandlerAdapter
public class ControllerV4HandlerAdapter implements MyHandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof ControllerV4);
}
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
ControllerV4 controller = (ControllerV4) handler;
Map<String, String> paramMap = createParamMap(request);
HashMap<String, Object> model = new HashMap<>();
String viewName = controller.process(paramMap, model);
ModelView mv = new ModelView(viewName); //어댑터 변환
mv.setModel(model);
return mv;
}
private Map<String, String> createParamMap(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
return paramMap;
}
}
supports()
: handler의 클래스가 ControllerV4인지 판단해줌 → handler가 V4일 경우에만 처리ModelView()
: handler를 ControllerV4로 캐스팅 후, paramMap과 model을 만들어서 호출- ControllerV4는 원래 ViewName을 반환했지만, 어댑터는 ModelView로 만들어서 반환한다.
ModelView mv = new ModelView(viewName); //어댑터 변환 mv.setModel(model); return mv;
반응형