하나의 클래스는 하나의 책임만 가져야 한다.

로버트 마틴이 얘기한 Single Responsibility Principle(SRP)이다. SOLID원칙 중 S에 해당하는 원칙으로 개체지향원칙의 중요 원칙 중 하나이다.

의미

단일 책임이란 것은 하나의 클래스가 변경되는 이유는 하나라는 뜻이다. 하나의 클래스가 여러개의 변경 이유를 갖는다면 그 클래스는 SRP를 위배한 것이다. 나는 JS를 주 언어로 사용하기에 JS로 예제를 만들었다.

코드

const App = class {
  students = [];
  async run() {
    this.students = await this.#getFetch('url');
    this.#renderTable();
  }

  async #getFetch(url) {
    // ... 데이터를 가져오는 함수
    const response = await fetch(url);
    // ... 여러가지 일
    return response;
  }

  #renderTable() {
    // 테이블로 렌더하기.
  }
}
const app = new App()
app.run();

위의 예는 외부에서 데이터를 받아와 렌더링을 하는 App 클래스를 생성하였다. App클래스의 run()를 통해 프로그램을 실행한다. 위 코드는 SRP를 위배하였다. App클래스가 데이터를 받아오는 일과 데이터를 렌더링 하는 일, 두 가지 책임을 하고 있기 때문이다. 즉, 데이터를 받아오는 형식이 바뀌게 되는 경우와 테이블 렌더링이 아니고 리스트로 렌더링 하게 된다면 App클래스는 바뀌어야 한다. 위 코드는 데이터를 받아오는 일을 하는 클래스와 데이터를 렌더링하는 클래스로 나눠야 한다.

개선된 코드

const App = class {
  #fetchService;
  #renderer;
  #students = [];
  constructor(fetchService, renderer){
    this.#fetchService = fetchService;
    this.#renderer = renderer
  }
  run() {
    this.#student = this.#fetchService.fetch();
    this.#renderer.render(this.#students);
  }
}

const FetchService = class {
  constructor(){}
  async fetch(url){
    const response = await fetch(url);
    // ... 데이터를 어떻게 해야할 지에 대한 책임.
    return response;
  }
}

const Renderer = class {
  constructor(){}
  render(){
    // 렌더링에 대한 책임
  }
}

const app = new App(new FetchService(), new Renderer());
app.run();

여러 책임을 갖던 App클래스를 App클래스, FetchService클래스, Renderer클래스로 나누었다. App클래스는 앱을 어떤 순서로 실행할지에 대한 책임, FetchService클래스는 데이터를 받아와 어떤 형태의 데이터를 받아올지에 대한 책임, Renderer는 데이터를 어떻게 렌더할지에 대한 책임으로 세분화 되었다. 이로써 각 클래스는 SRP를 만족하게 되었다.

효과

하나의 클래스가 하나의 책임을 가지면 얻는 효과가 무엇일까? 책임이란 것은 변경에 대한 이유이다. 그렇기에 하나의 책임을 갖는다는 이유는 하나의 변경사항에만 변한다는 의미이고 변경이 일어나는 수준이 작은 클래스 단위에 국한된다는 것이다.