Intro to Inversion of Control and Dependency Injection with Spring (Baeldung 번역)

Jay Jang·2022년 7월 25일
0

Spring

목록 보기
2/2

https://www.baeldung.com/inversion-control-and-dependency-injection-in-spring

What is a Spring Bean? | Baeldung
Baeldung의 Intro to Inversion of Control and Dependency Injection with Spring을 번역한 것입니다.

모든 저작권은 Baeldung에 있습니다. All copyrights are in Baeldung.


개요


이 튜토리얼에서는 IoC(Inversion of Control 제어의 역전)DI(Dependency Injection 의존성 주입)의 개념을 소개하고 스프링 프레임워크에서 이러한 개념이 어떻게 구현되는지 알아본다.


What is Inversion of Control?


Inversion of Control 제어의 역전은 소프트웨어 공학에서 객체나 프로그램 일부의 제어권을 컨테이너 또는 프레임워크에 위임하는 원리를 말한다.

개발자가 작성한 코드가 라이브러리를 호출하는 전통적인 프로그래밍 방식과는 반대로, IoC는 프레임워크가 프로그램의 흐름을 제어하고 작성한 코드를 호출할 수 있게 한다. 이를 위해, 프레임워크는 추가 동작이 내장된 추상화를 사용한다.

우리가 의도하는 동작을 추가하고 싶다면, 프레임워크의 클래스를 확장하거나 우리 자신의 클래스를 플러그인 해야한다.

이 아키텍처의 장점은 다음과 같다.

  • 작업 실행(execution of task)과 구현의 분리
  • 서로 다른 구현 간에 쉬운 전환
  • 프로그램의 모듈성 향상
  • 컴포넌트를 분리하거나, 컴포넌트 의존성을 mocking하며, 컴포넌트들이 규약을 통해 통신할 수 있도록함으로써 프로그램을 테스트하는데 더 큰 용이성을 준다.

우리는 Strategy design pattern, Service Locator Pattern, Factory pattern, 그리고 DI와 같은 다양한 메커니즘을 통해 IoC를 누릴 수 있다.

이제 다음으로 DI에 대해 알아보자.


What is Dependency Injection?


Dependency Injection 의존성 주입IoC를 구현하는 데 사용할 수 있는 패턴으로, 제어권이 역전되어 객체의 의존성을 설정한다.

객체를 다른 객체와 연결하거나, '주입'하는 작업은 객체 자체보다는 어셈블러에 의해 수행된다.

기존 프로그래밍에서 객체 의존성을 만드는 방법은 다음과 같았다.

public class Store {
    private Item item;
 
    public Store() {
        item = new ItemImpl1();    
    }
}

위의 예에서, Store 클래스 자체 내부에서 Item 인터페이스의 구현을 인스턴스화해야 한다.

DI를 사용하면, 우리는 원하는 Item 구현체를 명시하지 않고 예제 코드를 다시 작성할 수 있다.

public class Store {
    private Item item;
    public Store(Item item) {
        this.item = item;
    }
}

우리는 다음 섹션에서 메타데이터를 통해 Item 클래스의 구현체를 제공하는 방법에 대해 살펴볼 것이다.

IoCDI 모두 단순한 개념이지만, 시스템 구성에 깊은 영향을 미치므로 충분히 이해할 가치가 있다.


The Spring IoC Container


IoC 컨테이너는 IoC를 구현하는 프레임워크의 일반적인 특성이다.

Spring 프레임워크에선, ApplcationContext 인터페이스가 IoC 컨테이너를 나타낸다.

Spring 컨테이너는 bean으로 알려진 객체를 인스턴스화, 구성 및 조립하고 수명 주기 lifecycle을 관리하는 역할을 한다.

Spring 프레임워크는 ApplicationContext의 다양한 구현체를 제공한다.

독립 실행형 프로그램의 경우 ClassPathXmlApplicationContext 및 FileSystemXmlApplicationContext, 웹 응용프로그램의 경우 WebApplicationContext가 그것이다.

Bean을 조합하기 위해 컨테이너는 XML configuration 혹은 어노테이션 형태의 configuration metadata를 사용한다.


다음은 컨테이너를 인스턴스화 하는 방법 중 하나이다.

ApplicationContext context
  = new ClassPathXmlApplicationContext("applicationContext.xml");

위 예시에서 item 속성을 세팅하기 위해 metadata를 사용할 수 있다. 그리고 컨테이너는 이 metadata를 읽고 런타임에 bean을 조합하는 데 사용한다.

Constructor-Based Dependency Injection


constructor-based dependency injection 생성자 기반 DI의 경우, 컨테이너는 설정하고자 하는 의존성을 각각 나타내는 인수로 생성자를 호출한다.

Spring은 일반적으로 타입별로 각 인수를 해결하고, 그 다음 속성 이름, 명확화를 위한 인덱스를 제공한다.

이제 @annotation을 사용하여 bean의 구성과 의존성을 알아보자.

@Configuration
public class AppConfig {

    @Bean
    public Item item1() {
        return new ItemImpl1();
    }

    @Bean
    public Store store() {
        return new Store(item1());
    }
}

@Configuration annotation에는 클래스가 bean으로 정의된 자원임을 나타낸다. 또한 다양한 configuration 클래스들에 이를 추가할 수 있다.

우리는 bean 정의를 위해 @Bean 어노테이션을 메소드 위에 사용한다. 만약 이름을 구체적으로 명시하지 않으면, bean은 method 이름을 기본 값으로 이름을 가질 것이다.

기본 싱글톤 scope를 가진 bean의 경우, Spring은 먼저 bean의 캐시된 인스턴스가 이미 있는지 먼저 확인하고, 캐시되지 않은 경우에만 새 instance를 만든다.

prototype scope를 사용하면, 컨테이너는 각 메소드 호출에 대해 새로운 인스턴스를 반환한다.

bean configuration을 만드는 또 다른 방법은 XML configuration을 사용하는 것이다.

<bean id="item1" class="org.baeldung.store.ItemImpl1" /> 
<bean id="store" class="org.baeldung.store.Store"> 
    <constructor-arg type="ItemImpl1" index="0" name="item" ref="item1" /> 
</bean>

Setter-Based Dependency Injection


setter 기반의 DI의 경우 컨테이너는 bean을 인스턴스화 하기 위해 인수가 없는(NoArgs) 생성자 또는 인수가 없는 static factory method를 호출한 뒤 자체 setter method를 호출한다.

@annotation을 사용하여 configuration을 생성해 보자.

@Bean
public Store store() {
    Store store = new Store();
    store.setItem(item1());
    return store;
}

XML 사용해서도 같은 동작을 할 수 있다.

<bean id="store" class="org.baeldung.store.Store">
    <property name="item" ref="item1" />
</bean>

우리는 동일한 bean에 대해 생성자 기반과 setter 기반 주입 유형을 결합할 수있다.
Spring 문서에는 필수 의존성은 생성자 기반 주입을 사용하고, 선택 의존성에는 setter 기반을 사용할 것을 권장한다.


Field-Based Dependency Injection


필드 기반 DI의 경우 의존성을 @Autowired 애노테이션을 통해 주입할 수 있다.

public class Store {
    @Autowired
    private Item item; 
}

Store 객체를 구성하는 동안 Item bean을 주입할 생성자 또는 setter 메소드가 없는 경우 컨테이너는 Reflection을 사용하여 StoreItem을 주입한다.

XML을 사용해서도 가능하다.

이 방법은 단순하고 깔끔해 보이지만 다음과 같은 몇가지 단점이 있으므로 사용하지 않는 것이 좋다.

이 메소드는 Reflection을 이용하여 의존성을 주입하는데, 이는 생성자 기반 또는 setter 기반보다 많은 비용이 든다.

이 방법을 사용하면 여러 의존성을 계속해서 추가할 수 있다. 만약 생성자 주입을 사용하면, 여러 개의 인수를 갖는 것은 클래스가 한 가지 이상의 일을 한다고 생각하게 만들 것이고, 이는 단일 책임 원칙을 위반할 수 있다.


Autowiring Dependencies


Spring 컨테이너는 wiring을 통해 정의된 bean을 검사하여 협력하는 bean 사이의 종속성을자동으로 해결할 수 있다.

XML Configuration을 사용하는 bean autowiring에는 다음과 같은 네 가지 모드가 있다.

  • no: 기본값이다 - 즉, bean에 autowiring이 사용되지 않으며 의존성의 이름을 명시적으로 지정해야 한다.
  • byName: autowiring은 속성 이름을 기반으로 수행되므로 spring은 설정해야 하는 속성과 이름이 같은 bean을 찾는다.
  • byType: byName autowiring과 유사하며 속성 type만 기반으로 한다. 이는 spring이 설정할 동일한 유형의 속성을 가진 bean을 찾을 것이라는 것을 의미한다. 이러한 유형의 콩이 둘 이상 있으면 프레임크는 예외를 발생시킨다.
  • constructor: autowiring은 생성자 arguments를 기반으로 수행되며, 이는 spring이 생성자 arguments와 동일한 유형의 bean을 찾는 것을 의미한다.

예시로, item1 bean을 store bean에 위의 by type으로 정의해보자.

@Bean(autowire = Autowire.BY_TYPE)
public class Store {
    
    private Item item;

    public setItem(Item item){
        this.item = item;    
    }
}

우리는 또한 @Autowired 어노태이션을 사용해 type으로 autowiring 할 수 있다.

public class Store {
    
    @Autowired
    private Item item;
}

동일한 유형의 bean이 두 개 이상 있는 경우 @Qualifier 어노테이션을 사용하여 bean을 이름으로 참조할 수 있다.

public class Store {
    
    @Autowired
    @Qualifier("item1")
    private Item item;
}

이번엔 XML Configuration을 사용하여 bean을 타입으로 autowire 해보자.

<bean id="store" class="org.baeldung.store.Store" autowire="byType"> </bean>

다음으론, XML을 통해 이름별로 store bean의 프로퍼티인 item을 주입해보자.

<bean id="item" class="org.baeldung.store.ItemImpl1" />

<bean id="store" class="org.baeldung.store.Store" autowire="byName">
</bean>

또한 생성자 인수 또는 configures를 통해 의존성을 명시적으로 정의하여 autowiring을 override 재정의 할 수 있다.


Lazy Initialized Beans


기본적으로, 컨테이너는 초기화 시에 모든 싱글톤 bean을 생성하고 구성한다. 이를 방지하기 위해 bean configration에서 true 값을 가진 lazy-init 속성을 사용할 수 있다.

<bean id="item1" class="org.baeldung.store.ItemImpl1" lazy-init="true" />

따라서 item1 bean은 처음 요청시에만 초기화되며, 시작시에 초기화되지 않는다.
초기화 시간이 더 빠르다는데 장점이 있지만, bean을 요청한 후(이미 애플리케이션을 실행한 후 시간이 지나서) configuration 오류를 발견할 수 있다는 것이다.


결론


이 문서에서 Inversion of Control과 Depndency Injection의 개념을 제시하고, Spring Framework에서 예시를 알아보았다.

추가로, Spring Framework Reference Documentation에서 IoC 및 DI의 스프링 구현에 대해 배울 수 있다.



REFERECNE


https://www.baeldung.com/inversion-control-and-dependency-injection-in-spring

profile
그때는맞고지금은틀리다

0개의 댓글