티스토리 뷰

Spring

IoC/DI 의존성 주입

장진혁 2023. 6. 1. 01:55
Inversion of Control (IoC)
  • 프로그램 흐름이 더 이상 개발자에 의해 제어되지 않고, 대신 프레임워크나 컨테이너가 객체의 생성부터 생명주기 관리까지 전체적인 제어권을 가지는 디자인 원칙입니다. 
Dependency Injection (DI)
  • 의존성 주입은 객체에 다른 객체를 주입하는 것을 의미합니다.
  • 일반적으로 객체 간의 관계에서 하나의 객체가 다른 객체를 사용하거나 의존하는 경우입니다.
  • 예를 들어, 클래스 A가 클래스 B를 사용하고자 할 때, A는 B에 대한 의존성을 가집니다. 이때 의존성 주입을 통해 클래스 A에 클래스 B의 인스턴스를 주입하면, A는 B 객체의 기능을 사용할 수 있게 됩니다.
  • 필요한 의존성을 클래스 내에서 직접 생성하거나 찾는 대신, 외부에서 주입받아 사용하는 기법입니다. 
  • 의존성 주입은 일반적으로 생성자 주입(Constructor Injection), 세터 주입(Setter Injection), 인터페이스 주입(Field Injection) 방법으로 이루어집니다.

생성자 주입 방식 3가지

생성자(Constructor) 주입

public class UserService {
    private UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • UserService 클래스는 UserRepository 객체에 의존하고 있다.
  • 의존 객체는 생성자를 통해서 주입 받아서 할당한다.
  • 하나의 생성자이면 @Autowired를 사용하지 않아도 스프링은 해당 생성자를 자동으로 사용하여 의존성 주입을 수행한다.
public class UserService {
    private UserRepository userRepository;
    private Logger logger;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public UserService(UserRepository userRepository, Logger logger) {
        this.userRepository = userRepository;
        this.logger = logger;
    }
}
  • 생성자가 2개 이상인 경우 @Autowired를 사용하여 어떤 생성자를 주입할지를 명시해야 한다.

Setter 메서드(Setter Method) 주입

public class UserService {
    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • Setter 메서드 주입은 의존 객체가 선택적인 경우에 유용하다.
  • 의존 객체가 동적으로 변경될 수 있는 경우에 Setter 메서드 주입을 사용하면 된다.
  • 하지만 의존성을 누락시키는 실수가 있을때 해당 메서드를 호출할 경우 NullPointerException 예외가 발생한다.

필드(Field) 주입

public class UserService {
    @Autowired
    private UserRepository userRepository;
}
  • 필드에 @Autowired 어노테이션만 추가하면 스프링이나 컨테이너가 해당 필드에 자동으로 의존 객체를 주입해서 코드를 간결하게 유지 할 수 있다.
  • 여기서 단점이 존재한다. 의존성을 직접 주입하는 것이 아니기 때문에 테스트 환경에서는 해당 필드에 접근하여 의존성을 설정하기 어렵다 그리고 두 개 이상의 클래스가 서로를 필드 주입으로 의존하고 있는 경우, 순환 의존성 문제가 발생할 수 있다.

Spring 팀에서 생성자 주입 방식을 권장하는데 그 이유는?

https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html#beans-constructor-injection

 

Dependency Injection :: Spring Framework

Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency. Calling a static factory method with specific arguments to construct the bean is nearly equivalent, and this discussion

docs.spring.io

공식 문서를 살펴보면 필수 종속성에는 생성자를 사용하고
선택적 종속성에는 세터를 사용하는 것이 좋다고 하지만
Spring 팀에서도 생성자 주입이 더 좋다고하고 추천을 한다고 한다.

1. 불변성과 의존성 보장

  • 수정자 주입과 필드 주입은 final로 선언을 할 수 없어서 변경에 유연해서 클래스의 동작을 동적으로 수정할 수 있습니다.
  • 생성자 주입은 의존성을 클래스의 생성자를 통해 주입하므로, 주입된 의존 객체는 불변한 상태로 유지된다. 즉 객체의 상태가 변경되지 않고 일관성을 유지할 수 있음을 의미한다. 그러므로 생성자 주입은 필수적인 의존성에 대한 보장을 제공한다.
  • 생성자를 통해 의존성을 주입받지 않으면 객체가 생성되지 않으므로, 필수적인 의존성이 누락되는 상황을 방지할 수 있습니다.

2. 테스트 코드 작성

  • 수정자 주입과 필드 주입은 모두 외부 의존성을 직접 주입하기 때문에 Mock객체로 테스트가 어려울 수 있습니다.
  • 수정자 주입과 필드 주입은 의존성을 나중에 주입하므로 객체의 상태 변화가 명확하지 않을 수 있다.
  • 1번에서 말한 불변성에서 생성자 주입으로 불변성을 가지면 테스트 코드에서 변경되는 상황을 방지하고 테스트의 일관성과 안정성을 유지하는 데 도움이 된다.

3. 순환 의존성 문제 회피 가능

순환 의존성이란?
A 객체가 B 객체에 의존하고, 동시에 B 객체가 A 객체에 의존하는 상황을 의미한다.

생성자 주입을 사용한 경우에는
BeanCurrentlyInCreationException 이 발생해서
애플리케이션이 실행이 되지 않는다.
즉 발생할 수 있는 순환 의존성 오류를 사전에 알 수 있다.

위에 글을 번역해서 자세히 알아보면 "순환 종속성이 없는 경우"라고 언급되어 있다.
순환 참조 문제가 발생하지 않는 상황을 가정하고 설명하고 있는데

스프링 IoC 컨테이너는 빈들 간의 종속성을 관리하면서,
각 빈이 필요로 하는 다른 빈을 미리 구성하고 설정함으로써
의존성이 완전히 설정된 상태에서 빈을 사용할 수 있도록 보장 한다고 한다.

  • 수정자 주입과 필드 주입 방식은 빈을 먼저 생성한 후에 어노테이션이 붙은 필드에 해당하는 빈을 찾아서 주입하는 방식이다.
  • 생성자 주입은 생성자의 파라미터 빈 객체를 찾아서 먼저 주입한 뒤에 주입받은 빈 객체를 이용하여 생성한다.
  • 정리하면 수정자 주입과 필드 주입은 빈을 먼저 생성한 후에 필드에 대해서 주입하기 때문에  순환 참조의 발생 여부를 알 수가 없고 생성자 주입은 실행 시점에서 순환 참조 오류를 감지가 가능하다.

객체지향 관점에서 DI 방식으로 의존성 문제를 해결하면 어떤한 장점?

  1. 느슨한 결합 : DI를 통해 의존성을 주입받는 객체는 구체적인 의존 객체의 구현에 대해 알 필요가 없다.
  2. 단일 책임 원칙(SRP) : 의존성을 주입받는 객체는 자신의 주 역할에만 집중할 수 있다. 의존성을 주입받는 객체가 자신의 의존 객체를 직접 생성하거나 관리하지 않아도 되므로 SRP를 준수하는 객체를 설계할 수 있다.
  3. 테스트 용이성 : Mock객체를 주입하여 객체의 동작을 모방해서 테스트 가능하다.
  4. 코드 재사용성 : 동일한 기능을 가진 객체를 다양한 환경에서 사용가능하다. 중복코드를 피하고 개발 생산성은 향상된다.

 

 

 

 

 

 

 

참조 블로그

https://wildeveloperetrain.tistory.com/139

 

생성자 주입과 필드 주입, 수정자 주입 정리 (feat. 의존성 관계 주입)

생성자 주입(Constructor Injection), 필드 주입(Field Injection), 수정자 주입(Setter Injection)은 모두 의존성 관계 주입이라고 합니다. 각각의 의존성 관계 주입 방법에 대해서 알아보기 전에 의존성과 의존

wildeveloperetrain.tistory.com

 

'Spring' 카테고리의 다른 글

Filter, Interceptor, AOP  (0) 2023.06.04
application.yaml 로 전환하기  (0) 2023.04.20
테스트 코드 작성하기  (0) 2023.04.13
Tomcat Thread Pool  (0) 2023.04.12
트랜잭션 격리수준, 비관적 락  (0) 2023.04.06
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함