코드 그라데이션
Model 추가 v3 본문
Model 추가
서블릿 종속성 제거
뷰 이름 중복 제거
V3 구조
ModelView
- sevlet의 종속성을 제거 -> request가 쓸모가 없으니까.
@Getter
@Setter
public class ModelView {
private String viewName; //뷰의 논리적 이름
private Map<String, Object> model = new HashMap<>(); // 모델 직접 생성
//이렇게 모델에다가 내가 원하는 데이터를 넣어두면 나중에 꺼내서 jsp에서 쓸 수 있도록 후처리 해줄 예정
public ModelView(String viewName) {
this.viewName = viewName;
}
}
ControllerV3 - 인터페이스
public interface ControllerV3 {
ModelView process(Map<String, String> paramMap);
// 얘는 서블릿 기술들이 구현되어있지 않음(v2까지는 있었던 것에 비해)
// 프레임워크에 종속적이지, 서블릿에 종속적이지 않다.
}
MemberFormControllerV3 - 회원 등록 폼
public class MemberFormControllerV3 implements ControllerV3 {
@Override
public ModelView process(Map<String, String> paramMap) {
// 새로운 ModelView 객체를 생성하고 "new-form"이라는 논리적인 뷰 이름을 설정
// ModelView 객체는 뷰의 경로 및 모델 데이터를 포함하는 객체
return new ModelView("new-form"); // 전체 경로를 넣지 않고, 논리이름만 넣는다!
}
}
이 코드의 주요 목적은 "new-form"이라는 논리적인 뷰 이름을 가진 ModelView 객체를 생성하여 컨트롤러의 동작을 나타내는 것.
뷰의 실제 경로는 다른 구성 요소에서 설정하거나 결정될 수 있다.
MemberSaveControllerV3 - 회원 저장 클래스
public class MemberSaveControllerV3 implements ControllerV3 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public ModelView process(Map<String, String> paramMap) {
/*
HttpServletRequest에서 getAttribute 해서 막 이렇게 복잡하게 꺼내는 게 아니라
그 작업은 다 프론트 컨트롤러에서 처리하고
Map에다가 요청 파라미터 정보들을 다 담아서 넘겨줄 것이다.
그럼 단순히 여기서는 꺼내서 쓰기만 하면 된다.
*/
// 파라미터에서 "username"과 "age" 값을 추출
String username = paramMap.get("username");
int age = Integer.parseInt(paramMap.get("age")); // 문자로 오므로 숫자로 바꿔줘야 한다.
// 회원 객체를 생성하고 회원 저장소에 저장
Member member = new Member(username, age);
memberRepository.save(member);
// 결과를 나타내는 ModelView 객체 생성
ModelView mv = new ModelView("save-result"); // 모델 뷰 생성
mv.getModel().put("member", member); // 멤버 집어넣고
return mv; //리턴
}
}
MemberListControllerV3 - 회원 목록
public class MemberListControllerV3 implements ControllerV3 {
private final MemberRepository memberRepository = MemberRepository.getInstance();
// ControllerV3 인터페이스의 메서드를 구현
@Override
public ModelView process(Map<String, String> paramMap) {
// 회원 저장소에서 모든 회원을 조회하여 리스트로 얻어옴
List<Member> members = memberRepository.findAll();
// 결과를 나타내는 ModelView 객체 생성
ModelView mv = new ModelView("members"); // 모델 뷰 생성
mv.getModel().put("members", members); // 모델에 회원 목록을 추가
return mv; // ModelView 객체 반환
}
}
FrontControllerServletV3
@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")
public class FrontControllerServletV3 extends HttpServlet {
private Map<String, ControllerV3> controllerMap = new HashMap<>();
public FrontControllerServletV3() {
// 서블릿 초기화 시, 요청 경로에 따른 컨트롤러를 매핑하는 초기화 작업 수행
controllerMap.put("/front-controller/v3/members/new-form", new MemberFormControllerV3());
controllerMap.put("/front-controller/v3/members/save", new MemberSaveControllerV3());
controllerMap.put("/front-controller/v3/members", new MemberListControllerV3());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 요청 URI를 기반으로 해당 컨트롤러를 가져옴
String requestURI = request.getRequestURI();
ControllerV3 controller = controllerMap.get(requestURI);
if (controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// MyView view = controller.process(request, response);
// view.render(request, response); 이거 아님.
// HTTP 요청에서 파라미터를 추출하여 paramMap에 저장
Map<String, String> paramMap = createParamMap(request); // 디테일하니까 메서드로 분리
ModelView mv = controller.process(paramMap); // 여기서 받기
// 컨트롤러를 실행하고, 실행 결과로 ModelView 객체를 얻음
String viewName = mv.getViewName(); // 이 부분에서는 논리이름만 얻음
// 논리 뷰 이름을 실제 뷰 경로로 변환 (뷰 리졸버 역할)
MyView view = viewResolver(viewName); // 뷰 리졸버가 객체까지 만들어주는 것. 설계상 메서드 분리
// 위 코드가 바로 이 것. new MyView("/WEB-INF/views/" + viewName + ".jsp")
// 모델을 render에 같이 넘겨줘야 함.
// Model 데이터를 함께 렌더링에 전달하여 화면에 표시
view.render(mv.getModel(), request, response); // MyView에 아랫쪽에 있는 render(~);
}
private MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
// HTTP 요청에서 파라미터를 추출하여 paramMap에 저장하는 메서드
private Map<String, String> createParamMap(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>(); // Map 만들기
request.getParameterNames() // 모든 파라미터 다 가져오기
.asIterator() // 돌면서 Map에다가 값을 다 넣어주기
.forEachRemaining(paramName -> paramMap.put(paramName, request.getParameter(paramName)));
return paramMap;
}
}
MyView 설명 추가 및 수정
public class MyView {
private String viewPath;
public MyView(String viewPath) { // 생성자: 뷰 경로를 인스턴스 변수에 저장
this.viewPath = viewPath;
}
// HttpServletRequest와 HttpServletResponse를 받아서 JSP로 포워드
public void render(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
/*
위의 render 메서드와 유사하게 HttpServletRequest와 HttpServletResponse 객체를 받지만,
더 많은 파라미터로 Map<String, Object> model도 받습니다.
이 메서드는 modelToRequestAttribute 메서드를 호출하여
모델에 있는 데이터를 HttpServletRequest 객체의 attribute로 설정한 다음, JSP 파일로 포워드합니다.
이렇게 하면 JSP 파일에서 모델의 데이터를 사용할 수 있게 됩니다.
*/
// 모델 데이터와 HttpServletRequest, HttpServletResponse를 받아서 JSP로 포워드
public void render(Map<String, Object> model, HttpServletRequest request, ]
HttpServletResponse response) throws ServletException, IOException {
// 모델 데이터를 HttpServletRequest의 attribute로 설정
modelToRequestAttribute(model, request);
// 모델에 있는 데이터를 requestAttribute로 바꾼다.
// modelToRequestAttribute가 다 끝나고 여기 오면 jsp가 getAttribute해서 값을 쓰겠죠.
// 설정된 데이터와 함께 JSP로 포워드
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
// 모델 데이터를 HttpServletRequest의 attribute로 설정하는 메서드
private void modelToRequestAttribute(Map<String, Object> model,
HttpServletRequest request) {
// 모델의 각 항목을 반복하며 HttpServletRequest의 attribute로 설정
model.forEach((key, value) -> request.setAttribute(key, value));
// 랜더가 오면 다 돌면서 리퀘스트의 키, 밸류를 다 담아놓는다.(setAttribute)
// jsp는 여기서 값을 꺼내기 때문에 이 작업이 필요하다.
}
}
뷰 리졸버
실행
등록: http://localhost:8080/front-controller/v3/members/new-form
목록: http://localhost:8080/front-controller/v3/members
등록
2개 저장하고 조회해보면
728x90
'Spring > MVC 1' 카테고리의 다른 글
유연한 컨트롤러(1) - v5 (0) | 2023.09.27 |
---|---|
단순하고 실용적인 컨트롤러 - v4 (0) | 2023.09.26 |
View 분리 - v2 (0) | 2023.09.25 |
프론트 컨트롤러 도입 - v1 (0) | 2023.09.24 |
프론트 컨트롤러 패턴 소개 (0) | 2023.09.24 |
Comments