의존성 주입(DI) 방식

쓰옹·2023년 3월 30일
0

스프링 프레임워크에서는 3갸지 의존성 주입 방식이 있다.
1. field injection (필드 주입)
2. constructor injection (생성자 주입)
3. setter injection (setter 주입)

field injection (필드 주입)

클래스 멤버변수에 직접 의존성 주입
코드가 간결하고, 쉽게 사용 가능

@Component
public class Injection {
	@Autowired
	private FieldInjection fieldInjection;
}

setter injection (수정자 주입)

Setter 메서드로 의존성 주입
객체 생성 후 의존성 주입 -> 객체 생성과 의존성 주입 분리

@Component
public class Injection {
	private SetterInjection setterInjection;

	@Autowired
	public void setSetterInjection(SetterInjection setterInjection) {
		this.setterInjection = setterInjection;
	}
}

Constructor injection (생성자 주입)

객체를 생성 시 생성자를 통해 의존성 주입
객체 생성과 동시에 의존성 주입

@Component
public class Injection {
	
	private final ConstructorInjection constructorInjection;

	// @Autowired 주입받을 객체가 빈으로 등록되어있고 생성자가 하나면 생략 가능
	public Injection(ConstructorInjection constructorInjection) {
		this.constructorInjection = constructorInjection;
	}
    
    public void doSomething() {
    	
    }
}

2개 이상의 생성자가 있을 경우 @Autowired 붙여줘야함
다른 주입과 달리 필드 final 선언 가능
스프링에서는 생성자 주입 방식 사용을 추천함

필드 주입방식의 문제점

SRP(단일책임원칙) 위반

  • 하나의 클래스는 오직 하나의 책임을 가진다.
  • 클래스는 단 한가지의 변경 이유만을 가져야 한다.
@Component
public class Injection {
	@Autowired
	private FieldInjection fieldInjection;
    @Autowired
	private FieldInjection2 fieldInjection2;
    @Autowired
	private FieldInjection3 fieldInjection3;
    @Autowired
	private FieldInjection4 fieldInjection4;
    @Autowired
	private FieldInjection5 fieldInjection5;
    ...

}
// 생성자 주입으로 바꾸면
@Component
public class Injection {
	private final FieldInjction fieldInjection;
    private final FieldInjction2 fieldInjection2;
    private final FieldInjction3 fieldInjection3;
    private final FieldInjction4 fieldInjection4;
    private final FieldInjction5 fieldInjection5;
    ...
    public Injection(FieldInjction fieldInjection,
    				FieldInjction2 fieldInjection2, 
                    FieldInjction3 fieldInjection3, 
                    FieldInjction4 fieldInjection4,
                    FieldInjction5 fieldInjection5,...) {
                    this.fieldInjection = fieldInjection
                    this.fieldInjection2 = fieldInjection2
                    this.fieldInjection3 = fieldInjection3
                    this.fieldInjection4 = fieldInjection4
                    this.fieldInjection5 = fieldInjection5
                    ...
                    }
}

@Autowired만 붙이면 되기 떄문에 너무 많은 의존이 생김

의존성 감춤

DI 컨테이너를 사용하면 외부에서 의존관계를 주입시켜 주기 때문에 객체 스스로 의존성에 대한 책임이 없어지게 된다. 그럼 필요한 의존성에 대한 정보를 제공해야 함. setter와 생성자 주입의 경우 무엇을 선택사항으로 받는지, 필요로 하는지 명확히 제공해주고 있지만 field주입의 경우 숨은 의존성만 제공하고 있음

DI 컨테이너 결합성

스프링의 DI 컨테이너는 의존하는 bean 간에 느슨한 결합을 제공해주는 반면@Autowired를 이용한 필드 인젝션을 하면 스프링을 통해서만 의존성 주입이 가능하기 때문에 해당 Bean들이 스프링의 DI 컨테이너와의 강한 결합을 하게 된다.

생성자 주입 방식 장점

불변성

final 선언으로 불변성 보장

순환참조 방지

필드주입이나 setter주입은 빈 생성 후 참조를 하기 때문에 서버 동작 후에 순환참조 코드가 호출되면 서버가 죽는다. 실제 구동 전까지는 문제를 알 수 없음.
생성자 주입은 인자로 사용되는 빈을 찾거나 빈 팩토리에서 생성 후 찾아서 주입하려는 빈의 생성자를 호출함. 주입하려는 빈을 먼저 찾고 객체 생성 시점에 빈을 주입하기 떄문에 참조하는 객체가 생성되지 않은 상태에서 참조하게 되어 서버 구동 전에 BeanCurrentlyInCreationException오류가 발생함.
순환 참조뿐만 아니라 의존 관계에 내용을 외부로 노출시킴으로써 애플리케이션을 실행하는 시점에서 오류를 체크할 수 있음

테스트 코드 작성 용이

필드주입이나 세터주입은 mockito로 모킹 후 테스트를 진행해야 하는데 생성자주입은 객체 생성 후 생성자에 넣어주면 됨.

public class InjectionTest {
	@Test
    public void test() {
    	ConstructorInjection ci = new ConstructorInjection();
        Injection injecion = new Injection(ci);
        injection.doSomething();
    }
}



reference

https://shanepark.tistory.com/368
https://jackjeong.tistory.com/entry/Spring-%EC%83%9D%EC%84%B1%EC%9E%90-%EC%A3%BC%EC%9E%85-vs-%ED%95%84%EB%93%9C-%EC%A3%BC%EC%9E%85-Autowired
https://nect2r.tistory.com/58

profile
기록하자기록해!

0개의 댓글