소프트웨어 아키텍처

결합도와 응집도

meellon 2026. 6. 28. 05:20

학습 목표

결합도(coupling)응집도(cohesion) 의 의미·트레이드오프를 설명할 수 있다.

느슨한 결합·높은 응집변경 영향 범위를 줄이는 이유를 설명할 수 있다.

패키지 by layer vs by feature 구조의 장단점을 비교할 수 있다.

모듈 경계·공개 API·의존 방향으로 결합을 측정·개선하는 방법을 설명할 수 있다.

문제 상황

  • util 패키지에 300개 클래스 — 이름만 다른 헬퍼
    • 하나 고치면 20개 모듈이 재컴파일
  • order 기능 수정인데 payment·notification·report까지 동시 PR
    • 폴더는 layer별인데 변경은 가로로 퍼짐
  • OrderServicePaymentService 내부 DTO를 직접 import
    • 결제 API 바꿀 때 주문까지 깨짐
  • common 모듈이 모든 도메인 entity를 알고 있다
    • 순환 의존·빌드 5분
  • 테스트 하나 돌리려면 전체 Spring context 기동

SOLID는 클래스·인터페이스 단위 규율이다. 이번 편은 모듈·패키지가 서로 얼마나 묶이고, 안에서 얼마나 한 목적을 갖는지다.

1. 결합도와 응집도

  결합 (Coupling) 응집 (Cohesion)
질문 모듈끼리 얼마나 의존하는가 모듈 요소가 한 목적에 모였는가
목표 낮을수록 좋음 (느슨한 결합) 높을수록 좋음
나쁜 신호 import 지옥·순환·공유 mutable God package·"misc"·날짜만 같은 파일

  • 결합도 — 한 모듈 변경이 다른 모듈에 미치는 정도
  • 응집도 — 한 모듈 안 코드가 같은 이유로 함께 바뀌는 정도
  • 목표 조합: loose coupling + high cohesion (Richards/Ford)

결합도 유형 (실무에서 자주 보는 것)

유형 설명
Data 공개 API·DTO로만 데이터 전달 PlaceOrderCommand
Stamp 불필요하게 구조체 전달 Entity 전체 넘김
Control 다른 모듈 흐름을 직접 제어 if (payment.isOk()) order.next()
Common 공유 DB·상수·global state common entity 전역
Content 내부 구현 직접 접근 private 필드·SQL 직접
  • Data coupling이 이상적에 가깝다 — 나머지는 리팩터링 후보
  • Content·Common은 아키텍처 경계 붕괴 신호

응집도 유형

유형 설명
Functional 모듈 하나 = 기능 하나 (이상)
Sequential 출력 → 다음 단계 입력 파이프라인
Communicational 같은 데이터를 다루는 연산
Logical "logging"처럼 카테고리만 같음 (약함)
Coincidental 우연히 한 폴더 (최악)
  • misc·helper·util 과다 = coincidental/logical 응집 의심

2. SOLID와의 관계

SOLID 결합·응집
SRP 한 모듈 한 변경 이유 → functional cohesion
ISP 쓰지 않는 API 의존 제거 → coupling ↓
DIP 추상화 경유 → 구현 coupling ↓
  • SOLID 위반이 누적되면 모듈 간 결합도가 올라간다
  • 반대로 패키지 경계 없이 SOLID만 클래스에 적용하면 가짜 분리

3. 패키지 구조

By layer (기술 계층)

com.app.controller
com.app.service
com.app.repository
com.app.dto
장점 단점
익숙, 프레임워크 예제와 동일 기능 하나여러 패키지에 흩어짐
계층 규칙 lint 쉬움 기능 추가·삭제 시 가로 변경

By feature (기능·capability)

com.app.order
com.app.payment
com.app.catalog
  ├── domain
  ├── application
  └── adapter
장점 단점
변경이 한 vertical slice 안에 패키지 간 중복·공유 규칙 필요
ownership·MSA 분해와 정렬 계층 규칙을 모듈마다 지켜야 함
  • 모듈러 모놀리스 — Gradle multi-module + by feature가 흔한 조합
  • code/package-layout.md — 두 방식 예시

공개 API와 캡슐화

  • 모듈 바깥에 노출하는 것: port·DTO·facade
  • internal 패키지(Java) · @VisibleForTesting의도적 경계
  • ArchUnit · module-infoorderpayment 단방향 검증

4. 변경 전파

구조 Payment API 변경 시
강결합 Order·Notification·Report 전부 수정
경계 + port PaymentGateway 어댑터만, Order는 interface 유지
강결합:  A ── B ── C ── D   (한 곳 수정 → 연쇄)
경계:    A → [port] ← B_impl   C는 A API만 사용
측정·개선 방법
Fan-out 모듈이 import하는 외부 모듈 수
Fan-in 모듈에 의존하는 수 — 높으면 안정 core
순환 dependency-cruiser, ArchUnit noCycles
공유 DB 스키마 coupling — bounded context로 분리
  • Ripple effect 줄이기: 작은 공개 surface, 이벤트로 async decouple (필요 시)

5. 실무 체크리스트

질문 좋은 답
이 PR이 몇 모듈을 건드리는가 1~2 capability
다른 팀 모듈 내부를 import했는가 port/DTO만
common도메인 logic이 있는가 없음 — infra·진짜 공통만
테스트가 한 모듈만 로드하는가 가능
삭제할 기능 폴더 하나로 지울 수 있는가 by feature면 Yes

흔한 안티패턴

이름 증상
Big ball of mud 경계 없음, 어디든 import
Shared kernel abuse common에 모든 entity
Layer lasagna layer만 있고 기능 단위 ownership 없음
Shotgun surgery 한 요구사항에 파일 30개

6. 정리

  • 느슨한 결합 — 변경·테스트·배포 blast radius 축소
  • 높은 응집 — 한 모듈 = 한 capability·한 actor
  • by feature + 모듈 내부 hexagonal — 실무에서 자주 쓰는 조합
  • 공개 API 최소화, data coupling, port 경유가 경계를 만든다
  • ArchUnit·모듈 그래프로 순환·fan-out을 주기적으로 본다

다음에 다룰 것

  • 도메인 주도 설계 입문
  • Bounded Context, Ubiquitous Language, Context Map

해당 내용은 Fundamentals of Software Architecture (Mark Richards, Neal Ford) 및 Clean Architecture (Robert C. Martin) 의 내용을 기반으로 합니다.

'소프트웨어 아키텍처' 카테고리의 다른 글

분산 시스템 기초  (0) 2026.06.30
도메인 주도 설계 입문  (0) 2026.06.29
SOLID 원칙  (0) 2026.06.27
아키텍처 스타일 비교와 선택  (0) 2026.06.26
이벤트 기반 아키텍처  (0) 2026.06.25