DIP

안녕하세요 Jercy입니다. 오늘은 SOLID 원칙 중 마지막인 DIP(Dependency Inversion Principle)에 대해 알아보겠습니다.
로버트 C. 마틴은 다년간 개발하면서 코드에서 흔히 발생하는 문제를 발견했는데, 바로 상위 수준의 모듈이 하위 수준의 모듈에 의존하게 되는 것입니다. 즉, 정책이 구체적인 것에 의존하게 되는 경향성이 있다는 것이죠.
DIP는 이런 경우 모듈 간 의존관계를 어떻게 하면 끊고, 변경이 다른 코드에 미치는 영향을 최소화할 수 있는지에 대한 해답을 제시합니다.
의존관계가 존재할 수밖에 없다는 것을 인정하고, 코드가 무엇에 의존하느냐에 따라 다른 결과를 보여줍니다. 최악의 경우는 클래스들이 서로를 의존하는 경우인데, 안타깝게도 이런 코드를 자주 볼 수 있습니다.
이런 경우 클래스들은 독립적으로 사용될 수 없고, 항상 함께 움직여야 합니다. 예를 들어, 클래스 A를 사용하려면 클래스 B와 C도 함께 가져와야 하는 상황이 발생할 수 있습니다.
그렇다면 의존관계를 어떤 방향으로 두어야 할까요? 간단히 말해, 구체적인 부분에서 추상적인 방향으로 의존관계를 가져야 합니다. 추상적인 것이 구체적인 것에 의존해서는 안 됩니다.
iOS 개발에서 대표적인 DIP 예시로는 UITableView를 들 수 있습니다. UITableView의 동작은 추상적이고, 각 셀과 주변 동작들은 구체적입니다. UITableView의 코드는 개발자가 커스텀한 셀에 직접 의존하지 않고, 대신 추상적인 UITableViewCell을 사용하며, 구체적인 부분은 UITableViewDataSource와 UITableViewDelegate를 통해 위임됩니다.
또 다른 예로, UIViewController와 커스텀 뷰 컨트롤러 간의 관계가 있습니다. UIViewController는 뷰의 라이프사이클에 대한 정책을 결정하는 추상적인 클래스이고, 커스텀 뷰 컨트롤러는 구체적인 구현을 가집니다. 커스텀 뷰 컨트롤러는 UIViewController에 의존하지만, 반대로 UIViewController는 커스텀 뷰 컨트롤러에 의존하지 않습니다.
결국 DIP는 의존관계를 역전시켜, 상위 계층(추상적인 부분)이 하위 계층(구체적인 부분)에 의존하지 않도록 하는 원칙입니다. 이를 통해 추상화된 부분의 코드 재사용성이 증가하고, 모듈 간 결합도를 낮출 수 있습니다.
DIP를 설명할 때 "할리우드 원칙"이란 말이 자주 등장하는데, "우리가 당신을 부를 것이니, 당신이 우리를 부르지 마세요"라는 뜻입니다. UITableView의 예시처럼, 구체화된 쪽(셀)이 추상화된 쪽(테이블뷰)에 연락하지 말고, 추상화된 쪽이 필요할 때 구체화된 쪽을 부르는 것과 같은 이치입니다.
이상으로 Swift에서의 DIP에 대해 알아보았습니다. DIP를 활용하면 모듈 간 의존관계를 단방향으로 만들어 추상화된 부분의 재사용성을 높일 수 있습니다.
생각해볼 점:
1.
내가 현재 개발하고 있는 코드에서 DIP를 적용할 수 있는 부분이 있을까?
2.
DIP를 적용하면 어떤 이점이 있을까?
3.
DIP를 적용할 때 주의해야 할 점은 무엇일까?
저의 답변:
1.
DIP는 대부분의 코드에 적용할 수 있습니다. 특히 프레임워크나 라이브러리를 설계할 때 DIP를 적용하면 좋습니다. 사용자가 프레임워크의 추상적인 부분에 의존하게 하고, 구체적인 부분은 사용자가 직접 구현하도록 할 수 있습니다.
2.
DIP를 적용하면 모듈 간 결합도를 낮출 수 있어 변경에 유연하게 대처할 수 있습니다. 또한 추상화된 부분의 재사용성이 높아지므로 코드 중복을 줄일 수 있습니다.
3.
DIP를 적용할 때는 추상화 수준을 적절히 설정하는 것이 중요합니다. 지나치게 추상화하면 오히려 코드가 복잡해지고 이해하기 어려워질 수 있습니다. 따라서 적절한 수준의 추상화를 찾는 것이 중요합니다. 또한 DIP를 적용한다고 해서 무조건 좋은 것은 아닙니다. 상황에 맞게 적절히 사용해야 합니다.