결합도와 응집도

호이초이·2025년 4월 8일
post-thumbnail

우테코 레벨 1 자동차 경주 미션을 진행하며 처음으로 '설계'라는 단어에 진지하게 다가갔다. 그전까지는 그냥 기능이 돌아가면 된다고 생각했다. 하지만 첫 리뷰에서 받은 피드백은 달랐다.


이 말을 듣고 처음엔 막연했다. 도대체 '결합되어 있다'는 건 무슨 뜻일까? 내가 잘못 짠 건가? 시지프가 말한 결합도(Coupling)응집도(Cohesion) 측면에 대해 공부한 내용을 정리해보고자한다.


💡 결합도(Coupling)란?

모듈 간 얼마나 강하게 연결되어 있는가를 나타내는 지표

예를 들어, GameControllerInput, Output, RaceController, Car, Validator를 모두 직접 생성하고 사용하고 있다면?

  • Input 내부 구조가 바뀌면 GameController도 바뀌어야 함
  • Output 함수 명이 바뀌면 GameController에서 일일이 수정해야 함

이처럼 어떤 모듈이 다른 모듈의 내부 구현에 너무 의존하게 되면, 결합도가 높다고 한다.

❌ 결합도가 높으면?

  • 하나가 바뀌면 줄줄이 수정해야 함
  • 테스트가 어렵고 유지보수가 힘들어짐

코드 예시)


✅ 응집도(Cohesion)란?

하나의 모듈이 얼마나 한 가지 책임에 집중하고 있는가를 나타내는 정도

예를 들어, RaceController가 자동차 움직임만 담당한다면?

  • 응집도가 높다 ✅

하지만 여기에 자동차 생성, 출력, 유효성 검증까지 다 하고 있다면?

  • 응집도가 낮다 ❌

✅ 응집도가 높으면?

  • 모듈이 명확한 책임을 가지게 되어 이해, 수정, 테스트가 쉬워짐

🔁 코드로 직접 체감

자동차 경주 초기 구현에서는 GameController 안에 Input, Output, RaceController까지 모두 직접 생성해서 사용하는 구조였다. 기능 흐름은 단순해서 좋았지만, 구조를 바꾸기 어려웠다.

"Output 구조를 조금만 바꾸려 해도 GameController도 함께 바꿔야 했고, 테스트를 하려 해도 모든 의존 객체들을 함께 불러와야 했다."

이때 처음으로 '결합되어 있다'는 피드백이 무슨 의미인지 몸으로 체감하게 됐다.


결합도와 응집도에 대해 머리로만 이해하던 것을, 직접 구현하면서 아래 그림처럼 체감할 수 있었다.

결합도와 응집도 비유 그림

  • 결합도가 높은 구조: 하나의 멀티탭에 여러 전자기기가 얽혀 있는 모습처럼, 하나만 빠져도 전체에 영향이 간다.
  • 응집도가 낮은 구조: 도구함 안에 기능이 뒤섞인 공구처럼, 무엇이 어떤 역할을 하는지 파악하기 어려운 상태다.

🔨 결합도를 낮추는 방법들

1. 의존성 주입(DI: Dependency Injection)

class GameController {
  constructor(input, output) {
    this.input = input;
    this.output = output;
  }

  play() {
    const name = this.input.getCarName();
    this.output.print(name);
  }
}

const controller = new GameController(new Input(), new Output());

GameController는 Input이나 Output의 구체적인 클래스가 아니라, 인터페이스(계약)에만 의존한다.

2. 추상화(Abstraction)

인터페이스나 추상 클래스 형태로 의존하게 만들면, 내부 구현 변경에도 영향을 최소화할 수 있음

class ConsoleOutput {
  print(msg) {
    console.log(msg);
  }
}

print()라는 메서드만 지켜주면 어떤 출력 방식도 대체 가능 (ex. 웹 출력, 파일 출력 등)

3. 이벤트 기반 통신 (간접 연결)

컴포넌트 간 직접 호출이 아닌, 이벤트를 발행하고 구독하는 구조로 변경

eventBus.on('raceFinished', (data) => uiRenderer.render(data));

🤔 DI를 쓰면 정말 결합도가 없어질까?

나는 이런 고민을 했다.

"DI를 써도 결국 메서드 명이 바뀌면 A에서 수정해줘야 하는 거 아닌가요?"

인터페이스가 바뀌면 의존한 쪽도 함께 바뀌어야 한다.
하지만 DI는 전제 자체가 다르다.

핵심은 인터페이스가 자주 바뀌지 않는 안정적인 형태여야 한다는 점
즉, 인터페이스를 기준으로 설계하고, 구현체만 다양하게 바뀔 수 있게 만드는 것이다.

"인터페이스는 변하지 않는 약속이고, DI는 그 약속만 지키면 어떤 객체든 갈아끼울 수 있게 만들어준다."


🔚 마무리

그동안 글로만 접했던 결합도와 응집도라는 개념을, 내가 작성한 코드에서 실제로 체감해볼 수 있었다는 점이 가장 큰 수확이었다.

디자인 패턴이나 아키텍처는 단순히 외워서 적용하는 게 아니라, 코드에서 불편함을 느끼고, 그 불편함을 해결하려는 과정 속에서 자연스럽게 이해하게 되는 것이라는 걸 몸으로 배웠다.

profile
의 성장일지

0개의 댓글