[Spring] DI? IOC?

무1민·2023년 11월 28일
1

Spring

목록 보기
6/9
post-thumbnail

스프링을 공부하다가 보면 항상 나오는 이야기가 있다.

스프링은 IOC 컨테이너로 빈을 관리한다.
DI를 사용한다.
DI방법에는 생성자 주입, setter 주입, 필드 주입 등이 있다
...

우리는 기초이자 필수! DI와 IOC에 대해서 알아보는 시간을 가져보도록 하겠다.

🤸IoC(Inversion of Control)

IoC란 "제어의 역전"이라는 의미로, 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것을 의미한다.


기존에는 다음과 같은 순서로 객체가 만들어지고 실행되었다.
1. 객체 생성
2. 의존성 객체 생성 - 클래스 내부에서 생성
3. 의존성 객체 메소드 호출


하지만, 스프링에서는 다음과 같은 순서로 객체가 만들어지고 실행된다.
1. 객체 생성
2. 의존성 객체 주입 - 스스로 만드는 것이 아니라 제어권을 스프링에게 위임하여 스프링이 만들어 놓은 객체를 주입
3. 의존성 객체 메소드 호출


스프링이 모든 의존성 객체를 스프링이 실행될 때 다 만들어 주고 필요한 곳에 주입시켜줌으로써 Bean들은 싱글턴 패턴의 특징을 가지며, 제어의 흐름을 사용자가 컨트롤하는 것이 아니라 스프링에게 맡겨 작업을 처리하게 된다.


예를 들어, 스프링 프레임워크를 사용할 때 Controller, Service같은 객체들의 동작을 직접 구현하기는 하지만, 해당 객체들이 어느 시점에 호출될 지는 신경쓰지 않는다. 프레임워크가 요구하는대로 객체를 생성하면, 프레임워크가 해당 객체들을 가져다가 생성하고, 메서드를 호출하고, 소명시킨다. 프로그램의 제어권이 역전된 것이다.


여기서, 프레임워크라이브러리의 차이를 말할 수 있다.
라이브러리를 사용하는 어플리케이션은 어플리케이션의 제어 흐름을 라이브러리에 내주지 않고, 필요한 시점에 라이브러리에 작성된 객체를 가져다 쓸 뿐이다.
하지만 프레임워크를 사용하면, 어플리케이션 코드에 작성한 객체들을 프레임워크가 필요한 시점에 가져다가 프로그램을 구동한다.


다른 예시로, Junit 프레임워크를 사용할 때를 생각해보자. 개발자는 각각의 테스트 메서드를 작성하지만, 해당 메서드들에 대한 제어권은 Junit 프레임워크에 있다. 직접 테스트 메서드를 호출하는 것이 아니라, @Test 어노테이션을 붙이기만 하면 Junit 프레임워크가 해당 메서드를 호출한다. @BeforeEach@AfterEach 어노테이션이 붙은 메서드들에 대해서도 마찬가지다.


또 다른 예시로, 템플릿 메서드 패턴에 있다.
템플릿 메서드 패턴은 알고리즘의 구조를 메서드에 정의하고, 하위 클래스에서 알고리즘 구조의 변경없이 알고리즘을 재정의하는 패턴이다.
상위의 추상 클래스에 흐름을 제어하는 메서드들을 정의해두고, 해당 템플릿 메서드에서 사용할 하위 메서드들은 변경이 필요없으면 공통 메서드로, 변경이 필요하면 추상 메서드로 정의해둔다. 이렇게 되면 하위 클래스에서 메서드의 실제 구현을 하긴 하지만, 해당 메서드의 호출 제어권은 상위 추상 클래스에 있게 되어 제어의 역전이 일어나는 것이다.


SpringBoot는 classPath와 다양한 설정들을 바탕으로 필요한 빈들을 자동으로 설정한다. 이는 IoC의 한 형태로 볼 수 있다.

🛠️IoC 컨테이너

스프링 어플리케이션에서는 오브젝트(빈)의 생성과 의존 관계 설정, 사용, 제거 등의 작업을 어플리케이션 코드 대신 스프링 컨테이너가 담당한다.
스프링 컨테이너가 코드 대신 오브젝트에 대한 제어권을 갖고 있다고 해서 IoC라고 부르고,
이에, 스프링 컨테이너를 IoC 컨테이너라고도 부른다.


스프링에서는 IoC를 담당하는 컨테이너를 빈 팩토리, DI 컨테이너, 어플리케이션 컨텍스트라고도 부른다.

👨‍👩‍👧‍👦DI(Dependency Injection)

DI란 스프링이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입 기능으로,
객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입시켜주는 방식이다.


DI를 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아진다.

첫 번째 방법이 A객체가 B와 C객체를 New 생성자를 통해 직접 생성하는 방법이고, 두 번째 방법이 외부에서 생성된 객체를 setter()를 통해 사용하는 방법이다.


예시를 들어보겠다.

public class A {
  private B b = new B();
}

위 A 클래스는 B라는 클래스를 필드로 가진다. 그런데, B의 final 필드가 추가/변경되면 어떻게 될까?
new B() 부분에서 컴파일 에러가 난다.
B 내부의 변경이 일어났는데, A에도 영향을 미치게 되는 것이다.
이런 경우를 A가 B에 의존한다.라고 한다.


그런다면 의존성 주입이란? 이 의존성을 외부에서 주입해준다.
예시를 들어보겠다.

public class A {
    private B b;
    public A(B b) {
        this.b = b;
    }
}

A가 B에 의존한다.는 같지만, 의존 대상을 직접 생성하는 것이 아니라 외부로부터 주입받는다.
만약 B를 인터페이스로 추상화하고 하면 다양한 구현체가 들어옴으로써 의존성을 다각화할 수 있다.

토비의 스프링에서는 DI를 다음과 같이 설명하고 있다.

  • 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서 인터페이스만 의존하고 있어야 한다.
  • 런타임 시점의 의존관계는 컨테이너나 팩토리같은 제3의 존재가 결정한다.
  • 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공해줌으로써 만들어진다.

🤷‍♀️DI 방법

DI에는 세 가지 주요 스타일이 있다.
생성자 주입, Setter 주입, 인터페이스 주입

생성자 주입(Constructor Injection)

스프링에서 권장하는 방식이다.

public class A {
    private B b;
    
    public A(B b) {
        this.b = b;
    }
}

Setter 주입(Setter Injection)

public class A {
    private B b;
    
    public void setB(B b) {
        this.b = b;
    }
}

인터페이스 주입(Interface Injection)

public interface BInjection {
    void inject(B b);
}

public A implements BInjection {
    private B b;
    
    @Override
    public void inject(B b) {
        this.b = b;
    }
}

💁DI의 장점?

그렇다면 DI의 장점이 뭘까?

  • 의존성이 줄어든다. (변경에 덜 취약해진다.)
  • 모의 객체를 주입할 수 있기 때문에 단위 테스트가 쉬워진다.
  • 가독성이 높아진다.
  • 재사용성이 높아진다.

👀SpringBoot에서 DI

@Service
public class MyService {
    private final MyRepository repository;
    public MyService(MyRepository repository) {
        this.repository = repository;
    }
    // 기타 메소드
}

SpringBoot에서 MyService 클래스는 MyRepository를 필요로 한다. 생성자 주입 방식을 사용하여, SpringBoot는 MyRepository의 인스턴스를 MyService에 자동으로 주입한다.
특별한 @Autowired어노테이션 없이도 생성자를 통한 자동 주입이 이루어진다.


이러한 방식으로, Spring과 SpringBoot는 개발자가 객체의 생성과 관리에 대한 부담을 덜고, 의존성 관리에 더 집중할 수 있도록 돕는다.

🙌출처

https://steady-coding.tistory.com/600#google_vignette
https://devmango.tistory.com/174
https://velog.io/@gillog/Spring-DIDependency-Injection
https://velog.io/@ohzzi/Spring-DIIoC-IoC-DI-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0

profile
야호

0개의 댓글