본문 바로가기

Book

컴포넌트 응집도, 결합

13장 컴포넌트 응집도

어떤 클래스를 어느 컴포넌트에 포함시켜야 하나? 이거에 관련한 컴포넌트 응집도와 관련된 세 가지 원칙에 대해 다룸

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

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

사실 재사용/등가 원칙은 너무 당연하게 여겨짐. 직관적으로만 봐도 릴리즈 번호가 다르면 컴포넌트들이 서로 호환되는지 보증할 방법이 없음.
이 원칙을 소프트웨어 설계와 아키텍쳐 관점에서 보면 단일 컴포넌트는 응집성 높은 클래스와 모듈들로 구성되어야 함을 뜻함.
하나의 컴포넌트로 묶인 클래스와 모듈은 반드시 함께 릴리스할 수 있어야 함. 하나의 컴포넌트로 묶인 클래스와 모듈은 버전 번호가 같아야 하며, 동일한 릴리즈 버전, 문서에 포함되어야 함.
이 뒤에 CCP와 CRP는 REP를 엄격하게, 하지만 제약을 가하는 측면에서 정의함

CCP: 공통 폐쇄 원칙

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

이 원칙은 SRP 를 컴포넌트 관점에서 다시 쓴 거임. 대다수의 애플리케이션에서 유지보수성은 재사용성보다 훨씬 중요함. 애플리케이션에서 코드가 반드시 변경되어야 한다면, 이러한 변경이 여러 컴포넌트 도처에 분산되어 발생하기보다는 , 차라리 모든 변경이 단일 컴포넌트에서 발생하는 편이 낫다고 함.
이 원칙은 OCP 와도 밀접하게 관련되어 있음. 발생할 가능성이 있거나 과거에 발생했던 대다수의 공통적인 변경에 대해서 클래스가 닫혀 있도록 설계해야 함.
동일한 유형의 변경에 대해 닫혀 있는 클래스들을 하나의 컴포넌트에 묶음으로써 변경이 필요한 요구사항이 발생했을때 영향을 주는 컴포넌트들이 최소한으로 한정될 가능성을 높이기 위함.

CRP: 공통 재사용 원칙

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

보통 개별 클래스가 단독으로 재사용되는 경우는 거의 없음. 대체로 재사용 가능한 클래스는 재사용 모듈의 일부로써 해당 모듈의 다른 클래스와 상호작용 하는 경우가 많음. CRP에서는 이런 클래스들이 동일한 컴포넌트에 포함되어야 한다고 말함. 이런 컴포넌트 내부에서는 클래스들 사이에 수많은 의존성이 있으리라 예상 할 수 있음.
예시로 컨테이너 클래스와 이터레이터 클래스같은 경우가 있음. CRP는 단순히 어떤 클래스를 포함시켜야 할지 말해줄 뿐만 아니라, 묶어서는 안될 클래스가 무엇인지도 말해 줌. 예를 들어 어떤 컴포넌트가 다른 컴포넌트에 있는 단 하나의 클래스만 사용해도 그건 의존하게 되는거임. 이런 의존성으로 별로 크게 관계도 없는 변경이 이뤄지면 사용하는 컴포넌트는 재컴파일,재검증, 재배포를 해야하는 가능성이 있음. 따라서 의존하는 컴포넌트가 있다면 해당 컴포넌트의 모든 클래스에 대해 의존함을 확실히 인지해야 함. 그래서 웬만하면 다른 컴포넌트와 애매하게 결합을 하게 만든 클래스가 있으면 최대한 동일 위치에 위치시키지 않는게 좋음.

ISP와의 관계

CRP는 인터페이스 분리 원칙의 포괄적인 버전임. ISP는 사용하지 않은 메서드가 있는 클래스에 의존하지 말라고 조언함.(그니까 사용하지 않는 메서드가 많으면 많을수록 안좋은거임) CRP는 그걸 컴포넌트로 확장한거임.

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

응집도에 관한 3가지 원칙은 서로 상충됨. REP와 CCP는 포함 원칙이고 CRP는 배제 원칙으로 컴포넌트를 더 작게 만듬. 각 변은 반대쪽 꼭지점에 있는 원칙을 포기했을 때 감수해야 할 비용을 나타냄


프로젝트 초기에는 CCP가 REP보다 훨씬 더 중요함, 개발 가능성이 재사용성보다 더욱 중요하기 때문임.
일반적으로 프로젝트는 삼각형의 오른쪽에서 시작하는 편이고, 이때는 오직 재사용성만 희생하면 됨. 프로젝트가 성숙해지면 점차 왼쪽으로 이동해 감.
그래서 매순간 고민해야함 잘 지켜지던 균형도 언제 깨질지 모름.

14장 컴포넌트 결합

3가지 원칙을 다룰거임, 이 원칙들은 컴포넌트 사이의 관계를 설명함.

ADP: 의존성 비순환 원칙

컴포넌트 의존성 그래프에 순환이 있으면 안 된다.
내가 작업한 이후에 다른 사람이 수정하게 되어서 작동하지 않는 경우가 생기는데 이런걸 '숙취 증후군' 이라고 함. 규모가 커지면 이런 문제가 발생하는데 해결 방법으로 2가지를 제안함. 하나는 '주 단위 빌드' 다른 하나는 의존성 비순환 원칙(ADP) 임. 근데 주 단위 빌드는 규모가 커지게 되면서 자연스럽게 점점 통합에 걸리는 시간이 늦어짐 그럼 금요일에 하던걸 점점 주의 중반으로 당겨오게 됨. 그러다가 못맞추면 그냥 격주로 하자고 하는 경우가 생김.
그래서 해결책은 순환 의존성을 제거하는건데, 릴리즈 가능한 컴포넌트 단위로 개발 환경을 분리하는거임. 개별 컴포넌트를 각 팀에서 개발하고 릴리즈 번호를 붙임. 다른 팀들은 상관없이 개발 가능함. 릴리즈된 버전을 사용하면 됨. 근데 이러한 결과를 만들어 내려면 각 컴포넌트의 의존성 구조를 반드시 관리해야함. 의존성 구조는 순환적인 구조를 가지면 안되고 DAG 형태로 되야함. 보통 의존하는 컴포넌트가 의존당하는 컴포넌트의 정보를 알고 있으니 의존당하는 컴포넌트는 수정되면 의존하는 컴포넌트 전반적으로 수정해야함.
아래 그래프에서도 Main은 수정되도 다른 컴포넌트 상관없는데 Entities같은 경우 모든 컴포넌트에 영향 줌

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

여기 예시로 엔티티의 User클래스가 authorizer에 의존하게 되면 어떻게 되는지 보여줌.
순환이 생기게 되어서 관련없는 Database 컴포넌트도 수정해야 하는 경우가 생길 수 있음. 순환이 생기면 전체 컴포넌트에 영향을 줄 수도 있음. 또한 Entities 컴포넌트를 테스트할 때 다른 Authorizer 와 Interactors 까지도 반드시 빌드하고 통합 해야함. 순환이 생기면 분리하기가 어려워짐. 그래서 순환 끊는 법은 2가지 방법이 있음.
하나는 의존성 역전 법칙 사용.


다른 하나는 아예 다른 컴포넌트를 만드는 거임

흐트러짐

두 번째 해결책에서 시사하는 바는 요구사항이 변경되면 컴포넌트 구조도 변경될 수 있다는 사실임.
순환이 발생하면 어떤 식으로든 끊어야함. 이 말은 새로운 컴포넌트를 생성하거나 의존성 구조가 더 커질수도 있음을 의미함.

하향식 설계

컴포넌트 구조는 하향식으로 설계될 수 없음. 컴포넌트는 시스템에서 가장 먼저 설계할 수 있는 대상이 아니며, 오히려 변경될 때 함께 진화한다.
컴포넌트 의존성 다이어그램은 애플리케이션의 기능을 기술하는 일과는 거의 관련이 없음. 오히려 컴포넌트 의존성 다이어그램은 애플리케이션의 빌드 가능성과 유지보수성을 보여주는 지도와 같다. 그렇기 때문에 컴포넌트 구조는 프로젝트 초기에 설계할 수 없음.
의존성 구조와 관련된 최우선 관심사는 변동성을 격리하는 일임.

SDP: 안정된 의존성 원칙

안정성의 방향으로 의존하라.

여기서는 안정성에 대한 정의와 그걸 측정하기 위한 지표들에 대해 다룸. 안정성은 컴포넌트 안쪽으로 들어오는 의존성이 많으면 많을수록 안정성이 높다고 볼 수 있음. 사실 수정되면 안되기 때문에 안정성이 높아야 한다는 거임. 아무튼 결론적으로 다른 컴포넌트로 부터 의존을 많이 사용되면 안정적임. 반대로 나가는 의존성만 있다면 그건 불안정 한거임. 용어가 나오는데, 다음과 같음

  • Fan-in: 안으로 들어오는 의존성
  • Fan-out: 바깥으로 나가는 의존성
  • I(불안정성): I= Fan-out / (Fan-in+ Fan-out) 이 지표는[0,1] 범위의 값을 가짐 I=0 이면 최고로 안정된 컴포넌트라는 뜻임, 반대로 1이면 최고로 불안정한 컴포넌트 임.

I가 1에 가까우면 그 컴포넌트는 책임성이 없으며 의존적임. 자신에게 의존하는 컴포넌트가 없으므로 이 컴포넌트는 변경하지 말아햐 할 이유가 없음. 0인 경우는 의존을 많이 하니까 변경 안하는게 좋음.

SDP에서 컴포넌트의 I 지표는 그 컴포넌트가 의존하는 다른 컴포넌트들의 I보다 커야 함. 즉, 의존성 방향으로 갈수록 I지표 값이 감소해야 함.

모든 컴포넌트가 안정적이어야 하는 것은 아님

안정적인 컴포넌트 아닌 컴포넌트 둘다 있어야 하고 그럴 수 밖에 없음 보통 의존성 다이어그램 표현할때 위에 있는 컴포넌트가 불안정하고 아래로 갈수록 안정적인걸 배치함. 만약 그걸 어기는 방향이 있다면 역시나 의존성 역전을 시켜야함.

SAP: 안정된 추상화 원칙

컴포넌트는 안정된 정도만큼만 추상화되어야 한다.

예를 들어 고수준 정책을 안정된 컴포넌트에 위치시키면, 그 정책을 포함하는 소스 코드는 수정하기가 어려움. 컴포넌트가 최고로 안정된 상태면서도 동시에 변경에 충분히 대응할 수 있을 정도로 유연하게 하려면 추상화를 잘 사용하면됨.
SAP는 안정성과 추상화 정도 사이의 관계를 정의함. 보통 안정된 컴포넌트는 추상 컴포넌트여야 하고, 불안정한 컴포넌트는 반드시 구체 컴포넌트여야함. 쉽게 변경할 수 있어야 하기 때문.
SAP와 SDP를 결합하면 컴포넌트에 대한 DIP나 마찬가지가 된다고 함.

추상화 정도 측적

추상화 정도는 컴포넌트 내의 추상 클래스와 인터페이스개수 / 컴포넌트 클래스 개수임
그니까 1이면 오직 추상 클래스만 있는거임. 아래 그림에서는 안정성과 추상화 정도 사이의 관계를 보여줌
보통 저 주계열 선상에 있는 컴포넌트가 바람직한거임. 고통의 구역을 자세히 보면 구체적인데 안정적임. 그니까 의존하는 외부 컴포넌트들이 많은데 구체화된 클래스가 많은거임. 이럼 힘들어짐. 변경이 잦아지게 됨.반대로 쓸모없는 구역은 추상화 클래스인데 의존하는 애들이 없음 그럼 필요가 없음.


보통 고통의 구역에 위치한 예시가 데이터베이스 스키마임. 변동성은 높은데 극단적으로 구체적이고 많은 컴포넌트가 의존함. 그래서 애플리케이션과 DB사이에 위치한 인터페이스는 관리하기 어려움. 또 다른 예시는 유틸리티 라이브러리들이 있음 예시로 자바 String 라이브러리 같은 경우임. 진짜 의존하는 애들이 많음 개발하는데 String 안쓰는경우는 없음. 아무튼 근데 변동이 없기 때문에 그래도 괜찮다고 볼 수 있는거임.

주계열과의 거리는 컴포넌트 상태를 평가하는데 좋은 지표임. 그니까 주계열 라인으로 릴리즈를 할때마다 멀어지면 체크할 필요가 있음.

결론

지표는 도움을 줄 수 있지만 완벽하지 않음. 의존성은 나쁜것도 있지만 좋은 의존성도 분명히 있음.

확실히 컴포넌트 부분은 익숙하지 않아서 이햐하는데 그전 내용들 보다는 자연스럽지는 않았음 그래도 의존성에 관한 이야기였기 때문에 어떻게 보면 클래스 단위에서 보는 관점이랑 비슷함. 당연히 다른 점도 있음.

 

 

참고 및 출처: <클린 아키텍처> (로버트 C.마틴지음, 송준이옮김, 인사이트 , 2019)

'Book' 카테고리의 다른 글

부분적 경계  (1) 2024.02.02
정책과 수준 그리고 업무 규칙  (0) 2024.01.19
프로그래밍 패러다임  (0) 2023.12.22
계층형 설계 1,2  (2) 2023.12.04
의존성 주입(그리고 부트스트래핑)  (0) 2023.11.16