컴포넌트 원칙

Cleann Architecture[1]에서 컴포넌트는 배포 단위를 나타낸다. 즉, "시스템의 구성 요소"로 배포할 수 있는 "가장 작은 단위"로서, 자바의 경우 jar 파일이 루비에서는 gem파일 그리고, 닷넷에서는 DLL이 그것이라고 할 수 있다. 컴포넌트가 마지막에 어떤 형태로 배포되든, 잘 설계된 컴포넌트라면 반드시 독립적으로 배포 가능한, 따라서 독립적으로 개발 가능한 능력을 갖춰야 한다. 즉, 팀이 개발하는 독립된 단위가 컴포넌트여야 한다.

 

이전에 이야기한 SOLID원칙이 벽과 방에 벽돌을 배치하는 방법을 알려 준다면, 여기서 이야기 하는 컴포넌트 원칙은 빌딩에 방을 배치하는 방법을 설명해 주는 것이라 생각하면 된다고 한다. 

 

주로 응집도(Cohesion)과 결합도 혹은 의존도(Coupling)에 대해서 논의 한다. 응집도는 관련된 것들이 모여 있도록 하는 법칙이다. 반대로 의존도는 관련 없는 것들을 분리하는 것이라고 볼 수 있다. 컴포넌트들이 관련 없을 수 없기 때문에 효과적으로 의존하도록 만든는 것이라 볼 수 있다.

컴포넌트 응집도 관련한 원칙

 

재사용/릴리즈 등가 원칙(Reuse/Relese Equivalence Principle, REP)

REP를 한마디로 말하자면, "재사용 단위는 릴리스 단위와 같다"라는 것이다.

 
재사용/릴리즈 등가 원칙은 너무 당연해 보인다. 소프트웨어 컴포넌트가 릴리스 절차를 통해 추척 관리되지 않거나 릴리스 번호가 부여되지 않는다면 해당 컴포넌트를 재사용하고 싶어도 할 수도 없고, 하지도 않을 것이다.


이 원칙을 소프트웨어 설계와 아키텍처 관점에서 보면 단일 컴포넌트는 응집성 높은 클래스와 모듈들로 구성되어야 함을 뜻한다. 단순히 뒤죽박죽 임의로 선택된 클래스와 모듈로 구성되어서는 안 된다. 컴포넌트를 구성하는 모든 모듈은 서로 공유하는 중요한 테마나 목적이 있어야 한다.


이 조언만으로는 클래스와 모듈을 단일 컴포넌트로 묶는 방법을 제대로 설명하기 힘들기에 이 조언이 약하다고 하는 것이다. 그렇더라도 이 원칙 자체는 중요하다. 이 원칙을 어기면 쉽게 발견할 수 있기 때문이다.

 

공통 폐쇄 원칙 (Common Closure Principle, CCP)

CCP는 "동일한 이유로 동일한 시점에 변경되는 클래스를 같은 컴포넌트로 묶어라. 서로 다른 시점에 다른  이유로 변경되는 클래스는 다른 컴포넌트로 분리하라."라는 원칙이다.


이 원칙은 단일 책임 원칙(SRP)을 컴포넌트 관점에서 다시 쓴 것이다. 공동 폐쇄 원칙(CCP)에서도 마찬가지로 단일 컴포넌트(Component)는 변경이 이유가 여러 개 있어서는 안 된다고 말한다.

 

대다수의 애플리케이션에서 유지보수성(maintainability)은 재사용성보다 훨씬 중요하다. 애플리케이션에서 코드가 반드시 변경되어야 한다면, 이러한 변경이 여러 컴포넌트 도처에 분산되어 발생하기보다는, 차라리 변경 모두가 단일 컴포넌트에서 발생하는 편이 낫다. CCP는 같은 이유로 변경될 가능성이 있는 클래스는 모두 한곳으로 묶을 것을 권한다.

 

이 원칙은 개방 폐쇄 원칙(OCP)과도 밀접하게 관련되어 있다. 실제로 CCP에서 말하는 '폐쇄closure'는 OCP에서 말하는 '폐쇄closure'와 그 뜻이 같다. OCP에서는 클래스아 변경에는 닿혀 있고 확장에는 열려 있어야 한다고 말한다. 100%완전한 폐쇄란 불가능하므로 전략적으로 폐쇄해야 한다.

 

공통 재사용 원칙(Common Reuse Principle, CRP)

CRP는 "컴포넌트 사용자들은 필요하지 않은 것에 의존하게 강요하지 말라"라는 원칙이다.

 
공통 재사용 원칙(CRP)도 클래스와 모듈을 어느 컴포넌트에 위치시킬지 결정할 때 도움되는 원칙이다. CRP에서는 같이 재사용되는 경향이 있는 클래스와 모듈들은 같은 컴포넌트에 포함해야 한다고 말한다.

 

간단한 사례로 컨테이너 (Container) 클래스와 해당 클래스의 이터레이터(Iterator) 클래스를 들 수 있다. 이들 클래스는 서로 강하게 결합되어 있기 때문에 함께 재사용 된다. 따라서 이들 클래스는 반드시 동일한 컴포넌트에 위치해야 한다. 따라서 CRP는 어떤 클래스를 한데 묶어도 되는지 보다는, 어떤 크래스를 한데 묶어서는 안되는지에 대해서 훨씬 더 많은 것을 이야기 한다. CRP는 강하게 겨합되지 않은 클래스들을 동일한 컴포넌트에 위치시켜서는 안 된다고 말한다.

 

CRP는 SOLID의 인터페이스 분리 원칙(ISP)의 포괄적인 버전이라 할 수 있다. ISP는 사용하지 않은 메서드가 있는 클래스에 의존하지 말라고 조언한다. CRP는 사용하지 않는 클래스를 가진 컴포넌트에 의존하지 말라고 조언한다.

컴포넌트 응집도에 대한 균형 다이어그램

아마도 응집도에 관한 세원칙이 서로 상충된다. REP와 CCP는 포함(Inclusive)원칙이다. 즉, 두 원칙은 컴포넌트를 더욱 크게 만든다. CRP는 배제(exclusive)원칙이며, 컴포넌트를 더욱 작게 만든다.

 

컴포넌트응집도(Component Cohesion)[1]


원칙에 반대 위치에 있는 Edge에는 원칙을 따르지 않을 때 감수해야 하는 비용을 나타낸다. 즉, 위에 그림에서 오로지 REP와 CRP에만  중점을 두면, 사소한 변경이 생겼을 때 너무 많은 컴포넌트에 영향을 미친다. 반대로 CCP와 REP에만 과도하게집중하면 불필요한 릴리즈가 너무 빈번해 진다.

 

뛰어난 아키텍트라면 이 균형 삼각형애서 개발팀이 현재 관심을 기울이는 부분을 충족시키는 위치를 찾아야 하며 또한 시간이 흐르면서 개발팀이 주의를 기울이는 부분 역시 변한다는 사실도 이해하고 있어야 한다. 일반적으로 프로젝트는 삼각형의 오른쪽에서 시작하는 편이며, 이대는 오직 재사용성만 희생하면 된다. 프로젝트가 성숙하고,, 그 프로젝트로 부터 파생된 또 다른 프로젝트가 시작되면 프로젝트는 삼각혀엥서 점차 왼쪽으로 이동해 간다.

 

 

컴포넌트 결합

 

의존성 비순환 원칙(Acyclic Dependencies Principles, ADP)

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

 

숙취 증후군 the morning after syndrome'이라고 부른다. 퇴근 후, 이튿날 다른 사람의 작업으로 전혀 돌아가지 않는 경험을 말하는 것으로 많은 개발자가 동일한 소스 파일을 수정하는 환경에서 발생한다. 소수의 개발자로 구성된 상대적으로 작은 프로젝트에서는 이 증후군이 그다지 큰 문제가 되지 않는다.

 

이 문제의 해결책은 개발 환경을 릴리스 가능한 컴포넌트 단위로 분리하는 것이다. 컴포넌트가 새로 릴리스되어 사용할 수 있게 되면 다른 팀에서는 새 릴리스를 당장 적용할지를 결정해야 한다. 적용하지 않기로 했다면 그냥 과거 버전의 릴리스를 계속 사용한다. 이렇게 될 수 있다면, 어떤 팀도 다른 팀에 의해 좌우 되지 않는다. 특정 컴포넌트가 변경되더라도 다른 팀에 즉각 영향을 주지 않는다. 각 팀은 특정 컴포넌트가 새롭게 릴리스되면 자신의 컴포넌트를 해당 컴포넌트에 맞게 수정할 시기를 스스로 결정할 수 있다. 뿐만 아니라 통합은 작고 점진적으로 이뤄진다. 특정 시점에 모든 개발자가 한데 모여서 진행 중인 작업을 모두 통합하는 일은 사라진다.

 

이 절차가 성공적으로 동작하려면 컴포넌트 사이의 의존성 구조를 반드시 관리해야 한다. 의존성 구조에 순환이 있어서는 안 된다. 구조가 방향 그래프(Directed graph)이라고 하면, 컴포넌트는 정점(Vertex)에 해당하고, 의존성 관계는 방향이 있는 간선 (Directed edge)에 해당한다 볼 수 있다. 이렇게 비순환 방향 그래프(Directed Acyclic Graph, DAG)로 구조를 만들면 문제가 해결 된다.  이렇게 할 수 있는 두 가지 방법은 아래와 같다.

 

  1. 의존성 역전 원칙(DIP)을 적용한다.
  2. Entities와 Authorizer가모두 의존하는 새로운 컴포넌트를 만든다.

 

안정된 의존성 원칙 (Stable Dependencies Principles, SDP)

SDP는 "안정성의 방향으로(더 안정된 쪽에) 의존하라"라는 원칙이다.

하나의 모듈에 누군가가 의존성을 매달아 버리면 이 모듈은 변경하기 어려워진다. 이 모듈에서는 단 한 줄의 코드도 변경되지 않았지만, 어느 순간 갑자기 해당 모듈을 변경하는 일이 상당히 도전적인 일이 되어 버리는 것이다. 

 

안전성(Stability)이란 무엇인가? '쉽게 움직이지 않는'이라고 정의할 수 있다. 아래 그림5의 X는 안정된 컴포넌트다. 세 컴포넌트가 X에 의존하며, 따라서 X 컴포넌트는 변경하지 말아야 할 이유가 세 가지나 되기 때문이다. 이 경우 X는 세 컴포넌트를 책임진다(Responsible)라고 말한다. 반대로 X는 어디에도 의존하지 않으므로 X가 변경되도록 만들 수 있는 외적인 영향이 전혀 없다. 이 경우 X는 독립적이다(Independent)라고 말한다.

 

아래 그림의 Y는 상당히 불안정한 컴포넌트다. 어떤 컴포넌트도 Y에 의존하지 않으므로 Y는 책임성이 없다고 말할 수 있다. 또한 Y는 세 개의 컴포넌트에 의존하므로 변경이 발생할 수 있는 외부 요인이 세 가지다. 이 경우는 Y는 의존적이라고 말한다.

 

안정성(Stability)[1]

 

이와 같은 정의에서 안정성 지표는 아래와 같이 정의 될 수 있다.

  • Fan-in 안으로 들어오는 의존성
  • Fan-out 바깥으로 나가는 의존성
  • I(불안정성):I = Fan-out / (Fan-in + Fan-out) 이 지표는 [0,1] 범위의 값을 갖는다. 1이면 최고로 불안정한 상태이고, 0이면 최고로 안정된 상태

안정된 추상화 원칙(Stable Abstractions Principle, SAP)

SAP는 "컴포넌트 안정된 정도만큼만 추상화되어야 한다"라는 원칙이다.

안정된 추상화 원칙(Stable Abstractions Principle, SAP)은 안정성(Stability)과 추상화 정도(Abstractness) 사이의 관계가 정의 한다. 이 원칙은 한편으로는 안정된 컴포넌트는 추상 컴포넌트여야 하며, 이를 통해 안정성이 컴포넌트를 확장하는 일을 방해해서는 안 된다고 말한다.

 

추상화 정도 측정하기 위한 A지표는 컴포넌트의 추상화 정도를 측정한 값으로 다음과 같이 정의 될 수 있다.

  • Nc: 컴포넌트의 클래스 개수
  • Na: 컴포넌트의 추상 클래스와 인터페이스의 개수
  • A: 추상화 정도. A=Na/Nc" A가 0이면 컴포넌트에는 추상 클래스가 하나도 없다는 뜻이다. A가 1이면 컴포넌트는 오로지 추상 클래스만을 포함한다는 뜻이다.

 

주계열(the main sequence)

이제 안정성(I)과 추상화 정도(A) 사이의 관계를 정의해야 할 때가 왔다. 우선 고통의 구역과 쓸모 없는 구역을 살펴 보고, 주 계열(The main sequence)에 대해서 살펴 보자.

고통의  구역(Zone of Pain), (0,0) 주변 구역에 위치한 컴포넌트를 살펴보자. 이 컴포넌트는 매우 안정적이며 구체적이다. 일부 소프트웨어 엔티티는 고통의 구역에 위치하곤 한다. 데이터베이스 스키마가 한 예다. 데이터베이스 스키마는 변동성이 높기로 악명이 높으며, 극단적으로 구체적이며, 많은 컴포넌트가 여기에 의존한다. 유틸리티 라이브러리로 실제로는 변동성이 거의 없다. 변동성이 없는 컴포넌트는 (0,0)구역에 위치했더라도 해롭지 않다.

 

쓸모없는 구역(Zone of Uselessness), (1, 1) 주변의 컴포넌트를 생각해 보자. 이 영역도 바람직하지 않은데, 여기위치한 컴포넌트는 최고로 추상적이지만, 누구도 그 컴포넌트에 의존하지 않기 때문이다. 이러한 컴포넌트는 아무도 쓰지 않으므로 쓸모가 없다.

 

일반적으로 변동성이 큰 컴포넌트 대부분은 두 배제 구역으로부터 가능한 한 멀리 떨어뜨려야 한다. 각 배제 구역으로부터 최대한 멀리 떨어진 점의 궤적은 (1,0)과 (0,1)을 잇는 선분이다. 나는 이 선분을 주계열(Main Sequence)이라고 부른다.

 

여기서, 주계열과의 거리, 세 번째 지표가도출된다. 컴포넌트가 주계열 바로 위에 또는 가까이 있는  것이 바람직하다면 이 같은 이상적인 상태로 부터 컴포넌트가 얼마나 떨어져 있는지 특정하는 지표를 만들어 볼 수 있다.

  • D: 거리. = |A+I-1| 유효범위는 [0,1]

 

주 계열(Main Sequence)와 컴포넌트 산점도[1]

 

결론

Clearn Architecture에서 설명하는 응집도(Cohesion)와 관련된 REP, CRP, CCP원칙에 대해서 살펴 보았고, 응집도 균현 다이어그램을 살펴 보았다. 그리고, 결합도와 관련된 ADP에서는 어떻게 순환을 끊을 수 있는지 살펴 보았고, SDP와 SAP 원칙에서 분안정성(Instability)와 추상화 정도(Abstractness)의 관계를 통해서 주 계열(Main sequence)를 살펴 보았다.

 

참고문헌

[1] 로버트 C. 마틴 저, "클린 아키텍처 소프트웨어 구조와 설계의 원칙" 인사이트(insight) 2019년 08월 20일, 송준이 역

+ Recent posts