Spring Framework 6.2 문서 읽고 이해하기 ... IOC Container (1)
현재 정산 백엔드 직군으로 일하고 있고 사용하는 기술은 Spring Boot와 Kotlin이다.
(사람마다 깊이에 대한 기준은 다르지만) Spring을 깊은 이해 없이 활용 가능한 정도로 공부한 후
사용하다가 문제 생기면 그때 필요한 부분만 알아가는 식이였다.
IOC Container라는 게 있어서 POJO + Meta정보를 이용해서 Bean을 생성하고 lifecycle도 관리해 주고,
Reflection을 이용해서 DI 해준다 정도... 아는 것도 모르는 것도 아닌 두리뭉실한 느낌
이를 반대로 말하면 문제없는 영역은 잘 모른다는 게 문제다.
Spring Boot가 알아서 해주는 게 많아 개발자는 비즈니스 로직에만 집중한다는 장점이 있지만
프로젝트가 커지다 보면 비즈니스 로직만 알아서는 부족함을 느낀다. 개발 속도가 느려진다.
지금 상황이 그렇다.
코드를 봐야겠다고 결심하게 된 결정적인 계기는
지금 프로젝트를 리팩토링 하려고 테스트 코드 작성하는데 감으로만 알고 있던 걸로는 진행이 안 됐다.
더 이상 구글 검색결과에 나온 방법들로는 해결되지 않았다.
내가 생각한 개발자의 가장 큰 장점은 공개되어 있는 코드들이 많아
시간과 노력을 투자한다면 내부 동작 원리를 알 수 있다는 점이다.
그래서 당장 업무에 도움 될지를 상상하며 내부 동작을 알아보려고 한다.
첫 시작은
Spring framework 6.2!!
나의 지적호기심 채우려고 너무 깊게 알아가는 모습을 항상 경계하자.
Core Technologies / The IoC Container
IOC는 Inversion of Control의 약자로 제어를 내부가 아닌 외부에서 한다는 개념이고
DI는 생성자, property, factory method, setter method 등을 이용하여 의존 관계에 있는 객체를 주입받는 걸 말한다.
IoC 컨테이너는 두 개 패키지가 기본이고 `org.springframeowrk.beans`, `org.springframeowrk.context`
beans 패키지는 BeanFactory interface를, context 패키지는 ApplicationContext interface를 root로 보면 된다.
BeanFactory는 모든 유형의 객체를 관리할 수 있는 메커니즘을 제공하고,
ApplicationContext는 BeanFactory의 하위 interface로 아래와 같은 추가 기능을 제공한다.
- Spring AOP 기능과 쉬운 연동
- i18n 관리
- 이벤트 메시지 관리
- 웹에서 사용하기 위한 WebApplicationContext 같은 계층별 Context
BeanFactory는 프레임워크 설정과 기본 기능을 제공하고, ApplicationContext는 추가로 엔터프라이즈 기능을 제공한다.
이후 Spring IoC Container 설명 ApplicationContext으로 진행한다.
※ BeanFactory에 대한 자세한 내용은 The BeanFactory API 읽을 때 알아보겠습니다.
Container Overview
Spring IoC Container = ApplicationContext interface 느낌.
bean의 인스턴스화, 설정, 조합등을 책임지는데 메타정보를 읽어 처리한다.
다양한 ApplicationContext interface 구현체가 있는데 그중에
AnnotationConfigApplicationContext, ClassPathXmlApplicationContext 가 단독으로 생성 가능하다.
비즈니스 로직이 있는 객체(POJO)와 메타정보를 가져와서 ApplicationContext에서 즉시 사용가능한 Bean 만들어준다.
Configuration Metadata
메타정보는 다양한 방식으로 정의할 수 있는데 대부분의 개발자들은 주로 Java-based configuration 방식을 사용한다.
- Annotation-based configuration: 애플리케이션 컴포넌트 클래스에 annotation을 사용해서 bean을 정의하는 방식
- Java-based configuration: 애플리케이션과 별개의 클래스에 bean을 정의하는 방식
XML 방식도 있는데 XML에 <beans/>를 적고 하위에 <bean/>을 적어서 bean을 정의한다.
※ <import /> element를 이용해서 다른 xml파일에 정의한 bean 내용을 가져올 수도 있다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- services -->
<bean id="petStore" class="com.tistory.dol9.context.support.PetStore"/>
</beans>
ClassPathXmlApplicationContext 생성자 argument로 Java classpath 기준 xml을 넘겨주면 IoC container가 생성된다.
XML 경로는 상대경로 대신 절대경로 사용을 권장한다.
추가로 GenericApplicationContext와 reader delegate를 이용해서 container를 생성할 수도 있다.
// create and configure beans using ClassPathXmlApplicationContext
val context: ApplicationContext = ClassPathXmlApplicationContext(SINGLE_CONTEXT)
val service: PetStore = context.getBean("petStore", PetStore::class.java)
// create and configure beans using GenericApplicationContext
val context = GenericApplicationContext()
val beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(PetStore::class.java).beanDefinition
context.registerBeanDefinition("petStore", beanDefinition)
context.refresh()
val service: PetStore = context.getBean("petStore", PetStore::class.java)
val userList = service.userName
// PetStore
class PetStore {
val userName: List<String>
get() = mutableListOf("dol9", "dol10")
}
위에 작성한 XML과 코드
https://github.com/kgcrom/learn-something/commit/e95a391445213127ff9eb6e7017b7c9a833d98bc
Spring Framework 6.2 git clone 한 뒤 코드를 봤지만 이해 안 되는 게 참 많았다.
문서 Resource까지 보면서 대강 분위기 익히고 코드를 봐야겠다.