본문 바로가기

Grew from/Book-Memo

[북메모] 클린 아키텍처 - 프로그래밍 패러다임

구조적 프로그래밍


구조적 프로그래밍이 오늘날까지 가치 있는 이유는 프로그래밍에서 반증 가능한 단위를 만들어 낼 수 있는 바로 이 능력때문이다. 또한 흔히 현대적 언어가 아무런 제약 없는 goto 문장은 지원하지 않는 이유이기도 하다. 뿐만 아니라 아키텍처 관점에서는 기능적 분해를 최고의 실천법 중 하나로 여기는 이유이기도 하다.
가장 작은 기능에서부터 가장 큰 컴포넌트에 이르기까지 모든 수준에서 소프트웨어는 과학과 같고, 따라서 반증 가능성에 의해 주도된다. 소프트웨어 아키텍트는 모듈, 컴포넌트 서비스가 쉽게 반증 가능하도록(테스트하기 쉽도록) 만들기 위해 분주히 노력해야 한다. 이를 위해 구조적 프로그래밍과 유사한 제한적인 규칙들을 받아들여 활용해야 한다.

객체지향 프로그래밍


객체지향이란 무엇인가? 이 질문에 답하기 위해 다양한 의견과 수많은 답변이 있었다. 하지만 소프트웨어 아키텍트 관점에서 정답은 명백하다. 객체지향이란 다형성을 이용하여 전체 시스템의 모든 소스 코드 의존성에 대한 절대적인 제어권한을 획득할 수 있는 능력이다. 객체지향을 사용하면 아키텍트 플러그인 아키텍처를 구성할 수 있고, 이를 통해 고수준의 정책을 포함하는 모듈은 저수준의 세부사항을 포함하는 모듈에 대해 독립성을 보장할 수 있다. 저수준의 세부사항은 중요도가 낮은 플러그인 모듈로 만들 수 있고, 고수준의 정책을 포함하는 모듈과는 독립적으로 개발하고 배포할 수 있다.

함수형 프로그래밍


여러가지 의미로 함수형이라는 개념은 프로그래밍 그 자체보다 앞서 등장했다. 이 패러다임에서 핵심이 되는 람다 계산법은 알론조 처리가 1930년대에 발명했다.

불변성과 아키텍처


아키텍트는 왜 변수의 가변성을 염려하는가? 경합(race), 교착상태(deadlock), 동시 업데이트(concurrent update) 문제가 모두 가변 변수로 인해 발생하기 때문이다. 만약 어떠한 변수도 갱신되지 않는다면 경합이나 동시 업데이트 문제가 일어나지 않는다. 락이 가변적이지 않다면 교착상태도 일어나지 않는다.
다시 말해 우리가 동시성 애플리케이션에서 마주치는 모든 문제, 즉 다수의 스레드와 프로세스를 사용하는 애플리케이션에서 마주치는 모든 문제는 가변 변수가 없다면 절대로 생기지 않는다.
아키텍트라면 동시성(concurrency)문제에 지대한 관심을 가져야만 한다. 우리는 스레드와 프로세스가 여러 개인 상황에서도 설계한 시스템이 여전히 강건하기를 바란다. 그렇다면 이제 불변성이 정말로 실현 가능한지를 스스로에게 반드시 물어봐야 한다.

가변성의 분리


불변성과 관련하여 가장 주요한 타협 중 하나는 애플리케이션 또는 애클리케이션 내부의 서비스를 가변 컴포넌트와 불변 컴포넌트로 분리하는 일이다. 불변 컴포넌트에서는 순수하게 함수형 방식으로만 작업이 처리되며, 어떤 가변 변수도 사용되지 않는다. 불변 컴포넌트는 변수의 상태를 변경할 수 있는, 즉 순수 함수형 컴포넌트가 아닌 하나 이상의 다른 컴포넌트와 서로 통신한다.
말하려는 요지는 애플리케이션을 제대로 구조화하려면 변수를 변경하는 컴포넌트와 변경하지 않는 컴포넌트를 분리해야 한다는 것이다. 그리고 이렇게 분리하려면 가변 변수들을 보로하는 적절한 수단을 동원해 뒷받침 해야한다.

이벤트 소싱


이벤트 소싱은 상태가 아닌 트랜잭션을 저장하자는 전략이다. 상태가 필요해지면 단순히 상태의 시작점부터 모든 트랜잭션을 처리한다.
물론 지름길을 택할 수도 있다. 예를 들어 매일 자정에 상태를 계산한 후 저장한다. 그 후 상태 정보가 필요해지면 자정 이후의 트랜잭션만을 처리하면 된다.
이제 이 전략에 필요한 데이터 저장소에 대해 생각해보자. 아마 저장 공간이 많이 필요할 것이다. 실제로 오프라인 데이터 저장소는 급격하게 증가하여 이제는 수테라바이트도 작다고 여기는 시대다. 따라서 우리는 저장 공간을 충분히 확보할 수 있다.
더 중요한 점은 데이터 저장소에서 삭제되거나 변경되는 것이 하나도 없다는 사실이다. 결과적으로 애플리케이션 CRUD가 아니라 그저 CR만 수행한다. 또한 데이터 저장소에서 변경과 삭제가 전혀 발생하지 않으므로 동시 업데이트 문제 또한 일어나지 않는다.
저장 공간과 처리 능력이 충분하면 애플리케이션이 완전한 불변성을 갖도록 만들 수 있고, 따라서 완전한 함수형으로 만들 수 있다.
이 이야기가 여전히 터무니없게 들린다면, 소스코드 버전 관리 시스템이 정확히 이 방식으로 동작한다는 사실을 떠올려 보면 도움이 될 것이다.

결론


요약하면

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

이들 세 패러다임 모두 우리에게서 무언가를 앗아갔다. 각 패러다임은 우리가 코드를 작성하는 방식의 형태를 한정시킨다. 어떤 패러다임도 우리의 권한이나 능력에 무언가를 보태지는 않는다.
지난 반세기 동안 우리가 배운 것은 해서는 안되는 것에 대해서다.
이 사실을 깨닫는다면 우리는 환영받지 못할 사실 즉 소프트웨어는 급격히 발전하는 기술이 아니라는 진실과 마주하게 된다. 1946년 앨런 튜링이 전자식 컴퓨터에서 실행할 거의 최초의 코드를 작성할 때 사용한 소프트웨어 규칙과 지금의 소프트웨어 규칙은 조금도 다르지 않다. 도구는 달라졌고 하드웨어도 변했지만, 소프트웨어의 핵심은 여전히 그대로다.
소프트웨어, 즉 컴퓨터 프로그램은 순차(sequence), 분기(selection), 반복(iteration), 참조(indirection)로 구성된다, 그 이상도 이하도 아니다.