티스토리 뷰

728x90

DI(Dependency Injection)란?

스프링에서 의존성 주입(DI)이란, 객체간 의존성을 개발자가 객체 내부에서 직접 호출(new연산자)하는 대신, 외부(스프링 컨테이너)에서 객체를 생성해서 넣어주는 방식이다.

외부에서 두 객체 간의 관계설정을 해주는 디자인 패턴으로, 인터페이스를 사이에 두어 클래스 레벨에서는 의존관계가 고정되지 않도록 하고, 런타임 시 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 한다.

 

DI 방법 3가지

  1. 생성자 주입
  2. setter 주입
  3. 필드 주입

 

생성자 주입(Constructor Injection)

생성자 주입은 아래와 같이 Constructor에 @Autowired Annotation을 붙여 의존성을 주입받을 수 있다.

 

@Component
public class SampleService {
    private SampleDAO sampleDAO;
 
    @Autowired
    public SampleService(SampleDAO sampleDAO) {
        this.sampleDAO = sampleDAO;
    }
}

@Component
public class SampleController {

	private final SampleService sampleService = new SampleService(new SampleDAO());
    
	...
}

 

생성자 주입은 생성자의 호출 시점에 1회 호출 되는 것이 보장된다. 그렇기 때문에 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우에 강제하기 위해 사용할 수 있다. 또한 Spring 프레임워크에서는 생성자 주입을 적극 지원하고 있기 때문에, 생성자가 1개만 있을 경우에 @Autowired를 생략해도 주입이 가능하도록 편의성을 제공하고 있다.

 

Setter Injection

수정자 주입(Setter 주입, Setter Injection)은 필드 값을 변경하는 Setter를 통해서 의존 관계를 주입하는 방법이다. Setter 주입은 생성자 주입과 다르게 주입받는 객체가 변경될 가능성이 있는 경우에 사용한다. (실제로 변경이 필요한 경우는 극히 드물다.)

@Component
public class SampleController {
    private SampleService sampleService;
 
    @Autowired
    public void setSampleService(SampleService sampleService) {
        this.sampleService = sampleService;
    }
}

@Autowired로 주입할 대상이 없는 경우에는 오류가 발생한다. 위의 예제에서는 XXX 빈이 존재하지 않을 경우에 오류가 발생하는 것이다. 주입할 대상이 없어도 동작하도록 하려면 @Autowired(required = false)를 통해 설정할 수 있다.

 

필드주입(Field Injection)

변수 선언부에 @Autowired Annotation을 붙인다.

@Component
public class SampleController {
    @Autowired
    private SampleService sampleService;
}

필드 주입(Field Injection)은 필드에 바로 의존 관계를 주입하는 방법이다. IntelliJ에서 필드 인젝션을 사용하면 Field injection is not recommended이라는 경고 문구가 발생한다.

필드 주입을 이용하면 코드가 간결해져서 과거에 상당히 많이 이용되었던 주입 방법이다. 하지만 필드 주입은 외부에서 접근이 불가능하다는 단점이 존재하는데, 테스트 코드의 중요성이 부각됨에 따라 필드의 객체를 수정할 수 없는 필드 주입은 거의 사용되지 않게 되었다. 또한 필드 주입은 반드시 DI 프레임워크가 존재해야 하므로 반드시 사용을 지양해야 한다. 그렇기에 애플리케이션의 실제 코드와 무관한 테스트 코드나 설정을 위해 불가피한 경우에만 이용하도록 하자.

 

생성자 주입을 사용해야 하는 이유

1. 객체의 불변성 확보

실제로 개발을 하다 보면 의존 관계의 변경이 필요한 상황은 거의 없다. 하지만 수정자 주입이나 일반 메소드 주입을 이용하면 불필요하게 수정의 가능성을 열어두어 유지보수성을 떨어뜨린다. 그러므로 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장하는 것이 좋다.

 

2. 테스트 코드의 작성

필드 주입으로 작성된 경우, 순수 자바 코드로 단위테스트를 실행하는 것이 불가하다. 메인코드는 Spring과 같은 DI프레임워크 위에서 동작하는데 단위테스트 시 단독적으로 실행되기 때문에 의존관계 주입이 null상태여서 NullPointerException이 발생하게 된다. 생성자 주입 시 단독으로 실행할때도 의존관계 주입이 성립된다.

 

3. final 키워드 작성 및 Lombok과의 결합

생성자 주입을 사용하면 필드 객체에 final 키워드를 사용할 수 있으며, 컴파일 시점에 누락된 의존성을 확인할 수 있다. 반면에 다른 주입 방법들은 객체의 생성(생성자 호출) 이후에 호출되므로 final 키워드를 사용할 수 없다.

또한 final 키워드를 붙이면 Lombok과 결합되어 코드를 간결하게 작성할 수 있다. Lombok에는 final 변수를 위한 생성자를 대신 생성해주는 @RequiredArgsConstructor가 있다.

 

4. 순환 참조 에러 방지

순환참조란 A객체는 B객체를 참조하고, B객체는 A객체를 서로 동시에 참조하고 있을때 발생한다. 

어플리케이션을 실행하고 A또는 B에서 test()메서드를 호출하면 서로 메서드를 계속해서 호출하다 StackOverFlow가 발생하면서 시스템이 다운된다. 이때 컴파일 시에는 아무런 에러가 없다가 메서드 호출시에 발생한다는 것이 문제가 된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

Reference Link

https://mangkyu.tistory.com/125

https://cheershennah.tistory.com/227

https://velog.io/@gillog/Spring-DIDependency-Injection-%EC%84%B8-%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95

 

 

 

 

 

 

728x90