Spring Framework: MVC Project의 기본 구조와 원리

IToriginal·2023년 6월 13일
0

남정네들: Web Study

목록 보기
7/7
post-thumbnail
post-custom-banner
앞서 MVC 패턴에 대해서 알아보고, 스프링의 핵심요소인 IoC/DI, AOP, PSA에 대해 개념을 알아보았다. 이번에는 스프링의 동작원리를 알아보려고 한다.

Spring Web Project의 디렉터리 구조

Sample Web Project인 helloworld 디렉토리 구조

애플리케이션 이해하기

Sample Project인 helloworld 프로젝트를 이루는 설정 파일과 소스 파일에 대한 설명을 하려고 한다. 애플리케이션에는 기본적으로 아래와 같은 설정파일이나 소스파일이 존재한다.

Controller Class

스프링 웹 MVC 애플리케이션의 요청 처리 로직은 컨트롤러 클래스에 들어 있다.

HelloWorldController.java

요청처리를 담당하는 스프링 웹 MVC 컨트롤러이다.

위의 예제코드에서 implements Controller 를 보면 HelloWorldController 클래스가 스프링 Controller 인터페이스를 구현하는 것을 볼 수 있다.

Controller 인터페이스에는 handle Request 메서드 정의가 들어 있다.

이 메서드 안에 요청 처리 로직을 구현해야한다. handleRequest 메서드는 ModelAndView 객체를 반환하며 객체에는 아래의 정보가 들어간다.

  • 사용자에게 보여줄 데이터(모델)
  • 모델을 보여주기 위해 사용한 JSP 페이지의 논리적 이름

보통 java.util.Map 타입 객체로 모델 데이터를 표현한다. java.util.Map 객체의 각 원소는 모델 속성을 표현한다. 사용자에게 보여줄 JSP 페이지인 뷰의 이름은 String으로 저장하게 된다.

HelloWorldController의 handleRequest 메서드가 helloworld(String 값) 뷰와 modelData(java.util.Map 타입 객체)가 들어 있는 ModelAndView 객체를 반환하는 모습을 보여준다.

modelData에는 'Hello World !!' 메시지가 값인 msg 모델 속성이 들어있다.

위의 코드는 helloworld.jsp 코드이다. 코드를 살펴보면 helloworld 뷰(jsp 페이지)가 'Hello World !!' 메시지를 사용자에게 보여주기 위해 msg 모델 속성을 사용하는 것을 볼 수 있다.

HelloWorldController의 handleRequest 메서드가 JSP 페이지를 랜더링(표시)하는 과정은 아래의 이미지와 같다.

스프링 웹 MVC 프레임워크는 들어오는 HTTP 요청을 가로채 HelloWorldController의 handleRequest 메서드를 호출한다. handleRequest 메서드는 모델데이터와 뷰 정보가 들어 있는 ModelAndView 객체를 handleRequest 메서드에서 받으면 helloworld.jsp 페이지로 HTTP 요청을 보내면서(dispatch) helloworld.jsp 페이지가 요청 속성으로 모델 속성을 사용할 수 있게 한다.

정리: 스프링 웹 MVC 프레임워크는 HelloWorldController의 handleRequest 메서드를 호출하고 메서드가 반환하는 ModelAndView 객체를 사용해 helloworld.jsp 페이지를 표시한다.

웹 애플리케이션 컨텍스트 XML 파일

myapp-config.xml

helloworld 샘플 프로젝트에 설정된 빈 정의를 보여주는 config 파일이다.

myapp-config.xml 파일에서 HelloWorldController와 별도로 스프링 SimpleUrlHandlerMapping과 InternalResourceViewResolver 빈을 설정하는 것을 볼 수 있다.

SimpleUrlHandlerMapping 빈은 들어오는 HTTP 요청을 처리할 책임이 있는 컨트롤러에 전달하고 URL 경로를 사용해 요청을 컨트롤러에 매핑한다.
여기서 url property는 URL과 컨트롤러 빈 사이의 매핑을 설정한다.
key 속성으로 설정한 URL 경로인 /sayhello를 HellooWorldController 빈으로 매핑하는 것이다.

InternalResourceViewResolver 빈은 ModelAndView에 들어 있는 뷰 이름으로 실제 뷰(JSP나 서블릿 등)의 위치를 찾는다.
실제 뷰 위치는 prefix 프로퍼티 값을 앞에 붙이고, suffix 프로퍼티 값을 뒤에 붙이는 방식으로 정해진다.

정리: SimpleUrlHandlerMapping은 호출한 컨트롤러를 찾고, InternalResourceViewResolver는 뷰 이름으로 실제 뷰를 찾는다.


Spring Web MVC는 SimpleUrlHandlerMapping과 InternalResourceViewResolver 빈을 자동으로 감지해서 요청을 처리하는 컨트롤러와 뷰를 찾는데 사용한다.

웹 애플리케이션 배포 디스크립터

web.xml

스프링 웹 MVC기반 애플리케이션에서는 요청을 DispatcherServlet(스프링 웹 MVC가 제공하는 서블릿)이 가로챈다.

DispatcherServlet은 요청을 적절한 컨트롤러에 전달하는 역할을 한다.

DispatcherServlet 설정


DispatcherServlet은 contextConfigLocation 서블릿 초기화 파라미터가 지정하는 웹 애플리케이션 컨텍스트 XML 파일과 관련이 있다. 코드를 보면 contextConfigLocation 초기화 파라미터는 myapp-config.xml 파일을 가르킨다.

DispatcherServlet은 웹 애플리케이션 컨텍스트 XML에 정의된 HandlerMapping과 ViewResolver 빈을 요청 처리에 사용한다.
DisplatcherServlet이 HandlerMapping 구현을 사용해서 요청에 맞는 적절한 컨트롤러를 찾고, ViewResolver 구현을 사용해 컨트롤러가 반환하는 뷰 이름을 가지고 실제 뷰를 찾는 것이다.

스프링 웹 MVC 요청을 처리하는 동안 벌어지는 활동 순서

  • 요청을 DispatcherServlet이 가로챈다.
  • DispatcherServlet은 HandlerMapping 빈을 사용해서 요청을 처리하기 적합한 컨트롤러를 찾는다. (예시로 사용된 샘플의 helloworld 프로젝트는 SimpleUrlHandlerMapping을 사용해서 찾는다.)
  • DispatcherServlet은 컨트롤러가 반환하는 뷰 이름을 ViewResolver 빈에게 전달해서 표시할 실제 뷰(JSP나 서블릿)를 찾는다.
  • DispatcherServlet은 실제 뷰에 요청을 전달하고 컨트롤러가 반환한 모델 데이터를 뷰에서 요청 속성으로 사용할 수 있게 한다.

스프링 웹 MVC에서 URL 경로를 연결하는 방법

프론트 컨트롤러

DispatcherServlet

앞에서 DispatcherServlet이 웹 애플리케이션 컨텍스트 XML 파일에 정의된 HandlerMapping과 ViewResolver 빈과 상호작용해서 요청을 처리하는 것을 알아보았다.

DispatcherServlet은 내부적으로 어떻게 작동할까?
초기화시 DispatcherServlet은 자신에게 대응하는 웹 애플리케이션 컨텍스트 XML 파일을 로드하고(별도 지정이 되지 않는 상황에서는 WEB-INF 디렉토리에 <name-of-DispatcherServlet>-servlet.xml 파일을 지정한다.) SpringWebApplicationContext 객체 인스턴스를 만들게 된다.

SpringWebApplicaiontContext

SpringWebApplicaiontContext는 ApplicationContext 인터페이스의 하위 인터페이스로, 웹 애플리케이션에 특화된 기능을 제공한다.

아래는 WebApplicationContext XML 파일에서 빈에 지정할 수 있게 추가된 스코프이다.

빈 스코프

request session application websocket
이들은 모두 스프링 컨테이너가 HTTP 요청/HTTP Session 생성/ServletContext 생성/WebSocket 생성에 새로운 빈 인스턴스를 생성하고 파괴될 때 스프링 컨테이너는 이 빈 인스턴스를 파괴한다.

아래의 그림은 DispatcherServlet에 해당하는 웹 애플리케이션 컨텍스트 XML 파일에 정의된 빈과 루트 웹 애플리케이션 컨텍스트 XML 파일에 정의된 빈 사이의 관계이다.

루트 WebApplicationContext에 정의된 빈을 DispatcherServlet에 해당하는 WebApplicationContext 인스턴스에서 상속한다.
이 그림에서 servlet1, servlet2, servlet3은 web.xml 파일에 설정된 DispatcherServlet 인스턴의 이름인데, DispatcherServlet 인스턴스를 초기활 때 servlet1-servlet.xml ... 2,3 파일에 대응하는 WebApplicationContext가 생성되고 DispatcherServlet 인스턴스와 연관지어진다.

음...

필자는 이번 스터디를 하면서 해당 분야를 이해하는데 어려웠다.
지금까지 알아본 내용은 Controller 인터페이스를 구현하면 컨트롤러를 만들수 있다! 인데, 이렇게 한 단계씩 보고나니 설정 부분들이 복잡하고 번거롭다고 느껴지게 되었다. 하지만, Controller 클래스를 개발할 때 이러한 방법을 사용하지 않고 필자는 @Controller를 사용해서 개발해왔다. @Controller와 @RequestMapping을 사용하게 되면 Controller를 더욱 쉽게 개발할 수 있다는 것을 느끼게 되었다.

🗣️ 정리


🤔 Spring Web MVC의 동작원리는?

우선, Spring Web MVC는 클라이언트의 요청을 받아들이고 처리하는 웹 애플리케이션의 프레임워크입니다.
Spring Web MVC는 클라이언트로부터 요청이 들어오면 DispatcherServlet이 해당 요청을 가로채고, 요청을 처리할 적절한 핸들러(컨트롤러)를 찾습니다. 이때 HandlerMapping을 사용하여 요청과 핸들러 간의 매핑을 수행합니다.
선택된 핸들러(컨트롤러)는 비즈니스 로직을 실행하고, 필요한 데이터를 모델에 담아서 반환합니다. 그리고 반환된 모델은 ViewResolver를 통해 적절한 View로 전달됩니다.
ViewResolver는 View를 찾아서 선택하고, 선택된 View는 모델 데이터를 기반으로 화면을 생성합니다. 최종적으로 생성된 응답은 클라이언트에게 전달됩니다.
Spring Web MVC는 이러한 과정을 기반으로 클라이언트의 요청을 처리하고, 비즈니스 로직과 뷰의 분리를 통해 유연하고 확장 가능한 웹 애플리케이션을 개발할 수 있도록 지원합니다.

🤔 그렇다면, Spring Web MVC의 DispatherServlet의 동작원리는?

Spring Web MVC에서 DispatherServlet은 클라이언트로부터 오는 모든 요청을 받아들이고, 요청을 처리할 적절한 Controller에 전달하는 역할을 하게 됩니다. DispatcharServlet은 웹 어플리케이션의 웹 계층에서 중심적인 역할을 수행하는데, 클라이언트로부터 들어오는 요청은 먼저 DispattcherServlet으로 전달됩니다.
DispatcherServlet은 요청을 처리하기 위해서 Handler Mapping에게 적절한 컨트롤러를 찾도록 요청을 위임하게되고, Handler Mapping은 URL과 Controller 사이의 매핑 정보를 가지고 있어서 요청된 URL에 맞는 Controller를 선택합니다.
선택된 Controller는 요청을 처리하게 되고, 결과를 DispatcherServlet에 반환하게 됩니다. DispatcherServlet은 Controller가 반환한 결과를 기반으로 적절한 View를 선택하고, 모델 데이터를 View에 전달합니다.
마지막으로 DispatcherServlet은 선택된 View에게 랜더링을 요청하고 최종적으로 클라이언트에게 응답을 전송하게 됩니다.
이러한 방법으로 클라이언트의 요청을 받아들여 처리할 컨트롤러를 찾고, View에게 렌더링을 요청하여 클라이언트에게 최종응답을 제공하는 역할을 합니다.

Reference

📚 배워서 바로 쓰는 스프링 프레임워크

profile
👾ISTP의 개발자 도전기🧐
post-custom-banner

0개의 댓글