SOLID 1탄 - `단일 책임 원칙`(SRP)을 이해하기


SRP - 단일 책임 원칙

프로그래머라면 알아야하는 SOLID 원칙을 잘 이해해 봅시다. 1탄으로 SRP 를 설명하겠습니다.

책임이라는 단어가 모호하죠?

프로그래밍에서 책임은 일상적으로 쓰는 책임과 같은 의미

제가 처음 SOLID 원칙을 공부할 때에 프로그래밍에서 말하는 책임과 일상에서 말하는 책임이 다를 것 같은 느낌을 받았습니다. 마틴 파울러는 책임이란 ‘변경 하려는 이유’라고 설명했기 때문입니다. 이 말은 어느 정도 동의는 하지만 결국 제가 단일 책임원칙을 이해하는데 오해를 가져오게 한 주된 이유라고 생각합니다.

그렇다면 책임이란 무엇 일까요? 사전적 의미보다 우리가 어떤 용도로 사용하는지 생각해 볼까요? . 어떤 문제가 발생합니다. 그럼 책임자가 누구인지 찾게 됩니다.

'이러 이러한 문제가 발생했는데 누가 책임을 져야 하나요?', 
'만약에 여기서 문제가 발생한다면 누구에게 책임이 있습니까?'

그렇습니다. 책임은 ‘문제’가 있다면 혹은 발생한다면 찾게 되는 무엇입니다. 단일 책임 원칙에서 말하는 책임도 마찬가지죠. 어떤 기능에 문제가 발생하면 그건 어떤 것의 책임인가? 그것을 담당하는 모듈(패키지, 클래스, 함수)은 무엇 인가를 찾게 되는 것이죠. 책임은 ‘문제’가 발생하거나 발생이 된다는 전제에서 자연스럽게 알게 되는 것입니다. 일종의 예상과 추측입니다.

책임은 문제가 발생하면 자연스럽게 예상(추측)되는 문제의 근원입니다.

클래스에 국한된 것이 아니다.

SRP 를 설명할 때에 많이 만나는 문장은 모든 클래스는 하나의 책임만 가지며로 시작합니다. 하지만 책임은 클래스만 갖는 것이 아닙니다. 패키지도 책임을 갖고 네임스페이스도 책임을 갖고 함수도 책임을 갖습니다. ‘이 이슈와 관련된 패키지는 어디에요?’, ‘이 이슈와 관련된 네임스페이스는 어디에요?’, ‘이 이슈와 관련된 함수는 어디에요?’라고 질문 하더라도 이상하지 않습니다. 일상에서도 책임은 그 범위와 상세에 따라서 국가, 기관, 가정, 팀, 개인 모두가 갖을 수 있습니다.

국가 단위로 책임을 생각 해볼게요. 국가의 책임은 국민들이 행복하게 살 수 있도록 하는 것입니다. 이 책임에 여러 많은 것이 포함됩니다. 행복하게 하기 위해서는 재산을 보호해 주고 인프라를 건설해 주고 사람 간에 문제가 있으면 해결해 주고 개인이 해결 못 하는 것들을 해결해 주고 여러가지 책임으로 나눌 수 있죠. 그 책임을 더 작게 나눌 수도 있죠. 예를 들어 인프라는 전기, 수도, 도로, 행정 등 다양하게 나눌 수 있습니다.

프로그래머는 책임을 큰 것에서 작은 것으로 나눠 담습니다. 프로젝트에서 패키지로, 패키지에서 네임스페이스로, 네임스페이스에서 클래스로, 함수로, 더 작게는 변수로 말입니다. 책임은 쪼게 지고 합쳐질 수 있습니다.

책임의 범위는 클 수도 작을 수도 있고 포괄적이거나 구체적일 수 있습니다.

단일 책임 원칙

책임은 예상이라고 설명했습니다. 또한 책임은 작거나 크거나 포괄적이거나 구체적이거나 할 수 있음을 설명했습니다. 이러한 배경 지식으로 단일 책임 원칙을 해석하면 ‘문제가 발생 할 때, 찾을 곳을 ‘단일’하게 하는 것이 원칙’이라는 것입니다.

`단일 책임 원칙` : 문제가 발생할 때를 예상해서 찾아야 하는 곳을 단일하게 하는 원칙

이러한 단일 책임 원칙의 적용 예를 설명하겠습니다. 유명한 MVC 패턴을 보죠. Model은 데이터를 나타내고 View는 렌더링을 Controller 는 비지니스 로직을 제어합니다. 만약에 원하는 비지니스 로직이 이상하게 나온다면 Controller 를 찾아서 수정합니다. 화면이 깨지거나 원하는 모양이 나오지 않는다면 View 를 찾아 수정합니다. 데이터의 형식이 바뀌면 Model를 찾아가서 수정합니다.

단일 책임 코드 예시

자동차 클래스를 생성하는 자동차 팩토리 클래스를 만들어 보겠습니다.

class Car {}

class CarFactory {
    db:Database;
    newCar() {
        const car = new Car;
				this.db.saveCar(car); // 데이터베이스에 저장 -> 레파지토리에서 하는 것을 기대합니다.
        return new Car;
    }
}

newCar 함수 내부를 살펴보면 db 객체를 활용해서 자동차의 영속화를 진행하는 것을 알 수 있습니다. 위의 코드는 다음과 같이 사용 될 수 있습니다.

//... IoC Container do something...
const fac = container.get(CarFactory);
const car = fac.newCar();

CarFactory 클래스만 사용하는 것을 보고 우리는 newCar 함수에서 데이터가 저장되는 것을 예상 할까요? 문제가 발생 했을 때, 예상해서 찾아 갈 수 있을 까요? 라인 바이 라인 디버깅을 하다가 찾아 갈 수 있겠지만 시간을 많이 사용한 후일 것입니다. 영속화는 ORM 이나 레파지토리에서 할 것이라 예상 할 수 있고 그들의 책임이라고 할 수 있습니다.

요약

  • 책임은 예상이다.
  • 책임은 포괄적 이거나 구체적이라 프로젝트, 패키지, 클래스, 함수, 네임스페이스 등이 모두 가질 수 있는 것이다.
  • 단일 책임 원칙문제가 발생할 때를 예상해서 찾아야 하는 곳을 단일하게 하는 원칙이다.