본문 바로가기
Programming

[SOLID] 1. SRP(Single Responsibility Principle) - 단일 책임 원칙

by jewook3617 2021. 9. 9.

이번 글에서는 SOLID 원칙의 S에 해당하는 SRP에 대해 정리해보겠습니다.

 

SRP(Single Responsibility Principle)

SRP는 Single Responsibility Principle의 줄임말입니다. 한글로 번역하면 단일 책임 원칙입니다.

언뜻 보면 하나의 클래스가 하나의 역할만 해야 한다는 뜻으로 이해하기 쉽습니다.

하지만 이 말의 진짜 의미는 하나의 클래스는 하나의 액터만을 담당해야한다는 의미입니다. 여기서 액터클래스를 변화시킬 수 있는 주체를 의미합니다. 예시 코드를 보며 더 자세히 알아보겠습니다. 예시 코드는 javascript 코드입니다.

class Employee {
  name;
  positon;

  constructor(name, position) {
    this.name = name;
    this.positon = position;
  }

  calculateExtraHour() {
    // ...
  }

  calculatePay() {
    // ...
    this.calculateExtraHour();
    // ...
  }

  reportHours() {
    // ...
    this.calculateExtraHour();
    // ...
  }

  save() {
    // ...
  }
}

직원 정보를 담당하는 Employee 클래스에는 3가지의 주요 메서드가 있습니다.

  • calculatePay() : 급여를 계산하는 메서드 -> 회계팀에서 사용
  • reportHours() : 근무시간을 계산하는 메서드 -> 인사팀에서 사용
  • save() : 변경된 정보를 DB에 저장하는 메서드 -> 기술팀에서 사용

 

calculatePay() 메서드는 회계팀에 급여를 계산하기 위해 사용하는 메서드고 reportHours() 메서드는 인사팀에서 근무시간을 계산하기 위해 사용하는 메서드이며 save() 메서드는 기술팀에서 변경된 정보를 저장하기 위해 사용하는 메서드입니다.

회계팀에서 급여를 계산하는 방식을 변경했다고 개발팀에게 알리면 개발팀에서 caculatePay() 메서드를 수정하고 인사팀에서 근무시간을 계산하는 방식을 변경했다고 개발팀에게 알리면 개발팀에서 reportHours() 메서드를 수정할 것입니다. 이렇게 클래스를 변화시키는 원인이 되는 주체를 액터라고 부릅니다. Employee 클래스는 회계팀, 인사팀, 기술팀 3개의 액터가 존재합니다.

위 예시 코드에서는 calculatePay() 메서드와 reportHours() 메서드에서 초과 근무 시간을 계산하기 위해 calculateExtraHour() 메서드를 공유하여 사용하고 있습니다. 회계팀에서 급여를 계산하는 방식을 변경해 초과근무시간을 계산하는 알고리즘이 변경되었다고 가정해보겠습니다. 회계팀의 요청에 따라 calculateExtraHour() 메서드가 변경되고 이렇게 변경된 내용이 의도치 않게 인사팀에서 사용하는 reportHours() 메서드에도 영향을 주게 됩니다. 하지만 인사팀에서는 이러한 변경 사실을 알지 못하고 데이터가 잘못되었다며 개발팀에 새로운 요청을 보내게 될 것입니다.

이러한 상황이 SRP에 위배되는 상황입니다. Employee 클래스에서 회계팀, 인사팀, 기술팀 이렇게 3개의 액터를 담당하고 있기 때문에 의도치않게 변경이 생기게 된 것입니다. 이를 해결하기 위한 방법으로 Facade Pattern이 있습니다.

 

Facade Patter(퍼사드 패턴)

Facade란 건물의 정면을 의미합니다. Facade Pattern은 말 그대로 건물의 뒷부분이 어떻게 생겼는지는 보여주지 않고 건물의 정면만 보여주는 패턴입니다. 위 예시의 Employee 클래스에 Facade Pattern을 적용한 코드를 보겠습니다.

class EmployeeFacade {
  private name;
  private positon;

  constructor(name, position) {
    this.name = name;
    this.positon = position;
  }

  calculatePay() {
    return new PayCalculator().caculatePay();
  }

  reportHours() {
    return new HourReporter().reportHours();
  }

  save() {
    return new EmployeeSaver().save();
  }
}

class PayCalculator {
  calculateExtraHour() {
    // ...
  }

  caculatePay() {
    // ...
    this.calculateExtraHour();
    // ...
  }
}

class HourReporter {
  calculateExtraHour() {
    // ...
  }
  
  reportHours() {
    // ...
    this.calculateExtraHour();
    // ...
  }
}

class EmployeeSaver {
  public save() {
    // ...
  }
}

Employee 클래스가 EmployeeFacade 클래스로 변경되었고 EmployeeFacade 클래스엔 아무런 로직이 들어있지 않습니다. 단순히 PayCaculator, HourReporter, EmployeeSaver 클래스의 인스턴스를 생성하고 각 클래스의 메서드를 사용하는 역할만 합니다. 그림으로 보면 다음과 같습니다.

EmployeeFacade 클래스는 메서드의 구현이 어떻게 되어있는지는(건물의 뒷부분) 보여주지 않고 어떤 메서드가 있는지(건물의 정면)만 보여줍니다. 그리고 메서드의 구현은 각각 PayCalculator, HourReporter, EmployeeSaver 클래스에 위임합니다.

이렇게 되면 EmployeeFacade 클래스는 어떠한 액터도 담당하지 않습니다. 변경사항이 생겨도 각각의 구현 클래스만 변경하면 되기 때문에 EmployeeFacade 클래스는 변경이 생기지 않습니다. 회계팀에서 초과근무시간 계산 방법이 변경되었다고 하면 PayCalculator의 calculateExtraHour() 메서드를 수정하면 됩니다. 이 변경사항이 HourReporter 클래스에 전혀 영향을 주지 않습니다. PayCalculator, HourReporter, EmployeeSaver 클래스가 각각 하나의 액터만을 담당하도록 변경되었기 때문입니다.

위에서 살펴본 것과 같이 하나의 클래스가 하나의 액터만을 담당해야한다는 원칙이 SRP(Single Responsibility Principle)입니다.

위 코드는 로버트 C. 마틴의 Clean Architecture에 등장하는 코드입니다.