본문 바로가기
iOS/Architecture

클린 아키텍쳐 1, 2, 3부 정리 (1장~11장)

by HaningYa 2022. 3. 31.
728x90

1장 설계와 아키텍쳐

  • 설계: 저수준 구조 결정사항
  • 아키텍쳐: 고수준의 무언가

둘은 명확한 경계를 나눌수 없고 고수준에서 저수준으로 가는 의사결정의 연속성만 있다.

의사결정이라는 키워드를 봤을때 좋은 S/W설계를 목표로 하여 의사결정을 진행해야 하고

좋은 S/W 설계는 "필요한 시스템을 만들고 유지보수 하는데 투입되는 '인력'을 최소화 하는데 있다.

약간 인력의 최소화보다는 인력이 늘어남에 따라 생산성이 비례할 수 있는게 적절한것 같다.

결국엔 돈 "경제성" 이다.

현재 우리팀은 사례처럼 엉망진창이 되어가는 신호를 나타내고 있을까?
만약 그렇다면 원인이 무엇일까. 좋은 아키텍쳐를 통해 해결할 수 있을까.

2장 두가지 가치에 대한 이야기

두가지 가치는

  • 행위: 요구사항을 코드로 구체화
  • 구조: 변경이 용의한 프로그램을 만들어야 한다.

프로그램이 동작하는 것 vs 프로그램이 더 쉽게 변경 가능한 것

결국 타협해야 하는 상황이지만 프로그램이 동작하는 가치는 누구나 알지만 프로그램이 더 쉽게 변경 가능한 가치는 개발자가 아니라면 중요함을 놓치기 쉽기 떄문에 개발자가라면 구조에 더 가치를 두어야 할것 같다.

어떤 방법으로 구조에 대한 가치를 지켜낼 수 있을까?
처음부터 구조에 대한 고민을 충분히 하더라도 뒤에 바뀔 수도 있다.
팀 내에서 주기적으로 구조 개선에 대한 의견을 모아 특정 기간에 진행하는게 효과적일까

3장 패러다임 개요

패러다임: 프로그래밍을 하는 방법 (언어에 독립적)

  • 구조적 프로그래밍: 제어 흐름의 직접적인 전환에 대한 규칙 부여
  • 객체지향 프로그래밍: 제어 흐름의 간접적인 전환에 대한 규칙 부여
  • 함수형 프로그래밍: 할당문에 대해 규칙을 부여

공통점: 프로그래머에게서 권한을 박탈한다.


4장 구조적 프로그래밍

goto 문장 사용시 재귀적으로 분해하는 과정에 방해가 된다.

결국 분할 정복 접근법을 사용하지 못하기 때문에 if/then/else do/while과 같은 분기와 반복을 통해

증명 가능한 단위까지 재귀적으로 세분화 할 수 있다.

모든 프로그램은 순차/분기/반복 세가지 구조만으로 표현이 가능하다.

결론적으로 완벽한 프로그램은 존재할 수 없고 최대한 쪼개고 분리하여
증명 가능한 세부 기능들을 테스트 하고 목표에 부합할 만큼 충분히 참이라고 여긴다. 


5장 객체지향 프로그래밍

캡슐화, 상속, 다형성 개념들의 조합

#캡슐화: 데이터의 은닉을 통해 데이터와 함수가 응집력 있게 구성된 집단을 서로 구분짓는 선을 그을 수 있다.

하지만 새로운 OO 언어들은 캡슐화를 강제하지 않는다.
-> 프로그래머가 올바르게 행동함으로서 캡슐화된 데이터를 우회해서 사용하지 않을거라는 믿음에 기반

#상속: 어떤 변수와 함수를 하나의 유효범위로 묶어서 재정의

OO 언어들 이전부터 비슷한 개념의 방식은 있었다.

#다형성: Unix운영체제의 모든 입출력 드라이버의 표준함수를 예시로 들 수 있다.
-> 여튼 요지는 함수를 가리키는 포인터를 응용한 것이 다형성 이다.

다형성 = 포인터의 포인터

플러그인 아키텍쳐를 통해 입출력 장치의 독립성을 지원했지만 대다수의 프로그래머가 직접 함수를 가리키는 포인터를 사용하기는 위험했다. (포인터들을 초기화 하는 작업의 위험성)

이 위험성을 OO 언어들이 해결했다. 그래서 다형성을 안전하고 편리하게 제공했다. 
결론적으로 소스코드 의존성을 어디서든 역전시킬 수 있다.


6장 함수형 프로그래밍

동시성 application에서 마주하는 문제들은 가변 변수가 없으면 절대 생기지 않는다.


3장 설계원칙

#SOLID 원칙: 함수와 데이터 구조를 클래스로 배치하는 방법, 이들 클래스를 결합하는 방법

좋은 아키텍쳐는 깔끔한 코드로 부터 시작된다.

깔끔한 코드 -> 중간 수준의 S/W구조가 다음을 만족하도록 개발한다.

  • 변경에 유연하다.
  • 이해가 쉽다.
  • 많은 S/W시스템에 사용될 수 있는 컴포넌트의 기반이 된다.

여기서 말하는 중간수준: 코드레벨 보다는 조금 상위, 모듈과 컴포넌트 내부에서 사용되는 소프트웨어 구조를 정의하는데 도움을 준다.

  • SRP: 모듈 변경의 이유는 하나여야 한다.
  • OCP: 기존 코드를 수정하기보다 새로운 코드를 추가하는 방식을 사용해야한다.
  • LSP: 상호 대체 가능한 구성요소를 이용해 S/W 시스템을 만드려면 이들 구성요소는 반드시 치환 가능해야 한다.
  • ISP: 사용하지 않는것에 의존해야 한다.
  • DIP: 고수준의 정책코드는 저수준의 코드에 의존하면 안되고 대신 세부사항의 정책에 의존해야 한다.

7장 SRP

하나의 책임만 가져야 한다    단일 모듈 변경의 이유는 단 하나여야 한다.

-> 하나의 모듈은 하나의 액터에 대해서만 책임져야 한다.
모듈: 소스파일/ 함수와 데이터 구조로 구성된 응집된 집합
응집: cohensive: 단일 액터를 책임지는 코드를 함께 묶어주는것

#SRP 위반 사례
1. 우발적 중복
2. merge conflict

결론

  • SRP는 메서드 클래스 수준의 원칙이다.
  • 컴포넌트 수준에서는 공통 폐쇄 원칙(Common closure principle)
  • 아키텍쳐 수준에서는 아키텍쳐의 경계를 생성을 책임지는 변경의 축

8장 OCP

S/W개체는 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.

-> S/W 는 확장되기만 하고 변경되서는 안된다.

예시) 사고실험: 정보 보고서를 출력하는 기능 추가시 변경되는 코드의 양은 이상적으로 0이여야 한다.

  • 화살표 받는 쪽이 보호받아야 할 영역
  • Interactor가 OCP를 가장 잘 준수할 수 있는 곳에 위치한다.
  • 왜냐하면 interactor가 가장 변하지 않을 업무 규칙을 포함하기 떄문에
  • OCP를 통해 계층 구조로 조직화 하면 저수준의 컴포넌트에서 발생할 변경으로부터 고수준의 컴포넌트를 보호할 수 있다.
    • 방향성 제어: 인터페이스를 통해 의존성 역전
    • 정보 은닉: 인터페이스를 통해 Interactor 내부에 대해 많이 알지 못하도록

결론

- OCP는 시스템을 확장하기 쉬운 동시에 변경으로 인해 시스템이 너무 많은 영향을 받지 않도록 한다.
어떻게?
- 시스템을 컴포넌트 단위로 분리하고, 고수준의 컴포넌트를 저수준의 컴포넌트의 변경으로 부터 보호할 수 있는 형태의 계층구조가 만들어 져야 한다.


9장 LSP

하위타입: 치환하더라도 행위가 변하지 않으면 하위타입이라 할 수 있다.

#정사각형 직사각형 문제

LSP와 아키텍쳐
- 초기: 상속을 사용하는 가이드 정도로 취급
- 지금: 인터페이스와 구현체에도 적용되는 광범위한 S/W설계 원칙

아키텍쳐 관점에서 LSP를 이해하려면 이 원칙을 어겼을때 아키텍쳐에 어떤 일이 일어나는지 관찰하는것

LSP 위반사례
- 인터페이스를 따르지 않아서 if 조건문들 (타입 캐스팅)이 추가되는것

결론
- LSP는 아키텍쳐 수준까지 확장할 수 있고 반드시 확장해야 한다.
- 위배되면 별도의 메커니즘을 추가해야 하기 떄문이다.

10장 ISP

인터페이스 분리 원칙
-> 필요 이상으로 많은걸 포함한 모듈에 의존하는 건 해로운 일다. (재컴파일 재배포의 문제 등)


11장 DIP

의존성 역전 원칙

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

DIP를 논할 때 OS나 플랫폼과 같이 안전성이 보장된 환경에 대해서는 무시한다.
의존하지 않도록 피하려 하는건 변동성이 큰 구체적인 요소들 이다.

#안정된 추상화

  •  변동성이 큰 구체 클래스를 참조하지 마라
    - 대신 추상 인터페이스를 참조해라 eg 추상 팩토리
  • 변동성이 큰 구체 클래스로 부터 파생하지 말라
    - 상속은 신중하게 사용
  • 구체 함수를 오버라이드 하지 말라
    - 대체로 구체 함수는 소스코드 의존성을 필요로 한다. 구체 함수를 오버라이드 하면 의존성을 제거할 수 없게되어 실제로 그 의존성을 상속하게 된다.
  • 구체적이며 변동성이 크다면 절대 그 이름을 언급하지 말라

소스코드 의존성은 제어흐름과는 반대방향으로 역전된다. -> 의존성 역전이라 부른다.

DIP 위배를 전부 없앨 수 는 없지만 구체/추상 클래스의 경계를 나눴을떄 DIP를 준수해야 한다.


https://www.youtube.com/channel/UCEuyt4RB4mlMfADQMz-d2DQ

 

베어코드

 

www.youtube.com

 

728x90

댓글