byungil
12장. 컴포넌트
컴포넌트는 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위이며, 자바의 경우 jar파일이 컴포넌트임
컴포넌트는 다양한 형태로 만들어질 수 있음
여러 컴포넌트를 서로 링크하여 실행 가능한 단일 파일로 생성할 수 있음
여러 컴포넌트를 묶어서 war 파일과 같은 단일 아카이브로 만들 수 있음
컴포넌트 각각을 jar과 같이 동적으로 로드 할 수 있는 플러그인이나 실행 가능한 파일로 만들어 독립적으로 배포할 수 있음
컴포넌트가 최종적으로 어떤 형태로 배포되든, 잘 설계된 컴포넌트라면 반드시 독립적으로 개발 가능해야 함
13장. 컴포넌트 응집도
REP: 재사용/릴리스 등가 원칙
정의: 재사용 단위는 릴리스 단위와 같다.
이는 당연한데, 컴포넌트가 릴리스 절차를 통해 관리되지 않거나, 릴리스 번호가 없다면 재사용하고 싶지도, 할 수도 없음
릴리스 번호가 없다면 컴포넌트들이 호환되는지 보증할 수 없음
개발자는 새로운 버전이 언제 출시되고 무엇이 변했는지 알아야 함(새로운 버전으로의 통합 여부 및 시기 결정)
REP를 소프트웨어 설계와 아키텍처 관점에서 보면 다음과 같음
단일 컴포넌트는 응집성이 높은 클래스와 모듈들로 구성되어야 함
이를 다르게 보면 하나의 컴포넌트로 묶인 클래스와 모듈은 반드시 함께 릴리스 할 수 있어야 한다는 것
하나의 컴포넌트로 묶인 클래스와 모듈은 버전이 같고, 동일한 릴리스로 관리되고, 동일한 릴리스 문서에 포함되어야 함
이 원칙 만으로는 클래스와 모듈을 단일 컴포넌트로 묶는 방법을 제대로 설명하지 못하지만(약점), 이 원칙 자체는 중요함
이 원칙을 어기는건 쉽게 발견할 수 있으며, 이 원칙의 약점은 다음 두 원칙(CCP와 CRP)으로 보완할 수 있음
CCP: 공통 폐쇄 원칙
정의: 동일한 이유로 동일한 시점에 변경되는 클래스는 같은 컴포넌트로 묶고, 다른 시점에 다른 이유로 변경되는 클래스는 분리하라
대다수의 애플리케이션에서 유지보수성은 재사용성보다 훨씬 중요하며, 변경은 단일 컴포넌트에서 발생해야 함(독립적인 배포)
이를 통해 소프트웨어 릴리스, 재검증, 배포와 관련된 작업량을 최소화할 수 있음
이는 OCP와도 관련이 있으며, 발생 가능성이 있거나 발생했던 대다수의 동일한 변경에 대해 클래스가 닫혀 있도록 설계해야 함
CCP는 SRP를 컴포넌트 관점에서 다시 적은 것이며, 단일 컴포넌트는 변경의 이유가 여러 개 있어서는 안됨
SRP: 서로 다른 이유로 변경되는 메소드를 서로 다른 클래스로 분리하라
CCP: 서로 다른 이유로 변경되는 클래스를 서로 다른 컴포넌트로 분리하라
CRP: 공통 재사용 원칙
정의: 컴포넌트 사용자들을 필요로 하지 않는 것에 의존하게 강요하지 말라.
CRP도 클래스와 모듈을 어느 컴포넌트에 위치시킬지 결정할 때 도움이 됨
같이 재사용되는 경향이 있는 클래스와 모듈들은 같은 컴포넌트에 포함해야 한다고 말함
개발 클래스가 단독으로 재사용되는 경우는 거의 없음
대체로 재사용 가능한 클래스는 재사용 모듈의 일부로써 해당 모듈의 다른 클래스와 상호작용함
CRP에서는 이런 클래스들이 동일한 컴포넌트에 포함되어 있어야 한다고 말함
CRP는 심지어 동일한 컴포넌트로 묶어서는 안되는 클래스도 말해줌
어떤 컴포넌트가 다른 컴포넌트를 사용하면, 두 컴포넌트 사이에는 의존성이 생김
사용되는 컴포넌트에서 단 하나의 클래스만 사용할 수도 있는데, 그렇다고 해서 의존성은 약해지지 않음
이로 인해 사용되는 컴포넌트가 변경될 때마다 같이 변경해야 할 가능성이 높음
그러므로 의존하는 컴포넌트가 있다면 해당 컴포넌트의 모든 클래스에 대해 의존함을 확실히 인지해야 함
그렇지 않으면 필요 이상으로 많은 컴포넌트를 재배포하느라 노력을 허비하게 됨
CRP는 어떤 클래스를 한데 묶어도 되는지보다는, 어떤 클래스를 한데 묶어서는 안되는 지에 대해 훨씬 많은 것을 이야기함
CRP는 강하게 결합되지 않은 클래스들을 동일한 컴포넌트에 위치시켜서는 안 된다고 말함
CRP는 ISP의 포괄 버전이며, 모두 필요하지 않은 것에 의존하지 말라는 것으로 요약됨
ISP: 사용하지 않는 메소드가 있는 클래스에 의존하지 말라
CRP: 사용하지 않는 클래스를 가진 컴포넌트에 의존하지 말라
컴포넌트 응집도에 대한 균형 다이어그램
각 변은 반대쪽 꼭지점에 있는 원칙을 포기했을 때 감수해야 할 비용을 나타냄
CCP를 포기하면, 사소한 변경이 생겼을 때 너무 많은 컴포넌트에 영향이 미침
CRP를 포기하면 불필요한 릴리스가 너무 빈번해짐
REP를 포기하면 재사용이 어려워짐
뛰어난 아키텍트라면 개발팀이 현재 관심을 기울이는 부분을 충족시키는 위치(균형)을 찾아야 함
또한 시간이 흐르면서 개발팀이 주의를 기울이는 부분 역시 변한다는 사실도 이해하고 있어야 함
14장. 컴포넌트 결합
ADP: 의존성 비순환 원칙
정의: 컴포넌트 의존성 그래프에 순환이 있어서는 안된다.
많은 개발자가 동일한 소스 파일을 수정하는 환경에서 코드가 동작하지 않게 될 수 있으며, 2가지 해결방법이 발전되어 옴
주단위 빌드
순환 의존성 제거하기
주단위 빌드
4일은 서로를 신경쓰지 않고, 금요일이 되면 코드를 통합하여 시스템을 빌드함
프로젝트가 커지면서 통합에 드는 시간이 계속해서 늘어나게 됨
결국 빌드 일정을 늘려야 하고, 통합과 테스트는 수행하기 점점 어려워지며, 빠른 피드백이 주는 장점을 잃게됨
순환 의존성 제거하기
이 문제의 해결책은 개발 환경을 릴리스 가능한 컴포넌트 단위로 분리하는 것
이를 통해 컴포넌트는 개별 관리자 또는 단일 개발팀이 책임질 수 있는 작업 단위가 됨
개발자가 해당 컴포넌트가 동작하도록 만든 후, 릴리스하여 다른 개발자가 사용할 수 있도록 만듬
이는 단순하며 합리적이라 널리 사용되지만 컴포넌트 사이의 의존성 구조를 반드시 관리해야 함
의존성 구조에 순환이 있어서는 안되며, 컴포넌트 간의 의존성은 비순환 방향 그래프(DAG, Directed Acyclic Graph)여야 함
하향식(top-down) 설계
프로젝트 초기에는 컴포넌트 구조를 설계할 수 없음. 즉, 컴포넌트 구조는 하향식(top-down)으로 설계될 수 없음
컴포넌트 의존성 다이어그램은 애플리케이션 기능과는 거의 관련이 없고, 빌드 가능성과 유지보수성의 지도와 같음
빌드 및 유지보수할 소프트웨어가 없다면, 지도 또한 필요 없으므로 컴포넌트 구조는 프로젝트 초기에 설계할 수 없음
컴포넌트는 시스템에서 가장 먼저 설계할 수 있는 대상이 아니며, 시스템이 성장하고 변경될 때 함께 진화함
하지만 프로젝트 초기에 모듈들이 점차 쌓이기 시작함
의존성 관리에 대한 요구가 점차 늘어나고, 변경되는 범위가 시스템의 가능한 한 작은 일부로 한정되기를 원함
그래서 단일 책임 원칙과 공통 폐쇄 원칙에 관심을 갖고, 이를 적용해 함께 변경되는 클래스는 같은 위치에 배치시킴
의존성 구조와 관련된 최우선 관심사는 변동성의 격리(자주 변경되는 컴포넌트로 부터 다른 컴포넌트를 보호함)
애플리케이션이 계속 성장하면서 재사용 가능한 요소를 만드는 일에 관심을 기울이기 시작함
이 시점에는 컴포넌트를 조합하는 과정에 공통 재사용 원칙이 영향을 미치기 시작함
결국 순환이 발생하면 ADP가 적용되고, 컴포넌트 의존성 그래프는 조금씩 흐트러지고 또 성장함
SDP: 안정된 의존성 원칙
정의: 안정성의 방향으로(더 안정된 쪽에) 의존하라.
컴포넌트 중 일부는 변동성을 지니는데, 이때 변경이 어려운 컴포넌트가 변동이 예상되는 컴포넌트에 절대 의존해서는 안됨
변경이 어려운 컴포넌트에 한번 의존하게 되면 변동성이 큰 컴포넌트도 결국 변경이 어려워짐
즉, 변경하기 쉽도록 모듈을 설계해도 이 모듈에 누군가가 의존성을 메달아 버리면 이 모듈도 변경하기 어려워짐
이때 안정된 의존성 원칙을 준수하면 변경하기 어려운 모듈이, 변경하기 쉽게 만들어진 모듈에 의존하지 않도록 만들 수 있음
안정성 지표
컴포넌트로 들어오고 나가는 의존성의 개수를 통해 컴포넌트의 불안정성(I)을 계산할 수 있음
fan-in: 안으로 들어오는 의존성으로 컴포넌트 내부의 클래스에 의존하는 컴포넌트 외부의 클래스 개수
fan-out: 바깥으로 나가는 의존성으로 컴포넌트 외부의 클래스에 의존하는 컴포넌트 내부의 클래스 개수
불안정성(I)은 fan-out / (fan-in + fan-out)으로 계산 가능하며, [0, 1] 사이의 값을 가짐
불안정성(I)가 0인 경우(X)
해당 컴포넌트에 의존하는 다른 컴포넌트는 있지만, 해당 컴포넌트 자체는 다른 컴포넌트에 의존하지 않음
이는 컴포넌트가 가질 수 있는 최고로 안정된 상태이며, 이러한 컴포넌트는 다른 컴포넌트를 책임지며 독립적임
자신에게 의존하는 컴포넌트가 있으므로 변경이 어렵지만, 해당 컴포넌트를 변경하도록 강제하는 의존성은 갖지 않음
불안정성(I)가 1인 경우(Y)
어떤 컴포넌트도 해당 컴포넌트에 의존하지 않지만, 해당 컴포넌트는 다른 컴포넌트에 의존함
최고로 불안정한 컴포넌트이며 책임성이 없으므로 의존적임
의존하는 컴포넌트가 없으므로 변경하지 말아야 할 이유가 없음
반대로 이 컴포넌트가 다른 컴포넌트에 의존한다는 사실은 언젠가 해당 컴포넌트를 변경할 이유가 있다는 뜻임
SAP: 안정된 추상화 원칙
컴포넌트는 안정된 정도만큼만 추상화되어야 한다.
안정된 추상화 원칙(SAP)
안정된 추상화 원칙은 안정성과 추상화 정도 사이의 관계를 정의함
안정된 컴포넌트는 추상 컴포넌트여야 하며, 이를 통해 안정성이 컴포넌트를 확장하는 일을 방해해서는 안됨
불안정한 컴포넌트는 반드시 구체 컴포넌트로써, 컴포넌트가 불안정하므로 내부의 구체적인 코드를 쉽게 변경할 수 있어야 함
주계열
최고로 안정적이며 추상화된 컴포넌트는 좌측 상단(0,1), 최고로 불안정하며 구체화된 컴포넌트는 (1,0)에 위치함
고통의 영역
(0, 0) 주변 구역에 위치한 컴포넌트들
매우 안정적이며 구체적인데, 컴포넌트가 뻣뻣한 상태이므로 바람직하지 않음
추상적이지 않으므로 확장이 어렵고, 안정적이므로 변경이 어려움
제대로 설계된 컴포넌트라면 여기에 위치하지 않으며, 배제해야 하는 구역임
쓸모없는 구역
(1, 1) 주변 구역에 위치한 컴포넌트들
최고로 추상적이지만, 누구도 그 컴포넌트에 의존하지 않음(쓸모 없음)
이는 누구도 구현하지 않은 채 남겨진 추상클래스인 경우가 많음
Last updated