Component Design Principle
컴포넌트 설계 원칙(Component Design Principles)은 모듈화(Modularity), 변경 용이성(Changeability), 재사용성(Reusability), 유지보수성(Maintainability)을 극대화하기 위해 소프트웨어 컴포넌트의 구성 방식과 의존 관계를 정의하는 원칙임.
본 문서는 이 원칙들을 Cohesion과 Coupling, Interface Design 관점에서 정리함.
컴포넌트 == "독립적으로 개발할 수 있는 단위"
1. Cohesion 원칙

컴포넌트 내부 클래스 간의 응집도(cohesion)를 높이기 위한 원칙임.
같은 이유로 변경되거나 함께 재사용되는 클래스들을 묶고, 그렇지 않으면 분리해야 함이 핵심임.
- Single Responsibility Principle (SRP)와 연관되어 있음
높은 응집도(Cohesion)의 장점
1.1 REP (Reuse/Release Equivalence Principle)
재사용/릴리스 등가 원칙
- 재사용 가능한 컴포넌트는 함께 릴리스되어야 하며,
- 동일한 버전 관리, 릴리스 추적, 릴리스 문서를 공유해야 함.
- 즉, 컴포넌트는 릴리스 단위임.
1.2 CCP (Common Closure Principle)
공통 폐쇄 원칙
- 같은 이유로 변경되는 클래스들은 하나의 컴포넌트로 묶어야 함.
- 다른 이유로 변경되는 클래스들은 분리해야 함.
- 즉, 컴포넌트는 변경의 단위임.
- 유지보수성이 재사용성보다 중요한 경우가 많음.
- 변경의 파급 범위를 최소화하기 위한 설계 전략임.
1.3 CRP (Common Reuse Principle)
공통 재사용 원칙
- 함께 재사용되는 클래스는 하나의 컴포넌트로 묶어야 함.
- 사용하지 않는 클래스가 포함된 컴포넌트에 의존하지 않아야 함.
- 이는 ISP(인터페이스 분리 원칙)의 일반화된 형태임.
- 불필요한 의존성 제거와 재사용성 향상을 목표로 함.
1.4 각 원칙 간의 관계
- REP와 CCP는 포괄적(inclusive) 원칙으로, 컴포넌트를 더 크게 만들려는 경향이 있음.
- 반면, CRP는 배타적(exclusive) 원칙으로, 컴포넌트를 작게 만들려는 경향이 있음.
- 이 세 원칙은 서로 충돌하는 경우가 있으며, 균형 잡힌 설계가 필요함.
High Fan-in
많은 모듈이나 컴포넌트가 특정 하나의 모듈(또는 컴포넌트)을 참조하거나 호출하는 상황
- 장점
- 재사용성 증가: 하나의 컴포넌트가 다양한 곳에서 재사용됨
- 중복 제거: 반복되는 로직을 한 곳에 모을 수 있음
- 단점 / 주의점
- 변경의 영향 범위가 큼: 해당 컴포넌트를 수정하면 의존하는 모든 컴포넌트에 영향을 줄 수 있음
- 병목 가능성: 런타임 시 동시 호출이 많아지면 성능 저하나 동기화 문제 발생 가능
- 결합도 증가: 시스템 구조가 해당 공통 컴포넌트에 지나치게 의존하게 될 수 있음
2. Coupling 원칙

컴포넌트 간의 결합도는 모듈 간 상호 의존성의 정도를 나타냄.
결합도가 높을수록 다른 모듈의 변경이 연쇄적으로 영향을 주게 되며, 재사용성과 이해 용이성이 낮아짐.
컴포넌트 간의 결합을 효과적으로 제어하기 위한 세 가지 핵심 원칙을 설명함.
2.1 ADP (Acyclic Dependencies Principle)
비순환 의존 원칙
- “컴포넌트 의존성 그래프에 순환이 없어야 함.”
- 순환 의존이 존재할 경우, 빌드 순서 문제나 변경의 전파로 인해 코드 유지보수가 어려워짐.
- 흔히 발생하는 문제로는 Morning After Syndrome이 있으며, 이는 누군가의 변경사항이 다른 사람의 변경을 덮어쓰는 상황을 의미함.
- 예시: Entity → Interactor → Presenter → Controller → Main 순으로 구성된 계층에서, Entity가 다시 Controller를 참조하려 할 때 사이클이 생김.
- 이를 해결하는 방법은 다음과 같음:
- DIP(의존성 역전 원칙)를 적용하여 추상화 계층을 도입함.
- 양쪽 컴포넌트가 공통으로 참조할 수 있는 새로운 컴포넌트를 생성함.

2.2 SDP (Stable Dependency Principle)
안정적 의존 원칙
- “안정성의 방향으로 의존해야 함.”
- 안정적인 컴포넌트는 불안정한 컴포넌트에 의존해서는 안 됨.
- 불안정한 컴포넌트만 변경되도록 하고, 안정적인 컴포넌트는 변경으로부터 보호되어야 함.
- 안정성 지표(Instability):
I = Fan-out / (Fan-in + Fan-out)
- Fan-in: 외부에서 이 컴포넌트에 의존하는 클래스 수
- Fan-out: 이 컴포넌트가 외부 컴포넌트에 의존하는 클래스 수
- I 값은 0(매우 안정적) ~ 1(매우 불안정) 사이의 값을 가짐

- 모든 컴포넌트가 반드시 안정적일 필요는 없음.
변화가 자주 일어나는 컴포넌트는 오히려 불안정하게 유지하여 변경을 쉽게 해야 함.
2.3 SAP (Stable Abstractions Principle)
안정적 추상화 원칙
-
“컴포넌트는 안정적일수록 추상적이어야 함.”
-
자주 변경되지 않아야 하는 안정적인 컴포넌트(예: 정책, 아키텍처 핵심 로직)는 인터페이스나 추상 클래스로 설계해야 함.
-
반대로, 변화가 잦은 컴포넌트는 구체적이고 불안정해야 하며, 쉽게 수정될 수 있어야 함.
-
추상화 지표(Abstractness):
A = Na / Nc
- Na: 추상 클래스와 인터페이스 수
- Nc: 전체 클래스 수
- A 값은 0(완전 구체적) ~ 1(완전 추상적) 사이의 값을 가짐
-
SAP는 추상화(A)와 안정성(I)를 연계하여 Main Sequence(기울기 -1)에 가까운 위치에 설계되어야 함.
- (A=0, I=0): 안정적이고 구체적인 컴포넌트 → Zone of Pain (변경 어렵고 확장 어려움)
예: DB 스키마, 콘크리트 유틸리티
- (A=1, I=1): 추상적이지만 의존성 없는 컴포넌트 → Zone of Uselessness (사용되지 않음)
-
바람직한 위치는 A와 I가 비례하는 메인 시퀀스(Main Sequence) 근방이며,
너무 안정적인데도 추상화되지 않거나, 너무 추상적인데도 사용되지 않는 상태는 지양해야 함.

3. Component Interface Design Principles
컴포넌트의 외부 인터페이스를 정의할 때의 설계 원칙임.
3.1 Separate Interface
- 각 클라이언트가 사용하는 기능에 맞춰 인터페이스를 분리함.
3.2 ISP (Interface Segregation Principle)
- 클라이언트는 사용하지 않는 메서드에 의존해서는 안 됨.
- 작고 역할이 명확한 인터페이스로 나누는 것이 중요함.
3.3 Use Abstract Name
- 구체적인 구현보다는 역할 중심의 추상적 이름을 사용함.
- 결과를 드러내는 이름 (outcome-revealing name)
- 구현에 종속되지 않은 이름 (implementation-free name)
3.4 Make Interface Abstract
▸ Data Abstraction
- 파라미터 객체 도입
- 전체 객체 보존
- 추상 데이터 타입 사용
▸ Functional Abstraction
▸ Implementation Abstraction
- 컬렉션 캡슐화
- 파라미터를 메서드로 대체
- 명시적 메서드로 파라미터 처리
3.5 Minimize Dependency
▸ DIP (Dependency Inversion Principle)
- 상위 모듈은 하위 모듈에 의존하지 않음.
- 추상화에 의존하도록 설계해야 함.
▸ Law of Demeter (디메테르 법칙)
- “친한 친구에게만 말하라”
- 내부 구조를 외부에 노출하지 않고, 위임을 숨기는 설계를 지향함.