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