코드 그라데이션
View 분리 - v2 본문
View 분리 - v2
모든 컨트롤러에서 뷰로 이동하는 부분에 중복이 있고, 깔끔하지 않다.
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
이 부분을 깔끔하게 분리하기 위해 별도로 뷰를 처리하는 객체를 만든다.
V2 구조
MyView
뷰 객체는 이후 다른 버전에서도 함께 사용하므로 패키지 위치를 frontcontroller 에 두었다.
public class MyView {
// 기존에 컨트롤러에서 했던 로직을 MyView 만들어서 분리해서 넣음.
private String viewPath; // => new MyView("/WEB-INF/views/new-form.jsp") 뷰 페이지의 경로를 저장
// 생성자
public MyView(String viewPath) {
this.viewPath = viewPath; // 생성자를 통해 뷰 페이지의 경로를 설정
}
/**
* 요청된 내용을 해당 뷰 페이지로 포워딩하여 렌더링합니다.
* @param request HTTP 요청 객체입니다.
* @param response HTTP 응답 객체입니다.
* @throws ServletException 서블릿 예외가 발생할 수 있습니다.
* @throws IOException 입출력 예외가 발생할 수 있습니다.
*/
public void render (HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response); // 요청을 지정한 뷰 페이지로 전달하여 렌더링
}
/**
* 전달된 모델 데이터를 요청 객체의 속성으로 설정하고,
* 해당 뷰 페이지로 포워딩하여 렌더링합니다.
* @param model 뷰에 전달할 데이터를 담고 있는 맵 객체입니다.
* @param request HTTP 요청 객체입니다.
* @param response HTTP 응답 객체입니다.
* @throws ServletException 서블릿 예외가 발생할 수 있습니다.
* @throws IOException 입출력 예외가 발생할 수 있습니다.
*/
public void render(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
modelToRequestAttribute(model, request); // 모델 데이터를 요청 속성으로 설정
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response); // 요청을 지정한 뷰 페이지로 전달하여 렌더링
}
/**
* 모델 데이터의 내용을 요청 객체의 속성으로 설정합니다.
* @param model 뷰에 전달할 데이터를 담고 있는 맵 객체입니다.
* @param request HTTP 요청 객체입니다.
*/
private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
// 모델 데이터의 각 항목을 요청 객체의 속성으로 설정
model.forEach((key, value) -> request.setAttribute(key, value));
}
}
- 이 클래스는 컨트롤러와 뷰 사이의 관심사 분리를 도와주며, 뷰 렌더링 관련 로직을 중앙에서 관리하고 재사용할 수 있도록 돕는 역할을 수행
ControllerV2
public interface ControllerV2 {
// 기존 ControllerV1과 같은데 반환만 MyView를 한다.
// 기존에는 void였고 컨트롤러가 알아서 다 forward 이동했는데
// 그냥 MyView를 만들어서 넘기면 되는 식으로 인터페이스를 설계
MyView process(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
}
- 웹 요청을 처리하고 뷰 객체(MyView)를 반환하는 역할을 수행
- process 메서드 : 웹 애플리케이션의 비즈니스 로직을 처리하고, 결과로 MyView 객체를 반환
- ControllerV2에서는 뷰 페이지의 경로 등을 직접 처리하는 것이 아니라, MyView 객체를 반환
- 이렇게 함으로써 컨트롤러는 뷰 렌더링과 관련된 로직을 다루지 않고, 단순히 비즈니스 로직 처리에 집중 가능
- MyView 객체를 반환함으로써, 뷰 렌더링과 관련된 처리를 MyView 클래스 내에서 처리할 수 있게 됨.
- 컨트롤러와 뷰 간의 역할 분담과 관심사 분리가 이루어지며, 유지보수성과 확장성이 향상됨.
MemberFormControllerV2 - 회원 등록 폼
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");
}
}
/*
// //변수 인라인화 시켜서 깔끔하게
// MyView myView = new MyView("/WEB-INF/views/new-form.jsp");
// return myView;
// => return new MyView("/WEB-INF/views/new-form.jsp");
*/
- 위의 코드에서는 MyView 객체를 생성하고, 해당 뷰 페이지의 경로를 생성자에 전달하여 초기화.
- 이렇게 생성된 MyView 객체를 반환하면, 이후에 뷰 렌더링이 필요할 때 해당 객체를 사용할 수 있게 된다.
- MyView 객체를 반환하므로써 컨트롤러의 역할은 비즈니스 로직 처리에만 집중하며, 실제 뷰 렌더링에 대한 로직은 MyView 클래스 내부에서 처리됨.
위의 코드는 "회원 가입 폼을 보여주는 페이지" 컨트롤러로, new-form.jsp라는 뷰 페이지로 포워딩. 이렇게 하면 해당 뷰 페이지가 사용자에게 보인다.
MemberSaveControllerV2 - 회원 저장
public class MemberSaveControllerV2 implements ControllerV2 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// HTTP 요청에서 사용자명과 나이를 파라미터로 받는다.
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
// 받은 정보로 Member 객체를 생성.
Member member = new Member(username, age);
// MemberRepository를 사용하여 회원 정보를 저장합니다.
memberRepository.save(member);
// 저장된 회원 정보를 request 객체의 속성으로 저장.
// 모델에 데이터 저장 => 이렇게 하면 뷰 페이지에서 해당 정보를 사용할 수 있음
request.setAttribute("member", member);
// 뷰 페이지 경로를 설정하고 해당 경로로 포워딩할 MyView 객체를 반환.
/*
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request,response);
위의 세줄이 아래 한줄 코드
*/
return new MyView("/WEB-INF/views/save-result.jsp");
// => 이렇게 하면 사용자에게 회원 저장 결과를 보여주는 페이지로 이동.
}
}
MemberListControllerV2 - 회원 목록
public class MemberListControllerV2 implements ControllerV2 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public MyView process(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// MemberRepository를 통해 모든 회원 정보를 조회.
List<Member> members = memberRepository.findAll();
// 조회한 회원 정보를 request 객체의 속성으로 저장.
request.setAttribute("members", members);
// 뷰 페이지 경로를 설정하고 해당 경로로 포워딩할 MyView 객체를 반환.
return new MyView("/WEB-INF/views/members.jsp");
}
}
서블릿 만들기
FrontControllerServletV2
- 서블릿(Servlet)으로, 웹 애플리케이션의 프론트 컨트롤러(Front Controller) 역할을 하는 클래스.
- 이 클래스는 URL에 따라 적절한 컨트롤러를 호출하고, 컨트롤러의 결과를 뷰(View)로 렌더링하여 클라이언트에 응답을 제공함
@WebServlet(name = "frontControllerServletV2", urlPatterns = "/front-controller/v2/*")
public class FrontControllerServletV2 extends HttpServlet {
private Map<String, ControllerV2> controllerMap = new HashMap<>();
public FrontControllerServletV2() {
// 서블릿 초기화 시에 각 URL 패턴에 맞는 컨트롤러를 매핑.
// ex. url = /front-controller/v2/members/new-form 이면 * 에 걸린다
controllerMap.put("/front-controller/v2/members/new-form", new MemberFormControllerV2());
controllerMap.put("/front-controller/v2/members/save", new MemberSaveControllerV2());
controllerMap.put("/front-controller/v2/members", new MemberListControllerV2());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 클라이언트 요청의 URI를 가져온다.
String requestURI = request.getRequestURI();
// 요청 URI에 해당하는 컨트롤러를 매핑된 컨트롤러 맵에서 찾는다.
// controllerMap은 URI와 컨트롤러 클래스를 매핑하는 맵.
// 서블릿이 초기화될 때,
// 서블릿 컨텍스트에서 정의한 URL 패턴에 따라 각 컨트롤러 클래스를 매핑
ControllerV2 controller = controllerMap.get(requestURI);
if (controller == null) {
// 컨트롤러를 찾을 수 없을 경우 404 상태 코드를 반환하고 종료.
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 해당 컨트롤러를 실행하고 결과로 MyView 객체를 받음.
// 이제 반환을 하는 게 살짝 차이
// 생성한 결과 : new MyView("/WEB-INF/views/new-form.jsp")
MyView view = controller.process(request, response);
// MyView를 사용하여 뷰를 렌더링하고 클라이언트에 응답을 전송.
view.render(request, response);
}
}
- MyView 객체는 뷰 페이지의 경로와 모델 데이터를 관리
실행
등록: http://localhost:8080/front-controller/v2/members/new-form
목록: http://localhost:8080/front-controller/v2/members
결과 출력되는 것까지 확인 가능
728x90
'Spring > MVC 1' 카테고리의 다른 글
단순하고 실용적인 컨트롤러 - v4 (0) | 2023.09.26 |
---|---|
Model 추가 v3 (0) | 2023.09.26 |
프론트 컨트롤러 도입 - v1 (0) | 2023.09.24 |
프론트 컨트롤러 패턴 소개 (0) | 2023.09.24 |
MVC 패턴 - 한계 (0) | 2023.09.23 |
Comments