본문 바로가기

개발/자바&스프링

의존성, 의존관계, 의존관계 주입 정리하기!

[의존성, 의존관계, 의존 관계 주입]

헷갈리는 의존성, 의존관계, 의존관계 주입 개념 정리를 해보겠습니다. 

 


의존성

- 다른 것에 의지하여 존재함.
- 어떤 프로그램이나 실체의 정의와 기능이 전적으로 특정한 하드웨어와 소프트웨어 또는 앞서 진행된 이벤트나 상황에 의존하거나 종속적인 상태.

 

의존성은 굉장히 포괄적인 단어다. 프로그래밍에 적용해 봐도 라이브러리 간 의존성, 객체 간 의존성, 패키지 간 의존성 등 하나의 요소가 다른 요소에 의존하거나 종속적인 상태라면 적용 가능하다.

어느 한 요소가 완전히 독립적이지 않고 다른 요소와 상호작용, 즉 어느 정도 영향을 주고받으면 의존성이 있다고 말할 수 있다.

의존 관계

OOP 로 범위를 좁혀서 의존 관계를 정의해 보자면 두 가지로 정의할 수 있다.

  1. 정적인 의존관계
  2. 동적인 의존관계

정적인 의존관계

프로그래밍에서 정적(static) 은 컴파일 시점을 의미한다.
정적인 의존관계는 클래스 사이에 의존 관계가 생긴 것을 의미한다.

 

Printer 클래스는 System 클래스의 out 정적 변수에 의존하고 있다. 
Printer 클래스가 컴파일 시점에 System 클래스 구체적인 정보를 알아야 out 필드를 사용할 수 있다. 

 

public class Printer {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

동적인 의존관계

프로그래밍에서 동적(dynamic) 은 runtime, 즉 프로세스가 실행 중인 상태를 의미한다.
동적인 의존관계는 실행중에 의존 관계를 다이내믹하게 결정하는 것이다.

 

A → B 의존 관계가 있을 때, 컴파일 시점에 A는 의존할 구체적인 대상 B에 대해 알지 못하고
런타임 시에 구체적인 B와 의존 관계를 맺는다.

 

동적인 의존관계는 코드로 이해해보자

 

class Porsche {
    GasTank gasTank;

    void fuel(GasPump gasPump){
        gasTank.up(gasPump.supply(20));
    }
}

interface GasPump {
    int supply(int liter);
}

class SOil implement GasPump {
    int supply(int liter){
         // 바가지로 주유한다
    }
}

class SK implement GasPump {
    int supply(int liter){
        // 주유 기계로 주유한다
    }
}

 

Porsche는 클래스 레벨에서는 어느 회사의 GasPump로 기름을 공급받을지 모른다.
런타임시에 SOil 이냐 SK 냐 의존 관계가 결정된다.

의존 관계는 `fuel(GasPump soil)` 메서드에서 보듯이, 의존 대상 객체의 참조값을 사용할 때 맺어진다.

이 참조값은 구체적인 의존 대상 객체를 가리킨다. 

 

Porsche는 SOil 참조값을 사용할 수도 있고 SK 참조값을 사용할 수도 있다.

즉 다형성을 가능하게 해주는 기술인 다이내믹 바인딩 덕분에 가능하다.

의존 관계 주입

동적인 의존 관계는 런타임시에 의존 대상 객체의 참조값을 사용할 때 결정된다고 했다.
의존 관계 주입은 바로 이 참조값을 전달하는 방법이다.

 

class Porsche {
    GasTank gasTank;

    void fuel(){
        Sk sk = new Sk();
        gasTank.up(sk.supply(20));
    }
}

 

위 코드는 의존 주체가 직접 의존 대상 객체의 참조값을 생성해서 사용하고 있다.

 

의존 관계 주입은 제 3자로 부터 참조값을 전달받는 것이다.

제 3자를 통해 의존관계 주입으로 변경해보자. 
주유소 클래스가 포르쉐에게 전달할 구체적인 GasPump 객체를 결정하고 전달하고 있다.

 

class 주유소 {

    public void static main(String[] args){

        Porsche porsche = new Porsche();
        Avante avente = new Avante();

        porsche.fuel(new SK());
        porsche.fuel(new SOil());

    }
}

class Porsche {

    GasTank gasTank;

    void fuel(GasPump gasPump){
        gasTank.up(gasPump.supply(20));
    }
}

 

이렇게 의존관계에 있는 의존 주체가 의존할 대상을 직접 결정하는 것이 아니라 제 3자가 결정해서 참조값을 주입하는 것을 의존관계 주입이라 한다.

 

제어의 역행 IoC

포르세가 어떤 주유기를 사용할지 스스로 선택하지 않고 주유소에 의해 제어당했다. 

제어의 역행은 제어 흐름 구조가 뒤바뀌는 것을 말한다.

 

제어의 역행의 장점은 객체가 자신의 관심사에만 집중할 수 있게 한다.

의존 대상의 구체적인 정보는 모르게 함으로써 의존 대상의 변경에는 닫혀있고 자신의 변경에는 열려 있다.

 

스프링 프레임워크에는 IoC 개념을 구현한 IoC 컨테이너 오브젝트가 있다.

IoC 컨테이너는 DI를 편하게 적용할 수 있도록 싱글톤 레지스트리나 다양한 구성정보 설정 메커니즘을 보유하고 있다.