개발쿠키

[Spring] IoC, DI 본문

개발/spring boot

[Spring] IoC, DI

쿠키와개발 2024. 4. 24. 15:12

*구선생님과 김영한님의 스프링 핵심원리 기본편을 바탕으로 이해한 내용을 정리한 것입니다! 틀린 내용이 있다면 언제든 댓글로 알려주시면 감사합니다!!!*

IoC(Inversion of Control)란?

Spring을 접하면 가장 먼저 접하는 단어라고 생각한다. 물론 처음 보는 사람은 절대로 한번에 이해하기 힘든 내용이기도 하다.(난 그랬다) 우선 직역을 하면 제어의 역전이라는 의미이다. 무엇에 대한 제어를 역전했다는 것인지 솔직히 와닿지는 않는다. 결론부터 말하자면 객체의 생성, 생명주기의 관리까지 모든 객체에 대한 제어권이 뒤바뀐 것을 의미한다.

 

그럼 IoC가 없다면?

간단하다 제어의 역전의 반대라고 생각하면 된다! 객체의 생성, 생명주기 관리를 직접 관리하는 것이다.

아래 코드가 그 예시이다.

//주문서비스 구현객체
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository = new MemoryMemberRepository();

    ...
}

위 코드는 주문 서비스 구현 객체가 직접 회원 레포지토리 객체를 결정하고 생성을 하는 코드이다.

정상적으로 보이는 것 같지만 사실 객체지향의 원칙 중 OCP, DIP, SRP를 위반하는 코드이다.

1.OCP - 확장에는 열려있고 변경에는 닫혀 있어야 한다.

MemoryMemberRepository는 메모리에 회원 정보를 저장하는 것을 의미하는데 만약 DB에 회원 정보를 저장한다고 변경이 일어나면 new MemoryMemberRepository()가 아닌 new DbMemberRepository()로 코드의 변경이 일어난다. 즉 클아이언트에서 구현 객체에 대한 변경이 일어나는 것이다.

2.DIP - 클라이언트는 추상화에만 의존해야 한다.

언뜻 보기에는 추상화에 의존하는 것 같지만 결국 구현 객체의 생성 코드가 들어가면서 구현체에 의존하고 있다.

3.SRP - 하나의 책임 즉 실행에만 충실해야 한다.

자신의 역할과 책임에만 집중해야 하는 것을 의미한다. 하지만 주문 서비스 객체는 주문뿐만 아니라 자신이 어떤 회원 저장소 객체를 생성하고 어떤 회원 저장소 객체를 쓸지 직접 정하기까지 하고 있다. 이러한 역할을 하는 부분에서 SRP에 위배된 것이다.  
쉽게 생각하면 로미오와 줄리엣이라는 연극을 진행한다고 할 때 로미오를 맡은 배우가 줄리엣 배우를 누구로 할지 결정하고 연극을 준비한다고 생각하면 이해가 쉽다. 배우는 연극에만 집중하면 그만이지 상대 배우까지 결정해야 하는가를 생각해보자.  


결국 저 원칙들을 위배하지 않고 어떻게 해결할 수 있는지 생각해 보면 간단하다. 연극을 진행할 때는 진행 담당자가 따로 있다. 이처럼 프로그램에서도 객체의 생성과 구현객체에 대한 결정을 담당하는 외부 클래스를 만들어 역할을 담당하게 하면 된다. 여기서는 AppConfig라는 객체가 해당 역할을 담당하며 아래 코드가 그 예시이다.

//연극의 진행자라고 생각하자
public class AppConfig {
    public OrderServiceImpl orderService() {
        return new OrderServiceImpl(memerRepository());
    }

    public MemerRepository memerRepository() {
        return new MemoryMeberRepository();
    }

    ...
}


//주문서비스 구현객체
public class OrderServiceImpl implements OrderService {

    private final MemberRepository;

    public OrderServiceImpl(MemerRepository memerRepository) {
        this.memerRepository = memerRepository;
    }
    ...
}

//사용
main() {
    AppConfig appConfig = new AppConfig();
    OrderService orderService = appConfig.orderService();
}

이때 코드를 보면 알 수 있듯이 각 객체간의 의존성을 클라이언트 객체(OrderServiceImpl과 같은)가 정하는 것이 아닌 AppConfig라는 외부 객체가 결정하고 제어한다. 이러한 맥락을 IoC(제어의 역전)라고 한다.

그리고 AppConfig에서 객체의 생성과 동시에 필요한 의존 객체를 주입해준다. 이를 의존성 주입 또는 의존 관계 주입 DI라고 한다.

정리하면 IoC는 객체지향 원칙을 지키기 위한 디자인 패턴 중 하나이며 이를 구현하기 위해 DI를 활용한다.


그럼 Spring은 왜 IoC & DI를 구현했을까??

이것을 설명하기 전에 위 내용들을 제대로 이해했다면 조금만 생각해도 답이 나오는 질문이다.

IoC가 없다면 객체지향 원칙을 위반하여 설계를 할 수 밖에 없고 결국 유지보수에 많은 어려움을 겪게 될 것이다. 만약 OrderService와 같이 new MemoryMemberRepository()를 하는 클라이언트 객체가 1000개가 있다고 생각하MemoryMemberRepository에서 DbMemberRepository로 변경할 때 1000개의 코드를 수정해야 한다.

하지만 IoC & DI의 개념을 활용한다면 개발자는 객체간 의존 설정만 해놓고 비즈니스 로직 수행에만 집중하여 구현하면 된다. 또한 변경이 일어나더라도 객체의 생성과 생명주기의 관리는 개발자가 아닌 프레임워크와 같은 외부에서 진행하기 때문에 유지보수 측면에서도 편리하다. 이러한 이유로 Spring은 IoC & DI를 구현하여 IoC Container를 제공하는 것이다.