
Spring MVC - Dispatcher Servlet과 View Resolver
Spring MVC의 구조를 깊이 있게 알기위해서는 기존의 MVC패턴의 전체 구조를 알고 이해하는 것이 도움이 될 것이다. Spring MVC의 범위는 매우크고 기능 또한 매우 많기때문에 기본 베이스 없이 Spring MV
lee-dev-log.tistory.com
MVC패턴과 변천사 - 핸들러와 핸들러어댑터 패턴 적용
이전 포스팅에서는 Model개념을 적용하여 request.serAttribute로 수행하던 파라미터 값을 가져오는 것을 Model을 통해 해결함으로 Servlet에 종속성을 제거하였다. 또 뷰 리졸버를 사용해서 각 컨트롤러
lee-dev-log.tistory.com
이전 글들에서 사용했던 간단한 회원관리 웹 프로그램을 Spring MVC패턴을 적용해보며 Spring MVC를 사용하면 코드가 얼마나 간결해지는지 살펴보며 Spring MVC프레임워크에 대해서도 알아보도록하자
@Controller
먼저 new-form에 대한 컨트롤러이다.
@Controller
public class SpringMemberFormControllerV1 {
@RequestMapping("/springmvc/v1/members/new-form")
public ModelAndView process(){
return new ModelAndView("new-form");
}
}
위 코드는 URL이 입력되면 즉, "/springmvc/v1/members/new-form" 요청이 클라이언트로부터 들어오면 process()메서드를 실행하여 결론적으로는 ModelAndView를 생성하고 "new-form"이라는 View를 반환하게 된다.
이 때 new-form응답은 단순히 JSP HTML을 클라이언트에게 응답하는 로직을 수행하기 때문에 Model을 사용하여 데이터를 입력하는 로직은 수행하지 않는다.
@Controller
Controller어노테이션은 이름 그대로 컨트롤러의 역할을 수행하며 사용자의 요청을 받고 비즈니스 로직대로 처리하고 사용자에게 응답을 반환하는 역할을 하게 됩니다.
@Controller어노테이션이 붙게되면 Spring 컨테이너에 해당 클래스를 Spring Bean으로 등록하게 된다.
(->내부적으로 @Component가 선언되어있어 Component Scan대상에 포함된다.)@Controller내부에 @Component가 선언되어있는 모습
주요 역할
1. 요청 매핑처리: @RequestMapping어노테이션과 함께 사용해 특정 URL요청을 해당 메소드와 매핑하여 이를 통해 HTTP요청에 따라 처리할 메서드를 지정
2. 요청 데이터 처리: 클라이언트로부터 전달받은 데이터(HTML FORM, 쿼리 파라미터, JSON등)를 @RequestParam, @PathVariable, @RequestBody등의 어노테이션을 사용하여 추출해내고 메서드 파라미터를 통해 전달가능
3. 비즈니스 로직호출: @Controller메서드 내에서 서비스 계층의 메서드를 호출하여 비즈니스 로직을 수행
(레거시 프로젝트에서는 Controller와 Service의 분리가 없기도함)
4. 모델 데이터 설정: 비즈니스 로직의 실행 결과를 모델에 추가하여 뷰에 전달. 이 때 사용되는 타입이 ModelAndView타입을 사용하여 모델에 데이터를 넣어 View로 전달합니다.
5. 뷰 반환: 처리 결과를 표시할 View의 이름을 반환
반환된 이름은 ViewResolver를 통해 실제 물리주소 View와 매핑되고 클라이언트에게 HTML을 응답
Spring MVC적용 전 후 비교
Spring MVC를 적용하기 전 코드
FrontController Class
@WebServlet(name = "frontControllerServletV5", urlPatterns = "/front-controller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {
private final Map<String, Object> handlerMappingMap = new HashMap<>();
private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>();
public FrontControllerServletV5() {
initHandlerMappingMap();
initHandlerAdapters();
}
private void initHandlerMappingMap() {
handlerMappingMap.put("/front-controller/v5/v3/members/new-form", new MemberFormControllerV3());
handlerMappingMap.put("/front-controller/v5/v3/members/save", new MemberSaveControllerV3());
handlerMappingMap.put("/front-controller/v5/v3/members", new MemberListControllerV3());
//V4
handlerMappingMap.put("/front-controller/v5/v4/members/new-form", new MemberFormControllerV4());
handlerMappingMap.put("/front-controller/v5/v4/members/save", new MemberSaveControllerV4());
handlerMappingMap.put("/front-controller/v5/v4/members", new MemberListControllerV4());
}
private void initHandlerAdapters() {
handlerAdapters.add(new ControllerV3HandlerAdapter());
handlerAdapters.add(new ControllerV4HandlerAdapter());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object handler = getHandler(request);
if (handler == null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
MyHandlerAdapter adapter = getHandlerAdapter(handler);
ModelView modelView = adapter.handle(request, response, handler);
String viewName = modelView.getViewName();
MyView view = viewResolver(viewName);
view.render(modelView.getModel(), request, response);
}
private Object getHandler(HttpServletRequest request) {
String requestURI = request.getRequestURI();
return handlerMappingMap.get(requestURI);
}
private MyHandlerAdapter getHandlerAdapter(Object handler) {
for (MyHandlerAdapter adapter : handlerAdapters) {
if(adapter.supports(handler)){
return adapter;
}
}
throw new IllegalArgumentException("handler adapter를 찾을 수 없습니다. handler = " + handler);
}
private MyView viewResolver(String viewName) {
return new MyView("/WEB-INF/views/" + viewName + ".jsp");
}
}
ModelView Class
public class ModelView {
private String viewName; //JSP 주소
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;
}
}
MyView Class
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 {
modelToRequestAttribute(model, request);
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
model.forEach((key, value) -> request.setAttribute(key, value));
}
}
컨트롤러에 대한 코드는 생략하도록하겠습니다
Spring MVC프레임워크를 적용한 코드
위에 수많은 코드들을 SpringMVC 프레임워크를 사용하면 아래에 코드로 축약할 수 있다.
이는 SpringMVC프레임워크에 render, viewResolver, handler등의 기능들이 모두 프레임워크에 구현되어 있기때문에 우리는 이 프레임워크를 사용하기만 하면된다.
뿐만아니라 어노테이션기능을 제공하기때문에 RequestMapping등을 이용하여 유연하고 가독성있는 코드로 HTTP 요청을 처리할 수 있으며 지금은 배우지 않았지만 Spring MVC자체적으로 여러가지 기능들을 제공한다.
또한 Spring의 특징을 이용하여 객체지향에 극대화된 애플리케이션 설계를 가능하게한다.
@Controller
@RequestMapping("/springmvc/v2/members")
public class SpringMemberControllerV2 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/new-form")
public ModelAndView newForm(){
return new ModelAndView("new-form");
}
@RequestMapping("/save")
public ModelAndView save(HttpServletRequest request, HttpServletResponse response){
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
ModelAndView mv = new ModelAndView("save-result");
mv.addObject("member", member);
return mv;
}
@RequestMapping
public ModelAndView members() {
List<Member> members = memberRepository.findAll();
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", members);
return mv;
}
}
@Controller가 적용된 클래스는 기능별로 연관된 것 들끼리 메서드로 묶어서 정리할 수 있다.
위 정리한 내용들은 모두 인프런에서 김영한님의 강의를 듣고 학습한 내용을 스스로 정리한 것임을 밝힙니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1#
'Framework > Spring' 카테고리의 다른 글
Spring - 메시지 기능으로 HTML하드코딩 제거하기 (0) | 2024.04.23 |
---|---|
Spring MVC - @RequestMapping (0) | 2024.04.16 |
Spring MVC - Dispatcher Servlet과 View Resolver (0) | 2024.04.15 |
스프링 컨테이너와 싱글톤 (0) | 2024.03.23 |
객체지향 설계를 위한 5가지 원칙 (SOLID) (0) | 2024.03.20 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!