본문 바로가기
  • A space that records me :)
Framework & Library/Spring & String boot

[Spring] Spring boot 동작 원리

by yjkim_97 2024. 4. 26.

 


Spring boot로 개발을 한지 벌써 3년이 됐다.

하지만 Spring boot는 Request을 받았을 때 어떤 과정을 거쳐 Response하는지 기본적인 동작 원리를 모른다.

그래서 한번 간략히 알아보았다!


먼저 알아야하는 지식이 있다.

1.  사전 지식

더보기
  • web.xml
  • ContextLoaderLister
  • Servlet & Servlet Container
  • FrontController
  • RequestDispatcher
  • DispatcherServlet
  • ApplicationContext
  • MessageConverter
  • ViewResolver

web.xml

Spring framework 동작을 위한 모든 초기 설정들이 들어있다.  대표적인 설정은 아래와 같다.

  • ServletContext의 초기 파라미터
  • Session 유효시간 정보
  • MimeTypeMapping : Request, Response의 데이터 타입 매핑기능 (Body 데이터가 Header에 정의된 Type인지 확인)
  • Wellcom File List : Was 서버의 첫 화면 (ex. Tomcat wellcom page)
  • Error Handling : Request처리 도중 발생한 Error 처리
  • Listener/Filter 설정 : Listener와 Filter 생성 또는 동작을 위한 설정 (자동 생성되는 정보 + 개발자가 추가한 정보)
  • Security 설정

이 외에도 web.xml에서 담당하는 역할이 너무 많이 일부는 DispatcherServlet이 일부 분담하여 수행한다.

web.xml은 Application이 실행 될때 처음으로 읽는 파일이다.

 

ContextLoaderListener

Component Scan을 통해 DB, Service, Repository 등을 Bean으로 등록시키고 ApplicationContext의 root-appicationContext데 정의한다.

ContextLoaderListener가 등록하는 Bean은 모든 Request에서 공통으로 사용하는 것들을 등록시킨다.

(그외 Controller등 특정 Request에 종속된 Beandms DispatcherServlet이 등록시킨다.)

 

web.xml에서 DispatcherServlet이 실행되기전 ContextLoaderListener를 먼저 실행시킨다.

 

ServletContainer

자바 서블릿(Java Servlet)

Java를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말하며, 흔히 "서블릿"이라 불린다.

한마디로 Servlet은 Client 요청을 처리하고 결과를 응답해주는 Java web programing 기술이다. 

 

Servlet Container는 멀티쓰레드 환경을 지원하고 Servlet을 생성하고 Singletone pattern으로 관리하는 주체이며,

Spring framework에서는 DispatcherServlet이 Servlet Container이다.

 

FrontController

FrontController는 Servlet Request를 처리할 수 있는 Handler를 찾아 매핑해주는 패턴을 의미한다.

(여기서 Handler는 Spring에서 Controller를 의미한다.)

 

FrontController가 Handler를 찾아 매핑해준다는 것은 Servlet Request를 처리 할 수 있는 Controller를 호출한다는 건데, 이때 새로운 Request/Response 객체가 생성된다.

이렇게 되면 Client로 부터 들어온 Servlet Request/Response 객체가 사라지고 새로운 Request/Response 객체로 덮어 쓰여지는 것이기 때문에 기존 요청 정보를 잃어버리게 되어 Controller가 처리한 결과를 Client에게 응답할 수 없게 된다.

이 문제를 해결하기 위해 필요한 것이 RequestDispatcher이다.

 

RequestDispatcher

FrontController 패턴에서 Handler를 매핑할 때 새로운 Request/Response 대신 기존 Servlet Request/Response를 넘겨줄 수 있게 도와주는 기능이다.

 

Spring framework에는 FrontController와 RequestDispatcher 기능을 하나로 합친 DispatcherServlet이 존재한다.

(그래서 개발자는 FrontController와 RequestDispatcher에 대해서 전혀 신경쓰지 않아도 된다.)

 

DispatcherServlet

자 드디어 위에서 계속 등장한 DispatcherServlet이 나왔다.

Srping framework에서 DispatcherServlet은 요청를 받고 처리된 결과를 Client에게 응답하는

매우 중요한 객체라고 생각하면 될 것 같다.

 

Spring에서 Servlet 기능을 수행한다. 위에서 언급한 FrontController와 RequestDispatcher 기능을 제공한다.

 

DispatcherServlet이 Bean으로 등록되어 메모리에 올라갈 때 Component Scan을 통해 Controller 등을 Bean으로 등록시키고 Application Context의 servlet-applicationContext에 정의한다.

(Request별로 동작해야하는 Bean은 DispatcherServlet이, Request와 상관 없이 공통으로 동작하는 Bean은 ContextLoaderListener가 등록한다. ContextLoaderListner가 먼저 로딩되고 그 다음 DispatcherServlet이 로딩된다.)

 

DispatcherServlet는 web.xml의 설정값을 공유하고 web.xml의 역할 일부분을 담당한다.

 

(1) HandlerMapping 기능 수행

  • DispatcherServlet으로 요청(HttpServletRequest)이 들어오면 Handler Mapping을 통해 요청 URL에 매핑된 Controller를 찾는다.
  • HandlerMapping 기능을 수행하는 Interface는 여러 구현체가 있고 각 구현체 마다 매핑하는 방식이 조금 씩 다르다.
    1. DefaultAnnotationHandlerMapping : Annotation으로 매핑 (@RequestMapping 등과 같은 Annotation을 통해 자동 매핑)
    2. BeanNameUrlHandlerMapping : Bean 이름으로 매핑 (Bean 이름이 '/'로 시작하는 Controller를 찾아서 매핑)
    3. ControllerClassNameHandlerMapping : Class 이름으로 매핑 (URL과 일치하는 클래스 이름을 갖는 Bean을 찾아서 매핑)
    4. SimpleUrlHandlerMapping : URL 패턴으로 매핑

(2) HandlerAdapter 통해 Handler 호출

  • 매핑된 Controller를 실행 할 수 있는 HandlerAdapter를 찾는다.
  • 찾는 HandlerAdapter를 통해 Controller를 실행 한 후 처리 결과를 DispatcherServlet에게 반환한다.

 

ApplicationContext

이것도 위 내용에서 몇번 나왔다. 아마 두번 정도 일 거다. ApplicationContext은 Bean을 다루는 중요한 객체이다.

 

ApplicationContext은 Spring에서 관리(IoC)하는 Bean이 정의되어 있는 곳이며 아래 두가지로 나뉜다.

  1. root-applicationContext : ContextLoaderLister에 의해 등록된 Bean이 정의되어 있다. (Mapper, Service, Repository 등)
  2. servlet-applicationContext : DispatcherServlet에 의해 등록된 Bean이 정의되어 있다. (Controller 등)

이 곳에 정의된 Bean은 Spring Container에서 Singletone으로 관리되며 개발자는 저장된 위치, 호출하는 시점에 상관없이 동일한 객체를 가져다가 사용할 수 있다.

(이 것을 IoC와 DI라고 한다.)

 

ApplicationContext를 Spring Container 또는 IoC Container라고도 부른다.

 

부가 설명으로, Bean을 관리하는 Container의 최상위 interface는 BeanFactory이고

Baenfactory에서 Baen의 LifeCycle을 관리는 기능을 제공한다.

ApplicationContext는 BeanFactory를 상속받아 부가 기능을 확장하여 제공한다.

 

MessageConverter

Spring framework에는 default MessageConverter가 있다.

@RequestController 또는 @RequestBody를 선언한 경우 MessageConverter가 동작해 Object <-> Json으로 변환된다. 

 

ViewRresolver

@Controller일 때 사용된다. View 랜더링 역할을 담당하는 View 객체를 반환한다.

 


자! 내용이 길었다!!

그럼 이제 Java Application이 Run될때 그리고 Client로 부터 요청이 들어왔을 때 위 친구들이 어떤 역할을 수행하는지 알아보자~!

 

2. Application Run

  1. web.xml loading
  2. web.xml에 정의된 설정 정보를 모두 읽는다.
  3. ContextLoaderListener loading
  4. ContextLoadingListener에 의해 DB, Service, Repository 등 Request와 상관없이 공통으로 사용될 Bean이 등록된다.
  5. 등록된 Bean은 root-applicationContext에 정의된다.
  6. DispatcherServlet loading
  7. DispatcherServlet에 의해 Controller 등 Request에 해당하는 Bean이 등록된다. 
  8. 등록된 Bean은 servlet-applicationContext에 정의된다.

 

3. Request/Response 과정

  1. Client가 Application에게 요청
  2. 내장 Tomcat의 ServletContainer에서 ServletRequest/Response 객체를 생성한다.
  3. 내장 Tomcat은 Spring 컨테이너의 DispatcherServlet으로 ServletRequest/Response 객체를 전달한다.
  4. DispatcherServlet에서 HandlerMapping을 통해 요청을 처리할 수 있는 Controller를 찾는다.
  5. 찾은 Controller를 호출할 수 있는 HandlerAdapter를 찾는다.
  6. DispatcherServlet은 HandlerAdapter를 호출하고 HandlerAdapter는 Controller를 호출한다.
  7. 호출된 Controller가 실행되고 요청을 처리한 결과를 반환한다.
  8. HandlerAdapter는 Controller로 부터 반환된 결과를 ModelAndView로 가공해 DispatcherServlet으로 반환한다.
  9. DispatcherServlet에서 ModelAndView를 ViewResolver 또는 MessageConverter를 통해 Client에게 응답한다.
    • ViewResolver를 통해 View를 Client에게 응답 (@Controller 사용시)
    • MessageConverter를 통해 JSON을 Client에게 응답 (@RestController 또는 @RequestBody 사용시)

 


Reference

 

- 📖 스프링 부트 3_백엔드_개발자 되기

- https://velog.io/@zezeg2/Spring-Boot-동작원리

- https://innovation123.tistory.com/168

- https://djcho.github.io/springboot/spring-boot-chapter2-2/