스프링 삼각형! IoC와 DI를 이해해보자

MinKeun Kim·2021년 6월 8일
4

SPRING

목록 보기
1/1
post-thumbnail

IoC와 DI는 스프링의 3대 프로그래밍 모델의 한 부분을 차지하는 개념입니다.

아래 그림처럼, Plain Old Java Object(POJO)를 기반으로 IoC/DI, AOP, PSA 개념들이 삼각형으로 이루고 있어, 이를 스프링 삼각형이라고 부릅니다.

여기서, POJO는 프레임워크의 도움 없이도 돌아가는 순수 자바 객체를 말합니다.

여기서 IOC와 DI를 항상 IOC/DI라고 표기하는데, 같은 개념으로 이해하는 경우가 많습니다. (저도 스프링을 처음 접할땐 그랬습니다...)
하지만, 엄연히 말하자면 같은 개념은 아닙니다.

같은 개념은 아니지만 뗄 수 없는 관계인 IOC와 DI

그럼, 이 둘은 어떻게 다른지 또 어떤 관계인지 알아보겠습니다.


DI에 대하여

Dependency Injection : 의존성 주입

IoC를 이해하기 위해선 DI를 정확히 이해해야 하므로, DI 먼저 설명하겠습니다.

일단, 의존성 주입 받는 방법은 3가지가 있습니다.

스프링 없이 의존성 주입 받는 방법과, 스프링을 통한 의존성 주입 받는 방법 모두 설명하며 DI를 이해해봅시다 :)

스프링 없이 의존성 주입하는 방법에 대해 더 상세한 예제 설명을 원하시면 "스프링 입문을 위한 객체지향의 원리와 이해"의 저자님의 블로그에 책과 같은 내용이 담긴 글을 참조해주세요!

스프링 없이 의존성 주입하는 방법



최대한! 간략한! 예제 코드를 보며 이해해보자


첫 번째 방법 : 생성자를 통한 의존성 주입 (스프링 없이)

OwnerController.java

@Controller
class OwnerController {
    
    //1.
    private PetRepository petRepository;
    //2.
    public OwnerController(PetRepository petRepository) {
        this.petRepository = petRepository;
    }
    
}

PetRepository.java

public interface PetRepository extends Repository<Pet, Integer> {
	//선언문 생략
}    

자, 위와 같이 OwnerController 클래스와 PetRepository 인터페이스가 있을 때,
OwnerController 클래스에는 PetRepository 인터페이스를 인자로 받고 있는 생성자가 만들어져 있습니다.

만약 위와 같이, 기본 생성자 없이 별도의 인자 값을 받는 생성자를 만들었을 시, 컴파일러는 기본 생성자를 만들어주지 않습니다.

결국, OwnerController 클래스를 다른 곳에서 객체로 가져올 땐, PetRepository 인터페이스를 반드시! 꼭! 파라미터로 넘겨줘야 합니다.

OwnerController ownerController = new OwnerController(PetRepository petRepository);
ownerController.getMethod();   //...블라블라

이것을 바로 의존성 주입 (DI)라고 합니다.
OwnerController가 PetRepository를 의존 주입 받고 있다고 말할 수 있습니다.
OwnerController는 PetRepository없이는 아무것도 못하는거죠 :)
의존 관계가 어떤 느낌인지 알 수 있겠죠?

다시 말해, PetRepository라는 외부 기능을 OwnerController에 주입하고 있는 것입니다.


두 번째 방법 : 필드로 의존성 주입 (스프링 이용)

바로 예제코드를 보겠습니다.

OwnerController ownerController = new OwnerController(PetRepository petRepository);
ownerController.getMethod();   //...블라블라

예를 들어, 위와 같이 다른 곳에서 객체를 생성한다고 했을 때,
PetRepository가 반드시 필요합니다.
필드는 해당 인터페이스를 주입하기 위해 밑에와 같이 구현하는 방법을 말합니다.

@Autowired
private PetRepository petRepository;

OwnerController ownerController = new OwnerController(PetRepository petRepository);
ownerController.getMethod();   //...블라블라

스프링이 제공하는 @Autowired 어노테이션을 통해 해당 자원을 바로 가져올 수 있습니다.
앞서 설명 드린 1번과 코드를 비교해보겠습니다.

//1. 생성자를 통한 의존성 주입
private PetRepository petRepository;

public OwnerController(PetRepository petRepository) {
    this.petRepository = petRepository;
}

//2. 필드를 이용한 의존성 주입
@Autowired
private PetRepository petRepository;

코드가 확 줄죠?
스프링의 힘이라고 할 수 있죠 :)
근데 이게 왜 의존성 주입일까요?

물론, Autowired를 사용하기 위해선
import org.springframework.beans.factory.annotation.Autowired;
를 선언해야 합니다.

이와 같은 선언을 통해, IoC컨테이너(=Bean을 만들고 의존성 주입을 제공해주는 곳)는 PetRepository가 빈(Bean)으로 등록되어 있는지 확인 후,
PetRepository를 정상적으로 주입해줍니다.

IoC 컨테이너에 빈이 어떻게 등록되는지 까지 자세히 알아보면 좋겠지만,
글이 너무 길어지므로 넘어가겠습니다 :)
궁금하신 분들은, IoC컨테이너에 빈을 어떻게 등록할까? 참고 바랍니다!



세 번째 방법 : Setter로 의존성 주입 (스프링 없이)

private PetRepository petRepository;
public void setPetRepository(PetRepository petRepository) {	 
    this.petRepository = petRepository;
}

생성자, Autowired도 없이 우리에게 익숙한 setter를 이용하여 의존성을 주입 받을 수 있습니다.



하나 알고 넘어가자!

스프링 레퍼런스에서 가장 권장하는 의존성 주입은 생성자를 통한 의존성 주입 입니다.

그 이유는, OwnerController라는 클래스는 기능적으로 PetRepository가 없으면 안되는 클래스라고 가정해봅시다.

해당 인터페이스의 기능을 다 구현하고 있는 클래스 라는거죠.

필드, Setter를 이용한 의존성 주입은 PetRepository가 없어도 OwnerController 클래스를 불러올 수 있습니다.

그렇기 때문에, OwnerController를 사용하기 위해서 PetRepository를 강제로 주입 받는 코드가 더 알맞다는 겁니다.



IoC에 대하여

지금까지 의존성을 주입 받는 3가지 방법에 대해 알아보면서 DI에 대한 개념을 익혔습니다.

3가지 전부 다 외부의 기능을 계속 주입 받는 느낌을 받을 수 있습니다.
이처럼 제어권을 역전 당하는 느낌 (==주입 받는 느낌)을
IoC(제어의 역전)라고 합니다.

그럼, 도대체 일반적인 제어권이 뭔데? 라고 할 수 있겠죠?

class OwnerController {
    private PetRepository repository = new PetRepository();
    
    repository.getMethod();  //...블라블라
}

이렇게 내가 해당 객체를 만들어 사용하는 것을 일반적인 제어권이라고 합니다.
내가 주도해서 만드니까요.
어떠한 외부 개입도 없습니다.

하지만 의존성 주입은 외부에서 OwnerController의 사용을 제어하고 있습니다.
가령, 생성자를 통해 속성을 강제 주입하여 해당 속성이 없으면 그 클래스를 못 불러오게 해놓습니다.

이를 제어권이 외부로 역전되었다고 합니다.

결국, DI도 일종의 IoC라고 볼 수 있습니다 :)



마무리

IoC와 DI를 설명하면서 IoC 컨테이너, Bean, 어노테이션(Autowired)에 대한 개념이 중간 중간에 나왔습니다.
스프링의 내부를 정확히 이해하기 위해선, 알아야 할 필수 개념들이기에 글에선 생략했지만..
강의와 책을 통해 해당 개념들을 꼭 공부해야 합니다.
다음 스프링 포스팅은 삼각형의 나머지인 AOP와 PSA가 될 수 있기를 바라면서 글을 마치겠습니다 :)

그림 출처 : https://jinpyo900.tistory.com/55

profile
자바 백엔드 개발자 입니다

0개의 댓글