Spring 동작 과정 MVC의 기본구조
Spring 동작 과정
톰캣 실행과정은 생락하였습니다.
다음은 웹 애플리케이션 구동 시 출력되는 로그입니다.
INFO: Initializing Spring root WebApplicationContext
INFO : org.springframework.web.context.ContextLoader - Root WebApplicationContext: initialization started
INFO : org.springframework.web.context.support.XmlWebApplicationContext - Refreshing Root WebApplicationContext: startup date [Fri Mar 03 01:00:57 KST 2023]; root of context hierarchy
INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/spring/root-context.xml]
INFO : org.springframework.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 1085 ms
3월 03, 2023 1:00:58 오전 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom
WARNING: [SHA1PRNG] 알고리즘을 사용하여, 세션 ID를 생성하기 위한 SecureRandom 객체를 생성하는데, [225] 밀리초가 소요됐습니다.
3월 03, 2023 1:00:58 오전 org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring FrameworkServlet 'appServlet'
INFO : org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'appServlet': initialization started
INFO : org.springframework.web.context.support.XmlWebApplicationContext - Refreshing WebApplicationContext for namespace 'appServlet-servlet': startup date [Fri Mar 03 01:00:58 KST 2023]; parent: Root WebApplicationContext
INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/spring/appServlet/servlet-context.xml]
INFO : org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
INFO : org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/],methods=[GET]}" onto public java.lang.String com.zerock.controller.HomeController.home(java.util.Locale,org.springframework.ui.Model)
INFO : org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter - Looking for @ControllerAdvice: WebApplicationContext for namespace 'appServlet-servlet': startup date [Fri Mar 03 01:00:58 KST 2023]; parent: Root WebApplicationContext
INFO : org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter - Looking for @ControllerAdvice: WebApplicationContext for namespace 'appServlet-servlet': startup date [Fri Mar 03 01:00:58 KST 2023]; parent: Root WebApplicationContext
INFO : org.springframework.web.servlet.handler.SimpleUrlHandlerMapping - Mapped URL path [/resources/**] onto handler 'org.springframework.web.servlet.resource.ResourceHttpRequestHandler#0'
INFO : org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'appServlet': initialization completed in 1160 ms
3월 03, 2023 1:00:59 오전 org.apache.coyote.AbstractProtocol start
INFO: 프로토콜 핸들러 ["http-nio-8080"]을(를) 시작합니다.
3월 03, 2023 1:00:59 오전 org.apache.catalina.startup.Catalina start
INFO: 서버가 [3781] 밀리초 내에 시작되었습니다.
INFO : com.zerock.controller.HomeController - Welcome home! The client locale is ko_KR.
다음은 로그의 내용을 정리한 내용입니다. 시작점은 Web.xml 입니다.
Web.xml (Tomcat 구동과 관련된 설정, 프로젝트 구동의 시작점)
1. Context Listener가 구동됨 (ContextLoaderListener, 웹 애플리케이션 구동 시 가장 먼저 로그를 출력함)
(context-param에 root-context.xml의 경로가 설정되어 있음)
2. root-context.xml이 처리되고 해당 xml내 Bean 설정들이 동작하게 됨 (스프링의 영역 (context) 안에 생성)
3. 해당 Bean 간의 의존성이 처리됨
4. 스프링 MVC에서 사용하는 DispatcherServlet이라는 서블릿과 관련된 설정이 동작 ( web.xml의 appServlet 내용 )
(스프링 MVC 구조에서 가장 핵심적인 역할을 하는 클래스, 웹 관련 처리의 준비작업을 진행하는데 사용되는 파일이 servlet-context.xml)
5. DispatcherServlet에서 XmlWebApplicationContext 를 이용해서 servlet-context.xml을 로딩하고 해석함
( 이 과정에서 등록된 객체(Bean)들은 기존에 만들어진 객체(Bean)들과 같이 연동하게 됨 (의존성 처리) )
스프링 MVC의 기본 사상
기존의 Servlet/JSP에서 사용하는 HttpServletRequest/HttpServletResponse 타입으로 객체를 이용해서 브라우저에서 전송한 정보를 처리하는데, 스프링 MVC의 경우 이 위에 하나의 계층을 더한 형태가 됩니다.
개발자의 코드 영역
ㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁ ( 개발자는 Servlet/JSP의 API에 신경 쓰지 않고 웹 애플리케이션을 제작)
------------------------------
Spring MVC (내부적으로 Servlet/JSP 처리)
------------------------------
Servlet/JSP
스프링 MVC를 이용하게 되면 개발자는 직접적으로 HttpServletRequest/HttpServletResponse 등과 같이 Servlet/JSP의 API를 사용할 필요성이 현저하게 줄어듭니다. 스프링이 중간에 연결 역할을 하기 때문에 이러한 코드를 작성하지 않고도 원하는 기능을 구현할 수 있게 됩니다.
모델 2와 스프링 MVC
스프링 MVC 역시 내부적으로는 Servlet API를 활용합니다.
스프링 MVC는 '모델 2'라는 방식으로 처리되는 구조입니다. 모델 2방식은 MVC 구조를 사용하여 '로직과 화면을 분리' 하는 스타일의 개발방식 입니다.
모델 2방식에서 사용자의 Request는 특별한 상황이 아닌 이상 먼저 Controller를 호출하게 됩니다.
이렇게 설계하는 가장 중요한 이유는 나중에 View를 교체하더라도 사용자가 호출하는 URL 자체에 변화가 없게 만들어주기 때문입니다.
컨트롤러(Controller)는 데이터를 처리하는 존재를 이용해서 데이터(Model)를 처리하고, Response할 때 필요한 데이터(Model)를 View 쪽으로 전달하게 됩니다. 서블릿의 경우 RequestDispatcher 등을 이용해서 직접 처리해 왔지만 스프링 MVC는 내부에서 이러한 처리를 하고, 개발자들은 스프링 MVC의 API를 이용해서 코드를 작성하게 됩니다.
다음은 Request에 대한 동작 과정입니다.
1. Request -> DispatcherServlet
( 사용자의 Request는 Front-Controller인 DispatcherServlet을 통해서 처리합니다. )
2. DispatcherServlet -> HandlerMapping
( HandlerMapping은 Request의 처리를 담당하는 컨트롤러를 찾기위해 존재합니다.
컨트롤러를 찾은 뒤 DispatcherServlet에게 반환합니다. )
3. DispatcherServlet -> HandlerAdapter
( HandlerAdapter는 컨트롤러를 동작시키는 역할을 합니다. HandlerMapping을 통해 찾은 컨트롤러를 동작시킵니다.)
4. HandlerAdapter -> Controller
( Controller는 개발자가 작성하는 클래스로 실제 Requset를 처리하는 로직을 작성하게 됩니다.
View에게 전달해야 하는 데이터는 주로 Model 이라는 객체에 담아서 전달합니다. Controller의 반환값에 대한 처리는 ViewResolver를 이용하게 됩니다.)
5. DispatcherServlet -> ViewResolver
(Controller가 반환한 결과를 어떤 View를 통해서 처리하는 것이 좋을지 해석하는 역할을 합니다.
가장 흔하게 사용하는 설정은 servlet-context.xml에 정의된 Inter-nalResourceViewResolver 입니다. )
6. DispatcherServlet -> View -> JSP 및 기타
(View 는 실제로 응답 보내야 하는 데이터를 Jsp 등을 이용해서 생성하는 역할을 하게 됩니다. )
7. DispatcherServlet에서 Response 전송
( 6번에서 만들어진 응답을 전송합니다.)
위 처럼 모든 Requset가 DispatcherServlet을 통하도록 설계되는 방식을 Front-Controller 패턴 이라고합니다.
해당 패턴을 이용하면 전체 흐름을 강제로 제한할 수 있습니다. HttpServlet을 상속해서 이용하는 구조보다(상속받은 클래스를 그대로 이용하거나 구조대로 HttpServlet을 상속받아서 개발할 수도 있기때문에) 모든 Requset의 처리가 정해진 방식대로만 동작하기 때문에 좀 더 엄격한 구조를 만들어 낼 수 있습니다.
Spring MVC의 Controller
스프링 MVC를 이용하는 경우 작성되는 Controller는 다음과 같은 특징이 있습니다.
- HttpServletRequest, HttpServletResponse를 거의 사용할 필요 없이 필요한 기능 구현
- 다양한 타입의 파라미터 처리, 다양한 타입의 리턴 타입 사용 가능
- GET 방식, POST 방식 등 전송 방식에 대한 처리를 어노테이션으로 처리 가능
- 상속/인터페이스 방식 대신에 어노테이션만으로도 필요한 설정 가능
Controller의 파라미터 수집
Controller를 작성할 때 가장 편리한 기능은 파마미터가 자동으로 수집되는 기능입니다. 이 기능을 이용하면 매번 request.getParameter()를 이용하는 불편함을 없앨 수 있습니다.
DTO를 만들고(lombok을 사용해 @Data 어노테이션 추가) Controller에서 해당 DTO를 파라미터로 받게 정의를 한 후 URL을 통해 호출하게 되면 해당 DTO에 setter가 동작하여 파라미터를 자동으로 수집합니다.
TIP.
DTO = getter, setter
VO = getter
파라미터의 수집과 변환
Controller가 파라미터를 수집하는 방식은 파라미터 타입에 따라 자동으로 변환하는 방식을 이용합니다.
예를 들어 DTO의 멤버변수가 int 타입을 경우 URL에 입력된 값을 자동으로 int형 타입으로 변환하여 저장합니다.
만일 기본 자료형이나 문자열 등을 이용한다면 파라미터의 타입만을 맞게 선언해주는 방식을 사용할 수 있습니다.
@GetMapping("/ex02")
public String ex02(@RequestParam("name") String name, @RequestParam("age") int age) {
log.info("name: " + name);
log.info("age: " + age);
return "ex02";
}
Model이라는 데이터 전달자
Controller의 메서드를 작성할 때는 특별하게 Model이라는 타입을 파라미터로 지정할 수 있습니다.
Model 객체는 JSP에 컨트롤러에서 생성된 데이터를 담아서 전달하는 역할을 하는 존재입니다.
이를 이용해서 View로 전달해야 하는 데이터를 담아서 보낼 수 있습니다.
메서드의 파라미터에 Model 타입이 지정된 경우에는 스프링은 특별하게 Model 타입의 객체를 만들어서 메서드에 주입하게 됩니다. 개발자의 입장에서는 필요한 데이터를 담아 주는 작업만으로 모든 작업이 완료됩니다.
public String home(Model model) {
model.addAttribute("serverTime", new java.util.Date());
return "home";
// Servlet 모델 2방식에서 아래의 코드와 유사함
/* request.setAttribute("serverTime", new java.util.Date());
RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/jsp/home.jsp");
dispatcher.forward(request, response); */
}
주로 Controller에 전달된 데이터를 이용해서 추가적인 데이터를 가져와야 하는 상황에 사용합니다.
- 리스트 페이지 번호를 파라미터로 전달받고, 실제 데이터를 View로 전달해야 하는 경우
- 파라미터들에 대한 처리 후 결과를 전달해야 하는 경우
[@ModelAttribute("value") 타입 변수명] 어노테이션
웹 페이지의 구조는 Request에 전달된 데이터를 가지고 필요하다면 추가적인 데이터를 생성해서 화면으로 전달하는 방식으로 동작합니다. Model의 경우는 파라미터로 전달된 데이터는 존재하지 않지만 화면에서 필요한 데이터를 전달하기 위해서 사용합니다.
예를들어 페이지의 번호는 파라미터로 전달되지만, 결과 데이터를 전달하려면 Model에 담아서 전달합니다.
스프링 MVC의 Controller는 기본적으로 Java Beans의 규칙에 맞는 객체는 다시 화면으로 객체를 전달합니다.
좁은 의미에서의 Java Beans의 규칙
- 생성자가 없거나 빈 생성자를 가져야함
- getter/setter를 가진 클래스의 객체들을 의미
따라서 기본 자료형의 경우는 파라미터로 선언하더라도 기본적으로 화면까지 전달되지는 않습니다.
@ModelAttribute 어노테이션을 사용해 강제로 전달받은 파라미터를 Model에 담아서 전달할 ㅅ ㅜ있습니다.
타입에 관계없이 무조건 Model에 담아서 전달합니다.
RedirectAttribute
Model 타입과 더불어서 스프링 MVC가 자동으로 전달해 주는 타입 중에는 RedirectAttribute 타입이 존재합니다.
RedirectAttributes는 조금 특별하게도 일회성으로 데이터를 전달하는 용도로 사용됩니다.
RedirectAttributes는 기존에 Servlet에서는 response.sendRedirect()를 사용할 때와 동일한 용도로 사용됩니다.
Servlet에서 redirect 방식
- response.sendRedirect("/home?name=aaa&age=10");
스프링 MVC를 이용하는 redirect 처리
rttr.addFlashAttribute("name", "AAA");
rttr.addFlashAttribute("age", 10);
return "redirect:/";
RedirectAttributes는 Model과 같이 파라미터로 선언해서 사용하고, addFlashAttribute(이름, 값) 메서드를 이용해서 화면에 한 번만 사용하고 다음에는 사용되지 않는 데이터를 전달하기 위해서 사용합니다.
Controller의 리턴 타입
- String : jsp를 이용하는 경우에는 jsp 파일의 경로와 파일이름을 나타내기 위해서 사용합니다.
- void : 호출하는 URL과 동일한 이름의 jsp를 의미합니다.
- VO, DTO 타입: 주로 JSON 타입의 데이터를 만들어서 반환하는 용도로 사용합니다.
- ResponseEntity 타입: response 할 때 Http 헤더 정보와 내용을 가공하는 용도로 사용합니다.
- Model, ModelAndView: Model로 데이터를 반환하거나 화면까지 같이 지정하는 경우에 사용합니다.
(최근에는 많이 사용하지 않습니다.)
- HttpHeaders: 응답에 내용 없이 Http 헤더 메시지만 전달하는 용도로 사용합니다.