이전에 논의했던 대로 v2 버전에서는
viewPath의 반복되는 등장과 RequestDispatcher의 중복 그리고 forward 로직의 중복을 해결해보겠다.
이전에는 컨트롤러가 직접 JSP에 forward를 통해 request와 response를 전달한 것과 다르게
이번에 Controller는 MyView를 반환한다
FrontController는 반환된 MyView 객체에서 render()를 호출하게 되고
이 render() 객체는 아까 중복되었던 viewPath와 RequestDispatcher 그리고 forward를 포함한다
즉, MyView에 의해 forward가 일어난다.
정리하면 MyView의 역할은, 컨트롤러에 의해 반환된 내용을 갖고 view에 request, response 객체를 넘기는 역할이다
코드로 직접 구성해보자
이전과 동일하게 다형성을 적극 활용하기 위해 ControllerV2 인터페이스를 작성한다
package hello.servlet.web.frontcontroller.v2;
import hello.servlet.web.frontcontroller.MyView;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface ControllerV2 {
MyView process(
HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
// 이걸 가지고 여러가지, 폼 리스트 저장 컨트롤러로 인터페이스 다 구현할거임.
// 매핑 정보 호출할때 일관성 있게 다형성 이용해서 잘 호출 가능
// V2에서는 void 였던 v1과는 다르게 MyView 반환
}
1) MemberFormControllerV2
package hello.servlet.web.frontcontroller.v2.controller;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v2.ControllerV2;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MemberFormControllerV2 implements ControllerV2 {
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
return new MyView("/WEB-INF/views/new-form.jsp"); // 중복 제거 됨.
}
}
2) MemberSaveControllerV2
package hello.servlet.web.frontcontroller.v2.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v2.ControllerV2;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MemberListControllerV2 implements ControllerV2 {
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");
}
}
3) MemberListControllerV2
package hello.servlet.web.frontcontroller.v2.controller;
import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v2.ControllerV2;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MemberSaveControllerV2 implements ControllerV2 {
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);
// Model에 데이터를 보관한다.
request.setAttribute("member", member);
// setAttribute 내에 Map 존재하는데 거기에 들어가게 됨.
return new MyView("/WEB-INF/views/save-result.jsp");
}
}
세 코드를 보면 이전과 달리 viewPath 변수, RequestDispatcher, forward 로직이 보이지 않는다.
이 코드들은 모두 MyView 객체로 이동하여 역할과 책임을 분리했기 때문이다.
package hello.servlet.web.frontcontroller;
import java.io.IOException;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyView {
private String viewPath;
public MyView(String viewPath) {
this.viewPath = viewPath;
}
// 뷰를 만드는 행위 -> 렌더링이라 한다. 일단 이 메서드만 집중하자
public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
requestDispatcher.forward(request, response); // 이제 MyView가 forward 처리 (JSP 처리)
}
// 이 밑에 내용은 추후에 설명한다. (개선된 부분인데 점진적 개선과 설명을 위해 여기서는 설명을 생략한다)
public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
modelToRequestAttribute(model, request);
RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
requestDispatcher.forward(request, response);
}
private static void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
model.forEach((key, value) -> request.setAttribute(key, value));
}
}
MyView 객체의 생성은 viewPath를 인자로 받는 생성자에 의해 일어난다.
정리하면 다음과 같은 과정을 통해 컨트롤러가 처리된다.
-> FrontControllerServlet에 의해 특정 컨트롤러가 호출된다.
controllerV2.process(request, response)를 통해 viewPath가 반환된다.
반환된 view의 render 메서드를 이용해 request와 response를 전달하면 MyView 객체의 render 메서드 내에
RequestDispatcher에 의해 getRequestDispatcher의 인자로 전달된 viewPath에 소속된 jsp 파일로 forward 된다.
이 때 forward에도 request, response 가 전달된다.
'스프링 공부 (인프런 김영한 선생님) > 스프링 MVC 1편' 카테고리의 다른 글
[스프링 웹 MVC 1편] 14. MVC 프레임워크 제작해보기 - v4 (0) | 2023.05.23 |
---|---|
[스프링 웹 MVC 1편] 13. MVC 프레임워크 제작해보기 - v3 (0) | 2023.05.22 |
[스프링 웹 MVC 1편] 11. MVC 프레임워크 제작해보기 - v1 (0) | 2023.05.22 |
[스프링 웹 MVC 1편] 10. MVC 패턴 - 한계 (0) | 2023.05.22 |
[스프링 웹 MVC 1편] 9. MVC 패턴 - 개요 (0) | 2023.05.22 |