이번 파트는 반복해서 봐야한다. 어려운 부분이 확실히 있는 것 같다.
우선, 서블릿 종속성을 제거해 주었다. Controller에서 HttpServletRequest와 HttpServletResponse가 안 쓰이는 경우가 종종 있었는데 그것을 해결해 주었다. 요청 파라미터 정보를 Map으로 넘겨 Controller가 Servlet을 몰라도 동작하도록 하였다.
request 또한 강의에서 model로 사용했고 따로 Model 객체를 만들어 반환하도록 했다 이렇게 하면 코드가 단순해지고 테스트 코드 작성도 간단해 진다고 한다.
중복되는 것은 서블릿 관련만 있는 게 아니었다. 뷰 이름(경로) 또한 중복되고 있었다. 컨트롤러에서 뷰의 논리 이름을 반환하고 실제 물리 위치는 프론트 컨트롤러에서 처리하도록 단순화하는 작업도 했다. 이렇게 했을 때 장점으로는 향후에 뷰의 폴더 위치가 함께 이동해도 프론트 컨트롤러만 고치면 되어서 유지 보수하기가 좋다고 한다.
결국 이번 강의를 들으면서 코딩한 코드의 동작 원리는 클라이언트가 요청을 보내면 프론트 컨트롤러가 매핑 정보를 조회하여 알맞는 컨트롤러를 호출해주고 컨트롤러는 서블릿 종속을 제거하기 위해 만든 클래스인 모델뷰를 반환해 준다. 프론트 컨트롤러는 다시 컨트롤러가 반환한 논리 뷰 이름을 실제 물리 뷰 경로로 변경하는 뷰 리졸버를 호출하고 뷰 리졸버는 실제 물리 경로가 있는 MyView 객체를 반환한다. 다시 프론트 컨트롤러는 render(model)을 호출하고 MyView에서 HTML 응답을 하게 된다.
이론상은 이런데 잘 이해가 안 된다. 강의에서 강사님이 항상 말 하듯이 코드를 보면서 이해를 하는 게 제일 빠른 것 같다. 그래도 바탕이 있으면 좋으니까 바탕을 먼저 정리해 봤다.
public class ModelView {
private String viewName;
private Map<String, Object> model = new HashMap<>();
우선 ModelView 클래스에서 viewName과 model을 만들어주고 생성자와 겟,셋터를 만들어 주었다. model을 만들어 준 이유는 서블릿의 종속성을 제거하기 위해 사용하고 Controller에서 HttpServletRequest를 사용하지 않는 방식으로 구현을 해보기로 했기 때문에 request.setAttribute() 또한 호출할 수 없어서 별도 Model을 사용하기 위해 만들어 주었다. viewName은 View 이름까지 전달하는 객체를 만들기 위해 만들어 줬다.
model은 단순히 Map으로 되어 있기 때문에 컨트롤러에서 뷰에 필요한 데이터를 키,벨류 값으로 넣어주면 된다.
public interface ControllerV3 {
ModelView process(Map<String, String> paramMap);
}
인터페이스 컨트롤러도 하나 만들어 주었다. 이 컨트롤러는 Servlet을 사용하지 않아 단순하고 테스트 코드 작성이 쉽다. → HttpServletRequest가 제공하는 파라미터는 프론트 컨트롤러가 paramMap에 담아 호출해주면 뷰 이름과 뷰에 전달할 Model 데이터를 포함하는 ModelView 객체를 반환하면 된다.
public class MemberFormControllerV3 implements ControllerV3 {
@Override
public ModelView process(Map<String, String> paramMap) {
return new ModelView("new-form");
}
}
회원 정보을 등록하는 페이지에 들어가는 로직이다. 전보다 훨 간단해 졌다. view 이름도 절대 경로로 쭉 작성해 줄 필요가 없이 논리적인 이름만 넣고 물리적인 이름은 프론트 컨트롤러에서 처리하도록 한다.
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;
}
회원 정보를 저장하는 컨트롤러인데 역시 인터페이스 컨트롤러를 구현하면 된다.
paramMap에 파라미터 정보가 담겨 있어서 map에서 필요한 요청 파라미터를 조회하면 된다.
다른 컨트롤러도 비슷하게 작성해 주면 되고 프론트 컨트롤러를 체크해 봐야 한다.
Map<String, String> paramMap = createParamMap(request);
ModelView mv = controller.process(paramMap);
String viewName = mv.getViewName();//논리 이름 new -form
MyView view = viewResolver(viewName);
view.render(mv.getModel(), request, response);
}
private MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
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;
}
이 로직을 만들었는데 좀 어렵다.
우선 createParamMap은 HttpServletRequest에서 파라미터 정보를 꺼내어 Map으로 변환해 준다. 그리고 컨트롤러에 전달하면서 호출해준다.
viewResolver는 컨트롤러가 반환한 논리 뷰 이름을 실제 물리 뷰 경로로 변경을 해주고 물리 경로가 있는 MyView 객체를 반환해 준다.
private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
model.forEach((key, value) -> request.setAttribute(key, value));
}
MyView에는 modelToRequestAttribute을 만들어 주었고 model을 request.setAttribute에 넣어준다 JSP는 request.setAttribute에 넣어서 사용할 수 있다고 한다.
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술을 참고하여 공부하였습니다.
'Framework & Library > Spring Boot' 카테고리의 다른 글
[Spring Boot] : Controller를 여러 방식으로 구현하기 위해 사용한 Adapter pattern (0) | 2022.01.23 |
---|---|
[Spring Boot] : 단순하고 실용적은 ModelView 객체의 중복을 제거한 컨트롤러 코딩 (0) | 2022.01.23 |
[Spring Boot] : Controller에서 View 이동 시 중복되는 부분 리펙터링하기 (0) | 2022.01.22 |
[Spring Boot] : Front Controller 구현해보기 (0) | 2022.01.22 |
[Spring Boot] : Front Controller란? (0) | 2022.01.21 |
댓글