본문으로 건너뛰기

D.I (Dependency Injection) 란?

의존성

어떤 객체 또는 시스템이 자신의 기능을 수행하기 위해 외부의 다른 객체, 데이터, 상태, 실행 흐름,
혹은 이러한 환경에 기대고 있는 모든 관계를 의미합니다.

보다 구체적으로, 의존성은 다음과 같은 모든 형태를 포함합니다.

  • 특정 클래스인터페이스에 대한 직접 참조 (EX : IHealth, IMove 등)

  • 함수 호출을 통해 발생하는 행위 의존 (EX : Factory Pattern)

  • 외부에서 전달되는 데이터나 설정값에 대한 데이터 의존

  • 실행 순서, 초기화 타이밍 등에 영향을 받는 생명 주기 의존

  • 엔진, 프레임워크, 플랫폼에 묶이는 환경 의존

  • 전역 상태 (Singleton, Static 등)에 기대는 암묵적 의존

  • 런타임 탐색 (GetComponent, Find 등)에 의해 결정되는 동적 의존

즉, 내가 아닌 다른 무언가가 바뀌었을 때. 본인의 동작이나 결과가 영향을 받으면, 그것은 의존 관계 입니다.

굉장히 헌학적이고, 지루한 이야기지만 Unity 개발자를 업으로 삼고 계신다면, 반드시 알아야 할 내용입니다.


Unity에서 의존성이 특히 문제가 되는 이유

Unity에서 의존성이 문제가 되는 이유는, 의존성이 일련의 로직을 통제하는 것이 아닌,
의존성이 생성되고 연결되는 방식이 엔진의 생명주기와 런타임 구조에 강하게 종속되어 있기 때문입니다.

UnityMonoBehaviour 기반의 프레임 주도형 생명 주기를 사용하며, 객체의 생성과 실행 타이밍이 개발자의 코드가 아닌,
엔진 (Native C++) 에 의해 제어됩니다.

이로 인해 Unity에서의 의존성은 다음과 같은 특성을 가지게 됩니다.

  • 생성 시점 제어 불가

    • 객체가 언제 생성되고 초기화되는지 명확하게 통제하기 어렵습니다.
    • Awake, Start, OnEnable 간의 순서 의존이 발생하며, 이는 암묵적인 의존성으로 이어집니다.
  • 의존성 획득의 런타임화

    • GetComponent, Find, Singleton 접근 등 실행 중 의존성을 탐색하거나 가져오는 방식이 일반화됩니다.
    • 이는 성능 비용뿐 아니라, 의존 관계를 코드 상에서 명확히 드러내지 못하게 만듭니다.
  • 엔진 객체에 대한 강한 결합

    • 대부분의 객체가 MonoBehaviour를 상속하거나 Unity 타입에 직접 의존합니다.
    • 결과적으로 순수 로직과 엔진 로직이 분리되지 않고, 테스트 및 재사용이 어려워집니다.
  • 분산된 생명주기

    • 각 객체가 개별적으로 Update 등을 통해 동작하면서, 시스템 전체 흐름이 여러 객체에 분산됩니다.
    • 이로 인해 "누가 언제 무엇을 호출하는가"가 명확하지 않아 의존 관계 추적이 어려워집니다.
  • 암묵적이고 숨겨진 의존성 증가

    • 인스펙터 할당, 싱글톤, 정적 변수 등을 통해 외부에서 주입되는 값들이 증가합니다.
    • 이러한 의존성은 코드만으로는 파악이 어려워 유지보수 비용을 증가시킵니다.

이렇듯, Unity의 기본 구조에서는,
의존성이 명시적으로 설계되는 것이 아니라, 런타임과 생명 주기 흐름 속에서 암묵적으로 형성되는 경향을 가지게 됩니다.

이러한 특성은 객체 단위에서는 UE 엔진과 달리 빠른 개발을 가능하게 하지만,
시스템 규모가 커질수록 관리의 난이도가 기하급수적으로 어려워지며, 구조적 복잡도가 급격히 증가하게 됩니다.


그래서..

즉, Unity기본 구조객체 단위로는 강력하지만, 시스템 단위에서는 제어가 어렵다 라는 결론이 나옵니다.

그리고 이 문제의 본질은 단순히 코드 스타일이나 패턴의 문제가 아니라,
의존성이 언제, 어디서, 어떻게 생성되고 연결되는지개발자가 통제할 수 없다는 점에 있습니다.

따라서 중요한 것은 의존성을 제거하는 것이 아니라,
의존성이 형성되는 시점방식을 명시적으로 공개하고, 개발자가 제어 가능한 구조전환하는 것입니다.

이러한 문제를 해결하기 위한 대표적인 방법이 바로 D.I. Dependency Injection 입니다.


해결책. D.I 란?

D.I (Dependency Injection) 이란, 객체가 필요로 하는 의존성을 스스로 생성하거나 탐색하지 않고,
외부에서 전달받도록 구조를 설계하는 방식을 의미합니다.

이는 단순한 코드 스타일이 아니라, 의존성의 생성과 사용에 대한 책임을 분리하는 구조적 접근 방식입니다.


Provider / Consumer 구조

이러한 구조를 이해하기 위해, 의존성은 크게 두 가지 역할로 나누어 볼 수 있습니다.

  • Provider (공급자)

    • 의존성을 생성하고 제공하는 주체
    • 객체의 생성 시점과 생명주기를 관리
  • Consumer (소비자)

    • 의존성을 사용하는 주체
    • 필요한 기능만 정의하고, 구현에는 관여하지 않음

이 구조에서 중요한 점은 다음과 같습니다.

Consumer는 "무엇이 필요한지"만 알고,
Provider는 "어떻게 만들고 언제 줄지"를 담당합니다.


왜 이 구조가 중요할까?

Unity의 기본 구조에서는 다음과 같은 코드가 자연스럽게 발생합니다.

  • GetComponent

  • Find

  • Singleton Static Instance

이 방식은 모두 Consumer가 Provider의 역할까지 동시에 수행하는 구조입니다.
즉, 객체가 자신이 무엇을 사용할지 결정하고 그것을 직접 찾아오거나 생성까지 수행하게 됩니다.

이로 인해 다음과 같은 문제가 발생합니다.

  • 의존 관계가 코드에 명시되지 않음

  • 생성 시점이 런타임에 분산됨

  • 전역 상태(Singleton)에 대한 의존이 증가

  • 구조가 커질수록 결합도가 급격히 상승


D.I가 해결하는 것

D.I는 이 문제를 다음과 같이 해결합니다.

  • Provider가 의존성을 생성

  • Consumer는 이를 전달받아 사용

이로 인해:

  • 의존 관계가 명확하게 드러나고

  • 생성과 생명주기가 통제 가능해지며

  • Singleton과 같은 고정 인스턴스 남용을 구조적으로 방지할 수 있습니다


정리

D.I는 단순히 의존성을 "주입"하는 기술이 아니라,

Provider와 Consumer의 역할을 분리하여, 의존성을 통제 가능한 구조로 만드는 설계 방식입니다.