프론트 컨트롤러 패턴이란? 공통적인걸 앞에서 처리하는 패턴
단순하게 매핑 정보만 넣어보자
public interface ControllerV1 {
void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
먼저 컨트롤러 인터페이스를 도입, 각 컨트롤러는 이걸 구현한것 그리고 기존에 했던 서블릿을 그대로 구현해 준다.
@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();
//localhost:8080/ 여기서부터 받아옴
ControllerV1 controller = controllerMap.get(requestURI);
if(controller ==null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
controller.process(request,response);
co
frontcontroller호출
팁: 리팩토링할때 구조적인걸 개선하고 세밀한걸 개선
매핑정보에서 컨트롤러를 찾아와서 컨트롤러 호출 v1에서는 컨트롤러가 jsp를 직접 forward했지만
V2는 MyView가 대신 jsp를 호출해준다
http://localhost:8080/front-controller/v2/members 입력 -> 그럼 FrontControllerServlet2가 호출됨
MyView view = controller.process(request, response);
view.render(request,response);
}
process 호출 그다음 MemberFormControllerV2로 가서 new Myview를 넘겨줌
그다음 render를 호출
public class MyView {
private String viewPath;
public MyView(String viewPath) {
this.viewPath = viewPath;
}
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request,response);
}
}
jsp 호출
v2 의의: 화면을 렌더링하기위한 myview객체가 생성됨
기존에는 controller에서 view를 반환했다 이번에는 modelview를 반환 그리고 논리이름을 viewresolver가 물리이름으로 바꿔서 반환해준다
v3의의 : 컨트롤러가 서블릿 기술을 몰라도 동작할수 있게 하자, 뷰 이름에 중복이 있으니까 물리위치의 이름을 프론트 컨트롤러에서 처리하도록 하자
public class ModelView {
private String viewName;
private Map<String, Object> model = new HashMap<>();
public ModelView(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return viewName;
}
public void setViewName(String viewName) {
this.viewName = viewName;
}
public Map<String, Object> getModel() {
return model;
}
public void setModel(Map<String, Object> model) {
this.model = model;
}
}
모델 객체 생성으로 인해 controller에서 서블릿 종속성을 제거
import java.util.Map;
public class MemberSaveControllerV3 implements ControllerV3 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public ModelView process(Map<String, String> paramMap) {
String username = paramMap.get("username");
int age = Integer.parseInt(paramMap.get("age"));
Member member = new Member(username, age);
memberRepository.save(member);
ModelView mv = new ModelView("save-result");
mv.getModel().put("member", member);
return mv;
}
}
v2에서는 request.setAttribute에서 파라미터정보를 관리했지만 v3에서는 modelview 객체에 member를 담아준다.
@WebServlet(name = "frontControllerServletV3", urlPatterns = "/frontcontroller/v3/*")
public class FrontControllerServletV3 extends HttpServlet {
private Map<String, ControllerV3> controllerMap = new HashMap<>();
public FrontControllerServletV3() {
controllerMap.put("/front-controller/v3/members/new-form", new
MemberFormControllerV3());
controllerMap.put("/front-controller/v3/members/save", new
MemberSaveControllerV3());
controllerMap.put("/front-controller/v3/members", new
MemberListControllerV3());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
String requestURI = request.getRequestURI();
ControllerV3 controller = controllerMap.get(requestURI);
if (controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
Map<String, String> paramMap = createParamMap(request);
ModelView mv = controller.process(paramMap);
String viewName = mv.getViewName();
MyView view = viewResolver(viewName);
view.render(mv.getModel(), request, response);
}
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;
}
private MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
}
createParamap에서 파라미터 정보를 꺼내 Map으로 변환한다 그리고 viewresolver가 물리 경로를 반환
ModelView를 객체를 생성하고 반환해야 하는 부분이 살짝 번거롭다
그래서 컨트롤러가 modelview를 반환안하고 viewName만 반환한다.
public interface ControllerV4 {
String process(Map<String, String> paramMap, Map<String, Object> model);
}
modelview를 반환안함 => model 객체가 그냥 파라미터로 전달 되기 때문
public class MemberFormControllerV4 implements ControllerV4 {
@Override
public String process(Map<String, String> paramMap, Map<String, Object>
model) {
return "new-form";
}
}
new-form 이라는 논리 이름만 반환함
Map<String, Object> model = new HashMap<>();
String viewName = controller.process(paramMap, model);
MyView view = viewResolver(viewName);
모델 객체를 프론트 컨트롤러에서 생성해서 넘겨주고, 모델 객체에 값을 담으면 여기에 그대로 담김