로버트 C. 마틴의 클린 아키텍처: 소프트웨어 구조와 설계의 원칙을 읽으며 기록하고 싶은 부분만 발췌해 각색하여 작성한 글입니다. 하여 실제 글쓴이의 의도와 다르게 작성될 수 있음을 알립니다.
12장. 컴포넌트
컴포넌트는 배포 단위다. 컴포넌트는 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위다. 컴포넌트가 마지막에 어떤 형태로 배포되든, 잘 설계된 컴포넌트라면 반드시 독립적으로 배포 가능한, 따라서 독립적으로 개발 가능한 능력을 갖춰야 한다.
13장. 컴포넌트 응집도
어떤 클래스를 어느 컴포넌트에 포함시켜야 할까?
이 장에서는 컴포넌트 응집도와 관련된 세 가지 원칙을 논의한다.
- REP(Reuse/Release Equivalence Principle): 재사용/ 릴리스 등가 원칙
- CCP(Common Closure Principle): 공통 폐쇄 원칙
- CRP(Common Reuse Principle): 공통 재사용 원칙
REP: 재사용/릴리스 등가 원칙
재사용 단위는 릴리스 단위와 같다.
새로운 릴리스가 나온다는 소식을 접하면, 개발자는 새 릴리스의 변경 사항을 살펴보고 기존 버전을 계속 쓸지 여부를 결정한다. 따라서 릴리스 절차에는 적절한 공지와 함께 릴리스 문서 작성도 포함되어야 한다.
이 원칙을 소프트웨어 설계와 아키텍처 관점에서 보면 단일 컴포넌트는 응집성 높은 클래스와 모듈들로 구성되어야 함을 뜻한다. 단순히 뒤죽박죽 임의로 선택된 클래스와 모듈로 구성되어서는 안 된다.컴포넌트를 구성하는 모든 모듈은 서로 공유하는 중요한 테마나 목적이 있어야 한다.
CCP: 공통 폐쇄 원칙
동일한 이유로 동일한 시점에 변경되는 클래스를 같은 컴포넌트로 묶어라. 서로 다른 시점에 다른 이유로 변경되는 클래스는 다른 컴포넌트로 분리하라
이 원칙은 단일 책임 원칙 SRP를 컴포넌트 관점에서 다시 쓴 것이다. SRP에서 단일 클래스는 변경의 이유가 여러 개 있어서는 안된다고 말하듯이, 공통 폐쇄 원칙에서도 마찬가지로 단일 컴포넌트는 변경의 이유가 여러 개 있어서는 안된다고 말한다.
대다수의 애플리케이션에서 유지보수성은 재사용성보다 훨씬 주요하다. 애플리케이션에서 코드가 반드시 변경되어야 한다면, 이러한 변경이 여러 컴포넌트 도처에 분산되어 발생하기 보다는, 차라리 변경 모두가 단일 컴포넌트에서 발생하는 편이 낫다. 만약 변경을 단일 컴포넌트로 제한할 수 있다면, 해당 컴포넌트만 재배포하면 된다. 변경된 컴포넌트에 의존하지 않는 다른 컴포넌트는 다시 검증하거나 배포할 필요가 없다. 이를 통해 소프트웨어를 릴리스, 재검증, 배포하는 일과 관련된 작업량을 최소화할 수 있다.
이 원칙은 개방 폐쇄 원칙과도 밀접하게 관련되어 있다. CCP에서는 동일한 유형의 변경에 대해 닫혀 있는 클래스들을 하나의 컴포넌트로 묶음으로써 OCP에서 얻은 교훈을 확대 적용한다. 따라서 변경이 필요한 요구사항이 발생했을 때, 그 변경이 영향을 주는 컴포넌트들이 최소한으로 한정될 가능성이 확실히 높아진다.
CRP: 공통 재사용 원칙
컴포넌트 사용자들을 필요하지 않는 것에 의존하게 강요하지 말라
개별 클래스가 단독으로 재사용되는 경우는 거의 없다. 대체로 재사용 가능한 클래스는 재사용 모듈의 일부로써 해당 모듈의 다른 클래스와 상호작용하는 경우가 많다. CRP에서는 이런 클래스들이 동일한 컴포넌트에 포함되어야 한다고 말한다.
이게 전부가 아니다. CRP는 동일한 컴포넌트로 묶어서는 안되는 클래스가 무엇인지도 말해준다. 어떤 컴포넌트가 다른 컴포넌트를 사용하면, 두 컴포넌트 사이에는 의존성이 생겨난다. 어쩌면 사용하는 컴포넌트가 사용되는 컴포넌트에서 단 하나의 클래스만 사용할 수도 있다. 그렇다고 해서 의존성은 조금도 약해지지 않는다.
의존하는 컴포넌트가 있다면 해당 컴포넌트의 모든 클래스에 대해 의존함을 확실히 인지해야 한다. 다시 말해, 한 컴포넌트에 속한 클래스들은 더 작게 그룹지을 수 없다. 즉, 그중 일부 클래스에만 의존하고 다른 클래스와는 독립적일 수 없음을 확실히 인지해야 한다. 그렇지 않다면 필요 이상으로 많은 컴포넌트를 재배포하느라 우리의 소중한 노력을 허비하게 된다.
# 컴포넌트 응집도에 대한 균형 다이어그램
REP와 CCP는 포함 원칙이며, 컴포넌트를 더욱 크게 만든다.
CRP는 배체 원칙이며, 컴포넌트를 더욱 작게 만든다.

오로지 REP와 CRP에만 중점을 두면, 사소한 변경이 생겼을 때 너무 많은 컴포넌트에 영향을 미친다. 반대로 CCP와 REP에만 과도하게 집중하면 불필요한 릴리스가 너무 빈번해진다.
일반적으로 프로젝트는 삼각형의 오른쪽에서 시작하는 편이며, 이때는 오직 재사용성만 희생하면 된다. 프로젝트가 성숙하고, 그 프로젝트로부터 파생된 또 다른 프로젝트가 시작되면, 프로젝트는 삼각형에서 점차점차 왼쪽으로 이동해 간다. 즉, 프로젝트의 컴포넌트 구조는 시간과 성숙도에 따라 변한다는 뜻이다. 다시 말해 프로젝트가 실제로 수행하는 일 자체보다는 프로젝트가 발전되고 사용되는 방법과 더 관련이 깊다.
14장. 컴포넌트 결합
이 장에서 다룰 세 가지 원칙은 컴포넌트 사이의 관계를 설명한다.
ADP: 의존성 비순환 원칙
컴포넌트 의존성 그래프에 순환이 있어서는 안된다.
무언가를 작동해서 만들어 두었는데, 다음에 그대로 다시 보면 전혀 돌아가지 않는 경험을 해본 적이 있는가? 누군가 마지막 작업 이후로, 작업에서 의존하고 있던 무언가를 수정했기 때문이다. 이는 많은 개발자가 동일한 소스 파일을 수정하는 환경에서 발생한다. 이 문제의 해결책으로 두 가지 방법이 발전되어 왔는데, 이 두 해결책은 모두 통신 업계에서 만들어졌다.
# 주 단위 빌드 (Weekly Build)
방법은 다음과 같다. 먼저 모든 개발자는 일주일의 첫 4일 동안은 서로를 신경 쓰지 않는다. 개발자는 모두 코드를 개인적으로 복사하여 작업하며, 전체적인 기준에서 작업을 어떻게 통합할지는 걱정하지 않는다. 그런 후 금요일이 되면 변경된 코드를 모두 통합하여 시스템을 빌드한다. 이 접근법은 5일 중 4일동안 개발자를 고립된 세계에서 살 수 있게 보장해 준다는 아주 멋진 장점을 가진다. 물론 단점은 금요일에 통합과 관련된 막대한 업보를 치러야 한다는 사실이다.
안타깝게도 프로젝트가 커지면 프로젝트 통합은 금요일 하루 만에 끝마치는 게 불가능해진다. 개발보다 통합에 드는 시간이 늘어나면서 팀의 효율성도 서서히 나빠진다.
# 순환 의존성 제거하기
이 문제의 해결책은 개발 환경을 릴리스 가능한 컴포넌트 단위로 분리하는 것이다. 이를 통해 컴포넌트는 개별 개발자 또는 단일 개발팀이 책임질 수 있는 작업 단위가 된다. 개발자가 해당 컴포넌트가 동작하도록 만든 후, 해당 컴포넌트를 릴리스하여 다른 개발자가 사용할 수 있도록 만든다. 담당 개발자는 이 컴포넌트에 릴리스 번호를 부여하고, 다른 팀에서 사용할 수 있는 디렉토리로 이동시킨다. 그런 다음 개발자는 다신만의 공간에서 해당 컴포넌트를 지속적으로 수정한다. 나머지 개발자는 릴리스된 버전을 사용한다.
컴포넌트가 새로 릴리스되어 사용할 수 있게 되면, 다른 팀에서는 새 릴리스를 당장 적용할지를 결정해야 한다. 적용하지 않기로 했다면 그냥 과거 버전의 릴리스를 계속 사용한다. 새릴리스를 적용할 준비가 되었다는 판단이 들면 새 릴리스를 사용하기 시작한다. 따라서 어떤 팀도 다른 팀에 의해 좌우되지 않는다. 특정 컴포넌트가 변경되더라도 다른 팀에 즉각 영향을 주지는 않는다. 각 팀은 특정 컴포넌트가 새롭게 릴리스되면 자신의 컴포넌트를 해당 컴포넌트에 맞게 수정할 시기를 스스로 결정할 수 있다. 뿐만 아니라 통합은 작고 점진적으로 이뤄진다.
이 같은 작업 절차는 단순하며 합리적이어서 널리 사용되는 방식이다. 하지만이 절차가 성공적으로 동작하려면 컴포넌트 사이의 의존성 구조를 반드시 관리해야 한다. 의존성 구조에 순환이 있어서는 안 된다.

그림 14.1을 보면 어느 컴포넌트에서 시작하더라도, 의존성 관계를 따라가면서 최초의 컴포넌트로 돌아갈 수 없다. 이 구조에는 순환이 없는 비순환 방향 그래프다.
- Main은 새로 릴리스되더라도 시스템에서 이로 인해 영향받는 컴포넌트가 전혀 없다.
- Presenters 컴포넌트 개발자가 이 컴포넌트를 테스트하고자 한다면, 단순히 현재 사용 중인 버전의 Interactors와 Entities를 이용해서 자체 버전을 빌드하면 그만이다.
- 전체 시스템 릴리스는 상향식으로 진행된다. Entities → Database, Interactors → Presenters, View, Controllers, Authorizer → Main 순으로 진행한다.
# 순환이 컴포넌트 의존성 그래프에 미치는 영향
예를 들어 Entities의 User 클래스가 Authorizer의 Permissions 클래스를 사용한다고 해보자. 이렇게 되면 그림 14.2처럼 순환 의존성이 발생한다. 이제 Dtatabase 컴포넌트를 새로 릴리스하려면 Entities 뿐만 아니라 Authorizer와도 호환되어야 한다. 그런데 Authorizer는 Interactors에 의존한다. 이로 인해 Entities, Authorizer, Interactors는 사실 하나의 거대한 컴포넌트가 되어 버린다.

의존성 그래프에 순환이 생기면 하나의 단위 테스트를 실행에도 많고 다양한 라이브러리와 다른 사람들의 작업물이 포함된다. 그리고 순환이 생기면 컴포넌트를 분리하기가 상당히 어려워지며 에러도 쉽게 발생한다.
# 순환끊기
1. 의존성 역전 원칙을 적용한다. 그림 14.3처럼 User가 필요로 하는 메서드를 제공하는 인터페이스를 생성하고, 이 인터페이스는 Entities에 위치시키고, Authorizer에서는 이 인터페이스를 상속받는다. 이렇게 하면 Entities와 Authorizer 사이의 의존성을 역전 시킬 수 있고, 이를 통해 순환을 끊을 수 있다.

2. Entities와 Authorizer가 모두 의존하는 새로운 컴포넌트를 만든다. 그리고 두 컴포넌트가 모두 의존하는 클래스들을 새로운 컴포넌트로 이동시킨다.

# 흐트러짐 (Jitters)
두 번째 해결책에서 시사하는 바는 요구사항이 변경되면 컴포넌트 구조도 변경될 수 있다는 사실이다. 실제로 애플리케이션이 성장함에 따라 컴포넌트 의존성 구조는 서서히 흐트러지며 또 성장한다. 따라서 의존성 구조에 순환이 발생하는지를 항상 관찰해야 한다.
# 하향식 (top-down) 설계
- 컴포넌트 구조는 하향식으로 설계될 수 없다. 컴포넌트는 시스템에서 가장 먼저 설계할 수 있는 대상이 아니며, 오히려 시스템이 성장하고 변경될 때 함께 진화한다.
- 사실 컴포넌트 의존성 다이어그램은 애플리케이션의 기능을 기술하는 일과는 거의 관련이 없고, 이는 애플리케이션의 빌드 가능성과 유지보수성을 보여주는 지도와 같다.
- 의존성 구조의 최우선 관심사는 변동성을 격리하는 일이다. 결국 컴포넌트 의존성 그래프는 자주 변경되는 컴포넌트로부터 안정적이며 가치가 높은 컴포넌트를 보호하려는 방향으로 가다듬게 된다.
SDP: 안정된 의존성 원칙
더 안정된 쪽에 의존하라.
변경이 쉽지 않은 컴포넌트가 변동이 예상되는 컴포넌트에 의존하게 만들어서는 절대로 안 된다. 한번 의존하게 되면 변동성이 큰 컴포넌트도 결국 변경이 어려워진다.
# 안정성
안정성은 변경을 만들기 위해 필요한 작업량과 관련된다. 소프트웨어 컴포넌트를 변경하기 어렵게 만드는 데는 많은 요인이 존재하며, 그 예로는 컴포넌트의 크기, 복잡도, 간결함 등을 들 수 있다. 소프트웨어 컴포넌트를 변경하기 어렵게 만드는 확실한 방법 하나는 수많은 다른 컴포넌트가 해당 컴포넌트에 의존하게 만드는 것이다. 컴포넌트 안쪽으로 들어오는 의존성이 많아지면 상당히 안정적이라고 볼 수 있는데, 사소한 변경이라도 의존하는 모든 컴포넌트를 만족시키면서 변경하려면 상당한 노력이 들기 때문이다.

그림 14.5의 X는 안정된 컴포넌트다. 세 컴포넌트가 X에 의존하며, 따라서 X 컴포넌트는 변경하지 말아야 할 이유가 세 가지나 되기 때문이다. 이 경우 'X는 세 컴포넌트를 책임진다'라고 말한다. 반대로 X는 어디에도 의존하지 않으므로 X가 변경되도록 만들 수 있는 외적인 영향이 전혀 없다. 이 경우 'X는 독립적이다'라고 말한다.

그림 14.6의 Y는 상당히 불안정한 컴포넌트다. 어떤 컴포넌트도 Y에 의존하지 않으므로 Y는 책임성이 없다고 말할 수 있다. 또한 Y는 세 개의 컴포넌트에 의존하므로 변경이 발생할 수 있는 외부 요인이 세가지다. 이 경우 Y는 의존적이라고 말한다.
# 안정성 지표
어떻게 하면 컴포넌트 안정성을 측정할 수 있을까? 컴포넌트로 들어오고 나가는 의존성의 개수를 새어 보는 방법이 있을 수 있다. 이 숫자를 통해 컴포넌트가 위치상 어느 정도의 안정성을 가지는지 계산할 수 있다.
- Fan-in: 안으로 들어오는 의존성. 이 지표는 컴포넌트 내부의 클래스에 의존하는 컴포넌트 외부의 클래스 개수를 나타낸다.
- Fan-out: 바깥으로 나가는 의존성. 이 지표는 컴포넌트 외부의 클래스에 의존하는 컴포넌트 내부의 클래스 개수를 나타낸다.
- I(불안정성): I = Fan-out ÷ (Fan-in + Fan-out). 이 지표는 [0, 1] 범위의 값을 갖는다. I = 0이면 최고로 안정된 컴포넌트이고, 1이면 최고로 불안정한 컴포넌트라는 뜻이다.
SAP: 안정된 추상화 원칙
컴포넌트는 안정된 정도만큼만 추상화되어야 한다.
# 고수준 정책을 어디에 위치시켜야 할까?
시스템에서 고수준 정책을 캡슐화하는 소프트웨어는 반드시 안정된 컴포넌트(I=0)에 위치해야 한다. 불안정한 컴포넌트(I=1)는 반드시 변동성이 큰 소프트웨어, 즉 쉽고 빠르게 변경할 수 있는 소프트웨어만을 포함해야 한다. 하지만 고수준 정책을 안정된 컴포넌트에 위치시키면, 그 정책을 포함하는 소스 코드는 수정하기가 어려워진다. 이로 인해 시스템 전체 아키텍처가 유연성을 잃는다. 이는 개방 폐쇄 원칙로 해결할 수 있다. 추상 클래스를 사용해서 클래스를 수정하지 않고도 확장이 충분히 가능할 정도로 클래스를 유연하게 만들 수 있다.
# 안정된 추상화 원칙
안정된 추상화 원칙은 안정성과 추상화 정도 사이의 관계를 정의한다. 안정된 컴포넌트는 추상 컴포넌트여야 하며, 이를 통해 안정성이 컴포넌트를 확장하는 일을 방해해서는 안 된다고 말한다. 다른 한편으로는 불안정한 컴포넌트는 반드시 구체 컴포넌트여야 한다고 말하는데, 컴포넌트가 불안정하므로 컴포넌트 내부의 구체적인 코드를 쉽게 변경할 수 있어야 하기 때문이다.
따라서 안정적인 컴포넌트라면 반드시 인터페이스와 추상 클래스로 구성되어 쉽게 확장할 수 있어야 한다. SDP에서는 의존성이 반드시 안정성의 방향으로 향해야 한다고 말하며, SAP에서는 안정성이 결국 추상화를 의미한다고 말한다. 따라서 의존성은 추상화의 방향으로 향하게 된다.
로버트 C. 마틴의 클린 아키텍처: 소프트웨어 구조와 설계의 원칙을 읽으며 기록하고 싶은 부분만 발췌해 각색하여 작성한 글입니다. 하여 실제 글쓴이의 의도와 다르게 작성될 수 있음을 알립니다.
12장. 컴포넌트
컴포넌트는 배포 단위다. 컴포넌트는 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위다. 컴포넌트가 마지막에 어떤 형태로 배포되든, 잘 설계된 컴포넌트라면 반드시 독립적으로 배포 가능한, 따라서 독립적으로 개발 가능한 능력을 갖춰야 한다.
13장. 컴포넌트 응집도
어떤 클래스를 어느 컴포넌트에 포함시켜야 할까?
이 장에서는 컴포넌트 응집도와 관련된 세 가지 원칙을 논의한다.
- REP(Reuse/Release Equivalence Principle): 재사용/ 릴리스 등가 원칙
- CCP(Common Closure Principle): 공통 폐쇄 원칙
- CRP(Common Reuse Principle): 공통 재사용 원칙
REP: 재사용/릴리스 등가 원칙
재사용 단위는 릴리스 단위와 같다.
새로운 릴리스가 나온다는 소식을 접하면, 개발자는 새 릴리스의 변경 사항을 살펴보고 기존 버전을 계속 쓸지 여부를 결정한다. 따라서 릴리스 절차에는 적절한 공지와 함께 릴리스 문서 작성도 포함되어야 한다.
이 원칙을 소프트웨어 설계와 아키텍처 관점에서 보면 단일 컴포넌트는 응집성 높은 클래스와 모듈들로 구성되어야 함을 뜻한다. 단순히 뒤죽박죽 임의로 선택된 클래스와 모듈로 구성되어서는 안 된다.컴포넌트를 구성하는 모든 모듈은 서로 공유하는 중요한 테마나 목적이 있어야 한다.
CCP: 공통 폐쇄 원칙
동일한 이유로 동일한 시점에 변경되는 클래스를 같은 컴포넌트로 묶어라. 서로 다른 시점에 다른 이유로 변경되는 클래스는 다른 컴포넌트로 분리하라
이 원칙은 단일 책임 원칙 SRP를 컴포넌트 관점에서 다시 쓴 것이다. SRP에서 단일 클래스는 변경의 이유가 여러 개 있어서는 안된다고 말하듯이, 공통 폐쇄 원칙에서도 마찬가지로 단일 컴포넌트는 변경의 이유가 여러 개 있어서는 안된다고 말한다.
대다수의 애플리케이션에서 유지보수성은 재사용성보다 훨씬 주요하다. 애플리케이션에서 코드가 반드시 변경되어야 한다면, 이러한 변경이 여러 컴포넌트 도처에 분산되어 발생하기 보다는, 차라리 변경 모두가 단일 컴포넌트에서 발생하는 편이 낫다. 만약 변경을 단일 컴포넌트로 제한할 수 있다면, 해당 컴포넌트만 재배포하면 된다. 변경된 컴포넌트에 의존하지 않는 다른 컴포넌트는 다시 검증하거나 배포할 필요가 없다. 이를 통해 소프트웨어를 릴리스, 재검증, 배포하는 일과 관련된 작업량을 최소화할 수 있다.
이 원칙은 개방 폐쇄 원칙과도 밀접하게 관련되어 있다. CCP에서는 동일한 유형의 변경에 대해 닫혀 있는 클래스들을 하나의 컴포넌트로 묶음으로써 OCP에서 얻은 교훈을 확대 적용한다. 따라서 변경이 필요한 요구사항이 발생했을 때, 그 변경이 영향을 주는 컴포넌트들이 최소한으로 한정될 가능성이 확실히 높아진다.
CRP: 공통 재사용 원칙
컴포넌트 사용자들을 필요하지 않는 것에 의존하게 강요하지 말라
개별 클래스가 단독으로 재사용되는 경우는 거의 없다. 대체로 재사용 가능한 클래스는 재사용 모듈의 일부로써 해당 모듈의 다른 클래스와 상호작용하는 경우가 많다. CRP에서는 이런 클래스들이 동일한 컴포넌트에 포함되어야 한다고 말한다.
이게 전부가 아니다. CRP는 동일한 컴포넌트로 묶어서는 안되는 클래스가 무엇인지도 말해준다. 어떤 컴포넌트가 다른 컴포넌트를 사용하면, 두 컴포넌트 사이에는 의존성이 생겨난다. 어쩌면 사용하는 컴포넌트가 사용되는 컴포넌트에서 단 하나의 클래스만 사용할 수도 있다. 그렇다고 해서 의존성은 조금도 약해지지 않는다.
의존하는 컴포넌트가 있다면 해당 컴포넌트의 모든 클래스에 대해 의존함을 확실히 인지해야 한다. 다시 말해, 한 컴포넌트에 속한 클래스들은 더 작게 그룹지을 수 없다. 즉, 그중 일부 클래스에만 의존하고 다른 클래스와는 독립적일 수 없음을 확실히 인지해야 한다. 그렇지 않다면 필요 이상으로 많은 컴포넌트를 재배포하느라 우리의 소중한 노력을 허비하게 된다.
# 컴포넌트 응집도에 대한 균형 다이어그램
REP와 CCP는 포함 원칙이며, 컴포넌트를 더욱 크게 만든다.
CRP는 배체 원칙이며, 컴포넌트를 더욱 작게 만든다.

오로지 REP와 CRP에만 중점을 두면, 사소한 변경이 생겼을 때 너무 많은 컴포넌트에 영향을 미친다. 반대로 CCP와 REP에만 과도하게 집중하면 불필요한 릴리스가 너무 빈번해진다.
일반적으로 프로젝트는 삼각형의 오른쪽에서 시작하는 편이며, 이때는 오직 재사용성만 희생하면 된다. 프로젝트가 성숙하고, 그 프로젝트로부터 파생된 또 다른 프로젝트가 시작되면, 프로젝트는 삼각형에서 점차점차 왼쪽으로 이동해 간다. 즉, 프로젝트의 컴포넌트 구조는 시간과 성숙도에 따라 변한다는 뜻이다. 다시 말해 프로젝트가 실제로 수행하는 일 자체보다는 프로젝트가 발전되고 사용되는 방법과 더 관련이 깊다.
14장. 컴포넌트 결합
이 장에서 다룰 세 가지 원칙은 컴포넌트 사이의 관계를 설명한다.
ADP: 의존성 비순환 원칙
컴포넌트 의존성 그래프에 순환이 있어서는 안된다.
무언가를 작동해서 만들어 두었는데, 다음에 그대로 다시 보면 전혀 돌아가지 않는 경험을 해본 적이 있는가? 누군가 마지막 작업 이후로, 작업에서 의존하고 있던 무언가를 수정했기 때문이다. 이는 많은 개발자가 동일한 소스 파일을 수정하는 환경에서 발생한다. 이 문제의 해결책으로 두 가지 방법이 발전되어 왔는데, 이 두 해결책은 모두 통신 업계에서 만들어졌다.
# 주 단위 빌드 (Weekly Build)
방법은 다음과 같다. 먼저 모든 개발자는 일주일의 첫 4일 동안은 서로를 신경 쓰지 않는다. 개발자는 모두 코드를 개인적으로 복사하여 작업하며, 전체적인 기준에서 작업을 어떻게 통합할지는 걱정하지 않는다. 그런 후 금요일이 되면 변경된 코드를 모두 통합하여 시스템을 빌드한다. 이 접근법은 5일 중 4일동안 개발자를 고립된 세계에서 살 수 있게 보장해 준다는 아주 멋진 장점을 가진다. 물론 단점은 금요일에 통합과 관련된 막대한 업보를 치러야 한다는 사실이다.
안타깝게도 프로젝트가 커지면 프로젝트 통합은 금요일 하루 만에 끝마치는 게 불가능해진다. 개발보다 통합에 드는 시간이 늘어나면서 팀의 효율성도 서서히 나빠진다.
# 순환 의존성 제거하기
이 문제의 해결책은 개발 환경을 릴리스 가능한 컴포넌트 단위로 분리하는 것이다. 이를 통해 컴포넌트는 개별 개발자 또는 단일 개발팀이 책임질 수 있는 작업 단위가 된다. 개발자가 해당 컴포넌트가 동작하도록 만든 후, 해당 컴포넌트를 릴리스하여 다른 개발자가 사용할 수 있도록 만든다. 담당 개발자는 이 컴포넌트에 릴리스 번호를 부여하고, 다른 팀에서 사용할 수 있는 디렉토리로 이동시킨다. 그런 다음 개발자는 다신만의 공간에서 해당 컴포넌트를 지속적으로 수정한다. 나머지 개발자는 릴리스된 버전을 사용한다.
컴포넌트가 새로 릴리스되어 사용할 수 있게 되면, 다른 팀에서는 새 릴리스를 당장 적용할지를 결정해야 한다. 적용하지 않기로 했다면 그냥 과거 버전의 릴리스를 계속 사용한다. 새릴리스를 적용할 준비가 되었다는 판단이 들면 새 릴리스를 사용하기 시작한다. 따라서 어떤 팀도 다른 팀에 의해 좌우되지 않는다. 특정 컴포넌트가 변경되더라도 다른 팀에 즉각 영향을 주지는 않는다. 각 팀은 특정 컴포넌트가 새롭게 릴리스되면 자신의 컴포넌트를 해당 컴포넌트에 맞게 수정할 시기를 스스로 결정할 수 있다. 뿐만 아니라 통합은 작고 점진적으로 이뤄진다.
이 같은 작업 절차는 단순하며 합리적이어서 널리 사용되는 방식이다. 하지만이 절차가 성공적으로 동작하려면 컴포넌트 사이의 의존성 구조를 반드시 관리해야 한다. 의존성 구조에 순환이 있어서는 안 된다.

그림 14.1을 보면 어느 컴포넌트에서 시작하더라도, 의존성 관계를 따라가면서 최초의 컴포넌트로 돌아갈 수 없다. 이 구조에는 순환이 없는 비순환 방향 그래프다.
- Main은 새로 릴리스되더라도 시스템에서 이로 인해 영향받는 컴포넌트가 전혀 없다.
- Presenters 컴포넌트 개발자가 이 컴포넌트를 테스트하고자 한다면, 단순히 현재 사용 중인 버전의 Interactors와 Entities를 이용해서 자체 버전을 빌드하면 그만이다.
- 전체 시스템 릴리스는 상향식으로 진행된다. Entities → Database, Interactors → Presenters, View, Controllers, Authorizer → Main 순으로 진행한다.
# 순환이 컴포넌트 의존성 그래프에 미치는 영향
예를 들어 Entities의 User 클래스가 Authorizer의 Permissions 클래스를 사용한다고 해보자. 이렇게 되면 그림 14.2처럼 순환 의존성이 발생한다. 이제 Dtatabase 컴포넌트를 새로 릴리스하려면 Entities 뿐만 아니라 Authorizer와도 호환되어야 한다. 그런데 Authorizer는 Interactors에 의존한다. 이로 인해 Entities, Authorizer, Interactors는 사실 하나의 거대한 컴포넌트가 되어 버린다.

의존성 그래프에 순환이 생기면 하나의 단위 테스트를 실행에도 많고 다양한 라이브러리와 다른 사람들의 작업물이 포함된다. 그리고 순환이 생기면 컴포넌트를 분리하기가 상당히 어려워지며 에러도 쉽게 발생한다.
# 순환끊기
1. 의존성 역전 원칙을 적용한다. 그림 14.3처럼 User가 필요로 하는 메서드를 제공하는 인터페이스를 생성하고, 이 인터페이스는 Entities에 위치시키고, Authorizer에서는 이 인터페이스를 상속받는다. 이렇게 하면 Entities와 Authorizer 사이의 의존성을 역전 시킬 수 있고, 이를 통해 순환을 끊을 수 있다.

2. Entities와 Authorizer가 모두 의존하는 새로운 컴포넌트를 만든다. 그리고 두 컴포넌트가 모두 의존하는 클래스들을 새로운 컴포넌트로 이동시킨다.

# 흐트러짐 (Jitters)
두 번째 해결책에서 시사하는 바는 요구사항이 변경되면 컴포넌트 구조도 변경될 수 있다는 사실이다. 실제로 애플리케이션이 성장함에 따라 컴포넌트 의존성 구조는 서서히 흐트러지며 또 성장한다. 따라서 의존성 구조에 순환이 발생하는지를 항상 관찰해야 한다.
# 하향식 (top-down) 설계
- 컴포넌트 구조는 하향식으로 설계될 수 없다. 컴포넌트는 시스템에서 가장 먼저 설계할 수 있는 대상이 아니며, 오히려 시스템이 성장하고 변경될 때 함께 진화한다.
- 사실 컴포넌트 의존성 다이어그램은 애플리케이션의 기능을 기술하는 일과는 거의 관련이 없고, 이는 애플리케이션의 빌드 가능성과 유지보수성을 보여주는 지도와 같다.
- 의존성 구조의 최우선 관심사는 변동성을 격리하는 일이다. 결국 컴포넌트 의존성 그래프는 자주 변경되는 컴포넌트로부터 안정적이며 가치가 높은 컴포넌트를 보호하려는 방향으로 가다듬게 된다.
SDP: 안정된 의존성 원칙
더 안정된 쪽에 의존하라.
변경이 쉽지 않은 컴포넌트가 변동이 예상되는 컴포넌트에 의존하게 만들어서는 절대로 안 된다. 한번 의존하게 되면 변동성이 큰 컴포넌트도 결국 변경이 어려워진다.
# 안정성
안정성은 변경을 만들기 위해 필요한 작업량과 관련된다. 소프트웨어 컴포넌트를 변경하기 어렵게 만드는 데는 많은 요인이 존재하며, 그 예로는 컴포넌트의 크기, 복잡도, 간결함 등을 들 수 있다. 소프트웨어 컴포넌트를 변경하기 어렵게 만드는 확실한 방법 하나는 수많은 다른 컴포넌트가 해당 컴포넌트에 의존하게 만드는 것이다. 컴포넌트 안쪽으로 들어오는 의존성이 많아지면 상당히 안정적이라고 볼 수 있는데, 사소한 변경이라도 의존하는 모든 컴포넌트를 만족시키면서 변경하려면 상당한 노력이 들기 때문이다.

그림 14.5의 X는 안정된 컴포넌트다. 세 컴포넌트가 X에 의존하며, 따라서 X 컴포넌트는 변경하지 말아야 할 이유가 세 가지나 되기 때문이다. 이 경우 'X는 세 컴포넌트를 책임진다'라고 말한다. 반대로 X는 어디에도 의존하지 않으므로 X가 변경되도록 만들 수 있는 외적인 영향이 전혀 없다. 이 경우 'X는 독립적이다'라고 말한다.

그림 14.6의 Y는 상당히 불안정한 컴포넌트다. 어떤 컴포넌트도 Y에 의존하지 않으므로 Y는 책임성이 없다고 말할 수 있다. 또한 Y는 세 개의 컴포넌트에 의존하므로 변경이 발생할 수 있는 외부 요인이 세가지다. 이 경우 Y는 의존적이라고 말한다.
# 안정성 지표
어떻게 하면 컴포넌트 안정성을 측정할 수 있을까? 컴포넌트로 들어오고 나가는 의존성의 개수를 새어 보는 방법이 있을 수 있다. 이 숫자를 통해 컴포넌트가 위치상 어느 정도의 안정성을 가지는지 계산할 수 있다.
- Fan-in: 안으로 들어오는 의존성. 이 지표는 컴포넌트 내부의 클래스에 의존하는 컴포넌트 외부의 클래스 개수를 나타낸다.
- Fan-out: 바깥으로 나가는 의존성. 이 지표는 컴포넌트 외부의 클래스에 의존하는 컴포넌트 내부의 클래스 개수를 나타낸다.
- I(불안정성): I = Fan-out ÷ (Fan-in + Fan-out). 이 지표는 [0, 1] 범위의 값을 갖는다. I = 0이면 최고로 안정된 컴포넌트이고, 1이면 최고로 불안정한 컴포넌트라는 뜻이다.
SAP: 안정된 추상화 원칙
컴포넌트는 안정된 정도만큼만 추상화되어야 한다.
# 고수준 정책을 어디에 위치시켜야 할까?
시스템에서 고수준 정책을 캡슐화하는 소프트웨어는 반드시 안정된 컴포넌트(I=0)에 위치해야 한다. 불안정한 컴포넌트(I=1)는 반드시 변동성이 큰 소프트웨어, 즉 쉽고 빠르게 변경할 수 있는 소프트웨어만을 포함해야 한다. 하지만 고수준 정책을 안정된 컴포넌트에 위치시키면, 그 정책을 포함하는 소스 코드는 수정하기가 어려워진다. 이로 인해 시스템 전체 아키텍처가 유연성을 잃는다. 이는 개방 폐쇄 원칙로 해결할 수 있다. 추상 클래스를 사용해서 클래스를 수정하지 않고도 확장이 충분히 가능할 정도로 클래스를 유연하게 만들 수 있다.
# 안정된 추상화 원칙
안정된 추상화 원칙은 안정성과 추상화 정도 사이의 관계를 정의한다. 안정된 컴포넌트는 추상 컴포넌트여야 하며, 이를 통해 안정성이 컴포넌트를 확장하는 일을 방해해서는 안 된다고 말한다. 다른 한편으로는 불안정한 컴포넌트는 반드시 구체 컴포넌트여야 한다고 말하는데, 컴포넌트가 불안정하므로 컴포넌트 내부의 구체적인 코드를 쉽게 변경할 수 있어야 하기 때문이다.
따라서 안정적인 컴포넌트라면 반드시 인터페이스와 추상 클래스로 구성되어 쉽게 확장할 수 있어야 한다. SDP에서는 의존성이 반드시 안정성의 방향으로 향해야 한다고 말하며, SAP에서는 안정성이 결국 추상화를 의미한다고 말한다. 따라서 의존성은 추상화의 방향으로 향하게 된다.