본문 바로가기
  • SDXL 1.0 + 한복 LoRA
  • SDXL 1.0 + 한복 LoRA
Study/컴퓨터 공학

클린 아키텍처 #3

by 마즈다 2022. 1. 18.
반응형


2022.01.07 - [Study/컴퓨터 공학] - 클린 아키텍처

2022.01.11 - [Study/컴퓨터 공학] - 클린 아키텍처 #2

2022.02.15 - [Study/컴퓨터 공학] - 클린 아키텍처 #4

2022.03.02 - [Study/컴퓨터 공학] - 클린 아키텍처 #5

2022.05.16 - [Study/컴퓨터 공학] - 클린 아키텍처 #6

클린 아키텍처 : 3부 11장까지

저자 : 로버트 C. 마틴

출판 : 인사이트

출간 : 2019.08.20.


학습 방법 : 책을 읽고 소제목 단위로 핵심 내용을 1~3문장 정도로 요약

 


 

3부. 설계 원칙

⦿ SOLID 원칙 

✓ 함수와 데이터를 결합한 집합(클래스와 같은)에 적용되는 원칙 

✓ 목적 

- 변경에 유연하다. 

- 이해하기 쉽다. 

- 재사용 가능한 컴포넌트의 기반이 된다. 

✓ 모듈과 컴포넌트 내부에서 사용되는 소프트웨어 구조를 정의 


SRP(Single Responsibility Principle) : 단일 책임의 원칙

OCP(Open-Closed Principle) : 개방-폐쇄의 원칙

LSP(Liskov Substitution Principle) : 리스코프 치환 원칙

ISP(Interface Segregation Principle) : 인터페이스 분리 원칙

DIP(Dependency Inversion Principle) : 의존성 역전의 원칙

 

7장. SRP: 단일 책임의 원칙 

 

✓ SRP(Single Responsibility Principle) : 단일 책임의 원칙에 대한 오해 

- 모든 모듈이 단 하나의 일만 해야 한다. 


✓ 제대로 된 의미 

- 단일 모듈은 변경의 이유가 오직 하나뿐이어야 한다. 

- 하나의 모듈은 오직 하나의 사용자 또는 이해관계자에 대해서만 책임져야 한다. 

- 최종버전 : 하나의 모듈은 오직 하나의 액터에 대해서만 책임져야 한다. 

 

⦿ (SRP 위반)징후1 : 우발적 중복 

✓ 서로 다른 액터가 같은 기능을 공유하지만 공유된 기능에서 한 액터에게만 필요한 변경이 발생하는 경우 

 

⦿ (SRP 위반)징후2 : 병합 

✓ 서로 다른 액터를 담당하는 두 개발자가 동시에 각자가 담당하는 액터를 위한 수정을 진행할 때 버전 관리 시스템에서는 위험한 병합이 발생할 수 있다. 

 

⦿ 해결책 

✓ 액터별로 완전히 독립된 클래스를 만들고 데이터를 공유한다. 

✓ 다수의 클래스를 관리해야 하는 어려움을 피하기 위해 Facade 패턴을 이용한다. Facade 클래스는 독립된 각각의 클래스 객체를 생성하고 요청된 메서드를 가진 객체에 위임하는 역할을 한다. 

✓ 가장 중요한 메서드를 가진 클래스를 Facade로 만들어 데이터까지 관리하고 덜 중요한 메서드를 가진 클래스를 분리한다. 

 

⦿ 결론

✓ 단일 책임의 원칙은 메서드와 클래스 수준의 원칙이다. 

✓ 보다 상위에서는 다음 두 원칙으로 확장된다. 

- 컴포넌트 수준 : 공통 폐쇄의 원칙 

- 아키텍처 경계의 생성 : 변경의 축 

 

8장. OCP: 개방-폐쇄 원칙 

 

✓ 버트란트 마이어, 1988

“ 소프트웨어 개체(artifact)는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다. “

✓ 소프트웨어 개체의 행위는 확장할 수 있어야 하지만, 이 때 개체를 변경해서는 안된다. 

✓ 아키텍처 컴포넌트 수준에서 고려할 때 훨씬 중요. 

 

⦿ 사고 실험

✓ 서로 다른 책임이 있다면(액터가 다르다면) 각각의 책임을 (중요도의 계층 구조에 따라)분리한다(소스 코드도 분리). 

✓ 변경할 컴포넌트는 변경으로부터 보호되어야 할 컴포넌트에 의존해야 한다. 

✓ 가장 엄격하게 보호 받아야 하는 컴포넌트는 가장 높은 수준의 정책을 포함한(업무 규칙을 포함한) 컴포넌트이다. 


UML에서 화살표가 출발한 클래스는 화살표가 가리키는 클래스를 알고 있으나 그 반대로는 전혀 알지 못한다. 즉, 인터페이스 구현체는 인터페이스를 implements 하면서 그 존재를 알게 되지만 인터페이스는 어떤 자식 클래스들이 있는지 알지 못하는 것과 같다.

 

⦿ 방향성 제어

✓ 인터페이스는 의존성을 역전시키기 위해 존재한다. 

✓ 보호받아야 할 컴포넌트가 변경될 컴포넌트에 의존하는 상황을 인터페이스를 통해 역전시킨다. 

 

⦿ 정보 은닉

✓ 추이 종속성으로 인해 “자신이 직접 사용하지 않는 요소에는 절대로 의존해서는 안된다”는 원칙을 위반하는 경우를 방지 

✓ 인터페이스를 통해 불필요한 정보의 노출을 막는다. 

 

⦿ 결론

✓ OCP의 목표는 시스템을 확장하기 쉬운 동시에 변경으로 인해 시스템이 너무 많은 영향을 받지 않도록 하는 데 있다. 

✓ 이를 위해 시스템은 컴포넌트 단위로 분리하고 저수준 컴포넌트의 변경으로부터 고수준 컴포넌트가 보호받을 수 있도록 의존성 계층 구조를 만들어야 한다. 



9장. LSP: 리스코프 치환 원칙 

 

⦿ 바바라 리스코프의 하위 타입 

“여기에서 필요한 것은 다음과 같은 치환(substitution) 원칙이다. S 타입의 객체 01 각각에 대응하는 T 타입 객체 02가 있고, T 타입을 이용해서 정의한 모든 프로그램 P에서 02 자리에 01을 치환하더라도 P의 행위가 변하지 않는다면 S는 T의 하위 타입이다. “

 

⦿ 상속을 사용하도록 가이드하기 

✓ 주 프로그램이 인터페이스를 사용하게 되면 주 프로그램은 인터페이스의 하위 타입(인터페이스의 구현체)의 행위에 상관없이 기능할 수 있다. 

 

⦿ 정사각형/직사각형 문제 

✓ 수학적으로는 정각형은 직사각형의 부분집합(하위 타입)이지만 프로그램상으로는 그렇지 않다(정사각형은 가로와 세로가 동일한 값을 갖도록 강제됨). 

✓ 이로 인해 직사각형의 인터페이스를 만들고 이를 구현한 정사각형 클래스를 만들게 되면 정사각형 클래스는 직사각형 인터페이스를 대체할 수 없아 리스코프 치환 법칙에 위배된다. 

 

⦿ LSP와 아키텍처 

✓ 클래스의 상속, 인터페이스의 구현 등 LSP는 광범위한 소프트웨어 설계 원칙으로 변모해왔다. 

✓ 루비의 덕타이핑, 동일한 REST 인터페이스에 응답하는 서비스 집단 등에도 적용할 수 있다. 

 

⦿ LSP 위배 사례 

✓ LSP 원칙을 위해하는 순간 이에 대응하기 위한 수많은 분기와 설정이 추가되어야 한다(복잡도의 증가). 

 

⦿ 결론

✓ 시스템의 복잡도가 올라가는 것을 막기 위해 LSP는 아키텍처 수준까지 확장해야만 한다. 



10장. ISP: 인터페이스 분리 원칙 

 

✓ 정적 타입 언어의 경우 서로 다른 클래스에서 각각 전용으로 사용하는 함수들을 모두 포함하는 하나의 클래스가 존재할 경우 하나의 클래스와 관련된 함수가 변경되면 모든 클래스들을 다시 컴파일해서 배포해야 한다. 

✓ 이에 대한 해결책은 각각의 클래스에 대응하는 인터페이스를 만들어 연결하는 것이다.


- 정적 타입 언어 : 컴파일 시에 자료형을 결정해야 하는 언어 (C, C++, JAVA, C# 등)

 

⦿ ISP와 언어 

✓ 앞의 사례는 정적 타입 언어에서만 발생하는 문제(모듈 포함을 위한 선언문으로 야기된)로 아키텍처가 아닌 언어와 관련된 문제로 결론 내릴 여지가 있다.


- 89쪽 주석1 참조 필요

 

⦿ ISP와 아키텍처 

✓ 필요 이상으로 많은 것을 포함하는 모듈에 의존하는 것은 위험하다. 

✓ 필요로 하는 기능이 아닌 부분에서 문제가 발생할 경우 영향을 받는 상황이 발생할 수 있다. 

 

⦿ 결론

✓불필요한 기능을 포함한 모듈에 의존하는 것은 위험하다. 



11장. DIP: 의존성 역전 원칙 

 

✓ 유연성이 극대화된 시스템 : 소스 코드 의존성이 추상에 의존하며 구체에는 의존하지 않는 시스템 

✓ 안정성이 보장된 환경에서는 대체로 무시하는 원칙 

✓ 변동성이 큰 구체적인 요소에 대해서 지켜야 하는 원칙 

 

⦿ 안정된 추상화 

✓ 인터페이스는 구현체보다 변동성이 낮다. 

✓ 안정된 소프트웨어 아키텍처 : 변동성이 큰 구현체에 의존하는 일은 지양하고 추상 인터페이스를 선호하는 아키텍처 

- 변동성이 큰 구체 클래스를 참조하지 말라.

- 변동성이 큰 구체 클래스로부터 파생하지 말라.

- 구체 함수를 오버라이드 하지 말라.

- 구체적이며 변동성이 크다면 절대로 그 이름을 언급하지 말라. 

 

⦿ 팩토리 

✓ 변동성이 큰 구체적인 객체 참조를 막기 위해 추상 팩토리를 사용한다. 

✓ 추상 팩토리를 이용하면 소스 코드의 의존성은 제어흐름과는 반대 방향으로 향한다. 


팩토리 패턴의 이점 : 개별 클래스에서 객체를 생성하지 않아 객체 생성 방법이 변경된 경우 수정이 용이하다.

 

⦿ 구체 컴포넌트 

✓ DIP 위배를 모두 없앨 수는 없으며 다만 DIP를 위반하는 클래스들을 적은 수의 구체 컴포넌트 내부로 모을 수 있다. 

✓ 대표적인 구체 컴포넌트로 main 함수가 있다. 

 

⦿ 결론

✓ 아키텍처 다이어그램에서 가장 눈에 드러나는 원칙 

✓ 의존성은 더 추상적인 엔티티가 있는 쪽으로만 향한다. 

반응형

'Study > 컴퓨터 공학' 카테고리의 다른 글

클린 아키텍처 #6  (0) 2022.05.16
클린 아키텍처 #5  (0) 2022.03.02
클린 아키텍처 #4  (0) 2022.02.15
클린 아키텍처 #2  (0) 2022.01.11
클린 아키텍처  (0) 2022.01.07