728x90
반응형
책 정보
https://www.yes24.com/Product/Goods/77283734
SOLID
- SOLID는 좋은 아키텍처를 정의하는 원칙
- 함수와 데이터 구조를 클래스로 배치하는 방법, 그리고 이들 클래스를 서로 결합하는 방법을 설명
- '클래스'라는 단어를 사용했다고 해서 SOLID 원칙이 객체 지향 소프트웨어에만 적용된다는 뜻은 아님
- 클래스는 단순히 함수와 데이터를 결합한 집합을 가리킴
- SOLID 원칙의 목적은 중간 수준의 소프트웨어 구조가 아래와 같도록 만드는 것
- 변경에 유연하다.
- 이해하기 쉽다.
- 많은 소프트웨어 시스템에 사용될 수 있는 컴포넌트의 기반이 된다.
- SRP: 단일 책임 원칙 (Single Responsibility Principle)
- 콘웨이(Conway) 법칙에 따른 따름정리
- 소프트웨어 시스템이 가질 수 있는 최적의 구조는 시스템을 만드는 조직의 사회적 구조에 커다란 영향을 받음
- 따라서 각 소프트웨어 모듈은 변경의 이유가 하나, 단 하나여야만 함
- OCP: 개방-폐쇄 원칙 (Open-Closed Priciple)
- 기존 코드를 수정하기보다는 반드시 새로운 코드를 추가하는 방식으로 시스템의 행위를 변경할 수 있도록 설계해야만 소프트웨어 시스템을 쉽게 변경할 수 있음
- LSP: 리스코프 치환 원칙 (Liskov Substitution Principle)
- 1988년 바바라 리스코프가 정의한, 하위 타입에 관한 유명한 원칙
- 상호 대체 가능한 구성요소를 이용해 소프트웨어 시스템을 만들 수 있으려면, 이들 구성요소는 반드시 서로 치환 가능해야 함
- ISP: 인터페이스 분리 원칙 (Interface Segregation Principle)
- 소프트웨어 설계자는 사용하지 않은 것에 의존하지 않아야 함
- DIP: 의존성 역전 원칙 (Dependency Inversion Principle)
- 고수준 정책을 구현하는 코드는 저수준 세부사항을 구현하는 코드에 절대로 의존해서는 안됨
- 대신 세부사항이 정책에 의존해야 함
7장 SRP: 단일 책임 원칙
- 단일 책임 원칙
- 이름때문에 헷갈릴 수 있음 -> 단 하나의 일만 해야한다는 의미가 X
- 단일 모듈은 변경의 이유가 오직 하나, 오직 하나뿐이어야 한다.
- SRP에서 '변경의 이유'란 사용자와 이해관계자를 가리킴
- 따라서 이렇게 말할 수 도 있음 -> 하나의 모듈은 하나의, 오직 하나의 사용자 또는 이해관계자에 대해서만 책임져야 한다.
- 저자가 말하는 최종 버전: 하나의 모듈은 하나의, 오직 하나의 액터에 대해서만 책임져야 한다.
- 액터 : 변경을 요청하는 한 명 이상의 사람들
- 모듈
- 대부분 소스 코드.
- 하지만 일부 언어와 개발 환경에서는 코드를 소스 파일에 저장하지 않으므로, 단순히 함수와 데이터 구조로 구성된 응집된 집합
- 이해하기 가장 좋은 방법으로 위반하는 징후들 소개
- 징후 1: 우발적 중복
- 선호하는 사례는 급여 애플리케이션의 Employee 클래스
- 세 가지 메서드 calculatePay(), reportHours(), save()를 가짐
- SRP를 위반 (세 가지 메서드가 서로 매우 다른 세 명의 액터를 책임)
- calculatePay() : 회계팀에서 기능 정의, CFO 보고를 위해 사용
- reportHours() : 인사팀에서 기능을 정의하고 사용하며, COO 보고를 위해 사용
- save() : 데이터베이스 관리자(DBA)가 기능을 정의하고, CTO 보고를 위해 사용
- 세 메서드를 Employee라는 단일 클래스에 배치하여 세 엑터가 서로 결합 -> CFO 팀에서 결정한 조치가 COO팀이 의존하는 무언가에 영향을 줄 수 있음
- calculatePay() 메서드와 reportHours() 메서드가 초과 근무를 제외한 업무 시간을 계산하는 알고리즘을 공유한다면
- 코드 중복을 피하기 위해 이 알고리즘을 regularHours()라는 메서드에 넣는다
- CFO에서 초과 근무를 제외한 업무 시간을 계산하는 방식을 약간 수정하기로 결정 -> COO 팀은 이러한 일을 모르기 때문에 COO 팀에서 생성된 보고서는 엉터리
- 이러한 문제는 서로 다른 액터가 의존하는 코드를 너무 가까이 배치했기 때문에 발생
- SRP는 서로 다른 액터가 의존하는 코드를 서로 분리하라고 말함
- 징후 2: 병합
- DBA가 속한 CTO 팀에서 데이터베이스의 Employee 테이블 스키마를 약간 수정하기로 결정
- COO 팀에서는 reportHours() 메서드의 보고서 포맷을 변경하기로 결정
- 병합 발생 !!
- 많은 사람이 서로 다른 목적으로 동일한 소스 파일을 변경하는 경우에 해당
- 해결책
- 해결책은 다양하지만, 모두가 메서드를 각기 다른 클래스로 이동시키는 방식
- 가장 확실한 해결책
- 데이터와 메서드를 분리하는 방식
- 아무런 메서드가 없는 간단한 데이터 구조인 EmployeeData 클래스를 만들어, 세 개의 클래스가 공유하도록 함
- 각 클래스는 자신의 메서드에 반드시 필요한 소스 코드만을 포함
- 세 클래스는서로의 존재를 몰라야 하며 '우연한 중복'을 피할 수 있음
- 단점 : 세 가지 클래스를 인스턴스화하고 추적해야 함 -> 퍼사드(Facade) 패턴으로 해결
- EmployeeFacade에 코드는 거의 없음
- 세 클래스의 객체를 생성하고, 요청된 메서드를 가지는 객체로 위임하는 일을 책임
- 가장 중요한 메서드는 기존의 Employee 클래스에 그대로 유지하되, Employee 클래스를 덜 중요한 나머지 메서드들에 대한 퍼사드로 사용
- 결론
- 단일 책임 원칙은 메서드와 클래스 수준의 원칙
- 상위의 두 수준에서도 다른 형태로 등장
- 컴포넌트 수준 : 공통 폐쇄 원칙(Common Closure Principle)
- 아키텍처 수준 : 아키텍처 경계의 생성을 책임지는 변경의 축
8장 OCP: 개방-폐쇄 원칙
- 개방-폐쇄 원칙
- 1988년에 버트란트 마이어가 만듦
- 소프트웨어 개체(artifact)는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
- 즉, 소프트웨어 개체의 행위는 확장할 수 있어야 하지만, 이때 개체를 변경해서는 안 된다.
- 사고 실험
- 재무제표를 웹 페이지로 보여주는 시스템에 대해 생각해보자.
- 웹 페이지에 표시되는 데이터는 스크롤할 수 있으며, 음수는 빨간색으로 출력
- 이해관계자가 동일한 정보를 보고서 형태로 변환해서 흑백 프린터로 출력해 달라고 요청
- 페이지 번호
- 페이지마다 머리글 & 바닥글
- 표의 각 열에는 레이블
- 음수는 괄호로 감싸야 함
- 서로 다른 목적으로 변경되는 요소를 적절하게 분리(SRP)
- 이들 요소 사이의 의존성을 체계화(DIP)
- 두 책임으로 분리 -> 보고서용 데이터를 계산하는 책임, 이 데이터를 웹으로 보여주거나 종이로 프린트하기에 적합한 형태로 표현하는 책임
- 의존성 조직화 -> 처리 과정을 클래스 단위로 분할하고, 클래스를 컴포넌트 단위로 구분
- 방향성 제어
- 정보 은닉
- 결론
- OCP의 목표는 시스템을 확장하기 쉬운 동시에 변경으로 인해 시스템이 너무 많은 영향을 받지 않도록 하는 것
- 이러한 목표를 달성하려면 시스템을 컴포넌트 단위로 분리하고, 저수준 컴포넌트에서 발생한 변경으로부터 고수준 컴포넌트를 보호할 수 있는 형태의 의존성 계층구조가 만들어지도록 해야 한다.
9장 LSP: 리스코프 치환 원칙
- 리스코프 치환 원칙
- 1988년 바바라 리스코프는 하위 타입을 다음과 같이 정의
- S 타입의 객체 o1 각각에 대응하는 T 타입 객체 o2가 있고, T 타입을 이용해서 정의한 모든 프로그램 P에서 o2의 자리에 o1을 치환하더라도 P의 행위가 변하지 않는다면, S는 T의 하위 타입
- 상속을 사용하도록 가이드하기
- License라는 클래스가 존재, calcFee()라는 메서드를 지님
- Billing 애플리케이션에서 이 메서드를 호출
- License에는 PersonalLicense와 BusinessLicense라는 두 가지 '하위 타입'이 존재
- 정사각형/직사각형 문제
- LSP를 위반하는 전형적인 문제
- Square는 Rectangle의 하위 타입으로는 적합하지 않음
- Rectangle의 높이와 너비는 서로 독립적으로 변경될 수 있는 반면,
- Square의 높이와 너비는 반드시 함께 변경되기 때문
- LSP와 아키텍처
- 시간이 지나면서 인터페이스와 구현체에도 적용
- LSP 위배 사례
- 다양한 택시 파견 서비스를 통합하는 애플리케이션을 만들고 있다고 하자.
- 고객은 택시업체는 신경쓰지 않고 자신의 상황에 가장 적합한 택시를 찾음
- 고객이 이용할 택시를 결정하면, 시스템은 REST 서비스를 통해 선택된 택시를 고객 위치로 파견
- 택시 파견 REST 서비스의 URI가 운전기사 데이터베이스에 저장되어 있다면 -> 기사가 선택되면, 해당 기사의 레코드로부터 URI 정보를 얻은 다음, 그 URI 정보를 이용하여 해당 기사를 고객 위치로 파견
- 시스템은 URI에 파견에 필요한 정보를 덧붙인 후, PUT 방식으로 호출
- 다양한 택시업체에서 동일한 REST 인터페이스를 반드시 준수하도록 만들어야 함
- 어떤 택시업체가 destination 필드를 dest로 축약해서 사용한다면?
- 예외처리 해야 하나?
- 이 버그로부터 시스템을 격리하기 위해 파견 URI를 키로 사용하는 설정용 데이터베이스를 이용하는 파견 명령 생성 모듈을 만들어야 할 수도 있음
- 결론
- LSP는 아키텍처 수준까지 확장할 수 있고, 반드시 확장해야만 한다.
10장 ISP: 인터페이스 분리 원칙
- 인터페이스 분리 원칙
- User1은 오직 op1을, User2는 op2만을, User3는 op3만을 사용한다고 가정
- OPS가 정적 타입 언어로 작성된 클래스라고 하자.
- User1에서는 op2와 op3를 전혀 사용하지 않음에도 두 메서드에 의존하게 됨 -> OPS 클래스에서 op2으 ㅣ소스 코드가 변경되면 User1도 다시 컴파일한 후 새로 배포해야 함 (User1과 관련된 코드는 전혀 변경되지 않았음에도 불구하고)
- 이러한 문제는 오퍼레이션을 인터페이스 단위로 분리하여 해결
- ISP와 언어
- 정적 타입 언어는 import, use 또는 include와 같은 타입 선언문을 사용하도록 강제 -> 의존성
- 루비나 파이썬과 같은 동적 타입 언어에서는 소스 코드에 이러한 선언문이 존재하지 않음 -> 대신 런타임에 추론이 발생
- 따라서 ISP를 아키텍처가 아니라, 언어와 관련된 문제라고 결론내릴 여지가 있음
- ISP와 아키텍처
- 필요 이상으로 많은 걸 포함하는 모듈에 의존하는 것은 해로움 -> 불필요한 재컴파일과 재배포를 강제
- 고수준인 아키텍처 수준에서도 마찬가지
- 새로운 프레임워크 도입시 프레임워크는 특정 데이터베이스를 반드시 사용하도록 만들어졌다면...
- 시스템은 프레임워크, 프레임워크는 다시 데이터베이스에 의존
- 불필요한 기능이 포함되고 재배포해야 할지도 모름
- 결론
- 불필요한 짐을 실은 무언가에 의존하면 문제가 발생할 수 있음
11장 DIP: 의존성 역전 원칙
- 의존성 역전 원칙
- '유연성이 극대화된 시스템' -> 소스 코드 의존성이 추상에 의존하며 구체에는 의존하지 않는 시스템
- 안정된 추상화
- 추상 인터페이스에 변경이 생기면 이를 구체화한 구현체들도 따라서 수정해야 함
- 반대로 구체적인 구현체에 변경이 생기더라도 그 구현체가 구현하는 인터페이스는 변경도리 필요가 없음
- 즉, 안정된 소프트웨어 아키텍처란 변동성이 큰 구현체에 의존하는 일은 지양하고, 안정된 추상 인터페이스를 선호하는 아키텍처
- 코딩 실천법
- 변동성이 큰 구체 클래스를 참조하지 말라 : 대신 추상 인터페이스를 참조
- 변동성이 큰 구체 클래스로부터 파생하지 말라 : 상속은 신중하게
- 구체 함수를 오버라이드 하지 말라 : 대체로 구체 함수는 소스 코드 의존성을 필요로 하므로 의존성을 상속하게 됨
- 구체적이며 변동성이 크다면 절대로 그 이름을 언급하지 말라
- 팩토리
- 변동성이 큰 구체적인 객체는 주의해서 생성해야 함 -> 소스 코드 의존성 발생
- 대다수 객체 지향 언어에서 의존성 처리를 위해 추상 팩토리를 사용
- 구체 컴포넌트
- DIP 위배를 모두 없앨 수는 없음
- 다만, DIP를 위배하는 클래스들은 적은 수의 구체 컴포넌트 내부로 모을 수 있고 시스템의 나머지 부분과 분리가 가능
- 결론
- 의존성은 더 추상적인 엔티티가 있는 쪽으로만 향하며 이 규칙은 의존성 규칙이라 불릴 것이다.
728x90
반응형
'도서 리뷰 > IT 도서 리뷰' 카테고리의 다른 글
[IT 도서 리뷰] 클린 아키텍처 (5부 아키텍처) (0) | 2023.08.15 |
---|---|
[IT 도서 리뷰] 클린 아키텍처 (4부 컴포넌트 원칙) (0) | 2023.07.17 |
[IT 도서 리뷰] 클린 아키텍처 (2부 벽돌부터 시작하기: 프로그래밍 패러다임) (0) | 2023.07.02 |
[IT 도서 리뷰] 클린 아키텍처 (1부 소개) (0) | 2023.06.25 |
[IT 도서 리뷰] 구글 엔지니어는 이렇게 일한다 (CH. 8) (0) | 2023.02.09 |