실용적인 컨트롤러 개요
앞에서 만든 v3 컨트롤러는 서블릿 종속성을 제거하고 뷰 경로의 중복을 제거한 잘 설계된 컨트롤러 이지만 컨트롤러 인터페이스를 구현하는 개발자 입장에서는 항상 ModelView 객체를 생성하고 반환해야 하는 부분이 번거롭다. 좋은 프레임워크란 아키텍쳐도 중요하지만,
그와 더불어 개발자가 단순하고 편리하게 사용할 수 있어야 한다 ( = 실용성)
Goal : v3를 변경해서 개발자가 편리하게 사용할 수 있도록 v4를 개발해보자
실용적인 컨트롤러
v4 구조 아키텍쳐
ControllerV4
hello/servlet/web/frontcontroller/v4/ControllerV4.java
package hello.servlet.web.frontcontroller.v4;
import java.util.Map;
/**
* @parma paramMap
* @param model
* @return viewName
*/
public interface ControllerV4 {
String process(Map<String, String> paramMap, Map<String, Object> model);
}
- 이번 버전은 인터페이스에 ModelView가 없다. model 객체는 파라미터로 전달되기 때문에 그냥 사용하면 되고, 결과로 뷰의 이름만 반환해 주면 된다.
MemberFormControllerV4
hello/servlet/web/frontcontroller/v4/controller/MemberFormControllerV4.java
package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import java.util.Map;
public class MemberFormControllerV4 implements ControllerV4 {
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
return "new-form";
}
}
MemberSaveControllerV4
hello/servlet/web/frontcontroller/v4/controller/MemberSaveControllerV4.java
package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
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";
}
}
MemberListControllerV4
hello/servlet/web/frontcontroller/v4/controller/MemberListControllerV4.java
package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
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";
}
}
FrontControllerServletV4
hello/servlet/web/frontcontroller/v4/FrontControllerServletV4.java
package hello.servlet.web.frontcontroller.v4;
import hello.servlet.web.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 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;
import java.util.Objects;
@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 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)));
return paramMap;
}
}
모델 객체 전달
Map<String, Object> model = new HashMap<>(); // 추가
모델 객체를 프론트 컨트롤러에서 생성해서 넘겨준다. 컨트롤러에서 모델 객체에 값을 담으면 여기에 그대로 담겨있게 된다.
뷰의 논리 이름을 직접 반환
컨트롤러가 직접 뷰의 논리 이름을 반환하므로 이 값을 사용하여 실제 물리 뷰를 찾을 수 있다.
String viewName = controller.process(paramMap, model);
MyView view = viewResolver(viewName);
http://localhost:8080/front-controller/v4/members/new-form 등록 실행 결과
http://localhost:8080/front-controller/v4/members/members 목록 조회 실행 결과
** 정리
이번 버전의 컨트롤러는 매우 단순하고 실용적이다. 기존 구조에서 모델을 파라미터로 넘기고, 뷰의 논리 이름을 반환한다는 작은 아이디어를 적용했을 뿐이지만 컨트롤러를 구현하는 개발자 입장에서는 아주 효율적이다. 프레임워크나 공통 기능이 수고로워야 개발자가 편리해진다.
이 강의의 핵심 메세지는 결국 잘 짜여진 프레임워크도 개발자에게 쓰여지기 쉬워야 한다는 사실이다. 이번에 새로 진행되는 프로젝트에서 공통개발자 분께서 개발환경세팅과 공통작업을 진행해주고 계시는데 이전 프로젝트랑 공통의 영역과 기준이 달라서 소스 분석하다가 의문인 점이 많았다. 나는 기존 프로젝트에 익숙한 사고를 할 법 하지만, 현재 진행되는 프로젝트는 이전 보다 공통의 작업은 훨씬 줄었다. 그 말인 즉 각자 비지니스 영역에서 반복된 소스가 많아졌다는 말... 그걸 경험하고 이 강의를 듣는 마지막 전달 메세지는 매우 공감이 간다. 뚜뚜뚜..
@ 스프링 MVC 1편 - 백엔드 웹개발 by 김영한을 참조하고 있습니다.
'Spring > mvc' 카테고리의 다른 글
[Spring MVC 1] - MVC 프레임워크 - 유연한 컨트롤러 2 (0) | 2022.08.31 |
---|---|
[Spring MVC 1] - MVC 프레임워크 - 유연한 컨트롤러 1 (0) | 2022.08.30 |
[Spring MVC 1] - MVC 프레임워크 - Model 추가 (0) | 2022.08.29 |
[Spring MVC 1] - MVC 프레임워크 - view 분리 (0) | 2022.08.16 |
[Spring MVC 1] - MVC 프레임워크 - 프론트 컨트롤러 도입 (0) | 2022.08.16 |