Featured image of post 13. 컴포넌트 응집도

13. 컴포넌트 응집도

4부 - 컴포넌트 원칙

이 장에서는 컴포넌트 응집도와 관련된 세 가지 원칙을 논의한다.

REP: 재사용/릴리스 등가 원칙

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

재사용 가능한 컴포넌트나 컴포넌트 라이브러리가 매우 많이 만들어짐에 따라 모듈 관리 도구가 점점 중요해졌다.

소프트웨어 컴포넌트가 릴리스 절차를 통해 추적 관리되지 않거나, 릴리스 번호가 부여되지 않는다면 해당 컴포넌트를 재사용하고 싶어도 할 수도 없고, 하지도 않을 것이다.

  • 릴리스 번호가 없다면 재사용 컴포넌트들이 서로 호환되는지 보증할 방법이 전혀 없다.
  • 릴리스 번호를 통해 새로운 버전이 언제 출시되고 무엇이 변했는지를 소프트웨어 개발자들이 알아야 한다.
    • 따라서 릴리스 절차에는 적절한 공지와 함께 릴리스 문서 작성도 포함되어야 한다.

이 원칙을 소프트웨어 설계와 아키텍처 관점에서 보면 단일 컴포넌트는 응집성 높은 클래스와 모듈들로 구성되어야 함을 뜻한다.

다른 시각으로 바라보면 하나의 컴포넌트로 묶인 클래스와 모듈은 반드시 함께 릴리스할 수 있어야 한다.

  • 하나의 컴포넌트로 묶인 클래스와 모듈은 버전 번호가 같아야 한다.
  • 동일한 릴리스로 추적 관리되어야 한다.
  • 동일한 릴리스 문서에 포함되어야 한다.

REP의 이러한 내용은 개발자들 간의 관습이라는 점이 약점이다. 하지만 그렇기 때문에 컴포넌트가 REP를 위배하면 컴포넌트 사용자가 알게 되고, 아키텍트로서 능력을 높게 평가하지 않을 것이다.

이 원칙의 약점은 다른 두 원칙이 지닌 강점을 통해 충분히 보완할 수 있다.

CCP: 공통 폐쇄 원칙

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

SRP와 유사성

이 원칙은 단일 책임 원칙(SRP)을 컴포넌트 관점에서 다시 쓴 원칙이다.

동일한 시점에 동일한 이유로 변경되는 것들을 한데 묶어라. 서로 다른 시점에 다른 이유로 변경되는 것들은 서로 분리하라.

SRP에서 클래스의 변경 이유는 하나여야 한다고 말하듯이, 공통 폐쇄 원칙에서도 마찬가지로 단일 컴포넌트는 변경의 이유가 여러개여서는 안된다고 말한다.

대다수의 애플리케이션에서 유지보수성(Maintainability)은 재사용성보다 훨씬 중요하다.

애플리케이션의 코드가 반드시 변경되어야 한다면, 여러 컴포넌트 도처에서 분산되어 발생하기 보다는, 변경 모두가 단일 컴포넌트에서 발생하는 편이 낫다.

변경을 단일 컴포넌트로 제한할 수 있다면, 해당 컴포넌트만 재배포하면 되므로 의존하지 않는 다른 컴포넌트는 다시 검증하거나 배포할 필요가 없어진다.

CCP는 같은 이유로 변경될 가능성이 있는 클래스는 모두 한곳으로 묶을 것을 권한다.

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

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

OCP와의 유사성

이 원칙은 개방 폐쇄 원칙과도 밀접하게 관련되어 있다.

OCP에서는 클래스가 변경에는 닫혀있고 확장에는 열려있어야 한다고 말하는데, 100% 완전한 폐쇄란 불가능하므로 전략적으로 폐쇄해야 하며, 이에 따라 발생할 가능성이 있거나 과거에 발생했던 대다수의 공통적인 변경에 대해서 클래스가 닫혀 있도록 설계한다.

CCP에서는 동일한 유형의 변경에 대해 닫혀있는 클래스들을 하나의 컴포넌트로 묶음으로써 OCP에서 얻은 교훈을 확대 적용한다.

따라서 변경이 필요한 요구사항이 발생했을 때, 그 변경이 영향을 주는 컴포넌트들이 최소한으로 한정될 가능성이 확실히 높아진다.

CRP: 공통 재사용 원칙

컴포넌트 사용자들을 필요하지 않는 것에 의존하게 강요하지 말라.

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

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

재사용 가능한 클래스는 재사용 모듈의 일부로써 해당 모듈의 다른 클래스와 상호작용하는 경우가 많은데, CRP에서는 이런 클래스들이 동일한 컴포넌트에 포함되어야 한다고 말한다.

  • 예를 들어 컨테이너 클래스와 해당 클래스의 이터레이터 클래스는 서로 강하게 결합되어 있기 때문에 함께 재사용되므로 이들 클래스는 반드시 동일한 컴포넌트에 위치해야 한다.

이처럼 CRP는 각 컴포넌트에 어떤 클래스들을 포함시켜야 하는지를 설명해준다.

ISP와의 관계

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

필요하지 않은 것에 의존하지 말라.

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

이를 통해 CRP는 동일한 컴포넌트로 묶어서는 안되는 클래스가 무엇인지도 말해준다.

어떤 컴포넌트가 다른 컴포넌트를 사용하면, 두 컴포넌트 사이에는 의존성이 생겨난다.

사용하는 컴포넌트가 사용되는 컴포넌트의 극히 일부만을 사용한다고 하더라도 의존성은 조금도 약해지지 않는다.

  • 사용되는 컴포넌트가 변경될 때마다 사용하는 컴포넌트도 변경해야 할 가능성이 높다.
  • 사용하는 컴포넌트를 변경하지 않더라도, 재컴파일, 재검증, 재배포를 해야 하는 가능성은 여전히 남아있다.

따라서 의존하는 컴포넌트가 있다면 해당 컴포넌트의 모든 클래스에 대해 의존함을 확실히 인지해야한다.

  • 한 컴포넌트에 속한 클래스들은 더 작게 그룹지을 수 없다.
  • 그 중 일부 클래스에만 의존하고 다른 클래스와는 독립적일 수 없음을 확실히 인지해야한다.

CRP는 어떤 클래스를 한데 묶어도 되는지보다는, 어떤 클래스를 한데 묶어서는 안 되는지에 대해 훨씬 더 많은 것을 이야기한다.

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

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

응집도 관점으로 세 원칙이 상충된다.

  • 포함 원칙: REP, CCP
    • 컴포넌트의 크기를 더욱 크게 만든다.
  • 배제 원칙: CRP
    • 컴포넌트를 더욱 작게 만든다.

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

  • REP와 CRP에만 중점을 두면, 사소한 변경이 생겼을 때 너무 많은 컴포넌트에 영향을 미친다.
  • CCP와 REP에만 과도하게 집중하면 불필요한 릴리스가 너무 빈번해진다.

이 균형 삼각형에서 개발팀이 현재 관심을 기울이는 부분을 충족시키는 위치를 찾아야 하며, 시간이 흐르면서 개발팀이 주의를 기울이는 부분 역시 변한다는 사실도 이해하고 있어야 한다.

  • ex) 프로젝트 초기에는 개발 가능성이 더욱 중요하므로 CCP가 REP보다 훨씬 더 중요하다.

일반적으로 프로젝트는 삼각형의 오른쪽에서 시작하여 재사용성을 희생한다.

프로젝트가 성숙하고, 파생된 또 다른 프로젝트가 시작되면, 점차 왼쪽으로 이동해간다.

즉, 프로젝트의 컴포넌트 구조는 시간과 성숙도에 따라 변하며, 프로젝트가 실제로 수행하는 일 자체보다는 프로젝트가 발전되고 사용되는 방법과 더 관련이 깊다.

결론

과거에는 응집도를 ‘모듈은 단 하나의 기능만 수행해야한다.‘는 속성 정도로 훌씬 단순했다.

하지만 컴포넌트 응집도에 관한 세 가지 원칙은 응집도가 가질 수 있는 훨씬 복잡한 다양성을 설명해 준다.

어느 클래스들을 묶어서 컴포넌트로 만들지 결정할 때, 재사용성과 개발 가능성이라는 상충하는 힘을 고려하여 애플리케이션의 요구에 맞게 균형을 잡는 일은 중요하다.

이 균형점은 거의 항상 유동적이므로 컴포넌트를 구성하는 방식도 조금씩 흐트러지고 또 진화해야한다.