하나의 입구(Front-Controller)
입구를 통제하는 컨트롤러를 만들고 생성된 컨트롤러에서 요청에 맞는 컨트롤러를 호출
클라이언트 - HTTP 요청 - FrontController - 필요 Controller 호출
Controller Interface
package hello.servlet.web.frontcontroller.v;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public interface Controller {
void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
//각 컨트롤러는 인터페이스를 구현하여 구체화하여 사용한다
}
회원등록 Controller
package hello.servlet.web.frontcontroller.v.controller;
import hello.servlet.web.frontcontroller.v.ControllerV1;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MemberFormController implements ControllerV {
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
회원저장 Controller
package hello.servlet.web.frontcontroller.v.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.v1.ControllerV1;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MemberSaveController implements ControllerV {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age")); //웹에서 전송 받은 데이터 형식을 정수형으로 반환
//웹에서 전송받은 데이터를 저장 로직
Member member = new Member(username, age);
System.out.println("member = " + member);
memberRepository.save(member);
//데이터 보관
//request 객체에 데이터를 보관하여 뷰에 전달
request.setAttribute("member", member);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
회원 리스트 Controller
package hello.servlet.web.frontcontroller.v.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.v1.ControllerV1;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class MemberListController implements ControllerV {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
request.setAttribute("members", members);
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
FrontController
package hello.servlet.web.frontcontroller.v;
import hello.servlet.web.frontcontroller.v.controller.MemberFormControllerV;
import hello.servlet.web.frontcontroller.v.controller.MemberListControllerV;
import hello.servlet.web.frontcontroller.v.controller.MemberSaveControllerV;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
// *: 하위 모든 컨트롤러 호출
@WebServlet(name = "frontControllerServletV", urlPatterns = "/front-controller/v/*")
public class FrontControllerServletV extends HttpServlet {
private Map<String, ControllerV> controllerVMap = new HashMap<>();
// cmd + n : constructor none
public FrontControllerServletV() {
controllerVMap.put("/front-controller/v/members/new-form", new MemberFormControllerV());
controllerVMap.put("/front-controller/v/members/save", new MemberSaveControllerV());
controllerVMap.put("/front-controller/v/members", new MemberListControllerV());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("FrontControllerServletV1.service");
String requestURI = request.getRequestURI();
ControllerV1 controllerV1 = controllerV1Map.get(requestURI);
if (controllerV == null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
controllerV.process(request, response);
}
}
🪴 FrontController를 사용하여 입구를 하나로 만들어 다형성을 구현, 하지만 View의 코드가 중복
작성한 Controller 수정
View Controller
package hello.servlet.web.frontcontroller;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
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);
}
}
회원등록 Controller 수정
package hello.servlet.web.frontcontroller.v.controller;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v.ControllerV;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MemberFormControllerV implements ControllerV {
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
MyView myView = new MyView("/WEB-INF/views/new-form.jsp");
return myView;
}
}
회원저장 Controller 수정
package hello.servlet.web.frontcontroller.v.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v.ControllerV;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MemberSaveControllerV2implements ControllerV {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age")); //웹에서 전송 받은 데이터 형식을 정수형으로 반환
//웹에서 전송받은 데이터를 저장 로직
Member member = new Member(username, age);
memberRepository.save(member);
//데이터 보관
//request 객체에 데이터를 보관하여 뷰에 전달
request.setAttribute("member", member);
return new MyView("/WEB-INF/views/save-result.jsp");
}
}
회원리스트 Controller
package hello.servlet.web.frontcontroller.v.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v.ControllerV;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class MemberListControllerV implements ControllerV {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
request.setAttribute("members", members);
return new MyView("/WEB-INF/views/members.jsp");
}
}
View를 나누어 Controller의 코드가 간결해지고 하나의 동작을 할 수 있는 밑거름을 마련하지만 Controller 입장에서는 HttpServlet 정보가 꼭 필요하지 않을 수 있다. (파라미터를 Map으로 전달 필요)
📋 1. 뷰 이름의 중복 제거: 논리이름으로 반환
📋 2. Controller에 HttpServletRequest: Model에 request.setAttribute를 model 생성하여 분리
Moder View Class 생성
package hello.servlet.web.frontcontroller;
import java.util.HashMap;
import java.util.Map;
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 수정
package hello.servlet.web.frontcontroller.v;
import hello.servlet.web.frontcontroller.ModelView;
import java.util.Map;
public interface ControllerV {
ModelView process(Map<String, String> paramMap);
}
회원등록 Controller 수정
package hello.servlet.web.frontcontroller.v3.controller;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;
import java.util.Map;
public class MemberFormControllerV3 implements ControllerV3 {
@Override
public ModelView process(Map<String, String> paramMap) {
//논리 이름 반환
return new ModelView("new-form");
}
}
회원저장 Controller 수정
package hello.servlet.web.frontcontroller.v3.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;
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;
}
}
회원리스트 Controller 수정
package hello.servlet.web.frontcontroller.v.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v.ControllerV3;
import java.util.List;
import java.util.Map;
public class MemberListControllerV implements ControllerV3 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public ModelView process(Map<String, String> paramMap) {
List<Member> members = memberRepository.findAll();
ModelView mv = new ModelView("members");
//컨트롤러에 필요한 파라미터를 넘겨준다
mv.getModel().put("members", members);
return mv;
}
}
Front Controller 수정
package hello.servlet.web.frontcontroller.v;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v.ControllerV;
import hello.servlet.web.frontcontroller.v.controller.MemberFormControllerV;
import hello.servlet.web.frontcontroller.v.controller.MemberListControllerV;
import hello.servlet.web.frontcontroller.v.controller.MemberSaveControllerV;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
// *: 하위 모든 컨트롤러 호출
@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v/*")
public class FrontControllerServletV extends HttpServlet {
private Map<String, ControllerV> controllerV1Map = new HashMap<>();
// cmd + n : constructor none
public FrontControllerServletV() {
controllerV1Map.put("/front-controller/v/members/new-form", new MemberFormControllerV());
controllerVMap.put("/front-controller/v/members/save", new MemberSaveControllerV());
controllerVMap.put("/front-controller/v/members", new MemberListControllerV());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("FrontControllerServletV.service");
String requestURI = request.getRequestURI();
ControllerV controllerV = controllerV1Map.get(requestURI);
if (controllerV == null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
//1. paramMap 넘겨주기
Map<String, String> paramMap = createParamMap(request);
ModelView mv = controllerV.process(paramMap);
//2. url 맵핑
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<>(); //map 객체 생성
request.getParameterNames().asIterator() //request 의 모든 Parameter 모두 꺼내오기
.forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
return paramMap;
}
private MyView viewResolver(String viewName) {
// 폴더 경로가 바뀌어도 한번만 수정하면 된다.
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
}
📋 정리
FrontController: 입구를 하나로 만들어 요청에 맞는 컨트롤러 찾아서 호출
view 분리: FrontController에서 호출된 요청에 맞는 컨트롤러의 뷰를 보여줌
Model 추가: 서블릿의 종속 제거, view 이름 반환, HttpServletRequest의 정보는 Map(ParamMap으로 호출)
viewResolver: 컨트롤러가 반환해준 논리 뷰 이름을 물리 경로로 변경
viewRender: HTML의 화면 렌더링, Attribute 세팅으로 JSP에 데이터 전달