각각의 Controller마다 공통적인 부분을 처리 및 Servlet의 종속된 코드 수정 필요
문제)
jsp 페이지 경로 (중복되는 부분)
dispatcher.forward의 반복
Controller에서 잘 사용되지 않는, HttpServletRequest + HttpServletResponse (Servlet 종속)
Model , View, Controller 분리된 코드의 필요성
View의 영역에서도 jsp의 forward을 공통적으로 계속해서 처리해줘야했음
jsp 이름의 공통적인 부분을 viewResolver()를 이용하여 처리
MyView클래스를 이용하여 forward 하는 부분을 공통적으로 처리할 수 있도록 수정
HttpServletRequest 및 HttpServletResponse의 종속성을 최대한 줄이는 목적 (Controller에서 사용안할때도 많음)
데이터 저장 및 전달의 역할은 ModelView의 Map(String, Object)을 이용
MyView 클래스의 modelToRequestAttribute() 에서 마지막에 ModelView에서 사용되던 데이터 Object를 request.setAttribute를 이용하여 request로 전달
Front-Controller 도식화
Model 생성
Model 역할로서 데이터를 전달하는 역할
추가로, View 이름까지 전달하는 역할
View 페이지 이름 저장
Map을 통해 필요한 데이터값 저장 (데이터는 객체 형태) -> request.getParameter + request.setParameter 처럼 사용 목적
예시) Key("member") , value( [Member 객체] )
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 로직을 위한 인터페이스
HttpServlet의 service() 부분과 유사한 Controller 로직 실행 구간 추상화
실제로 paramMap에는 request.getParameter("name") 처럼 사용 목적
예시) Key(name) , value([폼에서 넘어온 이름])
public interface Controller{
ModelView process(Map<String, String> paramMap);
}
Controller 생성
view 이름 지정
paramMap을 통해 데이터를 처리 -> request.getParameter + request.setParameter 처럼 사용 목적
//new-form Controller
public class MeberFormController implements Controller{
@Override
public ModelView process(Map<String, String> paramMap){
return new ModelView("new-form");
}
}
//save Controller
public class MemberSaveController implements Controller{
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public ModelVIew process(Map<String, String> paramMap){
String name = paramMap.get("name");
int age = Integer.parseInt(paramMap.get("age"));
Member member = new Member(name, age);
memberRepository.save(member);
ModelView mv = new ModelView("save-result");
mv.getModel().put("member", member);
return mv;
}
}
//list Controller
public class MemberListController implements Controller{
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;
}
}
FrontController 생성
FrontController 생성 당시 ControllerMap 초기화 ( Controller 맵핑 준비 )
요청에서 들어온 URL을 통해 맞는 Controller을 호출 (맵핑 진행)
Controller을 특정했으면, 요청에 들어온 데이터를 ParamMap에 저장
ParamMap을 Controller로 보내고 로직 실행
MyView를 이용하여 렌더링
@WebServlet(urlPatterns="/main/*")
public class FrontControllerServlet extends HttpServlet{
private Map<String, Controller> controllerMap = new HashMap<>();
// 필요한 ViewName 초기화 (Controller 조회 Map)
public FrontControllerServlet(){
controllerMap.put("/main/new-form", new MemberFormController());
controllerMap.put("/main/save", new MemberSaveController());
controllerMap.put("/main/members", new MemberListController());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
String requestURI = request.getRequestURI();
// 요청 URL -> 해당 controller controllerMap에서 꺼내기
Controller controller = controllerMap.get(requestURI);
if(controller == null)
{
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
//request의 요청에 맞는 parameter 생성
Map<String, String> paramMap = createParamMap(request);
//controller에 파라미터를 넘겨서 로직 수행
ModelView mv = controller.process(paramMap);
// controller에서 데이터 처리한 ModelView를 이용하여 MyView로 전송 (jsp 호출)
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/" + viewName + ".jsp");
}
}
View 생성
jsp로 forward 해주는 역할
jsp페이지로 넘어가기전에 paramMap에 들어있던 model 객체를 Request에 저장함 -> modelToRequestAttribute
public class Member {
private Long id;
private String name;
private int age;
public Member(){
}
public Member(String name, int age){
this.name = name;
this.age = age;
}
//Getter,Setter
}
public class MemberRepository {
private Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
private static final MemberRepository instance = new MemberRepository();
private MemberRepository(){
}
public static MemberRepository getInstance(){
return instance;
}
public Member save(Member member){
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
public Member findById(Long id){
return store.get(id);
}
public List<Member> findAll(){
return new ArrayList<>(store.values());
}
public void clearStore(){
store.clear();
}
}