[Spring]의존성 주입 3가지 방법

김피자·2023년 2월 27일
0

Spring

목록 보기
12/28

지난 글에서 객체의 생성을 책임지고 의존성 관리하는 IoC Container 그리고 DL, DI에 대해 알아보았다.
DI는 참 쉬우면서도 어려운 것 같다.

Spring은@Autowired 어노테이션을 이용하여 다양한 의존성 주입 방법을 제공하는데 이를 통해 객체간의 결합도를 느슨하게 만들고 코드의 재사용성을 높일 수 있다.

@Autowired는 Spring에게 의존성을 주입하는 지시자 역할을 한다.

의존성 주입이 필요한 이유

  • 테스트 용이
  • 코드 재사용성 높임
  • 객체 간 의존성(종속성)을 줄이거나 없앨 수 있음
  • 객체 간 결합도 낮춰 유연한 코드 작성 가능

그럼 지금부터 의존성을 주입하는 세 가지 방법에 대해서 알아보자

  1. 생성자 주입(Constructor Injection)
  2. 필드 주입(Field Injection)
  3. 수정자 주입(Setter Injection)

1. 생성자 주입(Constructor Injection)

@Controller
public class SampleController{
	private final SampleService service;
    
    public SampleController(SampleService service){
    	this.service = service;
    }
}

별다른 어노테이션 없이 매개변수 생성자만 열어두면 사용 가능하다.


2. 필드 주입(Field Injection)

@Controller
public class SampleController{

	@Autowired
    private SampleService service;
}

필드에 @Autowired 어노테이션만 붙이면 자동으로 의존성 주입을 해준다.


3. 수정자 주입(Setter Injection)

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

단점
이렇게 수정자 주입을 사용하면 setSampleService를 public으로 열어 두어야해서 언제 어디서든 변경이 가능하다.


어떤 생성자를 사용하는 것이 좋을까?

Spring Framework Reference에서는 생성자를 통한 주입을 권장한다고 한다. 그 이유에 대해서 알아보자!

순환 참조를 방지한다.

생성자 주입을 사용하면 순환 의존성 발생 시, BeanCurrentlyInCreationException으로 문제 상황을 알 수 있게 해준다.

개발을 하다보면 여러 컴포넌트 간 의존성이 생기는데
예를 들어 A가 B를 참조하고, B가 A를 다시 참조하는 순환 참조 코드가 있다고 가정하고 생각해보자

// A
public class A{
	@Autowired
    private B b;
    
    public void hi_A(){
    	b.hi_B();
    }
}
//B
public class B{
	@Autowired
    private A a;
    
    public void hi_B(){
    	a.hi_A();
    }
}

A의 hi_A 메소드를 실행시켰더니 b.hi_B();로 가라해서 B의 hi_B로 갔더니 이번엔 또 A의 hi_A로 가란다
이렇게 순환 참조가 일어나는 경우 필드 주입과 수정자 주입은 빈이 생성된 후에 참조를 하기 때문에 애플리케이션이 아무런 오류나 경고 없이 구동이 된다ㅡㅡ

그리고 이 것은 실제로 코드가 호출되기 전까지 그 문제를 알 수가없다.
하지만, 생성자를 통해 주입하고 실행하면 위에서 말한 것 처럼 BeanCurrentlyInCreationException이 발생하게 되고 이를 통해, 순환 참조도 알고~ 오류도 체크하고~

필드 분변성을 보장한다.

생성자로 의존성을 주입할 때 final로 선언할 수 있어 변경에 안전하다.
(OOP의 5가지 원칙 중 Open-Closed Principle을 생각해보자)

생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장한다.
그리고 final로 선언한 생성자 주입 방식은 null이 불가능하다.

단일 책임의 원칙

필드 주입은 의존성 주입이 굉장히 쉬워서 무분별하게 의존성을 주입할 수 있다.
그러면 하나의 클래스에서 지나치게 많은 기능을 하게될 수 있고 이는 "클래스는 한가지 책임만 가져야한다"라는 단일 책임 원칙(SRP)을 위배하게 된다.

생성자 주입을 사용하면 의존성을 주입해야 하는 대상이 많아질 수록 생성자의 인자가 늘어나는데 이를 통해 의존관계의 복잡성을 쉽게 파악할 수 있어 너!! 리팩토링 해야 해!!! 하고 싶게 알아 차릴 수 있다 ^_^ (리팩토링 실마리 제공)

DI Container와의 낮은 결합도로 테스트 용이

생성자 주입은 생성자로 의존성을 주입받기 때문에 DI Container에 의존하지 않고도 의존성을 주입받아 사용할 수 있고, 이를 통해 코드 가독성이 높아지고, 유지보수 용이해지고 테스트의 격리성과 예측 가능성을 높인다.


결론

항상 결론은 객체지향적인 설계와 구조를 생각하며 코드를 작성해야 한다는 것이다. 특히 오늘 알아본 Constructor Injection은 이러한 코드 작성이 가능하도록 해주는 방법인 것 같다.

나는 개발할 때 거의 Field Injection 방식을 사용했는데 (여기저기 다 필요할 때 마다 불러와서 사용했다.) 이후, 코드를 리팩토링하며 Constructor Injection으로 수정해보아야겠다!!!!!! 끄으으응ㅆ


참고
https://dev-coco.tistory.com/70
https://tecoble.techcourse.co.kr/post/2020-07-18-di-constuctor-injection/

profile
제로부터시작하는코딩생활

0개의 댓글