컴포넌트 설계 원칙: 대규모 설계를 위한 모듈화 (REP, CCP, CRP)
컴포넌트 설계 원칙
대규모 설계를 위한 모듈화 – REP, CCP, CRP
대규모 소프트웨어 시스템에서 가장 어려운 문제는
기능을 만드는 것보다 변경을 통제하는 것이다.
시스템 규모가 커질수록 변경은 빈번해지고,
잘못 정의된 모듈 경계는 작은 수정 하나를
예상치 못한 영역까지 전파시킨다.
이러한 문제를 해결하기 위해 로버트 C. 마틴(Uncle Bob)은
컴포넌트 응집도 원칙(Component Cohesion Principles) 을 제시했다.
이 글에서는 다음 세 가지 핵심 원칙을 중심으로
대규모 설계를 위한 모듈화 전략을 살펴본다.
- REP (Reuse / Release Equivalence Principle)
- CCP (Common Closure Principle)
- CRP (Common Reuse Principle)
컴포넌트란 무엇인가?
컴포넌트는 단순한 폴더나 패키지 구조가 아니다.
독립적으로 배포되고, 버전 관리되며, 재사용 가능한 단위
예를 들면 다음과 같다.
- 라이브러리
- 모듈
- 패키지
- 마이크로서비스
컴포넌트 설계의 본질은
의존성의 방향과 변경 전파 범위를 설계하는 것이다.
REP – Reuse / Release Equivalence Principle
재사용 단위는 릴리스 단위와 같아야 한다
REP의 핵심 메시지
함께 재사용되는 코드들은 함께 릴리스되어야 한다.
REP가 필요한 이유
어떤 컴포넌트를 의존한다는 것은 단순히 코드를 가져다 쓰는 것이 아니라
그 컴포넌트의 버전 변경과 내부 변경까지 함께 받아들이는 것을 의미한다.
하지만 실제 시스템에서는 다음과 같은 상황이 자주 발생한다.
- 컴포넌트 내부의 일부 클래스만 사용
- 나머지 클래스들은 전혀 사용하지 않음
- 그럼에도 불구하고 전체 컴포넌트 변경에 영향을 받음
이는 컴포넌트 경계가 재사용 관점에서 부적절하게 설계되었음을 의미한다.
REP를 위반했을 때의 문제
- 사소한 변경에도 전체 컴포넌트 재배포 필요
- 사용하지 않는 기능 변경까지 의존하게 됨
- 재사용의 이점보다 유지보수 비용이 증가
설계 관점 정리
- 함께 재사용되는 코드만 하나의 컴포넌트로 묶는다
- 컴포넌트는 명확한 재사용 목적을 가져야 한다
- 릴리스 단위는 의도적으로 설계되어야 한다
CCP – Common Closure Principle
같이 변경되는 것들은 같이 묶어라
CCP의 핵심 메시지
동일한 이유로 변경되는 클래스들은 동일한 컴포넌트에 있어야 한다.
CCP는 SOLID 원칙 중
단일 책임 원칙(SRP) 을 컴포넌트 수준으로 확장한 개념이다.
CCP가 중요한 이유
대규모 시스템에서 비용의 대부분은 변경 비용에서 발생한다.
- 비즈니스 정책 변경
- 요구사항 변경
- 규칙 및 계산 로직 변경
이때 변경 대상이 여러 컴포넌트에 분산되어 있다면 다음과 같은 문제가 생긴다.
- 여러 컴포넌트 동시 수정
- 여러 번의 릴리스 필요
- 테스트 범위 급격히 증가
CCP 적용 예시
결제 도메인을 예로 들면 다음과 같은 요소들이 있다.
- 할인 정책
- 수수료 계산
- 결제 상태 전이
이들은 모두 결제 정책 변경이라는
동일한 변경 이유를 공유한다.
👉 따라서 하나의 컴포넌트로 묶는 것이 변경 비용을 최소화한다.
핵심 포인트
- 기술적 유사성보다 변경의 이유가 더 중요하다
- 변경의 축(axis of change)을 기준으로 컴포넌트를 설계한다
CRP – Common Reuse Principle
함께 사용되지 않는 것은 함께 의존하게 하지 마라
CRP의 핵심 메시지
컴포넌트에 의존하는 사용자는
그 컴포넌트의 모든 클래스에 의존하게 된다.
즉, 하나의 컴포넌트에 포함된 클래스들은
항상 함께 재사용된다고 가정해야 한다.
CRP가 다루는 문제
컴포넌트가 너무 크거나 성격이 다른 기능들을 포함하면
다음과 같은 문제가 발생한다.
- 사용하지 않는 코드 변경에도 영향받음
- 의존성 전파 범위가 불필요하게 커짐
- 변경 이유가 다른 코드들이 강하게 결합됨
이는 시스템 전반에 불필요한 결합(tight coupling) 을 유발한다.
대표적인 안티패턴
- 범용 util 패키지
- 무분별하게 커지는 common 모듈
- “언젠가 쓸 것 같아서” 추가된 코드
이러한 컴포넌트는
많은 클라이언트에게 불필요한 변경을 강요한다.
CRP의 설계 기준
- 컴포넌트는 함께 사용되는 클래스들의 집합이어야 한다
- 함께 사용되지 않는 클래스는 같은 컴포넌트에 두지 않는다
- 이는 인터페이스 분리 원칙(ISP) 을 컴포넌트 수준에서 적용한 것이다
REP · CCP · CRP의 관계와 트레이드오프
이 세 원칙은 서로 보완적이지만
항상 동시에 만족시킬 수는 없다.
| 원칙 | 초점 |
|---|---|
| REP | 재사용과 릴리스 단위 |
| CCP | 변경 비용 최소화 |
| CRP | 의존성 전파 최소화 |
현실적인 적용 전략
- 초기 설계 단계
- CCP 우선 → 변경에 유연한 구조 확보
- 시스템이 성장하면서
- CRP 강화 → 불필요한 의존성 제거
- 라이브러리 또는 플랫폼화 단계
- REP 적용 → 명확한 릴리스 단위 정의
컴포넌트 설계는
정적인 규칙이 아니라 지속적으로 조정되는 균형 문제다.
마무리: 컴포넌트 설계의 본질
컴포넌트 설계의 핵심 질문은 하나다.
이 변경은 어디까지 전파되어야 하는가?
- 기능 기준이 아니라 변경 기준으로 나누고
- 재사용을 고려하되 의존성 폭발을 경계하며
- 릴리스 단위에 명확한 의미를 부여하라
REP, CCP, CRP는
대규모 시스템에서 모듈화의 감각을 언어로 정리해주는 설계 도구다.