본문으로 바로가기

클린 아키텍처 - 컴포넌트

category software engineering 2023. 7. 26. 16:41
728x90

컴포넌트

컴포넌트는 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위로 정의하는데, 아키텍처는 컴포넌트를 독립적으로 배포할 수 있도록 개발할 수 있어야 합니다.

 

재미있는 역사

프로그램 초기에는 프로그램을 메모리의 어느 위치에 로드 할지 중요하게 고려해야 했고 프로그램 위치가 결정되면 재배치가 불가능했습니다. 이 때는 라이브러리 함수의 소스 코드를 애플리케이션에 직접 포함시켜 단일 프로그램으로 컴파일 했었죠.

(참조용) 그리고 약 50년간 다음과 같은 과정을 거치며 발전해왔습니다.메모리가 작아 소스 코드를 메모리에 상주시킬 수 없었고 디스크와 같이 느린 장치를 통해 소스코드와 함수 라이브러리를 통짜로 여러 차례 읽음

더보기

1. 컴파일 시간을 단축시키기 위해 함수 라이브러리를 별도로 관리하고 컴파일하여 특정 메모리에 로드하고 이후에 애플리케이션을 로드한 뒤 Link를 사용하는 방법을 이용

2. 애플리케이션 또는 함수 라이브러리가 커질 때마다 메모리에 추가공간을 할당하고 두 개의 주소 세그먼트로 분리하고 다시 Link를 사용하는 방법으로 변경

3.로더를 통해 메모리에 재배치할 수 있도록 바이너리를 생성하도록 컴파일하여 (외부 참조를 외부 정의에 링크시킬 수 있는) 프로그램을 개별적으로 컴파일하고 로드할 수 있는 Linking Loader를 적용

4. 퍼포먼스 문제를 해결하고자 실행 파일을 만드는 링커는 오래 걸렸지만 한번 만들어둔 실행 파일은 빠르게 로드할 수 있는 로더를 분리

5. 링킹로더도 사용할 정도로 메모리가 갑자기 빨라짐

6. 그리고 현재

 

컴포넌트 응집도

어떤 클래스를 어느 컴포넌트에 시켜야할지는 REP(Reuse/Release Equivalence Principle), CCP(Common Closure Principle), CRP(Common Reuse Principle)를 통해 알아보자.

 

REP 재사용/릴리즈 등가 법칙(Reuse/Release Equivalence Principle)

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

단일 컴포넌트는 응징섭 높은 클래스와 모듈들로 구성되어야 하고 하나의 컴포넌트로 묶인 클래스와 모듈은 동일한 릴리즈로 추적 관리되어야 한다.

 

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

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

SRP 와 비슷한 맥락으로 단일 컴포넌트는 변경의 이유가 여러 개 있어서는 안된다. 애플리케이션에서 코드가 반드시 변경되어야 할 때 여러 컴포넌트에 분산되어 발생하는거보다 단일 컴포넌트에서 발생하는 것이 낫기 때문에 변경될 가능성이 있는 클래스는 모두 한곳으로 묶는것이 좋다.

 

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

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

같이 재사용되는 경향이 있는 클래스와 모듈들은 같은 컴포넌트에 포함해야 한다. 강하게 결합되지 않은 클래스들을 동일한 컴포넌트에 위치시켜서는 안된다.

위 세 가지 원칙은 서로 상충된다. 뛰어난 아키텍처라면 이 원칙들이 균형을 이루는 방법을 찾아야한다.

일반적으로 프로젝트 초기에는 CCP가 REP보다 훨씬 중요하고 나중에 REP가 중요한것 처럼 컴포넌트는 시간과 성숙도에 따라 변한다.

 

컴포넌트 결합

컴포넌트 사이의 관계를 알아보기 위해 ADP, SDP, SAP 세 가지 원칙을 보자.

ADP: 의존성 비순환 원칙(Acyclic Dependencies Principle)

📌 컴포넌트 의존성 그래프에 순환이 있어서는 안된다.

누군가가 마지막으로 수정한 코드가 하나의 코드를 망가뜨리고 그 코드를 수정하기 위해 또 다른 코드가 망가지기 때문이다. 이를 위해 주 단위 빌드와 의존성 비순환 원칙 두가지 방법을 소개해준다.

  • 주 단위 빌드

쉽게 말해 5일중 4일 동안 개발자들이 자유롭게 개발하고 마지막 하루에 통합에 관한 업보를 보내는 방법이다. 하지만 프로젝트 규모가 커질수록 감당하기 힘들다.

  • 의존성 비순환 원칙

DAG(Direct Acyclic Graph)로 컴포넌트를 구성하여 어느 컴포넌트에서 시작하더라도 의존성 관계를 따라가면서 최초의 컴포넌트로 갈 수 없게 구성해야한다. 만약 애플리케이션이 성장함에 따라 의존성이 생겼다면 의존성 역전 원칙(DIP)을 통해 인터페이스를 생성하여 순환 의존성을 피해야한다.

 

SDP: 안정된 의존성 법칙(Stable Dependencies Principle)

📌 안정성의 방향으로(더 안정된 쪽에) 의존하라.

변경하기 쉬운 컴포넌트는 변동성이 예상되는 컴포넌트에 의존해선 안된다. 한번 의존하면 변동성이 큰 컴포넌트도 변경하기 어려워진다. 그럼 더 안정된 컴포넌트란 뭘까?

아래 차트를 보면 X는 어디에도 의존하지 않아 X가 변경되도록 만드는 외적인 영향이 없다. X는 세 가지 컴포넌트를 책임지는 독립적인 컴포넌트이고 따라서 안정적이다. 하지만 Y는 불안정하다. 세 가지 컴포넌트가 바뀌면 Y 또한 변경이 발생하기 때문이다.

안정적이면 변경이 불가능하다는 소리이므로 불안정한(변경 가능한) 컴포넌트가 안정된(변경 불가능한) 컴포넌트에 의존하는것이 이상적이다.

 

SAP: 안정된 추상화 법칙(Stable Abstractions Principle)

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

컴포넌트가 최고로 안정된 상태이면서 동시에 변경에 대응할 수 있도록 유연하게 만들기 위해선 OCP를 생각하면 된다. ‘추상 클래스’와 마찬가지로 컴포넌트 또한 ‘추상 컴포넌트’로 만들면 안정된 그리고 유연한 컴포넌트를 만들 수 있다.

물론 파생 클래스(다른 추상 클래스로부터 파생된 추상 클래스)는 추상적이면서도 의존성을 갖는다. 합리적으로 컴포넌트가 위치할 수 없는 구역도 있는데 고통의 구역쓸모없는 구역이다.

고통의 구역은 데이터베이스 스키마와 같은 경우는 변동성이 높지만 극단적으로 구체적이고 많은 컴포넌트가 의존한다. String과 같은 라이브러리 또한 유틸리티 라이브러리이고 광범위하게 사용된다. 하지만 변동성이 없어 괜찮다. 문제는 변동성이 있는 컴포넌트이다.

쓸모없는 구역은 최고로 추상적이지만 아무도 의존하지 않는 구역이다. 다시 말해 전혀 사용되지 않으면서 시스템에 남아있는 코드이다.

남은 구역을 주계열이라고 정의하고 컴포넌트가 주계열에 가까이 있도록 구성하고 멀리 떨어져 있다면 재구성을 염두해두어야 한다. 릴리즈에 따라 가까이 있었지만 멀어질 수도 있다. 하지만 이러한 지표도 시는 아니라고 한다. 그럼 어떻게 하라는걸까