DI(Dependency Injection)에 대해 알아보자

JoJo Develog·2020년 4월 16일
8

Android

목록 보기
13/19
post-thumbnail

요즘들어 삘을 받아서 폭풍 포스팅 중 입니다.🤗

이번 포스팅은 DI(Dependency Injection) - 의존성 주입에 대한 내용인데요.

먼저 DI가 무엇인지 알아보고 안드로이드에서 많이 쓰이는 DI 라이브러리들을 간단히 무엇이 있는지 알아보도록 하겠습니다.

1. DI...의존성...주입??? 이걸 왜 하나요??🤔

자 먼저 의존성이란것을 알아보도록 합시다.

(1) 의존성이란?
가령 직장인이 출근을 할때 직장이 멀다면 교통수단에 의존해서 출근을 해야합니다.
프로그래밍에서도 의존은 뜻이 같습니다. 의존성은 함수에 필요한 클래스 또는 참조변수나 객체에 의존하는 것이라고 볼 수 있습니다.

(2) 주입이란?
그렇다면 위의 함수에 필요한 클래스 또는 참조변수나 객체 같은 것들은 어떻게 생성될까요?
직접 생성하거나 누군가 생성해준 것을 이용하는 방법이 있지만 이럴때 DI(의존성 주입)를 사용합니다. 보통은 내부에서 필요한 내용(객체)을 생성하여 참조/사용을 내부가 아니라 외부에서 객체를 생성해서 넣어주는 것을 주입한다고 합니다. 그렇기 때문에 개발자들이 객체를 생성하는 번거로움과 다양한 테스트 케이스를 고려하는 경우를 줄이고 변수 사용과 개발에 더욱이 집중할 수 있게 해주며 클래스간의 결합도(coupling)를 낮추어 의존성을 줄입니다.

의존성 주입이란 정리하면 이렇습니다.

  • 코드에서 두 모듈 간의 연결의 의미함.
  • 일반적으로 둘 중 하나가 다른 하나를 어떤 용도를 위해 사용함.
  • 객체지향언어에서는 두 클래스 간의 관계라고도 말함.
  • 클래스간의 의존성이 줄어들면 유지보수시 매우 편함.

자 그럼 장점 또한 정리해보겠습니다.

  • Unit Test가 용이해짐.
  • 코드의 재사용성을 높여준다.
  • 리팩토링이 수월함.
  • 객체 간의 의존성(종속성)을 줄이거나 없앨 수 있음.
  • 객체 간의 결합도(coupling)를 낮추면서 유연한 코드를 작성할 수 있음.
  • 보일러 플레이트 코드 감소.
  • 스코프를 이용한 객체 관리.

다만 단점도 있습니다.

  • 의존성 주입을 위한 선행작업이 필요함.
  • 코드를 추적하고 읽기가 어려워짐.

자신이 처음부터 개발했던 프로젝트라면 모르겠으나 중간에 투입이되어 소스를 파악(Reading)해야하는 상황이라면 소스를 처음보기 때문에 코드를 추적하고 읽기가 어렵습니다.
하지만 장점이 더 많고 일단 유지보수하기가 용이해지므로 단점은 넘어가도록 합시다😜

2. 의존성 주입 예시🧑🏻‍💻

동숲맨

저는 어릴때부터 게임을 엄청좋아해서 간단하게 게임에 빗대어 예를 들어보겠습니다.

가령 "동물의 숲"이라는 게임에서 쇠로된 도끼, 쇠로된 삽을 만드려면 철광석이 필요합니다.

각 도구별로 공통적으로 철광석이 1개씩 있어야 합니다.

코드로 표현하면 아래와 같습니다.

  // 철광석 클래스
  class IronNugget {
    ...
  }

  // Axe클래스 내부에서 IronNugget 클래스를 생성하여 사용
  class Axe {
      val ironNugget = IronNugget()
    ...
  }
  
  // Shovel 클래스 내부에서 IronNugget 클래스를 생성하여 사용
  class Shovel {
      val ironNugget = IronNugget()
    ...
  }

이렇게 되면 의존성이 생기게 되는데요 도끼(Axe)라는 클래스가 내부에서 철광석(IronNugget)이라는 클래스를 참조하는 경우 도끼 클래스 -> 철광석 클래스 형식으로 의존성을 갖는다고 말할 수 있습니다.
삽(Shovel) 클래스도 마찬가집니다.

근데 이렇게 의존성이 생기게 되면 철광석 클래스의 변화가 생기면 의존성을 갖는 도끼 클래스도 같이 변경을 해야하는 문제가 발생합니다.
만약 철광석 클래스가 바뀌면 철광석 클래스의 의존성을 갖는 모든 클래스가 변경이 되어야 합니다.
애플리케이션의 규모가 작아서 철광석 클래스에 의존성을 갖는 클래스가 한 두개라면 상관이 없겠지만 애플리케이션의 규모가 커지면서 점점 의존성을 갖는 클래스가 많아지면 유지보수시 개판5분전이 되기 시작합니다. 🔥🔥🔥🧑🏻‍💻신난다! 워라밸 끝!

만약 철광석 클래스에 의존성이 생긴다고 가정해본다면 아래와 같이 생성자를 추가하여 바뀌어야 합니다.

예시 코드로 확인해보겠습니다.

  // 바위 클래스
  class Rock {
    ...
  }
  
  // 철광석 클래스
  class IronNugget(val rock: Rock) { {
    ...
  }

  // Axe클래스 내부에서 IronNugget 클래스를 생성하여 사용
  class Axe {
      val ironNugget = IronNugget(Rock()) // <-- 변경됨
    ...
  }
  
  // Shovel 클래스 내부에서 IronNugget 클래스를 생성하여 사용
  class Shovel {
      val ironNugget = IronNugget(Rock()) // <-- 변경됨
    ...
  }

저는 클래스 생성에서 의존성을 주입을 했습니다.
즉, 철광석(IronNugget) 클래스에 바위(Rock) 클래스에 대한 의존성을 생성자를 이용하여 주입(Injection)했습니다.
의존하는 클래스를 직접 생성하는 것이 아닌, 주입해줌으로써 객체 간의 결합도를 줄이고 좀 더 유연한 코드를 작성할 수 있게됩니다.

이때 외부에서 객체를 관리하게 되는데 이를 IOC(inversion of Control, 제어의 역전)라 합니다.
IOC는 객체의 생성부터 생명주기(LifeCycle) 관리까지 컨테이너에 의해 제어 되는 것을 의미하며 의존성 주입(DI)은 객체간의 의존성을 자기 자신이 아닌 외부에서 주입받는 개념입니다.

"외부에서 제어 및 객체간의 의존성을 최소화 한다"
여기서 무엇인가 눈치 챈 분들이 계실텐데 그렇습니다.
아키텍쳐 패턴들(MVP, MVM, MVVM)에서 중점적으로 보이는 장점중의 하나입니다.
그래서 해당 모델들을 구현할때는 DI 라이브러리를 사용하는 것이 개발함에 있어 이제는 필수중의 필수가 되었습니다.

3. 하지만 일일히 DI(의존성 주입)하기엔 너무 귀찮다.. 라이브러리를 쓰자!!

자 우리가 누굽니까?
귀찮은것을 극도로 싫어하는 개발자들 입니다.
이를 간단히 해결하려면 어떻게 해야 할까요??
잘 만들어진 DI 라이브러리를 사용하는겁니다.

대표적인 안드로이드 DI 라이브러리는 다음과 같습니다.

  • Dagger2
    Dagger2는 자바와 안드로이드를 위해 만들어진 컴파일타임 의존성주입 프레임워크 입니다.
    원래는 Square라는 회사에서 주도적으로 개발하였으나(Dagger) 지금은 구글이 관리하고 있는중(Dagger2)입니다.
    여기서 말하는 Dagger는 모두 Square의 Dagger를 포크(Fork)하여 Google이 만든 Dagger2를 뜻합니다.
    Dagger2는 추적 가능한 보일러 플레이트 자바 코드를 컴파일 타임에 자동으로 생성하고 리플렉션 사용이 없기 때문에 많은 안드로이드 개발자들이 사용하고 있습니다.
  • Koin
    Koin은 코틀린을 위한 DI 라이브러리로 러닝커브(학습곡선)가 가파른 Dagger에 비해 상대적으로 낮은 러닝커브를 가지고 있고 순수 코틀린만으로 작성이 되어있으며 어노테이션(@) 프로세싱을 및 리플렉션을 사용하지 않기 때문에 상대적으로 더 가볍습니다.
    또한 아키텍처 중에 하나인 VM(ViewModel)을 이용하기 위해 별도의 라이브러리도 제공하고 있기 때문에 아주 쉽게 MVVM 패턴을 만들 수 있습니다. 하지만 아직 UI의 Controller별 Scope 관리 및 Compile 시간에 오류를 확인하는 등의 기능은 Dagger에 비해서 많이 좀 아쉽습니다. 물론 유닛테스트 단계를 통해서 Runtime시 에러가 발생하는 것을 방지할 수는 있습니다.
    계속 발전하는 신규 라이브러리라 더욱 기대가 되지만 현재로서는 2019 Android Dev Summit에서 구글이 Dagger2를 권장을 했고 Dagger2가 이미 업계에서 굳건히 버티고 있어 기업에서 사용하는 경우를 찾기 힘드네요.
    더 자세한 내용은 Koin 오피셜 사이트에서 확인 부탁드립니다.

4. 결론

DI 또는 DI 라이브러리를 사용하면 관심사를 분리 했으므로 객체를 생성하는 부분과 아닌 부분이 나뉘어지게 되며 클래스간의 결합도가 크게 줄어들어 의존성도 줄어들게 되니 리팩토링 및 유닛테스트가 쉬워집니다. 또한 주입되는 모듈 코드를 재사용할 수 있으니 보일러 플레이트 코드도 줄어들게 됩니다.
그래서 이래저래 장점이 더 많다보니 요즘은 앱 개발시 필수라고 할 수 있겠습니다.

참고 내용
https://ko.wikipedia.org/wiki/의존성_주입
https://medium.com/@jang.wangsu/di-dependency-injection-이란-1b12fdefec4f
https://velog.io/@wlsdud2194/what-is-di
https://www.charlezz.com/?p=1259
https://jungwoon.github.io/android/2019/08/21/Koin/

도움이 많이 된 도서
아키텍처를 알아야 앱 개발이 보인다, 옥수환 저 | 비제이퍼블릭(BJ퍼블릭)

profile
12년도부터 대학에서 안드로이드 모바일을 전공으로 시작하여 "진짜 개발자"를 꿈꾸며 개발공부를 시작했습니다. SW 개발이 재밌어서 여러 방면으로 스터디하며 현재는 새로운 환경 및 새로운 트렌드에도 유연하게 적응을 잘하는 개발자로 성장해 나가는 중입니다. 글 내용에 대한 피드백은 언제나 환영입니다!

2개의 댓글

comment-user-thumbnail
2021년 6월 24일

비유도 너무 좋고 좋은 글 감사드립니다!! 저에게는 koin도 가파른 러닝커프인데

좋은 글 덕분에 도움 많이 받았네요 ㅎㅎ

1개의 답글