SRP:단일 책임 원칙

Gooreum·2021년 10월 28일
0

클린아키텍처

목록 보기
7/33
post-custom-banner

SOLID(SRP, OCP, LSP, ISP, DIP)

  • 좋은 벽돌로 좋은 아키텍처를 정의하는 원칙.

  • SOLID 원칙은 함수와 데이터 구조를 클래스로 배치하는 방법, 그리고 이들 클래스를 서로 결합하는 방법을 설명해준다.

    • '클래스' 단어를 사용했다고 해서 SOLID 원칙이 객체 지향 소프트웨어에만 적용된다는 뜻은 아니다.
    • 여기에서 클래스는 단순히 함수와 데이터를 결합한 집합을 가리킨다.
  • SOLID 원칙의 목적

    • 중간 수준의 소프트웨어 구조가 아래와 같도록 만드는 데 있다. (중간 수준이라 함은 프로그래머가 이들 원칙을 모듈 수준에서 작업할 때 적용할 수 있다는 뜻이다. 즉, 코드 수준보다는 조금 상위에서 적용되며 모듈과 컴포넌트 내부에서 사용되는 소프트웨어 구조를 정의하는 데 도움을 준다.)
      • 변경에 유연하다.
      • 이해하기 쉽다.
      • 많은 소프트웨어 시스템에 사용될 수 있는 컴포넌트의 기반이 된다.
  • SRP(Single Responsibility Principle) 단일 책임 원칙

    • 콘웨이(conway) 법칙에 다른 따름정리
      • 소프트웨어 시스템이 가질 수 있는 최적의 구조는 시스템을 만드는 사회적 구조에 커다란 영향을 받는다. 따라서 각 소프트웨어 모듈은 변경의 이유가 하나, 단 하나여야만 한다!
  • OCP(Open-Closed Principle) 개방-폐쇄 원칙

    • 기존 코드를 수정하기보다는 반드시 새로운 코드를 추가하는 방식으로 시스템의 행위를 변경할 수 있도록 설계해야만 소프트웨어 시스템을 쉽게 변경할 수 있다는 것!
  • LSP(Liskov Substitution Principle )리스코프 취환 원칙

    • 상호 대체 가능한 구성요소를 이용해 소프트웨어 시스템을 만들 수 있으려면, 이들 구성요소는 반드시 서로 치환 가능해야 한다는 계약을 반드시 지켜야 한다.
  • ISP(Interface Segregation Principle) 인터페이스 분리 원칙

    • 소프트웨어 설계자는 사용하지 않은 것에 의존하지 않아야 한다.
  • DIP(Dependency Inversion Principle) 의존성 역전 원칙

    • 고수준 정책을 구현하는 코드는 저수준 세부사항을 구현하는 코드에 절대로 의존해서는 안 된다.
    • 대신 세부사항이 정책에 의존해야 한다.

SRP

  • SOLID 원칙 중 그 의미가 가장 잘 전달되지 못한 원칙.
    • 모듈이 단 하나의 일만 해야 한다는 의미로 받아들이기 쉬운데 그게 아니다.
    • 단 하나의 일만 해야 한다는 원칙은 바로 함수는 반드시 하나의, 단 하나의 일만 해야 한다는 원칙이다.
      • 이 원칙은 커다란 함수를 작은 함수들로 리팩터링하는 더 저수준에서 사용된다.
      • 하지만 이 원칙은 SOLID원칙이 아니며, SRP도 아니다.
  • SRP란
    • 하나의 모듈은 하나의, 오직 하나의 액터(actor)에 대해서만 책임져야 한다.
    • 단일 모듈은 변경의 이유가 하나, 오직 하나뿐이어야 한다!
    • 소프트웨어 시스템은 사용자와 이해관계자를 만족시키기 위해 변경된다.
      • SRP가 말하는 '변경의 이유'란 바로 이들 사용자와 이해관계자를 가리킨다.
      • 단일 모듈은 변경의 이유가 하나, 오직 하나뿐이어야 한다! → '하나의 모듈은 하나의, 오직 하나의 사용자 또는 이해관계자에 대해서만 책임져야 한다'
  • 모듈이란?
    • 가장 단순한 정의는 '소스 파일'이다.
    • 하지만 일부 언어와 개발 환경에서는 코드를 소스 파일에 저장하지 않는데, 이러한 경우 모듈은 단순히 함수와 데이터 구조로 구성된 응집된 집합이다.
    • 응집된(cohesive)이라는 단어가 SRP를 암시한다.
      • 단일 액터를 책임지는 코드를 함께 묶어주는 힘이 바로 응집성(cohesioin)이다.
  • SRP 원칙은 SRP를 위반하는 경우를 살펴보면 더 잘 이해할 수 있다.

SRP 위반 사례1 : 우발적 중복

이미지 출처 : https://ryanpark.dev/750043c5-4901-4101-87f7-cdc9070a7edc

  • Employee 클래스에 있는 세 가지 메서드의 역할
    • calculatePay() : 회계팀에서 기능을 정의하며, CFO 보고를 위해 사용한다.
    • reportHours() : 인사팀에서 기능을 정의하고 사용하며, COO 보고를 위해 사용한다.
    • save() : 데이터베이스 관리자가 기능을 정의하고, CTO 보고를 위해 사용한다.

문제점

  • 이 세 메서드를 Employee라는 단일 클래스에 배치하여 세 액터(CFO, COO, CTO)가 서로 결합되어 버렸다.
  • 이 결합으로 인해 CFO 팀에서 결정한 조치가 COO 팀이 의존하는 무언가에 영향을 줄 수 있다.
    • 예를 들어 calculatePay() 메서드와 reportHours() 메서드가 초과 근무를 제외한 업무 시간을 계산하는 알고리즘을 공유한다고 생각해보자.

    • 개발자는 코드 중복을 피하기 위해 이 알고리즘을 regularHours()라는 메서드에 넣었다고 해보자.

      이미지 출처 : https://ryanpark.dev/750043c5-4901-4101-87f7-cdc9070a7edc

    • 만약 CFO 팀에서 초과 근무를 제외한 업무 시간을 계산하는 방식을 약간 수정하기로 결정했고, 인사를 담당하는 COO팀에서는 초과 근무를 제외한 업무 시간을 CFO 팀과는 다른 목적으로 사용하기 때문에 이 같은 변경을 원치 않는다면 어떻게 될까.

      • 이 변경을 적용하는 업무를 할당받은 개발자는 calculatePay() 메서드가 편의 메서드인 regularHours()를 호출한다는 사실을 발견한다.
      • 하지만 이 함수가 reportHours() 메서드에서도 호출된다는 사실은 눈치채지 못한다.
      • 개발자는 변경사항을 테스트한다.
        • CFO팀의 테스트 검증이 통과되면 시스템은 배포된다.
        • 그러나 COO 팀에서는 이러한 일이 벌어지고 있다는 사실을 모른다.
        • COO 팀 직원은 reportHours() 메서드가 생성한 보고서를 여전히 이용한다.
        • 보고서의 수치들은 엉터리가 된다... → SRP는 서로 다른 액터가 의존하는 코드를 서로 분리하라고 말한다.

SRP 위반 사례2 : 병합

  • CTO 팀에서 데이터베이스의 Employee 테이블 스키마를 약간 수정한다.
  • COO 팀에서 reportHours() 메서드의 보고서 포맷을 변경한다.
  • 두 명의 서로 다른 개발자가(각각 CTO, COO 팀) Employee 클래스를 체크아웃받은 후 변경사항을 적용한다.
  • 이들 변경사항은 충돌하여 병합이 발생한다.
    • 어떤 도구도 병합이 발생하는 모든 경우를 해결할 수는 없다.
  • 이러한 징후는 모두 많은 사람이 서로 다른 목적으로 동일한 소스 파일을 변경하는 경우에 해당한다.
  • 이 문제를 벗어나는 방법은 서로 다른 액터를 뒷받침하는 코드를 서로 분리하는 것이다!

해결책

  • 첫번째 해결책 - 클래스 분리 이미지 출처 : https://ryanpark.dev/750043c5-4901-4101-87f7-cdc9070a7edc
    • EmployeeData 클래스를 만든다.
    • 세 개의 클래스가 공유하도록 한다.
    • 각 클래스는 자신의 메서드에 반드시 필요한 소스 코드만을 포함한다.
    • 세 클래스는 서로의 존재를 몰라야 한다. → 우연한 중복을 피할 수 있다.
    • 그러나 개발자가 세 가지 클래스를 인스턴스화하고 추적해야 한다는 단점이 있다.
  • 두번째 해결책 - 파사드(Facade) 패턴 이미지 출처 : https://ryanpark.dev/750043c5-4901-4101-87f7-cdc9070a7edc
    • EmployeeFacade에 코드는 거의 없다.
      • 이 클래스는 세 클래스의 객체를 생성하고, 요청된 메서드를 가지는 객체로 위임하는 일을 책임진다.
  • 세 번째 해결책 - 중요하지 않은 경우만 파사드 적용

      이미지 출처 : [https://ryanpark.dev/750043c5-4901-4101-87f7-cdc9070a7edc](https://wedonttalknemore.tistory.com/13)
  • Employee 클래스를 덜 중요한 나머지 메서드들에 대한 파사드로 사용한다.
profile
하루하루 꾸준히
post-custom-banner

0개의 댓글