Spring/Spring

[Spring] IoC, DIP, DI란?

JoonYong 2024. 3. 5. 00:00

 

 

1. 의존성(Dependency)이란?

의존성(dependency)이란 한 객체, 함수, 모듈 또는 시스템이 다른 것 없이는 제대로 작동할 수 없는 상황을 의미합니다. 예를 들어, Store 클래스Pencil 클래스의 메서드를 사용하고 있다면, Store 클래스Pencil 클래스에 의존하고 있습니다. 즉, Pencil 클래스 의 변경이 Store 클래스에 영향을 미치게 됩니다.


IOC, DIP, DI는 모두 객체 간 의존성을 관리하는 데 사용되는 개념이지만, 서로 다른 역할과 특징을 가지고 있습니다.

 


2. IoC (Inversion of Control) : 제어의 역전

 

제어의 역전은 객체 생성 및 의존성 주입을 객체 자체가 아닌 외부에서 수행하는 방식입니다.

프로그램의 흐름을 개발자가 아닌 프레임워크 혹은 컨테이너가 관리하는 설계 원칙입니다. 이를 통해 개발자는 비즈니스 로직에만 집중할 수 있으며, 객체의 생명 주기와 설정, 의존성 등은 프레임워크가 관리합니다. 이로 인해 코드의 모듈성과 유연성이 향상됩니다.

 

IOC는 할리우드 법칙이라고도 불립니다.
Hollywood Principle
할리우드 법칙

“Don’t call us, we’ll call you”
우리한테 연락하지 마세요. 우리가 연락할게요.

 

1960년대 미국에서 면접관들이 쓰기 시작한 말인데, 나중에 극장에서 배우들의 오디션을 보고 거절할 때 사용합니다.

  • 배우 : 구현 모듈
  • 면접관 : 프레임워크

 

IOC를 구현하기 위한 방법

 

IOC를 구현하기 위한 방법에는 위 사진처럼 다양한 방법들이 있습니다.

그중 Dependency Injection(의존성주입)에 대한 설명은 Index 3번 글에서 하겠습니다.


3. DIP (Dependency Inversion Principle) : 의존 역전 원칙

 

 

 

의존성 역전 원칙 고수준 모듈이 저수준 모듈에 의존하지 않도록 설계하는 원칙입니다. 이 원칙에 따르면, 모든 모듈은 추상화에 의존해야 하며, 구체화에 의존해서는 안 됩니다. 이를 통해 모듈 간의 결합도를 낮추고, 코드의 재사용성을 높일 수 있습니다.

 

 

IoC와 DIP는 같은 목적을 갖고있는 원칙이다.

  • IOC가 컨테이너를 사용하여 객체 생성과 의존 관계 설정을 외부에서 관리함으로써 DIP를 실현하는 데 필요한 환경을 제공합니다. 
  • 클래스간 결합을 느슨히 하기 위함이라는 같은 목적을 갖고있다.
  • 애플리케이션을 지속가능하고 확장성 있게 하기 위함
  • IoC와 DIP는 함께 사용되길 권장

 

 

SOLID 원칙 중 하나입니다.

이전에 작성한 "OOP 5대 원칙 - SOLID"에 대한 내용입니다.

https://achieve-dev.tistory.com/25

 

[OOP] C6. SOLID: 효율적인 객체지향 설계를 위한 5대 원칙

0. 객체 지향 설계 5원칙 SOLID SOLID는 객체 지향 설계 5원칙을 앞 글자를 따서 만든 말이다. 소프트웨어의 결합도는 낮추고, 응집도는 높이는 고전 원칙을 객체 지향의 관점에서 재정립한 것이라고

achieve-dev.tistory.com


4. DI (Dependency Injection) : 의존성 주입 - IoC를 달성하는 디자인패턴 중 하나

 

의존성 주입은 객체 생성 시 필요한 의존성을 외부에서 주입하는 방식입니다.

객체가 자신이 의존하는 객체를 직접 생성하지 않고, 외부에서 주입받도록 설계하는 것입니다.

이를 통해 객체 간의 결합도를 더욱 낮출 수 있으며, 코드의 테스트 용이성을 높일 수 있습니다.

 

 

이전에 DI에 대하여 설명한 내용입니다.

https://achieve-dev.tistory.com/33

 

[Spring] 의존성 주입(Dependency Injection, DI) 이해하기

1. 의존성이란? DI에서 의존성이란 한 객체가 다른 객체의 기능에 의존하는 관계를 의미합니다. 예를 들어, 자동차(Car) 객체가 엔진(Engine) 객체의 기능을 사용한다면, 자동차 객체는 엔진 객체에

achieve-dev.tistory.com

 

4-1) 의존성 주입 방법

1. 생성자 주입

  • 생성자 호출시 외부로부터 의존성을 받는 방식

2. Setter 주입

  • 의존성을 입력받는 Setter 메서드를 만들고 이 메서드를 호출하여 의존성을 주입합니다.

3. Interface 주입

  • 의존성을 주입하는 메서드를 포함한 인터페이스를 작성하고 이 인터페이스를 구현하도록 함으로써 실행시에 이를통해 의존성을 주입합니다. 
  • Setter주입처럼 메서드를 외부에서 호출해줘야 하는 것은 비슷하지만 의존성 주입 메서드를 빠뜨릴 수 있는 Setter와 다르게 오버라이드를 통해 메서드 구현을 강제할 수 있다는 차이가 있습니다.

 

4-2) 의존성 분리

  • DIP를 이용해 의존 관계를 분리시킵니다.
  • 상위계층이 하위계층에 의존하는 상황을 Interface를 이용해 반전 시켜 하위계층의 구현으로 독립시킨다.

 


5. Spring DI - 자동주입

 

스프링 빈으로 등록된 객체에 스프링이 자동으로 생성해준다. 이때 필요한 의존성도 주입해준다.

 

@Autowired 어노테이션을 붙이게 되면 Spring이 자동으로 적절한 의존성을 주입해주게 됩니다.

어노테이션

 

5-1) @Autowired를 통해 의존성을 주입받는 방법 3가지 

1) 필드 주입

예시) 

@Controller
@Slf4j // 로깅 기능을 사용할 수 있는 어노테이션
public class ArticleController {

    // 레파지토리를 주입받음
    @Autowired // 스프링부트가 미리 생성해놓은 객체를 가져다가 자동 연결!(DI)
    private ArticleRepository articleRepository; // 데이터를 가져오는 역할

    // 폼 페이지 보여주기
    @GetMapping("/articles/new")
    public String newArticleForm() {
        return "articles/new";
    }
}

 

주입 받고자 하는 필드위에 @Autowired 어노테이션을 붙여주면 스프링은 의존성을 직접 주입해줍니다.

하지만 필드 주입을 사용하게 되면 테스트 등의 이유로 자동이 아닌 수동 의존성 주입을 하고 싶어도 생성자도 Setter도 없으므로 직접 의존성을 넣어 줄 수가 없습니다.

때문에 의존성이 프레임워크에 강하게 종속된다는 문제점이 있습니다.

 

2) Setter 주입

@Controller
@Slf4j // 로깅 기능을 사용할 수 있는 어노테이션
public class ArticleController {

    private ArticleRepository articleRepository; // 데이터를 가져오는 역할

    @Autowired // 스프링부트가 미리 생성해놓은 객체를 가져다가 자동 연결!(DI)
    public void setArticleRepository(ArticleRepository articleRepository) {
        this.articleRepository = articleRepository;
    }

    // 폼 페이지 보여주기
    @GetMapping("/articles/new")
    public String newArticleForm() {
        return "articles/new";
    }
}

 

setter 메서드에 @Autowired 어노테이션을 붙여주면 Spring이 setter를 사용해서 자동으로 의존성을 주입해줍니다.

 

3) 생성자 주입

@Controller
@Slf4j // 로깅 기능을 사용할 수 있는 어노테이션
public class ArticleController {

    // 레파지토리를 주입받음
    private ArticleRepository articleRepository; // 데이터를 가져오는 역할

    public ArticleController(ArticleRepository articleRepository) {
        this.articleRepository = articleRepository;
    }
    
    // 폼 페이지 보여주기
    @GetMapping("/articles/new")
    public String newArticleForm() {
        return "articles/new";
    }
}

 

생성자 주입을 사용하면 객체의 최초 생성 시점에 스프링이 의존성을 주입해줍니다.

이 방법은 스프링에서 공식적으로 추천하는 방법입니다!

 

 

용어 정의 목적 장점 단점
IOC (Inversion of Control) 제어의 역전 제어의 역전은 객체 생성 및 의존성 주입을 객체 자체가 아닌 외부에서 수행하는 방식입니다. * 코드의 결합도를 낮춥니다. * 유연성을 높입니다. * 테스트 용이성을 향상시킵니다.
* 구체적인 구현 방식에 대한 의존성이 남아 있습니다.
DIP (Dependency Inversion Principle) 의존성 역전 원칙 모든 모듈은 추상화에 의존해야 하며, 구체화에 의존해서는 안 됩니다.  * 객체 간의 결합도를 더욱 낮춥니다. * 변경 용이성을 향상시킵니다. * 확장성을 향상시킵니다.
* 설계가 더 복잡해질 수 있습니다.
DI (Dependency Injection) 의존성 주입 객체 생성 시 필요한 의존성을 외부에서 주입하는 방식입니다. * 객체 생성 및 의존성 관리를 컨테이너가 담당하여 코드를 간결하게 만듭니다. * 유연성을 높입니다. * 테스트 용이성을 향상시킵니다.
* DI 방법에 따라 단점이 발생할 수 있습니다. (예: 생성자 주입 - 생성자 인자 목록 길어짐, 순환 참조 문제)

 

 

 

 

[Reference]

https://velog.io/@betterfuture4/Spring-IoC-DI-DIP

https://www.youtube.com/watch?v=8lp_nHicYd4