단순하고 실용적인 컨트롤러 V4
앞서 만든 V3 컨트롤러의 경우 서블릿 종속성을 제거하고 뷰 경로의 중복을 논리 뷰 이름을 반환함으로써 제거했다.
그러나 실제 컨트롤러 인터페이스를 구현하는 개발자 입장에서 항상 ModelView 객체를 생성/반환하는 것은 번거롭다
좋은 프레임워크는 아키텍쳐도 중요하지만, 그와 더불어 개발자 편의성도 중요한 요소 중 하나이다
대표적으로 스프링이 등장하기 전 EJB는 아키텍쳐 관점에서는 좋은 프레임워크였지만, 개발자 편의성에 있어 다소 떨어졌다.
그래서 스프링이 등장한 이후로 거의 사장되었다고 한다.
따라서 좋은 웹 프레임워크는 좋은 아키텍쳐 설계에 더해, 개발자들이 쓸 수 있도록 실용성이 있어야 한다.
이제부터 V3를 조금 더 개발자가 쓰기 편리하게 설계를 변경해보겠다
기본적인 설계 구조는 V3와 같은데 차이점이라면 컨트롤러가 ModelView를 반환하지 않고 ViewName만 반환하는 것이다.
1) 컨트롤러 V4 인터페이스
package hello.servlet.web.frontcontroller.v4;
import java.util.Map;
public interface ControllerV4 {
/**
*
* @param paramMap
* @param model
* @return
*/
String process(Map<String, String> paramMap, Map<String, Object> model);
}
이번 버전의 인터페이스에는 ModelView가 없다.
model 객체는 파라미터로 전달되기 때문에 그냥 사용하면 되고, 결과로 view의 이름만 반환한다.
실제 구현 코드는 아래와 같다
2) MemberFormControllerV4
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";
}
}
3) MemberSaveControllerV4
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.Map;
public class MemberSaveControllerV4 implements ControllerV4 {
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";
}
}
모델이 파라미터로 전달되기 때문에 모델을 직접 생성하지 않아도 된다
4) MemberListControllerV4
package hello.servlet.web.frontcontroller.v4.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import java.util.List;
import java.util.Map;
public class MemberListControllerV4 implements ControllerV4 {
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";
}
}
구현 컨트롤러들에 대해 어느정도 구성을 알았으니, 이제 프론트 컨트롤러 구현에 대해 알아보겠다
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 java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@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/v3/members/new-form", new MemberFormControllerV4());
controllerMap.put("/front-controller/v3/members/save", new MemberSaveControllerV4());
controllerMap.put("/front-controller/v3/members/", new MemberListControllerV4());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestURI = request.getRequestURI();
ControllerV4 controllerV4 = controllerMap.get(requestURI);
if (controllerV4 == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
Map<String, String> paramMap = createParamMap(request);
Map<String, Object> model = new HashMap<>(); // 추가
String viewName = controllerV4.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;
}
}
RequestURI를 통해 컨트롤러를 호출해오는 부분까지는 같고, 거의 모든 구성이 V3 구조와 비슷하다
차이점은 이전에는 컨트롤러에서 ModelView를 통해 객체를 직접 생성하고 반환한것과 달리
이제는 프론트 컨트롤러에서 모델 객체를 생성하고 이를 인자로 컨트롤러에 넘겨준다
컨트롤러에서 모델 객체에 값을 담으면 여기에 그대로 담겨있게 된다
또한 뷰의 논리 이름을 String 객체로 직접 반환한다
코드에 보면 viewResolver 메서드가 Extraction 되어있는것을 확인할 수 있다
이를 통해 논리 뷰 이름을 반환하면 그 반환값을 이용하여 뷰의 논리주소를 파악할 수 있다.
이전에는 ModelView 객체에서 get해와야했었지만 이제는 컨트롤러의 메서드가 String으로 논리뷰를 직접 반환하기 때문에
해당 사실을 반영하여 코드를 변경했다.
기존 구조에서 모델을 파라미터로 넘기고 뷰의 논리이름을 반환한다는 아이디어 적용으로 인해
컨트롤러를 구현하는 개발자 입장에서 더 군더더기 없는 코드 작성이 가능하다
프레임워크의 점진적 발전 과정을 정리부분에서 보겠지만
점진적으로 어떻게 발전했는지에 대해 집중해서 보면 좋을 듯 하다.
'스프링 공부 (인프런 김영한 선생님) > 스프링 MVC 1편' 카테고리의 다른 글
[스프링 웹 MVC 1편] 16. 스프링 MVC - V1 ~ V5와 비교했을 때 (구조 이해) (0) | 2023.05.29 |
---|---|
[스프링 웹 MVC 1편] 15. MVC 프레임워크 제작해보기 - v5 (0) | 2023.05.23 |
[스프링 웹 MVC 1편] 13. MVC 프레임워크 제작해보기 - v3 (0) | 2023.05.22 |
[스프링 웹 MVC 1편] 12. MVC 프레임워크 제작해보기 - v2 (0) | 2023.05.22 |
[스프링 웹 MVC 1편] 11. MVC 프레임워크 제작해보기 - v1 (0) | 2023.05.22 |