3. IoC & DI

장현욱(Artlogy)·2022년 9월 28일
0

SpringBoot

목록 보기
3/4
post-thumbnail

DI (의존성 주입)

강한 결합


위 사진을 보자.

여러개의 컨트롤러가 하나의 서비스와 결합되어 있고 그 서비스는 하나의 레파지토리와 결합되어 있다.
근데 그림을 딱 봤을 때 "?"가 그려진다면 당신은 천상 개발자에 가까워진것이다.

"컨트롤은 여러개 근데 Service1은 하나인데 여러개의 새로운 객체로 각각 만들어 할당 할 필요가 있을까..?"

맞는 말이다.
각 객체 대한 생성은 딱 한번만 한 후 모든 곳에서 재사용 하는게 좋다.

물론 새로운 객체가 필요할때도 있는데 spring 할 실력이면 그 정도 상황 구분은 할거라 생각한다.

느슨한 결합

위 처럼 용도에 맞게 필요한 객체를 가져다가 사용하는 형태가 좋을것이다.
이러한 형태를 느슨한 결합 or IoC(Inversion of Control) 라고 한다.

왜 제어의 역전이라고 부를까?
사진을 다시보면 화살표 방향이 역순인걸 볼 수 있다.
일반적으로는 사용자가 필요한 객체를 생성해서 사용하는 방식으로 제어했다면
객체를 먼저 생성하고 필요 할때마다 가져오는 방식이다.

이때 가져오는 방식이 DI(Dependency Injection) 의존성 주입 방식이라고한다.

되게 있는척 용어 써가면서 설명했는데,
필요 할때마다 생성하는거 보다 하나 만들고 가져오는게
최적화 측면에서 좋다는 이야기가 핵심이다.

IoC 컨테이너

그럼 어떻게 객체를 만들고 가져올까? 사실 우린 IoC패턴을 쭉 써왔다.
저번에 MVC 패턴게시물에서 Service, Repository등의 객체를 선언하고 사용했던걸 기억 할 것이다.
곰곰히 생각해보자. 우리가 저 객체들을 한번 선언하고, 더 생성해서 썼던 적이 있었나?
아마 이런식으로 그냥 가져다가 썻을 것이다.
왜 이게 가능 했던건지 건지 지금부터 살펴보겠다.

@Component & @Configuration

...
@Component		//이녀석
public @interface Service {

	@AliasFor(annotation = Component.class)
	String value() default "";

}

@Service,@Repository등의 어노테이션의 내부 구조를 보면 @Component가 존재한다.
Spring의 핵심 어노테이션이니 설명을 하고 넘어가겠다. Spring의 Bean으로 등록하기 위해 사용하는 어노테이션으로 Bean은 스프링의 IoC 컨테이너에서 관리하게된다.
Spring Singleton패턴 핵심이기도 하다.

// @Configuration 
@Component
public class ProductService { ... }
  • 스프링 Bean 이름은 앞글자만 소문자로 변경된다.
    - public class ProductService -> productService
    IntellJ에선 저렇게 Bean 객체라는 아이콘이 뜬다고한다. (돈이좋긴하다)

@ComponentScan

@ComponentScan은 등록된 Bean의 적용 범위를 정할 수 있다.

@Configuration
@ComponentScan(basePackages = "com.study.artgloy_study")
class BeanConfig { ... }

근데 우리는 지금까지 @ComponentScan을 사용한적이 없다.
하지만 @Service, @Repository등의 @Component를 내장한 Bean들을 잘가져와 쓰고 있는데, 그 비밀은 SpringBootApplication에 있다.

MainApp.java
@SpringBootApplication	// <- 이녀석을 해체하여 분석해보자.
@EnableJpaAuditing
public class ArtlogySpringApplication {

	public static void main(String[] args) {
		SpringApplication.run(ArtlogySpringApplication.class, args);
	}

}
SpringBootApplication
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

@Bean

우리가 직접 만들지 않은 외부라이브러리는 IoC컨테이너에 어떻게 등록 할까?
이땐 @Bean, @Configuration을 이용하면 된다.

@Bean
public ObjectMapper objectMapper(){}

그럼 이런 생각을 할 수 있다. "그럼 @Bean쓰면 내가 만든거나 외부라이브러리 둘다 가능한거아닌가? @Component는 왜 알려준거야?" 할 수 있는데 아니다. 그건 @Bean, @Component의 설정을 보면 알 수 있다.



그 비밀은 @Target어노테이션에 있는데 @Target은 해당 객체가 선언 될 수 있는 타입을 정해주는거다. 보면 Bean은 Method(함수형), Component은 Type(객체형)에만 선언 할 수 있게 정해져있다.

뭐 요약하자면 IoC 컨테이너 등록을 위해 함수형 @Bean, 객체형 @Component를 쓴다.

@Bean 쓰는법

@Component
public class ProductService {
    private final ProductRepository productRepository;

    @Autowired
    public ProductService(ApplicationContext context) {
        // 1.'빈' 이름으로 가져오기
        ProductRepository productRepository = (ProductRepository) context.getBean("productRepository");
        // 2.'빈' 클래스 형식으로 가져오기
        // ProductRepository productRepository = context.getBean(ProductRepository.class);
        this.productRepository = productRepository;
    }

		// ...		
}

0개의 댓글