본문 바로가기
개발/Java || Spring

의존성 주입 (Dependency Injection, DI)

by leedonggeun 2022. 10. 23.
의존성 주입이란?

객체를 직접 생성하는 것이 아니라 외부에서 생성 및 주입하는 방식을 의미합니다.
이를 통해 각 모듈 간의 결합도는 낮아지고, 유연성은 높아지게 됩니다.

 

1. Spring에서의 의존성 주입

강한 결합
객체 내부에서 다른 객체를 생성하는 구조를 말합니다.
A 클래스에서 B 객체를 생성하고, B 객체를 C 객체로 바꾸려면 A 클래스를 수정해야 하기 때문입니다.

느슨한 결합
외부에서 생성된 객체를 인터페이스를 통해 넘겨받는 구조를 말합니다.
이렇게 하면 결합도를 낮추고, 런타임 시 의존 관계가 결정되기 때문에 유연한 구조를 가지게 됩니다.
이 때, SOLID 원칙 중 Open Closed Principle을 지키기 위해 전략 패턴(생성자 주입)을 사용합니다.

여기서 생성자 주입의 종류로는, Field Injection, Setter Injection, Constructor Injection이 있습니다.

 

1-1. Field Injection (필드 주입)

변수 선언부에 @Autowired 애노테이션을 붙여줍니다.

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

사용을 지양해야 하는 이유

  • 단일 책임(SRP)의 원칙을 위반한다.
      - @Autowired 선언을 무분별하게 하게 된다.
         이를 생성자 주입(Constructor Injection)으로 변경한다면, 생성자에 매개변수가 많아지는 것을 보고
         클래스가 많은 책임을 진다는 것을 알게 되어 리팩터링의 트리거가 될 수 있습니다.
  • 불변성을 보장하지 않는다.
      - final을 선언할 수 없으므로 객체가 변할 수 있다.
  • DI 컨테이너의 결합성과 테스트 용이성
      - Field Injection을 사용한다면 필요한 의존성을 곧바로 인스턴스화 시킬 수 없기 때문에
         테스트에 용이하지 않다.
  • 순환 참조를 방지
      - 생성자 주입을 사용하게 된다면, 순환 의존성을 미리 예방할 수 있다.

 

1-2. Setter Injection (수정자 주입)

Spring 3.x 버전 대에서 추천되었던 방식으로, Setter를 통해 의존 관계를 주입합니다.
객체가 변경될 필요성이 있을 경우에만 사용하게 됩니다.

@Controller
public class SampleController {
    private Service service;

    public setService(Service service) {
        this.service = service;
    }
}

 

2-3. Constructor Injection (생성자 주입)

현재의 Spring Framework에서 권장하는 방식입니다. 간략하게 아래와 같은 장점이 있습니다.

  • 테스트 용이성
      - 프레임워크에 의존적이지 않아 컴포넌트 별 테스트 코드를 작성 가능합니다.
  • 불변성 확보
      - final을 선언할 수 있습니다.
  • 순환 참조 방지
      - 컴파일 시 순환 참조 발생 여부를 판단할 수 있습니다.
@Controller
public class SampleController {
    final Service service;
    
    public SampleController(Service service) {
        this.service = service;
    }
}

Lombok을 사용한다면 아래와 같이 간략하게 축약할 수 있습니다.

@Controller
@RequiredArgsConstructor
public class SampleController {
  final Service service;
}

 

3. 마치며...

현재 재직 중인 회사도 그렇고, 많은 회사들의 레거시를 본다면 Field Injection으로 의존 관계가 주입된 곳이 대부분일 것입니다.
저는 위 내용을 인지하고 나서 새로운 코드를 작성할 땐 의식적으로 생성자 주입 방식으로 작성하고, 기존 레거시 또한 점진적으로 생성자 주입 방식으로 변경하고 있습니다. (+ 팀원 분들에게 왜 Field Injection을 사용하면 안 되는지를 교육하면서!)

댓글