결합도와 SRP에 대한 요약

발행: (2026년 4월 7일 AM 02:23 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

응집된 클래스란 무엇인가?

응집된 클래스는 단 하나의 책임만을 갖는 클래스입니다.
응집된 클래스는 더 작고, 더 체계적이며, 유지보수와 재사용이 용이합니다.

낮은 응집도를 가진 클래스 예시

class CalculadoraDeSalario {
  calcula(funcionario) {
    if (funcionario.cargo === "DESENVOLVEDOR") {
      return this.dezOuVintePorcento(funcionario);
    }

    if (funcionario.cargo === "DBA" || funcionario.cargo === "TESTER") {
      return this.quinzeOuVinteCincoPorcento(funcionario);
    }

    throw new Error("Funcionário inválido");
  }

  dezOuVintePorcento(funcionario) {
    if (funcionario.salarioBase > 3000) {
      return funcionario.salarioBase * 0.8;
    }
    return funcionario.salarioBase * 0.9;
  }

  quinzeOuVinteCincoPorcento(funcionario) {
    if (funcionario.salarioBase > 2000) {
      return funcionario.salarioBase * 0.75;
    }
    return funcionario.salarioBase * 0.85;
  }
}

이 클래스의 문제점

  • 클래스가 모든 직책을 알아야 함.
  • 많은 ifs → 새로운 직책이 생길 때마다 클래스를 수정해야 함.
  • 계산 규칙을 쉽게 재사용할 수 없음.
  • 클래스가 하나 이상의 책임을 가짐 (직책 결정 + 급여 계산).

즉, 낮은 응집도와 높은 결합도. 각 계산 규칙을 별도의 클래스로 분리하면 응집도가 높아지고 재사용 및 유지보수가 쉬워집니다.

왜 이 클래스는 재사용하기 어려운가?

시스템의 다른 곳에서 dezOuVintePorcento() 규칙만 재사용하고 싶다고 상상해 보세요. 이 메서드가 CalculadoraDeSalario 클래스 안에 있기 때문에 메서드만 사용할 수 없습니다. 따라서 다음을 해야 합니다:

  1. 클래스를 인스턴스화한다
  2. 직원 객체를 만든다
  3. 직원을 전달한다
  4. 그때서야 메서드를 사용한다

계산 규칙을 재사용하려면 전체 클래스를 함께 가져와야 합니다. 이는 클래스가 너무 많은 일을 하고 있어 낮은 응집도를 나타냅니다.

높은 응집도를 가진 리팩터링된 클래스 예시

class Cargo {
  calculaSalario(salarioBase) {
    throw new Error("Método deve ser implementado pela subclasse");
  }
}

class Desenvolvedor extends Cargo {
  calculaSalario(salarioBase) {
    return salarioBase > 3000
      ? salarioBase * 0.8
      : salarioBase * 0.9;
  }
}

class Dba extends Cargo {
  calculaSalario(salarioBase) {
    return salarioBase > 2000
      ? salarioBase * 0.75
      : salarioBase * 0.85;
  }
}

class Tester extends Cargo {
  calculaSalario(salarioBase) {
    return salarioBase > 2000
      ? salarioBase * 0.75
      : salarioBase * 0.85;
  }
}

const RegrasPorCargo = {
  DESENVOLVEDOR: new Desenvolvedor(),
  DBA: new Dba(),
  TESTER: new Tester()
};

class CalculadoraDeSalarioRefatorada {
  calcula(funcionario) {
    const regra = RegrasPorCargo[funcionario.cargo];

    if (!regra) {
      throw new Error("Funcionário inválido");
    }

    return regra.calculaSalario(funcionario.salarioBase);
  }
}

const funcionario2 = {
  cargo: "DESENVOLVEDOR",
  salarioBase: 3500
};

const calculadora2 = new CalculadoraDeSalarioRefatorada();

console.log(calculadora2.calcula(funcionario2));

리팩터링을 이해하기 위한 첫 번째 단계

리팩터링 전, CalculadoraDeSalario 클래스는 두 가지 이유로 변경되었습니다:

  • 새로운 직책이 생길 때
  • 새로운 계산 규칙이 생길 때

즉, 클래스는 변경 이유가 하나 이상이었으며, SRP (Single Responsibility Principle, 단일 책임 원칙)를 위반했습니다.

계산 메서드를 살펴보면 모두 동일한 형식을 따릅니다:

  • 급여를 받는다
  • 규칙을 적용한다
  • 계산된 급여를 반환한다

리팩터링 아이디어는 각 계산 규칙마다 클래스를 만들고, 모두 동일한 메서드 calculaSalario()를 갖게 하는 것입니다.

이를 통해 얻는 이점

  • 각 규칙이 격리됩니다
  • 한 규칙의 변경이 다른 규칙에 영향을 주지 않습니다
  • 클래스가 더 작고 응집력이 높아집니다
  • 시스템 유지보수가 쉬워집니다
  • 기존 코드를 변경하지 않고 새로운 직책을 추가할 수 있습니다

이제 각 클래스는 하나의 변경 이유만 가집니다: 바로 자신의 계산 규칙입니다.

왜 작은 메서드로 나누지 않을까?

큰 메서드를 작은 메서드로 나누는 것이 독립적인 재사용을 보장하지는 않습니다; 로직은 여전히 원래 클래스에 결합된 상태로 남아 있습니다.

참고

이 예제와 리팩터링 아이디어는 다음 책에서 영감을 받았습니다: Orientação a Objetos e SOLID para Ninjas - Projetando classes flexíveis – Maurício Aniche.

0 조회
Back to Blog

관련 글

더 보기 »

SOLID Principles 다시는 잊지 않을

소개 SOLID는 깨끗하고 유지보수가 용이하며 확장 가능한 코드를 작성하기 위한 설계 원칙들의 집합입니다. 이는 다음을 의미합니다: Single Responsibility Principle (SRP) A class...