Swift/개념 & 응용

[Swift] 의존성 주입 DI(Dependency Injection)와 IoC(Inversion Of Control)

유정주 2023. 4. 23. 10:57
반응형

 

서론

SOLID 원칙 with Swift의 D를 담당하고 있는 의존성은 중요한 개념입니다.

객체 지향에서 객체의 의존성은 빼놓을 수 없는 개념이고,

결합도와 응집도에서 다룬 낮은 결합도의 핵심 방법이기 때문입니다.

 

의존성과 의존성 주입의 개념 자체는 간단하기 때문에 짧게 다뤄보겠습니다.

Protocol을 이용한 ViewModel 의존성 주입 등 지난 여러 포스팅들에서 간접적으로 소개한 적이 있기 때문에

함께 읽어주시면 감사하겠습니다 OvO b

 

 

의존성

의존성이란 하나의 객체에서 다른 객체를 참조하는 것을 말합니다.

class AClass {
    var number: Int = 0
}

class BClass {
    var numberClass: AClass = AClass()
}

let bClass = BClass()
print(bClass.numberClass.number)

위 예시는 BClass에서 AClass에 의존하는 형태입니다.

 

의존도가 높으면 코드 수정에 사이드 이펙트가 커지고, 테스트가 힘들어집니다.

위 코드에서도 AClass의 number가 바뀌면 BClass의 결과에 영향을 줍니다.

또 BClass는 AClass가 없으면 테스트할 수 없습니다.

 

이런 부작용을 줄이기 위해 의존성 주입 개념이 나옵니다.

 

의존성 주입

의존성 주입이란 객체 외부에서 의존성을 주입하는 것입니다.

class AClass {
    var number: Int
    
    init(number: Int) {
        self.number = number
    }
}

class BClass {
    var numberClass: AClass
    
    init(number: Int) {
        self.numberClass = AClass(number: number)
    }
}

let bClass = BClass(number: 1)
print(bClass.numberClass.number)

AClass와 BClass의 변경점이 보이시나요?

AClass는 BClass 내부에서 number를 주입하고 있고,

BClass도 외부에서 number를 주입하고 있습니다.

 

위 방법처럼 init을 이용할 수도 있고, 파라미터, 메서드로 변경할 수도 있습니다.

class BClass {
    var numberClass: AClass?
}

let bClass = BClass()
bClass.numberClass = AClass(number: 1)
print(bClass.numberClass.number)

 

여기에서 끝낸다면 의존성 주입이라고 하기엔 아쉽습니다.

의존성 분리가 들어가야 합니다.

 

의존성 분리

의존성 분리란 DIP를 지키면서 의존 관계를 분리합니다.

 

의존관계 역전 원칙은 구체 개념이 아닌 추상 개념에 의존해야 한다는 원칙입니다.
또한 추상 개념은 구체 개념에 의존하면 안 되고, 구체 개념이 추상 개념에 의존해야 합니다.

 

모범 예시는 여기에서 볼 수 있고, 여기에선 위 코드에 적용해보겠습니다.

 

먼저 위 개념에서 말한 추상 개념을 구현하겠습니다.

Swift는 프로토콜로 구현 가능합니다.

protocol Numberable: AnyObject {
    var number: Int { get set }
}

Numberable이라는 프로토콜을 정의하고 AClass가 가지고 있던 number를 프로토콜 속성으로 추가했습니다.

 

이제 AClass에서 Numberable을 채택합니다.

class AClass: Numberable {
    var number: Int = 1
}

AClass는 Numberable의 number를 구현했습니다.

 

class BClass {
    var numberClass: Numberable
    
    init(numberClass: Numberable) {
        self.numberClass = numberClass
    }
}

let aClass = AClass()

let bClass = BClass(numberClass: aClass)
print(bClass.numberClass.number)

이제 BClass는 AClass를 의존하는게 아니라 Numberable이라는 추상 개념을 의존합니다.

 

Inversion Of Control

위 예시는 추상 개념에 의존하면서 제어권이 인터페이스로 변경되었습니다.

이를 IoC라고 합니다.

IoC는 인터페이스가 제어권을 갖는 형태입니다.
세부사항이 인터페이스를 제어하던 기존 형태와는 다르게
인터페이스가 제어권을 가지면서 의존 관계의 방향이 반대로 바뀝니다.

인터페이스가 공개하는 범위만 접근이 가능해진다는 장점이 있습니다.

 

이런 제어권들을 컨테이너 한 곳에 모아서 관리할 수 있는데요.

iOS에서는 Swinject이라는 라이브러리로 가능합니다.

이에 대해서는 따로 포스팅을 하겠습니다.

 

감사합니다.

 

참고

https://sihyungyou.github.io/iOS-dependency-injection/

https://medium.com/@jang.wangsu/di-dependency-injection-이란-1b12fdefec4f

https://mangkyu.tistory.com/226

https://velog.io/@server30sopt/DIP


아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

반응형