티스토리 뷰

반응형

[1] 서론

업무로 모듈화를 진행하고 있는데, cyclic dependency가 생각보다 많았다,,,

폴더 구조일 때는 편하게 이곳저곳 다 참조하고 있던 코드들이 모듈 구조로 바뀌니 

그제야 아픈 곳을 드러내기 시작했다.. 

 

22장 > 클린아키텍쳐 에 나오는

이 내용을 알고 있었으나.. 

 

 

소스 코드 의존성은 반드시 안쪽으로, 고수준의 정책을 향해야 한다.
내부의 원에 속한 요소는 외부의 원에 속한 어떤 것도 알지 못한다.
특히 내부의 원에 속한 코드는 외부의 원에 선언된 어떤 것에 대해서도 그 이름을 언급해서는 절대 안된다. 

 

이것을 지키려면 수정범위가 많으니까,,,

적당히... 타협해도 되지 않을까....?.... 내부의 원도.... 외부의 원을.... 조금... 알 수...있게.....

그럼...양방향...의존...관계가...되는게....좀...찝찝한데......

 

 

이런 고민을 하다가

14장 > 컴포넌트 결합 에 나오는 cyclic dependency 에 관한 내용이 그러면 안된다고 나를 잡아줘서(?) 

기록해두려고한다. 

 

 

참고로 Xcode에서 로컬 Swift Package 간 순환 dependencies를 한번 설정해보니..

어차피 "cyclic dependency declaration found"  라는 빌드에러가 나며 개발자를 강제하고 있었다 (👍)

 

 

[2] 의존성 비순환 원칙

컴포넌트 의존성 그래프에 순환 (cycle)이 있어서는 안된다. 

 

아래 컴포넌트 다이어그램처럼 의존성 관계에는 순환이 없고 단방향이여야한다. 

어느 컴포넌트에서 시작하더라도 의존성 관계를 따라가면서 최초의 컴포넌트로 되돌아 갈 수 없다.

 

 

이렇게 되면..

 

1. 변경이 영향을 미치는 범위 / 고려해야할 변수를 쉽게 파악가능

 

예를들어 Presenters 를 담당하는 팀에서 이 컴포넌트의 새로운 릴리스를 만들면

이 릴리스에 영향받는 팀을 쉽게 찾을 수 있다. 의존성 화살표를 거꾸로 따라가면 된다.

즉 View와 Main 컴포넌트가 영향을 받음을 바로 파악할 수 있다. 

 

또한 Presenter를 만드는 개발자는 테스트를 구성할 때 Interactors와 Entites가 고려해야할 변수임을 알 수 있다. 

 

 

2. 전체 시스템 릴리스 절차가 명료하고 쉬움

 

시스템 전체를 릴리스할 때 릴리스 절차는 상향식으로 진행된다. 

먼저 Entities 컴포넌트를 컴파일하고, 테스트하고, 릴리스한다.

그러고 나서 Database와 Interactors에 대해서도 동일한 과정을 거친다.

그 다음 Presetners, View, Controllers, Authorizier 순으로 진행하고 Main은 마지막에 처리!

이 같은 절차는 상당히 명료하며 쉽게 처리할 수 있다. 

 

 

[3] 순환이 컴포넌트 의존성 그래프에 미치는 영향

 

새로운 요구사항이 발생해서 Entities에 포함된 클래스 하나가 Authorizer에 포함된 클래스 하나를 사용하도록 변경할 수 밖에 없다고 가정해보자.

예를들어 Entities의 User 클래스가 Authorizer의 Permissions 클래스를 사용한다고 해보자. 

이렇게 되면 순환 의존성 (dependency cycle) 이 발생한다. 

 

 

이 순환은 즉각적인 문제를 일으킨다. 

 

Database 컴포넌트를 만드는 개발자는 컴포넌트를 릴리스하려면 Entities 컴포넌트와 반드시 호환되어야한다는 사실을 알고 있다.

하지만 Entities 컴포넌트에는 순환이 있으므로, Database 컴포넌트는 Authorizer와도 호환되어야한다. 

그런데 Authorizer는 Interactors에 의존한다. 이로 인해 Database는 릴리스하기가 어려워진다.

Entites, Authorizer, Interactors는 사실상 하나의 거대한 컴포넌트가 되어 버린다. 

 

 

또한 Entities 컴포넌트를 테스트할 때 무슨일이 벌어질지를 생각해보라. 유감스럽게도 Authorizer와 Interactors 까지도 

반드시 빌드하고 통합해야한다. 

 

 

여러 클래스 중 하나에 간단한 단위 테스트를 실행하는 데 왜 이렇게도 많고 다양한 라이브러리와 다른 사람들의 많은 작업물을 포함해야하는 지가 궁금할 것이다. 이 문제를 조금 조사해보면 의존성 그래프에 순환이 있기 때문이라는 사실을 발견할 것이다. 

 

이처럼 순환이 생기면 컴포넌트를 분리하기가 상당히 어려워지고 단위테스트, 릴리스도 어려워지며 에러도 쉽게 발생한다. 

게다가 모듈의 개수가 많아짐에 따라 빌드 관련 이슈는 기하급수적으로 증가한다. 

 

뿐만아니라 의존성 그래프에 순환이 생기면 컴포넌트를 어떤 순서로 빌드해야 올바를지 파악하기가 상당히 힘들어진다. 

 

[4] 순환 끊기

 

컴포넌트 사이의 순환을 끊고 의존성을 다시 비순환으로 원상복구 가능하다. 아래 두가지 방법을 통해!

 

1. 의존성 역전 원칙 (DIP)  을 적용하기

 

User가 필요로 하는 메소드를 제공하는 인터페이스를 생성한다. 이 인터페이스는 Entities에 위치시키고

Authorizer에서는 이 인터페이스를 상속받는다. 

이렇게 하면 Entities와 Authorizer 사이의 의존성을 역전시킬 수 있고 이를 통해 순환을 끊을 수 있다. 

 

 

 

2. 새로운 컴포넌트 만들기 

 

Entities와 Authorizer가 모두 의존하는 새로운 컴포넌트를 만든다. 그리고 두 컴포넌트가 모두 의존하는 클래스들을 새로운 컴포넌트로 이동시킨다. 

 

 

두번째 해결책에서 시사하는 바는 요구사항이 변경되면 컴포넌트 구조도 변경될 수 있다는 사실이다. 

실제로 애플리케이션이 성장함에 따라 컴포넌트의존성 구조는 서서히 흐트러지며 또 성장한다.

따라서 의존성 그래프에 순환이 발생하는 지를 항상 관찰하고 순환이 발생하면 어떤 식으로든 끊어야한다. 

 

 

+++ 

그리고 책에는 안나오지만 나는 이런 방법도 생각했었다.

Authorizer 컴포넌트에서 Entity를 extension해서 필요한 기능을 추가하는 것! 

(간단하지만 캡슐화를 해치는 것일까..? 하는 고민도 든다 @_@)

import Entity

extension User {
    
    public var permission: Permission {
         ~~~~~ 
    }
}

 

 

 

[ Reference ] 

 

http://www.yes24.com/Product/Goods/77283734

 

클린 아키텍처 - YES24

살아있는 전설이 들려주는 실용적인 소프트웨어 아키텍처 원칙『클린 코드』와 『클린 코더』의 저자이자 전설적인 소프트웨어 장인인 로버트 C. 마틴은 이 책 『클린 아키텍처』에서 이러한

www.yes24.com

 

 

반응형
댓글