책 [클린아키텍처] 4부 컴포넌트 원칙의 요약입니다.
안녕하세요, 휴먼스케이프 개발자 Tasha입니다. 이전 장에 나온 SOLID원칙이 방에 벽돌을 배치하는 방법에 대한 이야기였다면, 오늘은 빌딩에 방을 배치하는 방법인 컴포넌트 원칙에 대한 이야기입니다.
출처:http://www.uxctrl.com/tag/wireframes/
컴포넌트: 배포단위
컴포넌트의 역사를 알면 컴포넌트가 무엇이고, 어떤 특성을 가지는지 알 수 있습니다. 먼저 외부 라이브러리를 사용하는 어플리케이션을 로드하는 역사에 대해서 보도록 하겠습니다.
개발 초창기에는 프로그램을 로드할 메모리의 위치를 선정하는 일이 우선시되었고, 한번 위치가 결정되면 재배치가 불가능했습니다. 따라서 라이브러리 함수의 코드를 어플리케이션 코드에 직접 포함시켜서 컴파일 되었습니다. 그러나 이 방식으로는 함수 라이브러리가 커질수록 컴파일이 너무 오래걸린다는 단점이 있었습니다.
따라서 함수라이브러리 코드와 어플리케이션 코드를 분리하고 개별적으로 컴파일 한 후, 메모리의 특정 위치에 로드하였습니다. 어플리케이션을 실행해야한다면 함수 라이브러리를 먼저 로드해야 하는데, 이는 어플리케이션이 특정 주소 공간에서만 잘 동작한다는 단점이 있었습니다. 따라서 어플리케이션이나 함수 라이브러리가 커질수록 어플리케이션을 특정 주소공간에 위치시키기 위해 어플리케이션을 계속 세그먼트로 분리해야하는 단편화 현상이 발생했습니다.
이 문제를 해결하기 위해 ‘재배치성’과 ‘링크’개념이 등장하였습니다. 로더를 이용해 코드 자체를 포함시키는 것이 아니라 메모리에 재배치할 수 있는 바이너리를 생성하도록 컴파일러를 수정하도록 하는 것입니다. 또한 컴파일러가 재배치 가능한 라이브러리를 메타데이터 형태로 생성하도록 수정하여 외부링크가 가능하게 되었습니다. 따라서 더이상 어플리케이션을 세분화 하는 일 없이, 필요한 함수만 로드할 수 있게 되었습니다. 이를 통해 프로그램을 개별적으로 컴파일하고 로드할 수 있는 단위가 생겨나게 되었습니다.
이렇게 런타임에 플러그인 형태로 결합할 수 있는 동적 링크파일이 컴포넌트입니다. 이는 배포 단위로도 정의 할 수 있으며, 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위라고도 할 수 있습니다. 잘 설계된 컴포넌트는 반드시 독립적으로 배포가능하고, 독립적으로 개발 가능한 능력을 갖춰야 합니다.
컴포넌트 응집도: 어떤 클래스를 어느 컴포넌트에 포함시켜야 할까?
컴포넌트 응집도에 대해서는 세 가지의 원칙이 있습니다. 이 세 가지 원칙은 서로 상충되는데, 개발 단계에서의 중요성에 따라(개발가능성 또는 재사용성) 더 중점을 둬야할 원칙이 다르고, 세 원칙에 균형을 이루어야 한다는 특징이 있습니다. 세가지 원칙을 차례로 살펴보겠습니다.
출처: https://medium.com/@mammimia/clean-architecture-part-iv-component-principles-89d3cb58c195
REP(Reuse/Release Equivalence Principle): 재사용/릴리즈 등가 원칙
: 재사용 단위는 릴리즈 단위와 같다.
이 원칙을 조금 더 풀어서 설명하자면, 하나의 컴포넌트로 묶인 클래스와 모듈은 버전 번호가 같아야 하며, 동일한 릴리스로 추적 관리되고, 동일한 릴리스 문서에 포함되어야 한다는 뜻입니다. 이 원칙은 클래스와 모듈을 단일 컴포넌트로 묶는 방법에 대한 구체적인 원칙이 아니고 그냥 ‘이치에 맞는'듯한 이야기이지만 이 원칙을 어기면 ‘이치에 맞지 않는' 컴포넌트가 되어버리므로 중요한 원칙입니다.
CCP(Common Closure Principle): 공통 폐쇄 원칙
: 동일한 이유로 동일한 시점에 변경되는 클래스를 같은 컴포넌트로 묶어라.
이 원칙은 어플리케이션의 ‘유지보수성'을 고려한 원칙입니다. 변경될 가능성이 있는 클래스를 모두 한 곳으로 묶어 릴리스/재검증/배포하는 일과 관련된 작업량을 최소화하기 위함입니다.
이는 서로 다른 이유로 변경되면 서로 다른 클래스로 분리하라는 관점에서 컴포넌트 관점의 SRP(단일 책임 원칙)라고도 할 수 있고, 변경에는 닫혀있고 확장에는 열려있다는 관점에서 OCP(개방폐쇄원칙)과도 밀접한 관련이 있습니다.
CRP(Common Reuse Principle): 공통 재사용 원칙
: 컴포넌트 사용자들을 필요하지 않는 것에 의존하게 강요하지 말라.
이 원칙은 어떤 클래스를 한데 묶어도 되는지보다, 어떤 클래스를 한 데 묶어서는 안 되는지에 대해 이야기를 하는 원칙입니다. 의존하는 컴포넌트가 있다면 해당 컴포넌트의 모든 클래스에 대해 의존함을 확실히 인지해야 합니다. 사용하는 컴포넌트가 의존하지 않는 클래스를 포함한 컴포넌트의 변경에 영향을 받지 않도록 하기 위함입니다.
REP와 CRP에만 중점을 두면, 사소한 변경이 생겼을 때 너무 많은 컴포넌트에 영향을 미치고, REP와 CCP에만 과도하게 집중하면 불필요한 릴리즈가 너무 빈번해지므로, 각 개발단계에 맞게 균형을 잘 이뤄 설계하는 것이 중요합니다.
컴포넌트 결합: 컴포넌트 사이의 관계에 대해
앞에서 컴포넌트를 어떻게 응집할지에 대해서 알아봤다면 이번에는 컴포넌트와 컴포넌트의 관계를 어떻게 설계할 지에 대한 내용입니다. 컴포넌트 관계에 대해서도 세 가지의 원칙이 있습니다.
ADP(Acyclic Dependencies Principle): 의존성 비순환 원칙
: 컴포넌트 의존성 그래프에 순환이 있어서는 안된다.
[출처] https://alchetron.com/Acyclic-dependencies-principle
위와같이 의존성 관계가 순환구조로 되어있으면, D컴포넌트의 수정과 릴리즈를 위해 B, C컴포넌트까지 함께 호환을 함께 살펴봐야하는, 이른바 ‘숙취증후군'의 경험을 떠안게 됩니다. 이처럼 순환이 생기면 하나의 단위테스트를 하고 릴리즈 하는일이 굉장히 어려워질 뿐 아니라 어떤 순서로 빌드해야 올바를지를 파악하기가 어려워집니다. 순환이 생기면 올바른 순서라는 것 자체가 없을 수 있기 때문입니다.
이와 같은 순환 의존성을 끊기 위한 방법에는 두가지 매커니즘이 있습니다. 첫번째로는 의존성 역전 원칙(DIP)를 적용하는 것입니다. 예를 들어 컴포넌트가 의존하고 있는 C컴포넌트의 인터페이스를 D컴포넌트에 위치시키고, C컴포넌트은 그 인터페이스를 상속받을 수 있도록 하여 순환구조를 끊어버리는 것입니다.
두 번째 방법으로는 새로운 컴포넌트를 만들어 C와 D컴포넌트가 의존하는 클래스들을 새로운 컴포넌트로 이동시키는 것입니다. 이 말은 컴포넌트 구조도 변경될 수 있다는 점을 시사하는데, 실제로 어플리케이션이 성장함에 따라 컴포넌트 의존성 구조도 흐트러지며 성장하므로, 의존성 구조에 순환이 발생하는지 항상 관찰해야 합니다.
이는 컴포넌트 구조를 프로젝트 초기에 설계할 수 없음을 의미합니다. 어플리케이션이 성장함에 따라 재사용 가능한 요소를 만드는 일에 관심을 기울이면서 CRP(공통 재사용 원칙)이 영향을 미치게 되고, 결국 순환이 발생하면서 ADP가 적용되고, 따라서 컴포넌트 의존성 그래프는 조금씩 흐트러지고 또 성장하게 되는 것입니다.
SDP(Stable Dependencies Principle): 안정된 의존성 원칙
: 안정성의 방향으로(더 안정된 쪽에) 의존하라.
이 원칙은 말 그대로 변경이 쉽지 않은 컴포넌트가 변동이 예상되는 컴포넌트에 의존하게 만들지 않아야한다는 원칙입니다.
[출처] https://blog.damonkelley.me/2016/09/22/the-principles-of-package-coupling/
컴포넌트의 안정성을 측정할 수 있는 계산 방법은 다음과 같습니다.
I= Fan-out / (Fan-in + Fan-out)
I: 불안전성 / Fan-in: 내부에 의존하는 외부의 클래스 개수 / Fan-out: 외부에 의존하는 내부의 클래스 개수
따라서 외부에 의존하는 클래스 개수가 적을수록, I수치가 0에 가까울 수록 안정된 컴포넌트라고 할 수 있습니다.
SDP 위배의 예
이 예가 단적으로 SDP를 위배한 예인데, Stable의 I지표가 Flexible의 I지표보다 더 낮기 때문입니다. 이 역시 DIP를 도입하여 해결할 수 있는데, Stable이 의존하고 있는 Flexible의 메서드를 선언한 새로운 인터페이스를 만들어 이 인터페이스에 의존하도록 강제하는 것입니다.
SAP(Stable Abstraction Principle): 안정된 추상화원칙
: 컴포넌트는 안정된 정도만큼만 추상화되어야한다.
안정된 추상화원칙은 안정성과 추상화정도의 사이의 관계를 정의합니다. 안정된 컴포넌트는 추상컴포넌트여야 하며, 이를 통해 안정성이 컴포넌트를 확장하는 일을 방해해서는 안되고, 불안정한 컴포넌트는 반드시 구체 컴포넌트가 되어 내부의 구체적인 코드를 쉽게 변경할 수 있도록 해야합니다.
[출처]: https://jusths.tistory.com/149
y축 A는 추상화정도, x축 I는 불안정성을 의미합니다. SAP 원칙에 따르면 컴포넌트가 구체적이면서 안정되면 변경하기가 어려움으로 배제해야할 구역임으로 Zone of Pain이라고 불립니다. 한편으로 추상적이지만 누구도 그 컴포넌트에 의존하지 않는 불안정한 컴포넌트는 쓸모가 없어 Zone of Useless라 불리며 이 역시 배제해야할 구역입니다.
따라서 컴포넌트가 위치할 수 있는 가장 바람직한 지점은 주계열(Main Sequence)과 가까이 있는 지점입니다. 이 주계열로부터 얼마나 떨어져있는지 평균과 분산을 측정하여 컴포넌트 재구성을 검토하거나 관리한계를 측정할 수 있습니다.
여기까지가 책 [클린아키텍처]의 4부 컴포넌트 원칙에 대한 요약이었습니다. 읽어주셔서 감사합니다. 🙂
Get to know us better! Join our official channels below.
Telegram(EN) : t.me/Humanscape KakaoTalk(KR) : open.kakao.com/o/gqbUQEM Website : humanscape.io Medium : medium.com/humanscape-ico Facebook : www.facebook.com/humanscape Twitter : twitter.com/Humanscape_io Reddit : https://www.reddit.com/r/Humanscape_official Bitcointalk announcement : https://bit.ly/2rVsP4T Email : support@humanscape.io