SpringBoot 개념정리(13) - 3. 동작원리

Jang Seowoo·2022년 10월 5일
0
post-thumbnail

인프런 스프링부트 개념정리(이론)
이 글은 다음 강의의 이론 정리 글 입니다.


애플리케이션 컨텍스트란 무엇인가요?

DispatchServlet

DispatchServlet에 의해 생성되어지는 수 많은 객체들은 어디에서 관리될까?

외부에서 request가 오면 Spring은 전부 톰캣이 가져가게 된다. (앞에서 많이 반복했으니 설명은 패쓰-) 톰캣은 문지기인 web.xml이 있고, 이 web.xml에서 스프링 내부로 들어가기 위해서 두가지 일을 하는데 첫번째는 DispatchServlet(FrontController + RequestDispatcher)이 컴포넌트 스캔을 해서 주소를 분배한다. 근데 주소를 분배하려면 객체(Class)들이 메모리에 떠있어야 가능하다. 메모리에 안떠있으면 보낼 주소가 없다. 그래서 DispatchServlet은 주소를 분배하기 전에 src폴더 안에 있는 모든 수많은 자바파일을 메모리에 띄우기 위해 new() 해야된다. 이 new()를 내(개발자)가 하지 않고 Spring이 해주는 것을 IoC라고 불렀는데, 이 역할을 DispatchServlet이 해주고 있었던 것이다.

*참고: static폴더 안에 있는 자바파일은 src폴더 안에 있는 자바파일들과는 다르다. static폴더 안의 자바파일들은 main 메서드가 실행되기 전부터 메모리에 떠있다. 하지만 src폴더 안의 자바파일들은 어느 특정 순간에 메모리에 생성되고 특정 순간에 메모리에서 사라지는 객체들이다.

만약 내 프로젝트가 com.example.test라면 이 폴더 아래에 모든 파일이 스캔되어 필요한 것들이 메모리에 띄워진다.

그렇다면 뭐가 필요하고, 뭐가 안필요한지 어떻게 알까?
@Controller, @RestController, @Configuration, @Repository, @Service, @Component
이런 Spring이 정의해놓은 어노테이션들을 자바파일에서 찾는다. 이렇게 어노테이션이 붙어있는 자바파일 같은 경우 필요한 것에 해당된다. 필요한 경우에는 @Hello 처럼 필요한 어노테이션을 만들 수도 있다.

이렇게 필요한 객체들이 메모리에 띄워지면 주소 분배가 가능해진다.

100명의 요청이 있을 때, Servlet이 생성되고 이 Servlet에 20개의 스레드가 생성되었다고 생각해보자. (*참고: Servlet이 생성됐을 때 발생하는 일) 각각의 스레드는 독립적이기 때문에 서로 영향을 받지 않는다. 스레드1에 객체들이 만들어졌으면 새로운 요청이 오면 스레드2를 만들어 또 별개로 스레드2 안에 새로운 객체들이 메모리에 떠있을 것이다. 그렇다면 충돌이 나지 않을 채로 스레드를 사용할 수 있다.

💡 servlet-applicationContext에 등록된 객체들이 DispatchServlet에 의해 실행되어 스레드 속에서 메모리에 띄워진다. servlet-applicationContext는 ViewResolver, Interceptor, MultipartResolver 객체를 생성하고 웹과 관련된 어노테이션 @Controller, @RestController를 스캔 한다.

ContextLoaderListener

이렇게 컴포넌트 스캔을 하게 되면 스레드별로 객체를 따로 관리하게 되기 때문에 이 방법 대신 모든 스레드가 공통적으로 쓸 수 있는 것이 있다. 바로 Database Connection이다.

Database에 관련된 것 혹은 모든 스레드가 공통적으로 사용해도 되는 것들은 DispatchServlet에 진입하기 전에 미리 ContextLoaderListener를 통해서 띄운다. 이 ContextLoaderListener는 root_applicationContext라는 파일을 읽는데 이 파일은 공통적으로 사용해도 되는 객체들을 미리 메모리에 띄운다. 그 후에 DispatchServlet으로 진입해서 각 스레드에 객체들을 띄운다.

💡 root-applicationContext에 등록된 객체들이 ContextLoaderListener에 의해 실행되어 메모리에 띄워진다. root-applicationContext는 @Service, @Repository 등을 스캔하고 DB 관련 객체를 생성한다.

그러면 ContextLoaderListener를 실행해주는 녀석은 web.xml이기 때문에 root-applicationContext는 servlet-applicationContext보다 먼저 로드된다. 당연히 servlet-applicationContext에서는 root-applicationContext가 로드한 객체를 참조할 수 있지만 그 반대는 불가능하다. 생성 시점이 다르기 때문이다.

Bean Factory

필요한 객체를 Bean Factory에 등록할 수도 있다. 방법은 다음과 같다.

@Configuration
Class A {
	@Bean
    객체 메소드() {
    	return 객체();
    }
}

위와 같이 @Configuration이 붙은 클래스 A가 있으면 이 클래스 A는 컴포넌트 스캔을 할 때 메모리에 뜬다. 이 클래스 안쪽에서 return 하는 메소드 객체가 있다면 이런 객체는 @Bean을 사용해서 메모리에 띄울 수 있다.

Bean Factory에 등록하면 초기에 메모리에 로드되지 않고 필요할 때 getBean()이라는 메소드를 통하여 호출하여 메모리에 로드할 수 있다. 지금은 위와 같이 메소드 위에 @Bean을 써서 할 수도 있다. 이것 또한 IoC이다. 그리고 필요할 때 DI를 하여 사용할 수 있다. ApplicationContext와 다른 점은 Bean Factory에 로드되는 객체들은 미리 로드되지 않고 필요할 때 호출하여 로드하기 때문에 lazy-loading이 된다는 점이다.

profile
https://devseowoo.notion.site/Seowoo-Portfolio-b21365c3477345818913e8d8fe2e3b90

0개의 댓글

관련 채용 정보