본문 바로가기
개발서적 읽으며 끄적끄적/Clean Architecture

클린 아키텍처 3-6장

by 토끼찌짐 2019. 11. 16.

Robert C. Martin의 Clean Architecture 2부 '벽돌부터 생각하기: 프로그래밍 패러다임' 3-6장을 읽고 메모한 글입니다. 요약글은 아닙니다. 개인적인 의견도 포함되어 있으니 참고해주세요.

 

3장. 패러다임 개요

프로그래밍 패러다임이란 무엇인가?

사전에서의 패러다임
- 한 시대의 사람들의 견해나 사고를 근본적으로 규정하고 있는 인식의 체계. 또는, 사물에 대한 이론적인 틀이나 체계. 순화어는 '틀'.

프로그래밍 패러다임은 '프로그래밍에 대한 이론적인 틀이나 체계'. 프로그래밍을 하는 방법이라고 할 수 있다. 언제, 어디에 이 구조를 사용할지 결정하며, 따라서 대체로 언어에 독립적이다.

여기서는 다음 세 가지 프로그래밍 패러다임을 다룬다.

  1. 구조적 프로그래밍 (Structured Programming)

  2. 객체지향 프로그래밍 (Object-Oriented Programming)

  3. 함수형 프로그래밍 (Fuctional Programming)

이 세 가지 패러다임과 아키텍처의 세 가지 큰 관심사(함수, 컴포넌트 분리, 데이터 관리)가 어떻게 서로 연관되는지 주목해보라.

 

4장. 구조적 프로그래밍

구조적 프로그래밍의 개요

구조적 프로그래밍 이전의 프로그래밍 방식: goto문을 사용하여 필요할 때마다 필요한 곳으로 점프! > 무분별한 goto문의 남발 > 읽기도 어렵고 짜기도 어렵다. 시스템이 커지면 커질수록 고통스럽다. 개발자가 프로그래밍에 실패 하는 상황이 잦아짐.

구조적 프로그래밍이란, 이런 goto문의 남발 대신 시스템의 기능과 플로우를 파악하고, if/then/else 와 do/while 등의 제어구조를 사용하여 코드를 작성한다. 구조적으로 설계하며, 절차적으로 실행된다는 것이 이전과 다른 점이다.

 

구조적 프로그래밍의 탄생배경

재미있는 내용이라서 굳이 메모함.

구조적 프로그래밍 전에는, 앞서 언급했듯이 개발자들이 프로그래밍에 실패하는 경우가 많았다. 프로그래밍은 너무 어렵고 투박하며 무분별한 goto문등의 사용으로 인해 작은 세부사항이라도 간과하면 안 되는 분야였기 때문이다. 이런 문제를 해결하고자 한 사람이 바로 데이크스트, 구조적 프로그래밍을 도입한 사람이다.

그는 이 '실패'를 해결하기 위해 증명(proof)이라는 수학적 원리를 적용하고자 했다. 수학자가 유클리드 계층구조를 사용하는 방식처럼, 프로그래머도 입증된 구조를 이용하고, 이들 구조를 코드와 결합시키며, 그리하여 코드가 올바르다는 사실을 스스로 증명하게 되는 방식 을 취하는 것이 그의 비전이었다. 이 증명을 시도하는 과정에서 그는 모든 프로그램이 순차(sequence), 분기 (selection), 반복(iteration) 이라는 세 가지 구조만으로 표현할 수 있다는 사실을 알게 되고, 그것이 구조적 프로그래밍이 탄생할 수 있었던 배경이다. (참고로 그의 증명 시도는 결과적으로 실패한다. 이유는 밑에서 설명)

 

구조적 프로그래밍이 이룩한 것

(1) 기능적 분해

거대한 기술서(요구서)를 고수준의 기능들로 분해하고 그것들을 또 다시 저수준의 함수들로 분해하는 식으로, 모듈을 기능적으로 분해하는 방식을 가져다 주었다. 또한 이렇게 분해한 기능들을 구조적 프로그래밍의 제한된 제어 구조로 표현한다. (무분별한 goto문이 아니라, if/then/else , do/while등의 같은 분기와 반복이라는 단순한 제어 구조로.)

(2) 테스트

테스트가 가능할 정도의 모듈로 쪼갤 수 있도록 권장(강요)하는 방식이기 때문에, 증명 가능한 수준의 세부 기능들에 거짓이 없다는 것 을 테스트할 수 있게 되었다.

 

프로그래밍은 수학이 아니라 과학에 가깝다

수학적 증명 방식으로 프로그래밍을 증명하고자 한 데이크스트의 시도는 결과적으로 실패한다. 프로그래밍은 수학이 아니라 과학에 가까운 분야이기 때문이다.

수학은 참을 입증(증명, proof)하는 학문, 과학은 참을 증명하는 것이 아니라, 각고의 노력을 기울여도 반증을 할 수 없을 정도의 서술이 있다면 그것이 목표에 부합할 정도로 참이라고 보는 학문이다. 수많은 과학법칙은 역사적으로 반증되어왔고, 우리가 현재 믿고 있는 과학법칙도 새로운 실험을 통해 부정될 가능성이 항상 열려있다). 프로그래밍도 마찬가지이다.

"테스트는 버그가 있음을 보여줄 뿐, 버그가 없음을 보여줄 수는 없다."

완벽히 동작하는 프로그램이란 것은 없다. 최대한 꼼꼼하게 테스트를 거쳐도 버그가 발생하지 않는, 목표에 부합할 만큼은 동작하는 프로그램만이 존재할 뿐이다. 구조적 프로그래밍은 이런 테스트를 할 수 있도록 해주는 기능적 분해라는 개념을 우리에게 가져다주었다.

 

5장. 객체지향 프로그래밍

OO(객체지향)은 추상화와 다형성을 권장하며, 편리하게 사용할 수 있도록 해주는 기법. (본문에서 강조된 것은 Polymorphism 이라는 단어지만, 다형성의 기저에 깔려있는 것이 추상화 개념이기 때문에 두 가지를 같이 적어둔다.)

추상화와 다형성의 이점의 하나로 의존성 역전(Dependency Inversion)을 들 수 있다. 소스 코드의 의존성을 역전시킬 수 있다. 즉, 소스 코드의 의존성을 원하는 방향으로 설정할 수 있다. 따라서 저수준의 모듈과 고수준의 모듈의 의존성을 최소화할 수 있고, 저수준의 모듈을 마치 플러그인처럼 취급할 수 있다. 이렇게 독립적인 컴포넌트들로 구분짓는 것이 가능하기 때문에 (1) 배포에 독립적 (특정 컴포넌트의 코드가 변경되면 그 컴포넌트만 다시 배포하면 된다) 이며, (2) 개발에 독립적 (각각의 모듈을 각각의 팀에서 독립적으로 개발할 수 있다) 일 수 있다. 따라서 생산성이 증대된다.

개인적으로 이 장에서 엄청나게 시간을 잡아먹었는데, 엉클 밥이 Polymorphism 이라는 용어를 강조하며 그것을 Abstraction 개념과 접목해서 설명하지 않았기 때문이었다. 그 부분에 대해 따로 열심히 판 뒤에야 이미 그 내용이 본문에 녹아있었다는 것을 알게 되었다. (아는 만큼 보인다.)

추상화와 다형성의 개념과 장점, 관계성은 아예 따로 포스트를 작성했다. >> https://wlaxhrl.tistory.com/78

 

6장. 함수형 프로그래밍

FP에 대해서는 아직 잘 몰라서 별로 덧붙일 말이 없다.

함수형 프로그래밍의 키워드: 불변성(Immutable).

불변성이 중요한 이유: side-effect를 막을 수 있다. 멀티 스레드/프로세스 환경에서 발생하는 많은 동시성 문제(race condition, deadlock, concurrent update, ...)의 원인은 가변 변수에 있기 때문이다.

안전을 위해 가능하면 많은 부분을 불변 컴포넌트에서 처리해야 한다. 불변 컴포넌트는 순수 함수(pure fuction)로 이루어진다.

이벤트 소싱(event sourcing) 개념: '상태'가 아닌 '트랜잭션'을 저장하는 전략. 은행에서의 입출금 내역을 떠올려보면 된다. 모든 입출금 트랜잭션을 데이터로 간주하여 저장. 상태가 필요해지면 시작 시점부터의 모든 트랜잭션을 처리함. DB에서 CRUD가 아닌 CR만 이루어지기 때문에 동시성 문제가 일어나지 않는다.

 

결론

  • 구조적 프로그래밍은 제어흐름의 직접적인 전환에 부과되는 규율
  • 객체지향 프로그래밍은 제어흐름의 간접적인 전환에 부과되는 규율
  • 함수형 프로그래밍은 변수 할당에 부과되는 규율

세 패러다임 모두 우리에게서 무언가를 제한하고, 해선 안 되는 것에 대한 법칙이다. 각 패러다임은 우리가 코드를 작성하는 방식의 형태를 한정시킨다.

변하지 않는 소프트웨어의 핵심은 순차(Sequence), 분기(Selection), 반복(Iteration), 참조(Indirection).

여기까지가 엉클 밥의 결론인데, 저 세 가지 구분이 솔직히 확 와닿지는 않는다. 무슨 의미인지는 알 것 같지만, 영어 번역이라 그런가..

세 가지가 등장한 배경은 모두 보다 안전한 코드를 짜기 위해서일 것이다. 이전 방식에서 고통받았던 부분을 해결하고자 한 노력이 각 패러다임에 드러나있다. 그런 관점에서 나는 세 가지를 이렇게 정리해보았다.

  • 구조적 프로그래밍: 구조적으로 설계하고 절차적으로 실행되게끔 하여 안전하게 만듬
  • 객체지향 프로그래밍: 추상화와 다형성을 통해 의존성을 약화시킴으로써 안전하게 만듬
  • 함수형 프로그래밍: 불변성을 통해 부작용을 줄이고 안전하게 만듬