결합도 (Coupling)
결합도는 서로 다른 모듈 간의 상호 의존성이나 연관성을 측정하는 지표이다.
낮은 결합도는 모듈이 독립적이며 변경 시 다른 모듈에 미치는 영향이 적음을 의미한다.
모듈이란 클래스, 컴포넌트, 패키지 등 코드의 논리적 단위를 의미한다.
특징 및 기능
- 모듈 간 상호작용 정도를 나타냄
- 소프트웨어 구조의 품질을 평가하는 지표로 사용
- 유지보수성, 재사용성, 테스트 용이성에 영향을 미침
결합도의 종류
낮은 결합도부터 높은 결합도 순
내용 결합도(Content Coupling):
가장 강한 형태의 결합도로, 한 모듈이 다른 모듈의 내부 동작에 직접 관여하는 경우이다.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// 높은 내용 결합도의 예 class UserService { private UserRepository userRepo; public void createUser(User user) { // UserRepository의 내부 구현에 직접 의존 userRepo.userList.add(user); // 내부 데이터 구조에 직접 접근 } } // 개선된 버전 class UserService { private UserRepository userRepo; public void createUser(User user) { // 공개된 인터페이스를 통해 상호작용 userRepo.save(user); } }
공통 결합도(Common Coupling):
여러 모듈이 전역 데이터를 공유하는 경우.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// 높은 공통 결합도의 예 public class GlobalConfig { public static Map<String, Object> sharedData; } class ServiceA { public void process() { GlobalConfig.sharedData.put("key", "value"); } } // 개선된 버전 public class Configuration { private Map<String, Object> data; public void setValue(String key, Object value) { data.put(key, value); } public Object getValue(String key) { return data.get(key); } }
제어 결합도(Control Coupling):
한 모듈이 다른 모듈의 내부 로직을 제어하는 경우.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// 높은 제어 결합도의 예 class OrderProcessor { public void processOrder(Order order, String type) { if (type.equals("NORMAL")) { processNormalOrder(); } else if (type.equals("EXPRESS")) { processExpressOrder(); } } } // 개선된 버전 - 전략 패턴 사용 interface OrderProcessingStrategy { void process(Order order); } class OrderProcessor { private OrderProcessingStrategy strategy; public void processOrder(Order order) { strategy.process(order); } }
스탬프 결합도(Stamp Coupling):
모듈들이 동일한 자료구조를 매개변수로 전달하는 경우.
결합도를 측정하는 방법
정적 분석:
- 팬인(Fan-in): 해당 모듈을 사용하는 다른 모듈의 수
- 팬아웃(Fan-out): 해당 모듈이 사용하는 다른 모듈의 수
- 불안정성(I): I = Fan-out / (Fan-in + Fan-out)
동적 분석:
- 런타임 의존성 분석
- 메서드 호출 그래프 분석
결합도를 낮추기 위한 전략
의존성 주입(Dependency Injection) 사용:
인터페이스 활용:
이벤트 기반 통신:
도구
- SonarQube: 코드 품질 및 결합도 분석
- Structure101: 아키텍처 의존성 분석
- JDepend: Java 패키지 의존성 분석
- NDepend:.NET 코드 분석
주의사항
- 과도한 추상화는 오히려 복잡성을 증가시킬 수 있다.
- 모든 결합도를 제거하는 것은 불가능하며, 적절한 수준의 결합도는 필요하다.
- 결합도를 낮추기 위한 리팩토링은 신중하게 계획되어야 한다.
권장사항
- 인터페이스를 통한 계약을 정의하고 구현을 숨긴다.
- 의존성 주입을 활용하여 모듈 간 결합도를 낮춘다.
- 단방향 의존성을 유지하고 순환 의존성을 피한다.
- 공통 결합도를 피하기 위해 전역 상태 사용을 최소화한다.
- 정기적인 의존성 분석을 통해 결합도를 모니터링한다.