Spring IoC, DI, Container, Bean

김기현·2022년 7월 18일
0
post-thumbnail

의존성 역전(Inversion of Control)

IoC란 Inversion of Control의 두문자로, 객체의 의존성을 역전시키는 것을 말합니다.

AppConfig가 등장한 이후 프로그램에 대한 제어의 흐름 권한은 모두 AppConfig가 가지고 있습니다. 따라서 제어의 흐름을 개발자가 직접 제어하는 것이 아니라 외부에서 관리되어집니다.

객체 간의 결합도를 줄이고 유연한 코드를 작성하게 하여 코드의 중복을 줄이고, 가독성을 높이고, 유지보수를 편하게 한다는 장점이 있습니다.

프레임워크 VS 라이브러리
프레임워크는 내가 작성한 코드르 제어하고, 대신 실행하면 프레임워크입니다(JUnit). 하지만 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 라이브러리입니다.

의존관계 주입(Dependency Injection)

DI란 Dependency Injection의 두문자로, 문자 그대로의 의미로 '의존성을 주입한다'입니다. 실제 시점에 외부에서 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것을 말합니다.

DI의 특징으로 DI를 사용하면 클라이언트 코드를 변경하지 않고 클라이언트가 호출하는 대상의 타입 인스턴스를 변경할 수 있습니다. 그리고 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있습니다.

정적/동적 의존 관계
의존관계는 정적인 클래스 의존 관계와 실행 시점에 결정되는 동적인 객체(인스턴스) 의존관계 둘을 분리해서 생각해야 합니다.

  • 정적인 클래스 의존관계
    클래스가 사용하는 import 코드만 보고 의존관계를 쉽게 판단할 수 있습니다. 정적인 의존관계는 어플리케이션을 실행하지 않아도 분석 가능합니다.

  • 동적인 객체(인스턴스) 의존관계
    어플리케이션 실행 시점에 실제 생성된 인스턴스의 참조가 연결된 의존 관계입니다.


일반적으로 의존성에 대한 제어권은 객체 자기 자신이 갖습니다. 아래의 코드는 Example이라는 클래스에서 Pizza 객체를 불러오는 예제입니다. 의존관계는 new라는 키워드를 통해 생성됩니다.

class Example{
    private Pizza pizza = new Pizza();
}

아래는 ExampleTest라는 클래스에서 Pizza 객체를 생성한 뒤 Pizza 클래스의 생성자로 주입시켜 줍니다. 여기서는 Example이 직접 Pizza를 생성하는 것이 아닌 생성자로 주입 받습니다.

class Example{
	private Pizza pizza;
    
    public Example(Pizza pizza){
    	this.pizza = pizza;
    }
}

class ExampleTest{
	Pizza pizza = new Pizza();
    Example example = new Example(pizza);
}

첫 번째 예시에서는 Pizza 객체의 제어권이 Example에게 있었다면, 두 번째 예시에서는 Example이 아닌 ExampleTest에 있습니다. 이처럼 의존성을 역전시켜 제어권을 직접 갖지 않는 것을 IoC라고 하며 의존성을 외부에서 주입시켜 주는 것을 DI라고 합니다.

어떤 의미가 있나요?

스프링에서는 Spring Container, IoC Container라는 개념이 있습니다. 컨테이너는 생명주기를 관리하고 생성된 여러 인스턴스에게 추가적인 기능을 제공하도록 하는 역할을 합니다.

컨테이너는 개발자가 작성한 코드의 처리과정을 위임받은 독립적인 존재입니다. 컨테이너는 적절한 설정으로 누구의 도움 없이도 개발자가 작성한 코드를 스스로 참조한 뒤 알아서 객체의 생성과 소멸을 컨트롤합니다.

스프링 컨테이너는 스프링 프레임워크 핵심부에 위치하며, 종속객체 주입을 통해 애플리케이션을 구성하는 컴포넌트들을 관리합니다. 이 때 스프링 컨테이너에 생성되는 객체Bean이라고 합니다.

Bean

Bean은 Spring Bean Container에 존재하는 객체를 말합니다. Bean Container는 의존성 주입을 통해 Bean 객체를 사용할 수 있도록 합니다. 보통 Singleton으로 존재합니다.

Bean의 생성

Bean을 생성하는 방법으로 @Configuration에서 @Bean을 직접 등록해주는 방법과, Component scan의 방법이 있습니다.

@Configuration에서 @Bean으로 등록
@Configuration annotaion을 사용해 직접 @Bean을 등록해주는 방법입니다. @Bean annotaion을 사용하면 자동으로 Bean이 등록됩니다.

@Configuration
public class AppConfig{
	
    @Bean
	public MemberService memberService() {
    	return new MemberServiceImpl(memberRepository());
    }
    
    @Bean
    public MemberRepository memberRepository() {
    	return new MemoryMemberRepository();
    }
}

Component Scan
스프링부트로 애플리케이션을 생성하면 가장 상위 클래스에 @SpringBootApplication이 있습니다.

@SpringBootApplication
public class ModuleApiApplication {

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

}

그리고 @SpringBootApplication을 구현한 코드를 확인하면 아래의 코드와 같이 나옵니다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

@ComponentScan을 보면 @Component annotation이나 stereotype(@Service, @Controller, @Repository 등) annotation이 부여된 class를 찾아 자동으로 Bean으로 등록해주는 역할을 합니다. @ComponentScan을 해주는 범위는 해당 annotaion보다 하위의 class들만 스캔하기 때문에 @SpringBootApplication의 위치는 가장 상단에 있어야 합니다.

@Service, @Controller, @Repository 등의 어노테이션들도 @Component가 내장되어 있음을 확인할 수 있습니다.

추가적으로 Bean으로 등록하고 싶은 class가 있다면 @Component annotation을 붙여줍니다.

profile
피자, 코드, 커피를 사랑하는 피코커

0개의 댓글