본 포스트는 스프링 MVC 1편(김영한, 인프런) 강의를 통해 학습한 내용을 작성자 임의 대로 요약 및 정리한 것입니다.
1. Model 추가 - v3
1.1. 기능
- 서블릿 종속성 제거
컨트롤러 입장에서
HttpServletRequest
, HttpServletResponse
는 불필요!
요청 패러미터 정보는 자바 Map
으로 대신 넘기면
현 구조에선 컨트롤러가 서블릿 몰라도 동작 가능
request
객체를 Model로 사용 부분
==> 별도의 Model
객체 만들어서 반환하기
궁극적 목표
: 컨트롤러가 서블릿 기술 전혀 사용 않도록 변경하기!
- 뷰 이름 중복 제거
궁극적 목표
: 컨트롤러는 뷰의 논리 이름 반환하고,
실제 물리 위치 이름은 프론트 컨트롤러에서 처리하도록
단순화하기!!!
1.2. V3의 구조
- 클라이언트의 HTTP 요청
- FrontController –> 매핑 정보
: URL 매핑 정보에서 컨트롤러 조회 - FrontController –> Controller
: 컨트롤러 호출 - Controller –> FrontController
:ModelView
반환 - FrontController –> viewResolver
:MyView
반환 - FrontController –> MyView
:render(model)
호출 - MyView
: HTML 응답.
1.3. ModelView
지금까지는
서블릿에 종속적인 HttpServletRequest
사용했고,
Model
도 request.setAttribute()
통해
데이터 저장하고 뷰에 전달.
Model 직접만들고,
View 이름 전달하는 객체 만들기
/servlet/web/frontcontroller
에서
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ModelView {
private String viewName;
private Map<String, Object> model = new HashMap<>();
public ModelView(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return viewName;
}
public void setViewName(String viewName) {
this.viewName = viewName;
}
public Map<String, Object> getModel() {
return model;
}
public void setModel(Map<String, Object> model) {
this.model = model;
}
}
1.4. ControllerV3
/servlet/web/frontcontroller/v3
에서
1
2
3
4
public interface ControllerV3 {
ModelView process(Map<String, String> paramMap);
}
이 컨트롤러에선 서블릿 전혀 사용 안 함!
==> 그 대신,
HttpServletRequest
가 제공하는 패러미터는
프론트 컨트롤러가 paramMap
인자로 담아 호출함.
1.4.1 MemberFormControllerV3
- 회원 등록 폼
/servlet/web/frontcontroller/v3/controller
에서
1
2
3
4
5
6
7
8
public class MemberFormControllerV3
implements ControllerV3 {
@Override
public ModelView process
(Map<String, String> paramMap) {
return new ModelView("new-form");
}
}
ModelView
생성 시,
new-form
이라는 view의 논리적 이름을 지정함.
(실제 물리적 이름 ==> 프론트 컨트롤러에서 처리)
1.4.2. MemberSaveControllerV3
- 회원 저장
/servlet/web/frontcontroller/v3/controller
에서
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MemberSaveControllerV3
implements ControllerV3 {
private MemberRepository memberRepository
= MemberRepository.getInstance();
@Override
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;
}
}
1.4.3 MemberListControllerV3
- 회원 목록
/servlet/web/frontcontroller/v3/controller
에서
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MemberListControllerV3
implements ControllerV3 {
private MemberRepository memberRepository
= MemberRepository.getInstance();
@Override
public ModelView process
(Map<String, String> paramMap) {
List<Member> members
= memberRepository.findAll();
ModelView mv
= new ModelView("members");
mv.getModel()
.put("members", members);
return mv;
}
}
1.4.4 FrontControllerServletV3
- 프론트 컨트롤러
/servlet/web/frontcontroller/v3
에서
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@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 {
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);
Map<String, String> paramMap
= createParamMap(request);
ModelView mv
= controller.process(paramMap);
String viewName
= mv.getViewName();
MyView view
= viewResolver(viewName);
view.render(mv.getModel(),
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;
}
}
viewResolver()
: 컨트롤러가 반환한 논리 뷰 이름을
실제 물리 뷰 경로로 변경하고,
실제 물리 경로가 있는MyView
객체 반환함.- 논리 뷰 이름:
members
- 물리 뷰 이름:
/WEB-INF/views/members.jsp
- 논리 뷰 이름:
view.render(mv.getModel(), request, response)
- 뷰 객체 통하여 HTML 화면 렌더링.
- 뷰 객체의
render()
는
모델 정보도 함께 받음. - JSP는
request.getAttribute()
로
데이터를 조회함.
–> 모델의 데이터를 꺼내서
request.setAttribute()
에 담음. - JSP로 포워드 하여
JSP를 렌더링 함.
- 뷰 객체 통하여 HTML 화면 렌더링.
1.5. MyView
/servlet/web/frontcontroller
에서
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class MyView {
private String viewPath;
public MyView(String viewPath) {
this.viewPath = viewPath;
}
public void render(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher dispatcher
= request
.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
public void render(Map<String, Object> model,
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// model에 있는 데이터를 req. attribute로 바꿈
modelToRequestAttribute(model, request);
RequestDispatcher dispatcher
= request
.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
private static void modelToRequestAttribute
(Map<String, Object> model,
HttpServletRequest request) {
model.forEach((key, value)
-> request
.setAttribute(key, value));
}
}