[스프링 MVC 1편] 4 - (3) 프론트 컨트롤러 - v3
* 스프링 입문 = window, 스프링 MVC 1편 = Mac 으로 진행합니다
* 진도 : 섹션4 - (4)
* : 자바 클래스명, : 코드, : 단축키
1. 프론트 컨트롤러_구현 3단계
- 서블릿 종속제거
- HttpServletRequest
- HttpServletResponse
< 코드 예시 >
public MyView process(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
- 뷰 이름 중복제거
- 뷰 이름의 중복(/WEB-INF/views/)을 제거하고 논리 이름만 반환
- 뷰 이름의 중복(/WEB-INF/views/)을 제거하고 논리 이름만 반환
< 코드 예시 >
return new MyView("/WEB-INF/views/new-form.jsp");
- ModelView
- 서블릿의 종속성을 제거하기 위해 Model을 직접 만들고, 추가로 View 이름까지 전달하는 객체를 만들자
(컨트롤러에서 HttpServletRequest 사용 X → 직접 request.setAttribute() 호출 X → Model 별도로 필요)
- 서블릿의 종속성을 제거하기 위해 Model을 직접 만들고, 추가로 View 이름까지 전달하는 객체를 만들자
1) ModelView 클래스 만들자!
[ src - java - hello.servlet - web - frontcontroller - ModelView ]
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;
}
}
- 뷰 클래스는 제일 먼저 논리적 이름 가져가기 private String viewName
- 맵 생성 private Map<String, Object> model ~
(나머지는 Map 다 String 이다) - 생성자 만들기 1
- command + n - constructor 클릭 - viewName.String Ok 클릭
- command + n - constructor 클릭 - viewName.String Ok 클릭
- 생성자 만들기 2
- command + n - Getter and Setter 클릭 - 모두 선택한 다음 Ok 클릭
- 렌더링 public void render ~
- request.getRequestDispatcher (option + command + v)
- dispatcher.forward() : 다른 서블릿이나 JSP로 이동할 수 있는 기능(서버 내부에서 호출)
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);
}
}
2) 인터페이스를 만들자!
[ src - java - hello.servlet - web - frontcontroller .v3 - ControllerV3 ]
public interface ControllerV3 {
ModelView process(Map<String, String> paramMap);
- ModelView를 반환하는 인터페이스
- 서블릿과 앞부분 이름 차이
protected void service → MyView process
- 서블릿과 앞부분 이름 차이
- HttpServletRequest, HttpServletResponse 를 제거하고 Map<> paramMap으로 변경
- 프론트 컨트롤러는 이 인터페이스를 호출해서 로직의 일관성을 가져갈 수 있다
- 인터페이스는 어딘가에 상속 혹은 종속 되어있지 않아서 extend나 implements 사용 X
< 기존의 ControllerV2 인터페이스에서 삭제한 코드 >
- HttpServletRequest, HttpServletResponse
public interface ControllerV2 {
MyView process(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException;
}
3) 회원 등록 컨트롤러
[ src - java - hello .servlet - web - frontcontroller .v3 - controller - MemberFormControllerV3 ]
public class MemberFormControllerV3 implements ControllerV3 {
@Override
public ModelView process(Map<String, String> paramMap) {
return new ModelView("new-form");
}
}
- 상속 implements ControllerV3 ~ @Override (option + enter)
- 이제는 HttpServlet Re~ 코드가 아닌 맵이 자동으로 생성이 된다
- 이제는 HttpServlet Re~ 코드가 아닌 맵이 자동으로 생성이 된다
- 뷰 영역 return new ModelView("// JSP 경로 //") 에서 경로가 물리적 주소가 아닌 논리적 이름만 입력
- 논리적 이름 = "new-form"
- 논리적 이름 = "new-form"
< 기존의 MemberSaveControllerV2 클래스에서 삭제한 코드 >
- HttpServletRequest, HttpServletResponse
- WEB-INF/views/new-form.jsp (물리적 주소)
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");
}
}
4) 회원 저장 컨트롤러
[ src - java - hello .servlet - web - frontcontroller .v3 - controller - MemberSaveControllerV3 ]
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;
}
}
- 상속 implements ControllerV3 ~ @Override (option + enter)
- 이제는 HttpServlet Re~ 코드가 아닌 맵이 자동으로 생성이 된다
- 이제는 HttpServlet Re~ 코드가 아닌 맵이 자동으로 생성이 된다
- 리포지토리 가져오기 private MemberRepository.getInstance (서블릿 방식 때랑 같음)
- form data 값 읽어오기 request.get 이 아닌 paramMap.get으로 변경 (command + option + v)
- Member 객체 생성 및 저장 (서블릿 방식 때랑 같음)
- 뷰 영역 객체 생성 ~ new ModelView("// 논리적 이름 //")
- 뷰 객체에 member 담기 ~mv.getModel().put("member")
- 출력 return mv;
< 기존의 MemberSaveControllerV2 클래스에서 삭제한 코드 >
- HttpServletRequest, HttpServletResponse
- form data 값 읽어오기request.getParameter()
- 모델에 데이터 담기request.setAttribute()
- 뷰 영역 return new MyView("// JSP 경로 //")
public class MemberSaveControllerV2 implements ControllerV2 {
private 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);
request.setAttribute("member", member);
return new MyView("/WEB-INF/views/save-result.jsp");
}
}
5) 회원 목록 컨트롤러
[ src - java - hello .servlet - web - frontcontroller .v3 - controller - MemberListControllerV3 ]
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;
}
}
- 상속 implements ControllerV3 ~ @Override (option + enter)
- 이제는 HttpServlet Re~ 코드가 아닌 맵이 자동으로 생성이 된다
- 이제는 HttpServlet Re~ 코드가 아닌 맵이 자동으로 생성이 된다
- 리포지토리 가져오기private MemberRepository.getInstance(서블릿 방식 때랑 같음)
- 데이터 조회 memberRepository.findAll() (option + command + v) (서블릿 방식 때랑 같음)
- 뷰 영역 객체 생성 ~ new ModelView("// 논리적 이름 //")
- 뷰 객체에 member 담기 ~mv.getModel().put("member")
- 출력 return mv;
< 기존의 MemberSaveControllerV2 클래스에서 삭제한 코드 >
- HttpServletRequest, HttpServletResponse
- 데이터 조회 memberRepository.findAll() (option + command + v)
- 모델에 데이터 담기request.setAttribute()
- 뷰 영역 return new MyView("// JSP 경로 //")
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");
}
}
5) 프론트 컨트롤러 만들기!!
[ src - java - hello.servlet - web - frontcontroller. v3 - FrontControllerServletV3 ]
@WebServlet(name = "frontControllerServiceV3", urlPatterns = "/front-controller/v3/*")
public class FrontControllerServiceV3 extends HttpServlet {
private Map<String, ControllerV3> controllerMap = new HashMap<>();
public FrontControllerServiceV3() {
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;
}
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;
}
}
- 프론트 컨트롤러는 꼭 서블릿으로 작성한다!
1. 상속 extends HttpServlet
2. 서블릿 생성@WebServlet
- /* 표시는 해당 위치에 어떤 url 이 들어와도 서블릿 무조건 호출 가능하다는 의미
3. 서비스 생성 protected service
4. 맵 생성 private Map
- key = url, value = ControllerV2
- 매핑 정보 담을 공간 = controllerMap
5. 매핑 정보 public FrontControllerServiceV3()
- command + n 하고 constructor 클릭 → Select None 클릭
- 매핑정보를 담기!
- key 값이 요청되면 value를 실행하게끔 설정
6. URI 주소 가져오기 request.getRequestURI (option + command + v)
7. 맵에서 URI 찾아 컨트롤러 반환 controllerMap.get(requestURI)(option + command + v)
- 예외처리는 404 출력 SC_NOT_FOUND
8. 맵 생성2 Map<String, String> paraMap
9. 파라미터네임 꺼내기 request.getParameterName
- request.getParameter() : ("파라미터 이름")을 작성하면 값을 꺼낼 수 있다
- asIterator() : iterator 인터페이스형으로 바꿔서 반환
- Iterator.forEachRemaining() : Iterator의 forEachRemaining() 메서드를 사용하여 키-값 쌍을 콘솔에 출력
- controller.process() 에서 request, response 가 아닌 paramMap 을 넘겨주기 위해 사용
- param -> paramName.put(paramName, request.getParameter(paramName))
- key = paraName, value = request.getParameter(paramName)
★★ 너무 길어서 따로 메서드로 뽑는게 좋다!
마지막에 option + command + m하고 createParamMap 입력하기 ★★
10. 파라미터 받기 controller.process(paramMap) (option + command + v)
11. 뷰 설정
- viewName 객체 설정
- new MyView("/WEB-INF/views/" + viewName + ".jsp") (option + command + v) 하고
★★ option + command + m 해서 viewResolver(viewName) 입력하기 ★★-
컨트롤러가 반환한 논리 뷰 이름을 실제 물리 뷰 경로로 변경, 실제 물리 경로가 있는 MyView 객체를 반환
논리 뷰 이름: members
물리 뷰 경로: /WEB-INF/views/members.jsp
-
- 렌더 호출 view.render()에 mv.getModel() 넣고 (option + enter) 후 create~ 클릭
- mv.getModel() 에 커서두고 (option + enter)하면 MyView로 이동해서 아래코드로 작성
model.forEach((key, value) -> request.setAttribute(key, value));
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
하고 (option + command + m) 해서 modelToRequestAttribute 입력
<실행과정>
- URL =http://localhost:8080/front-controller/v3/*실행
* 자리에 아무거나 넣어도 하위 메서드는 모두 서블릿 호출 가능 - request.getRequestURI로 경로를 가져온다
- createParamMap 을 통해 파라미터를 모두 뽑고 paramMap으로 반환
- process.paramMap에서 논리 주소 찾기
- 논리 주소를 갖고 된viewResolver 호출
- MyView 반환 & 렌더 호출
- 렌더 호출하면 모델 정보(Attribute) 넘기기
[출처] 김영한 강사님 인프런 스프링 mvc1
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard
스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의
웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 원
www.inflearn.com