이전 게시물의 V3에 이어 개선해낸 V4, V5 에 대해 코드와 함께 설명을 하겠다!
V4
package hello.servlet.web.frontcontroller.v4;
import java.util.HashMap;
import java.util.Map;
public interface ControllerV4 {
/**
* @param paramMap
* @param model
* @return viewName
*/
String process(Map<String, String> paramMap, Map<String, Object> model); // model 도 넘겨줌.뷰의 이름만 전달.
}
- Controller 계층에서 model 도 같이 전달해준다.
package hello.servlet.web.frontcontroller.v4;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v4.controller.MemberFormControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberListControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberSaveControllerV4;
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;
@WebServlet(name = "frontControllerServletV4", urlPatterns = "/front-controller/v4/*") // * -> v1 하위 모든 요청
public class FrontControllerServletV4 extends HttpServlet {
private Map<String, ControllerV4> controllerMap = new HashMap<>(); // 요청 URI에 해당하는 controller를 찾기 위한 맵
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 {
request.getRequestURI(); // 요청 URI -> new-form
// get으로 해당하는 controller 찾기
ControllerV4 controller = controllerMap.get(request.getRequestURI()); // 요청 URI에 해당하는 controller 찾기 -> new form 된다.
if (controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);// 404 처리
return;
}
// request.getParameterNames().asIterator()
// .forEachRemaining(paramName -> System.out.println(paramName + "=" + request.getParameter(paramName)));
//paramMap
// form 에 값을 넣었을때.
Map<String, String> paramMap = createParamMap(request); // 파라미터 값을 다 넣음
Map<String, Object> model = new HashMap<>(); // model 생성
String viewName = controller.process(paramMap,model);// controller 호출
MyView view = viewResolver(viewName);// 물리이름 new-form.jsp
view.render(model, request,response); // view 호출 jsp 호출
}
private static MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
private static Map<String, String> createParamMap(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName))); // 파라미터에 있는 값을 다 꺼내서 paramMap에 저장
return paramMap;
}
}
Setting
- 기존과 같이 URI 에 해당하는 controller를 찾기 위한 맵을 생성한다.
- 생성자를 통해서 map에 controller와 URI 를 넣어준다.
Service
new-form
- V3와 동일하게 controller 를 찾는다.
- 이후 request에 파라미터가 아무것도 없기 때문에 빈 맵과 함께 controller 에 들어간다.
- 또한 model 생성을 한 후 같이 제공한다. 현재는 빈 맵
package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import java.util.HashMap;
import java.util.Map;
public class MemberFormControllerV4 implements ControllerV4 {
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
return "new-form";
}
}
- new-form 을 return 하고 , 해당 return 값은 viewName 이다.
- viewName에 알맞은 jsp를 resolve하고, view 를 렌더링 한다.
→ new-form.jsp 에서 save로 post를 보낸다.
save
- 해당 URI가 save를 호출 , saveController 를 호출한다.
- parameter 값에 username, age 가 담겨서 온다.
- 이후 빈 모델 맵을 같이 보낸다.
- → saveController
package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import java.util.HashMap;
import java.util.Map;
public class MemberSaveControllerV4 implements ControllerV4 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
String username = paramMap.get("username");
int age = Integer.parseInt(paramMap.get("age"));
Member member = new Member(username, age);
memberRepository.save(member);
model.put("member", member);
return "save-result";
}
}
- memberRepository 를 불러온다.
- 넘어온 paramMap 에서 username과 age를 꺼내 변수에 넣는다.
- Member 객체를 만들고 저장한다.
- 빈 model map에 member를 넣고 save-result 라는 viewName을 돌려준다.
- viewName을 가지고 view를 resolve 하고, 모델과 함께 렌더링 한다.
List
- URI 에서 List 를 가져온다. 파라미터가 없기 때문에 바로 빈 맵과 빈 모델이 들어간채로 controller 가 작동한다.
- controller
package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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);
return "members";
}
}
- memberRepository 에서 전부 가져와서 model 에 넣는다. 그러고 members 라는 viewName을 return
- viewName을 가지고 view를 rendering 할때 모델도 같이 넣어서 제공한다.
V3와 다른점이라면, controller를 통해서 제공되는 것이 ViewName이고, Model을 따로 객체로 빼서, model에 값을 넣고, 사용한다.
V5
- adapter를 이용한 개선
- Controller = handler 이다.
- 어떠한 handler를 사용할지, 핸들러를 처리할 수 있는 핸들러 어댑터를 만든다.
- 핸들러 어댑터: 중간에 어댑터 역할을 하는 어댑터가 추가되었는데 이름이 핸들러 어댑터이다. 여기서 어댑터 역할을 해주는 덕분에 다양한 종류의 컨트롤러를 호출할 수 있다.
- 핸들러: 컨트롤러의 이름을 더 넓은 범위인 핸들러로 변경했다. 그 이유는 이제 어댑터가 있기 때문에 꼭 컨트롤러의 개념 뿐만 아니라 어떠한 것이든 해당하는 종류의 어댑터만 있으면 다 처리할 수 있기 때문이다.
HandlerAdapter
package hello.servlet.web.frontcontroller.v5;
import hello.servlet.web.frontcontroller.ModelView;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public interface MyHandlerAdapter {
boolean supports(Object handler); // controller 가 넘어왔을때 이 컨트롤러를 지원할수 있는지 판단
ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException;
}
- controller 가 넘어왔을 때, 지원 가능한 controller 인지 판단하는 메서드
- ModelView를 넘겨주는 메서드 를 구현해야 한다.
frontController
package hello.servlet.web.frontcontroller.v5;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;
import hello.servlet.web.frontcontroller.v3.controller.MemberFormControllerV3;
import hello.servlet.web.frontcontroller.v3.controller.MemberListControllerV3;
import hello.servlet.web.frontcontroller.v3.controller.MemberSaveControllerV3;
import hello.servlet.web.frontcontroller.v4.controller.MemberFormControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberListControllerV4;
import hello.servlet.web.frontcontroller.v4.controller.MemberSaveControllerV4;
import hello.servlet.web.frontcontroller.v5.adapter.ControllerV3HandlerAdapter;
import hello.servlet.web.frontcontroller.v5.adapter.ControllerV4HandlerAdapter;
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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@WebServlet(name = "frontControllerServletV5", urlPatterns = "/front-controller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {
private final Map<String, Object> handlerMappingMap = new HashMap<>();// 어떤 컨트롤러도 들어갈 수 있게.
private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>(); // 리스트
public FrontControllerServletV5() {
initHandlerMappingMap();
initHandlerAdapters();
}
private void initHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
handlerAdapters.add(new ControllerV4HandlerAdapter());
}
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());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object handler = getHandler(request);
if (handler == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);// 404 처리
return;
}
MyHandlerAdapter adapter = getHandlerAdapter(handler);
ModelView mv = adapter.handle(request, response, handler);
// form 에 값을 넣었을때.
String viewName = mv.getViewName(); // 논리이름 new-form
MyView view = viewResolver(viewName);// 물리이름 new-form.jsp
view.render(mv.getModel(), request,response); // view 호출 jsp 호출
}
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");
}
private Object getHandler(HttpServletRequest request) {
String requestURI = request.getRequestURI();// 요청 URI -> new-form
return handlerMappingMap.get(requestURI); // 요청 URI에 해당하는 controller 찾기
}
}
- handelrMappingMap → 어떠한 컨트롤러도 들어갈 수 있게
<String, Object>
로 구현. - handlerAdatpers → 컨트롤러에 알맞은 adpater를 넘겨줄 수 있도록 리스트를 구현
- 생성자를 통해 controllerV3HandlerAdapter ,v4를 넘겨준다.
- 또한 map에 모든 URI에 대한 controller 를 넣는다.
Service
handler
: request에서 넘어온 URI 에 알맞은 controller 를 찾는 메서드
getHandlerAdapter
: 핸들러 어댑터를 반복문을 돌리면서 알맞은 Adapter를 찾음.
ControllerV3HandlerAdpater
package hello.servlet.web.frontcontroller.v5.adapter;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;
import hello.servlet.web.frontcontroller.v5.MyHandlerAdapter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class ControllerV3HandlerAdapter implements MyHandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof ControllerV3);// controllerV3의 인스턴스인지 확인
}
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
ControllerV3 controller = (ControllerV3) handler; // object 를 controllerV3로 캐스팅
Map<String, String> paramMap = createParamMap(request); // 파라미터를 다 꺼내서 paramMap에 저장
ModelView mv = controller.process(paramMap);// controller 호출
return mv;
}
private static Map<String, String> createParamMap(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName))); // 파라미터에 있는 값을 다 꺼내서 paramMap에 저장
return paramMap;
}
}
support
: handler와 V3를 비교해서 같으면 true 제공.ModelView
- object로 넘어왔기 때문에 casting을 먼저 한다.
- 이후 controllerV3에 알맞게 넘겨준다.
ControllerV4HandlerAdpater
package hello.servlet.web.frontcontroller.v5.adapter;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import hello.servlet.web.frontcontroller.v5.MyHandlerAdapter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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) throws ServletException, IOException {
ControllerV4 controller = (ControllerV4) handler; // 캐스팅
Map<String, String> paramMap = createParamMap(request);// 파라미터를 다 꺼내서 paramMap에 저장 (실제로 사용하지 않음
HashMap<String, Object> model = new HashMap<>();
String viewName = controller.process(paramMap, model);
ModelView mv = new ModelView(viewName);// ModelView를 생성해서 반환
mv.setModel(model);
return mv;
}
private static Map<String, String> createParamMap(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName))); // 파라미터에 있는 값을 다 꺼내서 paramMap에 저장
return paramMap;
}
}
- V4도 V3과 같은 알고리즘, V4에 알맞은 데이터 형식으로 제공한다.
adapter.handle
: 을 통해서 넘어온 controller에 알맞게 캐스팅, modelview를 제공한다.- 이후 modelView를 통해 jsp를 resolve 하고, 렌더링해서 view를 보여준다.