springboot

[SpringBoot] IoC/DI 가 뭘까?

inhooo00 2024. 8. 28. 11:04

IOC(Inversion of Control)란?

  • 지금까지 자바 코드를 작성해 객체를 생성할 때는 객체가 필요한 곳에서 직접 생성했다. 다음을 보면 B 객체를 사용하기 위해 클래스 A에서 객체를 직접 생성한다.

  • 제어의 역전은 다른 객체를 직접 생성하거나 제어하는 것이 아니라 외부에서 관리하는 객체를 가져와 사용하는 것을 말한다.
  • 이 말도 듣기에는 어렵게 들릴 수 있는데. 코드를 보면 훨씬 이해가 쉽다. 방금 본 위 예제에 제어의 역전을 적용하면 다음과 같이 코드의 형태로 바뀐다.

  • 이전과는 다르게 클래스 B 객체를 직접 생성하는 것이 아니므로, 어딘가에서 받아와서 사용하고 있다고 추측해볼 수 있다.
  • 이처럼 제어의 역전을 적용하면 객체를 외부에서 관리하게 되고, 실제로 사용할 때에는 외부에서 제공해주는 객체를 받아오게 된다.
  • 스프링에서는 제어의 역전 개념을 중요한 컨셉으로 삼고 있으며, 방금까지 설명한 내용에서 외부(=객체를 관리하고 관리하는 주체)를 “스프링 컨테이너”라고 한다.

DI(Dependency Injection)란?

  • DI는 어떤 클래스가 다른 클래스에 의존한다는 뜻. 즉 의존성 주입
  • 의존성 주입은 필요한 객체를 직접 생성하는 것이 아닌 외부로부터 객체를 받아 사용하는 것.
  • 스프링에서는 스프링 컨테이너에 등록된 빈을 사용할 수 있다.

❓ 스프링 빈(Bean) 사용이유 ?

  • 가장 큰 이유는 스프링 간 객체가 의존관계를 관리하도록 하는 것에 가장 큰 목적이 있다. 객체가 의존관계를 등록할 때 스프링 컨테이너에서 해당하는 빈을 찾고, 그 빈과 의존성을 만든다.

의존성을 주입해야하는 이유

  • Test가 용이해진다.
  • 코드의 재사용성을 높여준다.
  • 객체 간의 의존성(종속성)을 줄이거나 없앨 수 있다.
  • 객체 간의 결합도를 낮추면서 유연한 코드를 작성할 수 있다.

의존성 주입의 3가지 방법

자 이제 spring에서 의존성 주입 방법을 알아보자.

  • 생성자 주입(Constructor Injection)
private final CocoService cocoService;
    public CocoController(CocoService cocoService) {        
this.cocoService = cocoService;   
 } 
  • 필드 주입(Field Injection)
@Autowired     
private CocoService cocoService;

필드 주입을 사용하면 프레임워크에 의존적이고 객체 지향과 맞지 않다.

  • 수정자 주입(Setter Injection)
private CocoService cocoService;        
    @Autowired    
    public void setCocoService(CocoService cocoService) {        
    this.cocoService = cocoService;    
    }

수정자 주입을 사용하면 setXXX 메서드를 public으로 열어두어야 하기 때문에 언제 어디서든 변경이 가능하다.

어떤 의존성 주입 방법이 가장 좋을까?

Spring Framwork reference에서 권장하는 방법은생성자를 통한 주입이다.

  • @Autowired 어노테이션만으로 간단하게 의존성을 주입할 수 있는데 왜 생성자 주입 방법을 권장할까?
  1. 순환 참조를 방지할 수 있다.
    1. 예를 들어 A가 B를 의존, 또 B가 A를 의존하는 경우가 생길 수 있다.
    2. 필드 주입과 수정자 주입은 빈이 생성된 후에 참조를 하기 때문에 에플리케이션이 아무런 오류와 경고가 없이 구동된다.
    3. 반면 생성자 주입은 오류가 발생해서 오류 체크에 용이하다
  2. 불변성
    1. 생성자로 의존성 주입할 때 final로 선언할 수 있다. 즉 런타임에서 의존성을 주입받는 객체가 변할 일이 없어진다.
    2. OOP 원칙을 가장 잘 지키는 방법이다.
  3. 테스트에 용이하다.
    1. DI 컨테이너 없이도 의존성을 주입하여 사용할 수 있다.
    2. 테스트의 격리성과 예측 가능성을 높일 수 있다는 장점이 생긴다.

빈을 어떤 기준으로 스프링에 만들고 활용할까?

빈은 스프링 컨테이너가 관리하는 자바 객체를 뜻한다.
빈을 만드는 기준은 그 객체가 동일한 역할을 수행하는 객체를 여러 번 만들필요가 있나 없나라고 생각한다.
만일 스프링의 빈 객체를 사용하지 않는다면, 각각의 객체마다 new 연산을 수행해 객체를 만들어야 하는데 동일한 객체가 아니기에 서로 가진 정보도 다르고, 메모리는 또 메모리대로 쓰는 등의 비효율적인 문제가 생긴다.
마찬가지로 서비스, 레포지토리 등과 같은 로직도 어떤 상태를 저장하며 관리할 이유가 없고, 여러번 인스턴스를 생성할 이유가 없기에 스프링 빈으로 사용하는 것이다.

결론

테스트코드를 제외하고 거의 모든 상황에서 new 객체를 선언하는 것 보다 중앙 관리와 객체 생명 주기 관리의 관점으로 봤을 때 의존성 주입을 사용하게 좋다!