0. 복습
- 이전 포스팅에서 Spring을 사용해야 하는 이유에 대해서 알아보았다.
- java만으로 객체지향 프로그래밍의 원칙(SOLID)을 온전히 구현할 수 없기에 AppConfig 설정 파일을 사용하여
구현체 간의 의존관계를 설정하고, 이 의존관계를 외부에서 주입(DI)하는 방식을 사용했다.
- 그로 인해 프로그램의 전체적인 흐름이 프로그램 내부에 의해 결정되는 것이 아닌, 외부의 설정 파일(AppConfig)에 의해
결정되는 IoC의 특징에 대해 알 수 있었다.
- 그렇다면 Spring에서 SOLID, DI, IoC, AppConfig 등의 개념을 어떻게 사용하는지 알아보자.
1. Spring Container
- AppConfig의 역할(구현체의 생성, 관리 그리고 의존관계 형성)을 하는 대상을 IoC 또는 DI 컨테이너라고 한다.
- Spring에서는 이와 같은 역할을 Spring Container가 수행한다.
- Spring Container는 Singleton Container라고 불리는데, 이번 포스팅에서는 이에 관해 간단히 소개할 예정이다.
- Spring Container를 생성하는 방법을 배우기 전에, 지금까지 Java의 프로젝트 설정 방식이 어떻게 바뀌었는지 복기해보자.
1. AppConfig 파일을 사용하기 전에 client가 직접 구현체의 생성과 의존관계를 명시했다. (OCP, DIP 위반)
2. AppConfig를 도입하여 구현체 생성과 의존관계 형성을 외부에서 수행하여 내부로 주입했다. (DI, IoC 발현, 구조적 한계에 봉착)
3. Spring은 1,2번 방식의 모든 문제점을 해결하고 장점을 흡수한 Spring Container를 사용하여 객체와 의존관계를 관리한다.
a) Spring Container 생성 방법
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(AppConfig.class);
- 위의 예시는 AppConfig 파일을 Spring Container로 등록하는 방법이다.
- Spring Contianer에게 특정 객체를 요청해야하는 부분에 명시하여 사용하면된다.
- ApplicationContext는 Spring Container의 진짜 이름이며, 이는 인터페이스다.
- AnnotationConfigApplicationContext는 ApplicationContext(= 인터페이스)의 구현체다.
- Spring은 3가지 Spring Container 구현 방법(= 구현체)을 제공한다.
→ Annotation (= AnnotationConfigApplicationContext)
→ XML ( = GenericXmlApplicationContext)
→ 사용자 설정
- 현대의 Spring은 annotation을 사용한 개발방식을 추구 및 권장한다. 그래서 XML방식은 이제 잘 사용하지 않는다.
b) AnnotationCofigApplicationContext
- 위에서 Spring Container를 AnnotationConfigApplicationContext라는 구현체를 사용하여 생성하였다.
- 이는 Annotation 개발 방식을 사용하여 Spring Bean을 관리하겠다는 의미다.
- Annotation을 사용한 개발방식은 이전 포스팅에서 언급한 AppConfig의 구조적 문제의 해결책을 제시한다.
- 이를 위해, @Configuration과 @Bean의 역할과 의미를 알아보자.
2. @Configuration, @Bean의 역할과 기능
a) @Configuration
@Configuration
public class AppConfig {
@Bean
public TestService testService() {
return new TestServiceImpl(testRepository());
}
@Bean
public TestRepository testRepository() {
return new TestMemoryRepository();
}
}
- AppConfig 파일에 @Configuration을 부착한다. 그리고 내부의 구현체 생성 메서드에 @Bean을 부착한다.
- @Configuration은 해당 파일이 설정 파일이라는 것을 Spring에게 알려주는 역할을 한다.
- Spring은 이를 인식하고, @Bean이 부착된 모든 메서드의 반환 값(= 객체)을 Spring Container에 등록한다.
b) @Bean
- @Bean은 구현체(= 객체)를 생성하고 반환하는 메서드에 부착하는 annotation이다.
- 반환된 객체는 Spring Container에 의해 관리된다.
- 이처럼 Spring Container에 의해 관리되는 객체를 Spring Bean이라 부른다.
- Spring Bean은 클래스당 1개의 객체만 등록되며, 사용된다.
- 즉, Singleton 방식으로 관리된다.
c) Spring Bean이 등록되는 방식
- Spring Container 내부에는 Spring Bean을 관리하는 Bean 저장소가 있다.
- Spring Bean은 key:value 형식으로 등록된다.
- key는 @Bean이 부착된 메소드의 이름 또는 @Bean(name = "이름")으로 등록된다.
- value는 각 메소드에서 생성된 객체(= 구현체)가 저장되어있는 메모리 주소값이 등록된다.
- 각 Bean의 이름(= key)은 중복이 없어야 한다.
c) Singleton Container
- 위에서 Spring Container의 다른 이름에는 Singleton Conatiner가 있다고 언급했다.
- Singleton Continer라고 부르는 이유는 Spring Bean이 Singleton 방식으로 관리되기 때문이다.
- Singleton 방식은 하나의 객체를 모두가 공유하여 사용하는 방식인데, 다양한 모듈 또는 서비스에서 동일한 객체를 필요로 할 때
매번 해당 객체를 생성하는 것이 아니라, 하나의 객체만을 생성하여 공유하는 방식이다.
- Spring이 Singletone 방식을 사용하는 이유는 데이터의 동기화 문제 및 시스템 자원의 낭비를 방지하기 위해서다.
3. AppConfig 구조적 문제의 해결책
- AppConfig에서 관리되는 구현체 수가 증가함에 따라, 중복되는 코드가 증가한다.
- 더불어 각 코드의 역할을 한눈에 알아보기 힘들다는 구조적 문제가 있었다.
- 이를 Spring Container를 사용하므로 해결할 수 있다고 했는데, 아직은 그 의미를 잘 모르겠다.
- 어떤 차이점이 있을까?
a) 객체(Bean) 호출의 편리성
- AppConfig를 사용하여 객체의 생성과 의존성을 형성하는 경우, 다음과 같은 방식으로 객체를 호출하였다.
public class testApp {
public void static main(String[] args) {
AppConfig appConfig = new AppConfig();
TestService testService = appConfig.testService();
}
}
- AppConfig를 사용하는 경우, 개발자가 AppConfig를 객체로 생성하여 직접 필요한 객체를 호출하는 방식을 사용했다.
- 여기서 발생하는 문제점은, AppConfig에서 관리하는 객체가 수십 또는 수백 개인 경우다.
- 개발자는 AppConfig에 등록되어 있는 모든 메서드를 기억할 수 없다.
- 마찬가지로, 각 메서드의 모든 역할과 기능을 기억할 수 없다.
- 그렇다면 이를 Spring Container에서는 어떻게 해결할까?
public class testApp {
public void static main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(AppConfig.class);
TestService testService
= applicationContext.getBean("testService", TestService.class);
}
}
- 얼핏 보면, AppConfig와의 차이점을 알기 힘들다. 그러나 객체를 조회하는 데 있어서 명확한 기능적 차이점이 존재한다.
- 위의 코드는 다음과 같은 과정을 설명한다.
→ AppConfig 파일을 Spring Container로 등록한다.
→ Spring Container에서 getBean() 메서드를 사용하여 찾고 있는 Spring Bean을 조회한다.
- getBean() 메서드는 메서드 이름과 객체의 타입을 이용하여 Bean을 조회한다.
- 메서드 이름만으로 조회할 수도, 객체의 타입만을 이용하여 조회할 수 도 있다.
- 이게 무슨 차이가 있다는 것일까?
b) 상속관계를 이용한 Spring Bean 조회 기능
- Bean을 Spring Conatiner에게 요청할 때, getBean(메서드 이름, Bean 타입)을 사용하여 조회하게 된다.
- 이때 Spring Container는 Bean을 조회할 때 Java의 상속관계를 이용하여 조회할 수 있는 기능을 제공한다.
- 이는 상위 클래스를 이용하여 조회하는 경우, 해당 클래스를 상속받은 모든 하위 클래스의 객체를 조회할 수 있다는 것이다.
- 즉, 개발자가 AppConfig의 모든 메서드 이름 또는 역할(기능)을 기억하지 않더라도 필요로 하는 객체의 상위 클래스만 알고 있다면 상위 클래스를 이용한 특정 Bean을 찾아낼 수 있다는 것이다.
* 참고 - 모든 객체(Bean)의 최상위 클래스는 Object이다. getBean(Object.class)를 사용하면 Spring Container에
등록되어있는 모든 Bean을 조회할 수 있다.
c) 상위 클래스(부모 타입)로 조회 시 자식 객체가 둘 이상인 경우
- 이와 같은 경우에는 오류(NoUniqueBeanException)가 발생한다.
- 하지만, 어떤 객체가 조회되는지 확인을 한 후 필요한 객체의 메서드명을 getBean() 메서드에 입력하면 된다.
- 즉, 일일이 AppConfig를 찾아가면서 필요한 Bean의 메서드명을 확인할 필요가 없다는 것이다.
4. 요약정리
a) 그래서 왜 Spring Container를 사용해야 할까?
- 지금까지 Spring Container에 대한 간략한 소개와 기능에 대해서 알아보았다.
- 하지만 여전히 Spring Container를 사용해야 하는 이유가 공감되지 않았을 것이라 생각한다.
- 사실, Spring Container는 자동 Bean 등록을 사용했을 때 진면목을 발휘하기 때문이다.
- 간단하게 설명하자면 AppConfig를 사용하지 않고 Bean을 등록하고 관리하는 기술이다.
- 이에 대한 내용은 다음 포스팅 시리즈에서 이어가도록 하겠다.
- 이번 포스팅에서 다양한 개념이 나왔다.
→ Spring Container의 다양한 종류 (XML, Annotation, etc)
→ Singleton Container
→ 그리고 Spring Bean의 자동/수동 등록
- 다음 포스팅 시리즈에서 하나하나 집중적으로 다뤄보도록 하겠다.
'Back-end > Spring 개념' 카테고리의 다른 글
7. Singleton Container (0) | 2022.02.02 |
---|---|
6. Spring Container의 다형성 (0) | 2022.02.01 |
4. Spring을 사용하는 이유 (1) | 2022.01.27 |
3. SOLID 원칙 (0) | 2022.01.26 |
2. 다형성(Polymorphism)과 객체지향 프로그래밍(OOP) (0) | 2022.01.24 |
댓글