본문 바로가기

iOS

[iOS] Dependency Injection, (IoC, DI, DIP)

2020/08/14 - [iOS] - [iOS] 객체지향과 SOLID 원칙 - Swift

 

[iOS] 객체지향과 SOLID 원칙 - Swift

모든 객체지향 프로그래밍에서 사용되는 원칙인 SOLID를 공부해 볼 것입니다. 열심히 읽고, 열심히 공부하고 코드에 100% 반영은 어렵겠지만, 점차 코드에 객체지향맛이 나도록 코딩을 해 보는것��

lidium.tistory.com

저번에는 객체지향의 SOLID 원칙을 공부했습니다.

사실 저번 글은 오늘 글을 위해서 썼던 것입니다! Dependency에 대한 이야기를 쓰고자 했는데,

조금 더 근본적인 이야기를 쓰고자 저번 포스팅을 썼습니다. 

 

이번에 의존성 관련해서 공부하면서, 작성된 Swift 관련된 Dependency 자료가 드물고 전부 영어 번역이라 이해하기 어려웠는데요,

나름대로 공부해서 정리해 보았습니다. 오개념 지적은 언제든 환영입니다!

# dependency

 

 

다음과 같은 그림은 이제, A 객체가 B 객체를 의존한다 는 그림 표현이 됩니다. 의존한다라고 하는 것은,

A가 B를 멤버변수 및 로컬변수로 가지고 있거나 ( let foo = BInstance() ) B의 메소드를 호출하는 경우,

즉 B에 관련된 어떠한 연관된 일을 하는 경우 의존한다라고 표현할 수 있습니다.

 

A 객체와 B 객체는 여러 형태로 이해할 수 있는데, 서버통신 상황에서 class와 Service 관계로 생각할 수도 있습니다.

 

그럼, 의존하는것은 무엇이 문제일까요?

  • A 객체는 B 객체를 강하게 의존하고 있으므로, A객체가 아무것도 변하지 않았음에도 예상치 못한 동작을 하거나 컴파일이 안되는 일이 있을 수 있습니다. 
  • A의 재사용성이 낮아지거나 사라집니다. 만약 A를 다른 모듈 또는 다른 클래스에 재사용하고 싶은 경우, B도 끌어와야 하는 상황이 됩니다.

결국, 끝단의 서비스 말고는 재사용할 수 있는 코드가 없음을 나타냅니다. 위에서 말한 의존성 문제를 해결할 수 있는 방법인

DIP를 도입해 보겠습니다.

# DIP (Dependency Inversion Principle)

SOLID의 D 친구입니다. DIP는 class들 간에 의존성 부패(Dependency Rot)을 제거하기 위한 일반적인 디자인 방법입니다.

그림 1을 개선하면 다음과 같이 개선할 수 있습니다.

 

 

  • A 객체와 B객체 모두 추상화된 것에 의존해야 합니다. 
    A -> B로의 의존성을 끊고, A 객체와 B 객체를 추상화된 것을 의존하도록 합니다.

코드로 이해를 해 보겠습니다.

 

위 코드는 다음과 같은 그림으로 설명할 수 있습니다.

 

 

아까 문제가 있었던 그 예제와 같습니다. 재사용이 가능한 것은 FilesystemManager 뿐입니다. Handler를 재사용할 수는 없습니다.

현재 상태에서, Database를 다루는 로직이 필요하다면,  DatabaseHandler를 추가적으로 구현해야 할 것입니다.

 

DIP를 이용해 개선해보면,

다음과 같은 구조가 됩니다.

 

 

이제 Handler는 FileSystemManager와 직접적인 영향이 없습니다. 

그림에서 중요한 것이 몇가지 있는데, 

 

  • Handler와 FilesystemManager, DatabaseManager와의 Dependency 분리
  • 계층 구조

입니다.

 

 

위와 같은 그림임을 잘 알아두어야겠습니다. 각각을 Layer로 분리한 것입니다. 

Layer 개념은 익숙치 않지만, 모두 다 평행선에서 의존성이 발생한다는 것으로 생각하면 안된다는 것입니다.

 

더 나아가, 의존성을 완벽히 분리하기 위해 IoC Container 개념을 적용할 필요가 있습니다.

# IoC (Inversion of Control), 역전된 제어 구조

1. Hollywood Principle이다. 

> 전화하지 마세요. 우리가 연락할게요 (Don't call us, we'll call you.)

2. Swift에서는 인터페이스를 구현해서 넘기는 방식 / 프로토콜을 만드는 방식

3. 넘긴 객체가 호출되거나 사용되는지 아닌지는 고려되지 않는다 ( 그냥 넘김 )

 

이제 DatabaseManager는 다음과 같이 사용할 것입니다.

이제 Client는 Storage 인스턴스를 생성해서 FileSystemManager에 주입해주고 있습니다. 

FileSystemManager는 Storage의 생성을 전혀 모르는 상태이지만, 조금 어색합니다. Client가 Storage의 생성과 FileSystemManager의 관계 설정을 해주고 있는 상황입니다.

어쨌든  IoC에 의하면, Client는 Storage를 알 필요가 전혀 없는 상황이기 때문입니다.

 

부연설명을 하자면, 어쨌든 우리의 코드의 로직들은

Main에서 시작해서 그다음 객체, 각각의 객체는 그 다음 객체 등으로 다음에 사용할 것을 결정하고 호출하는 로직인데,

이는 Client 단에서 모든것을 제어하고 결정하는 구조와 같습니다. 

IoC란, 이러한 제어의 흐름을 뒤집는(Inversion)하는 것을 의미하게 됩니다.

다이어그램은 다음과 같게 됩니다.

 

 

위와 같은 그림에서, IoC Container에게 모든 관계설정에 관한 책임을 위임을 하게 됩니다.

컴파일 타임의 static한 class Dependency >>>>>>>>>> 런타임의 dynamic한 object Dependency로 변경되게 됩니다.

 

 

열심히 공부해 보았는데, DI에 관련한 내용은 아직 남아있습니다. > *(Dependency Injection), 세가지 방법들..

실전에서, 코드에 적용할 수 있는 날이 오면 좋겠습니다.

위 설명한 부분들은 Class 단위로 지키면 좋겠지만, 그렇게 되면 코드가 전혀 간결하지 않을것 같습니다.

명확한 계층 설계가 중요해 보입니다.

 

각 Layer 계층 단은 정확히 정의되어야 하며, 레이어 간의 약한 결합과 IoC Container를 통한 통제가 오늘의 핵심이라고 생각합니다.

감사합니다!

 

 

ref: 

https://develogs.tistory.com/19#comment11962949

https://wotjd.github.io/2019/05/architecture-ioc-%EA%B4%80%EB%A0%A8-%EB%81%84%EC%A0%81-ioc-di-dip-ioc-container/#ioc-di-dip-ioc-container-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0

https://architecture101.blog/2008/12/07/dependency_managment/

 

 

 

 

 

'iOS' 카테고리의 다른 글

[iOS] Moya에 대해서 공부해보아요  (0) 2020.09.08
[iOS] Swift Attributes를 배워보자  (0) 2020.09.04
[iOS] 객체지향과 SOLID 원칙 - Swift  (0) 2020.08.14
[iOS] throttle, debounce  (0) 2020.07.31
[iOS] Custom Popup, AlertView  (0) 2020.06.20