상세 컨텐츠

본문 제목

[Architecture] 4부 컴포넌트 원칙 : 13장 컴포넌트 응집도

프로그래밍/Architecture

by jisooo 2024. 1. 14. 17:42

본문

 

 

클린 아키텍처 4부, 13장의 "컴포넌트 응집도"에서는 소프트웨어 컴포넌트의 개념과 구성 요소가 무엇인지 알아보고,

컴포넌트를 결합하여 시스템을 구성하는 방법에 대해 논의한다.

개인적으로 마지막 "컴포넌트 응집도에 대한 균형"을 설명할 때

최근 이 "균형"에 대해서 실무에서 많이 고민하고,

팀원분들이랑 관련 내용을 토론하기도 하고,

팀과 파트 차원에서도 많은 시행착오를 겪은 내용이라 공감이 많이 된 내용이었다.

플랫폼팀에서 관리하는 모듈들이 너무나 많기도 하고,

한 가지 변경에 대한 업무 요청이 왔을 때, 여러가지 모듈들을 수정해야 했던 케이스도 있었고,

굳이 관련 없는 클래스들이 모인 모듈을 배포해야하는 상황도 있었다.

또한 모듈 간에 의존성이 강한 경우에는, 변경에 따른 수많은 모듈을 배포해야 하는 상황도 있었다.

이런 컴포넌트 간 배포 단위와 변경에 따른 수정이 필요한 상황에서

나 뿐만 아니라 다른 팀원들도 많은 짜증과 스트레스를 받는 상황이 지속되었고,

과연 현재의 컴포넌트의 구조가 이상적인 구조인지, 이상적으로 응집되고 분리되어있는지,

그 균형을 적절히 유지하고 있는지 고민해볼 수 있었다.

단순히 "모듈은 동일한 주제, 하나의 기능만 수행해야 한다"라는 속성을 넘어서,

각각의 컴포넌트를 구성할 때 통합과 분리를 어떤 관점에서 적절하게 수행해야 하는지,

그것들을 지킴으로써 생기는 장점과 어김으로써 발생하는 단점들이 무엇인지

깊이 고민할 수 있는 장이였다.


1) 컴포넌트란?

  • 컴포넌트는 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위
  • 컴파일형 언어에서 컴포넌트는 바이너리 파일의 결합체
    • java의 경우 jar 파일이 컴포넌트
  • 인터프리터형 언어의 경우 소스 파일의 결합체
  • 여러 컴포넌트를 서로 링크하여 실행 가능한 단일 파일로 생성할 수 있다.
  • 여러 컴포넌트를 묶어서 .war 파일과 같은 단일 아카이브로 만들 수도 있다.
  • 또는 컴포넌트 각가을 .jar.dll같이 동적으로 로드할 수 있는 플러그인이나 .exe 파일로 만들어서 독립적으로 배포할 수 있다.

잘 설계된 컴포넌트라면 반드시 독립적으로 배포 가능한,

따라서 독립적으로 개발 가능한 능력을 갖춰야 한다.


13장. 컴포넌트 응집도

13장에서는 클래스를 작성하고 어느 컴포넌트에 포함시켜야할지,

즉 컴포넌트의 응집도와 관련된 대한 원칙 3가지를 소개한다.


1) REP : 재사용/릴리즈 등가 원칙 Reuse / Release Equivalence Principle

  • 재사용 단위는 릴리즈 단위와 같다.

지난 십년동안 Maven, RVM과 같은 모듈 관리 도구가 엄청나게 등장했는데,

이 기간에 재사용 가능한 컴포넌트나 컴포넌트 라이브러리가 엄청나게 만들어졌다.

우리는 소프트웨어 재사용의 시대에 살고 있으므로 이와 같은 도구는 점점 중요해졌다.

소프트웨어 컴포넌트가 릴리즈 절차를 통해 추적 관리되고, 릴리즈 번호가 부여됨으로써

우리는 해당 컴포넌트를 재사용할 수 있다.

컴포넌트 라이브러리를 사용하는 개발자들은 새로운 버전이 출시될 때마다,

언제 출시되고 신버전에서는 무엇이 변했는지,

재사용 컴포넌트들간의 서로 호환성은 문제가 없을지같은 사항들을 알아야 하기 때문에,

컴포넌트들이 릴리즈 번호로 관리되는 것은 당연해보이는 내용이지만 생각보다 중요하다.

새로운 릴리즈가 출시되면,

개발자는 새 릴리즈의 변경사항을 살펴보고, 기존 버전을 계속 쓸지 결정한다.

따라서 릴리즈 절차에서는 적절한 공지와 함께 릴리즈 문서도 작성되어야 한다.

이러한 원칙을 소프트웨어 설계와 아키텍처 관점에서 보면

단일 컴포넌트는 응집성 높은 클래스와 모듈들로 구성되어야 함을 뜻한다.

단순히 뒤죽박죽 임의로 선택된 클래스와 모듈들로 구성되서는 안된다.

컴포넌트를 구성하는 모든 모듈은 서로 공유하는 중요한 테마나 목적이 있어야 한다.

초기 컴포넌트의 정의를 설명할 때,

시스템의 구성 요소로 배포할 수 있는 가장 작은 단위라고 설명하였다.

즉, 하나의 컴포넌트로 묶인 클래스와 모듈은 반드시 함께 릴리즈할 수 있어야 한다.

하나의 컴포넌트로 묶인 클래스와 모듈은 버전 번호가 같아야 하며,

동일한 릴리즈로 추적 관리되고, 동일한 릴리즈 문서에 포함되어야 한다.


2) CCP : 공통 폐쇄 원칙 Common Closure Principle

동일한 이유로 동일한 시점에 변경되는 클래스를 같은 컴포넌트에 묶어라.

반대로 서로 다른 시점에 다른 이유로 변경되는 클래스는 다른 컴포넌트로 분리해야 한다.

이 원칙은 3부의 "단일 책임 원칙"을 컴포넌트 관점에서 재해석한 것이다.

SRP에서 단일 클래스는 변경의 이유가 여러 개 있어서는 안된 다고 말하 듯이,

공통 폐쇄 원칙에서도 단일 컴포넌트는 변경의 이유가 여러 개 있어서는 안된다고 말한다.

즉 단일 컴포넌트에서 변경의 이유는 한 가지여야 하고,

한가지 사유로 변경된다면, 동일 컴포넌트에 있는 클래스들은 함께 변경되어야 한다.

이 원칙은 애플리케이션의 유지보수 관점에서 필요성이 특히 대두된다.

애플리케이션에서 코드가 반드시 변경되어야 한다면,

이러한 변경이 여러 컴포넌트에 분산되어 발생한다면?

한가지 변경으로 인해, 여러 컴포넌트의 소스 코드를 수정해야하고,

이는 각 컴포넌트 별로 배포가 필요함을 뜻한다.

이러한 불편함을 감수하기 위해

차라리 변경 모두가 단일 컴포넌트에서 발생하는 편이 낫다.

만약 변경을 단일 컴포넌트로 제한할 수 있다면, 해당 컴포넌트만 재배포하면 된다.

변경된 컴포넌트에 의존하지 않는 다른 컴포넌트는 굳이 다시 검증하거나 배포할 필요가 없다.

CCP와 같은 이유로 변경될 가능성이 있는 클래스는

모두 한 곳으로 묶을 것을 권한다.

물리적 , 개념적으로 강하게 결합되어 항상 함께 변경되는 클래스들은 하나의 컴포넌트에 속해야 한다.

이를 통해 소프트웨어를 릴리즈, 재검증, 배포하는 일과 관련된 작업량을 최소화할 수 있다.

또한 "개방 폐쇄 원칙"과도 밀접하게 관련이 있다.

OCP에서는 클래스가 변경에는 닫혀 있고 확장에는 열려 있어야 한다고 말한다.

우리는 발생할 가능성이 있거나, 과거에 발생했던 대다수의 공통적인 변경에 대해서 클래스가 닫혀 있도록 설계한다.

이와 비슷한 맥락으로,

CCP에서는 동일한 유형의 변경에 대해 당혀 있는 클래스들을 하나의 컴포넌트로 묶음으로써

OCP에서 얻은 교훈을 확대 적용한다.

따라서 변경이 필요한 요구사항이 발생했을 때,

그 변경이 영향을 주는 컴포넌트들이 최소한으로 한정될 가능성이 높아지게 된다.


3) CRP: 공통 재사용 원칙 Common Reuse Principle

공통 재사용 원칙도 클래스와 모듈을 어느 컴포넌트에 위치시킬지 결정할 때 도움을 주는 원칙이다.

CRP에서는 같이 재사용되는 경향이 있는 클래스와 모듈들은 같은 컴포넌트에 포함해야 한다고 말한다.

보통 개별 클래스가 단독으로 재사용되는 경우는 거의 없고,

대체로 재사용 가능한 클래스는 재사용 모듈의 일부로써 해당 모듈의 다른 클래스와 상호작용 하는 경우가 많다.

이러한 컴포넌트 내부에서는 클래스들 사이에 수많은 의존성이 있으리라 예상할 수 있다.

어떤 컴포넌트가 다른 컴포넌트를 사용하면,

두 컴포넌트 사이에는 의존성이 생겨난다.

사용하는 컴포넌트는 사용되는 컴포넌트에 여전히 의존한다.

이 같은 의존성으로 인해 사용되는 컴포넌트가 변경될 때마다 사용하는 컴포넌트도 변경해야 할 가능성이 높다.

또는 사용하는 컴포넌트를 변경하지 않더라도,

재컴파일, 재검증, 재배포를 해야 하는 가능성은 여전히 남아 있다.

심지어 사용되는 컴포넌트에서 발생한 변경이 사용하는 컴포넌트와는 전혀 관련 없는 경우라도 말이다.

따라서 의존하는 컴포넌트가 있다면,

해당 컴포넌트의 모든 클래스에 대해 의존함을 확실히 인지해야 한다.

즉 한 컴포넌트에 속한 클래스들은 더 작게 그룹지을 수 없다.

그중 일부 클래스에만 의존하고, 다른 클래스와는 독립적일 수 없음을 확실히 인지해야 한다.

그렇지 않다면 필요 이상으로 많은 컴포넌트를 재배포하느라 고생하게 된다.

이처럼 CRP는 어떤 클래스를 한데 묶어서는 안되는지에 대해 강조한다.

강하게 결합되지 않은 클래스들은 동일한 컴포넌트에 위치시켜서는 안된다.

CRP는 인터페이스 분리 원칙의 포괄적인 버전이다.

ISP는 사용하지 않는 메서드가 있는 클래스에 의존하지 말라고 조언한다.

CRP는 사용하지 않는 클래스를 가진 컴포넌트에 의존하지 말라고 조언한다.


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

여태 위에서 설명한 3가지 컴포넌트 응집도에 관한 원칙들은 서로 상충된다.

REP와 CCP는 포함 원칙이여서 컴포넌트를 더욱 크게 만든다.

반면 CRP는 배제 원칙이며 컴포넌트를 더욱 작게 만든다.

뛰어난 아키텍트라면 이 원칙들이 균형을 이루는 방법을 찾아야 한다.

위 균형 다이어그램을 보면,

우리가 REP와 CRP에만 중점을 두면 사소한 변경이 생겼을 때, 너무 많은 컴포넌트에 영향을 미친다.

반대로 CCP와 REP에만 과도하게 집중하면 불필요한 릴리즈가 너무 빈번해진다.

뛰어난 아키텍트라면 이 균형 삼각형에서 개발팀이 현재 관심을 기울이는 부분을 충족시키는 위치를 찾아야 하며,

또한 시간이 흐르면서 개발팀이 주의를 기울이는 부분 역시 변한다는 사실도 이해하고 있어야 한다.

예를 들어 프로젝트 초기에는 CCP가 REP보다 훨씬 중요한데 개발 가능성이 재사용성보다 중요하기 때문이다.

일반적으로 프로젝트는 삼각형의 오른쪽에서 시작하는 편이며, 이때는 재사용성만 희생하면 된다.

프로젝트가 성숙하고, 그 프로젝트부터 파생된 또다른 프로젝트가 시작되면

프로젝트는 삼각형에서 점차 왼쪽으로 이동해간다.

즉, 프로젝트의 컴포넌트 구조는 시간과 성숙도에 따라 변한다는 뜻이다.


이처럼 13장에서는 컴포넌트 응집도에 관한 세 가지 원칙을 설명하며,

이 원칙들은 응집도가 가질 수 있는 훨씬 복잡한 다양성을 설명해준다.

어느 클래스들을 묶어서 컴포넌트로 구성할지를 결정할 때,

재사용성과 개발가능성이라는 상충하는 힘을 반드시 고려해야 한다.

이들 사이에서 애플리케이션의 요구에 맞게 균형을 잡는 일을 중요하다.

결과적으로 시간이 흐름에 따라 프로젝트의 초점이 개발가능성에서 재사용성으로 바뀌고,

그에 따라 컴포넌트를 구성하는 방식도 조금씩 흐트러지고 또 진화할 것이다.

관련글 더보기

댓글 영역