클린 아키텍처: 소프트웨어 구조와 설계의 원칙) 챕터 2: 프로그래밍 패러다임

성승모·2024년 8월 25일
0

Clean Architecture

목록 보기
2/4
post-custom-banner

개요

 소개할 세가지 프로그래밍 패러다임은 개발자로 하여금 권한을 박탈한다.새로운 권한은 부여하지 않고 부정적인 의도를 가지는 규칠을 부과한다. 즉, 무엇을 해야하는지가 아닌 무엇을 하면 안되는지에 대해 말해준다. 이에 집중하여 글을 읽어보자.

구조적 프로그래밍

테이크스트라

 테이크스트라는 최초의 프로그래머 중 한명이다. 그는 당시 프로그래밍이 어렵고 복잡하다고 생각했다. 그는 goto 문장이 더 작은 모듈로 분해하는 과정을 방해한다고 생각했고 이는 더 좋은 방식인 분기와 반복에 해당한다는 사실을 발견하였다. 또한 모든 프로그램은 순차, 분기, 반복이라는 세가지 구조만으로 표현할 수 있다는 사실을 증명하였다.

 그가 "goto문장의 해로움"이라는 글을 투고한 후 goto 문장은 점점 사라져 현대적 언어엔 goto 문장이 포함되지 않는다.

과학적 증명

 과학은 수학과 달리 그 올바름을 증명할 수 없다. 과학적 사실은 많은 예시를 통해 그 입지를 굳건히 하며 지금 우리가 사실이라 믿는 과학적 사실들은 언제든지 반증당할 수 있다. 즉, 과학은 반증을 통해 서술이 틀렸음을 증명하며 발전해 나간다는 것이다.

 테이크스트라는 '테스트는 버그가 있음을 보여줄 뿐 없음을 보여줄 순 없다'라고 이야기하였다. 즉, 테스트를 통해 프로그램이 틀림을 증명할 순 있어도 맞음을 증명할 순 없다는 것이다. (소프트웨어는 수학적인 구조를 다루는듯 보였지만 사실은 과학과 같다는 것이 충격적이다...)

결론

  구조적 프로그래밍은 개발자로 하여금 "제어흐름의 직접적인 흐름 전환을 빼앗음"으로서 반증 가능한 단위를 만들 수 있었다.

객체 지향 프로그래밍

개요

 객체 지향 프로그래밍이란 무엇인가? 데이터와 함수의 조합, 실제 세계를 모델링하는 방법 등 많은 댇답이 있겠지만 애매하다. 또한 캡슐화, 상속, 다형성 이 세가지를 이야기하며 설명하려고도 하겠지만 이조차 애매하다. 자세히 알아보자.

캡슐화?

 OO언어가 등장하기 전 C에서 완전한 캡슐화를 제공했다. 헤더파일을 사용하는 측에서 아예 접근불가능한 멤버 변수가 있었다. 하지만 C++, C#, Java에선 캡슐화는 훼손되었고 클래스 선언과 정의를 구분하는게 아예 불가능하다. 따라서 OO 프로그래밍은 개발자가 충분히 올바르게 행동함으로써 캡슐화된 데이터를 우회하여 사용하지 않을 거라는 믿음을 기반으로 한다. OO언어가 오히려 캡슐화를 약화시켜 온것은 틀림없다.

상속?

 상속만큼은 OO언어가 확실히 제공했다. 물론 그전 대부분의 언어는 이를 간접적으로 구현했다. 하지만 불편하며 다중 상속을 구현하긴 매우 어려웠다. OO 언어가 확실히 상속을 편리한 방식으로 제공하긴 하였지만 새로운 개념을 만들었다고 보긴 어렵다.

다형성?

 C언어에서도 다형성이란 개념은 있었고 이를 포인터를 이용하여 구현했다. 따라서 다형성 또한 OO 언어가 새롭게 만든 것이 아니고 편리하게 사용할 수 있도록 도움을 줄 뿐이다.

 사실 포인터를 직접 사용하는 것은 매우 위험하다. 포인터를 사용하기 위해선 개발자가 특정 관례를 수동적으로 따라야 하는데, 개발자가 이를 망각하면 버그가 발생하고 포인터에 의한 버그는 찾아내고 고치기가 매우 어렵다. 따라서 OO언어는 개발자에게 포인터를 빼앗아 제어흐름을 간접적으로 전환하는 규칙을 부과한다.

다형성이 가진 힘

 다형성이 뭐가 그렇게 좋은가? 이 책의 예시를 살펴보자. 필기체 인식 장치로부터 데이터를 읽어 음성 합성 장치로 복사한다고 생각해보자. 기존 복사 프로그램은 어떤 변경을 필요로 할까? 아무런 변경도 필요없다. 왜냐하면 복사 프로그램은 IO 장치의 드라이버 소스 코드에 의존하지 않기 때문이다. 복사 프로그램은 단지 표준 함수가 구현된 Interface만 있다면 바로 사용이 가능하기 때문이다. 즉, IO 드라이버가 복사 프로그램의 플러그인이 된것이다. 이런 이유로, 하드웨어가 빠르게 발전하면서 등장한 이 플러그인 아키텍처는 대부분의 OS에 적용되었다.

의존성 역전

(내가 제일 이해하기 어려웠던 개념이었으며 이 책을 산 이유다.)

 OO 이전의 매커니즘을 생각해보자. 전형적인 호출 트리에선 고수준 함수 -> 중간 수준 함수 -> 저수준 함수 순대로 호출한다. 이러한 호출 트리에선 소스 코드 의존성의 방향 = 제어 흐름의 방향의 관계를 갖는다.

  • 소스 코드 의존성: 한 모듈이 다른 모듈을 어떻게 참조하고 사용하는지
    (모듈 간의 정적 의존성)
  • 제어 흐름: 프로그램 실행 시 코드가 실제로 실행되는 순서
    (런타임에 동적 제어 이동)

 다형성을 끼얹어보자. 고수준 모듈 HL1이 Interface I 를 통해 저수준 모듈 LL1의 함수 f를 호출하는 상황이다. 따라서, 소스 코드 의존성은 HL1 -> I <- LL1으로 표현된다. 하지만 제어흐름을 생각해보자. 런타임에 I는 존재하지 않으며 단순히 HL1이 LL1의 함수 f를 호출한다. 따라서, 소스 코드 의존성의 방향과 제어 흐름의 방향이 반대가 되었으며 이를 의존성 역전이라고 부른다.

(그래서 뭐가 좋은데..?)

 OO가 다형성을 편리하게 해준 결과, 개발자는 쉽게 의존성의 방향을 결정할 수 있다. 그렇다면 무엇을 할 수 있는가? 예를 들어보자. UI - Bussiness Rules - Database 시스템이 있다. Bussiness Rules가 UI와 Database를 제어하는 반면에 소스 코드 의존성을 반대로 배치하여 UI와 데이터베이스가 Bussiness Rules의 플러그인이 되도록 할 수 있다.

 결과적으로 세 모듈로 분리가 가능하며 이를 배포 가능한 단위로 컴파일 할 수 있다. 특정 모듈의 소스코드가 변경되면 해당 모듈만 수정하여 배포하면된다. 이것이 바로 배포 독립성이다. 또한 독립적으로 개발할 수 있으며 이를 개발 독립성이라 부른다.

결론

 OO란 무엇인가? 수많은 의견과 답변이 있지만 SA 관점에서 정답은 하나다. 다형성을 이용한 절대적인 제어 권한 획득. 이를 통해 우린 플러그인 아키텍처를 구성할 수 있고, 고수준 policies부터 저수준 세부사항까지 모든 모듈의 독립성을 보장할 수 있다.

함수형 프로그래밍

개요

 함수형 프로그래밍의 핵심인 람다 계산법은 프로그래밍 개념 등장 이전에 알론조 처치가 1930년대에 개발했다.

함수형 프로그래밍이란?
 side-effect가 없는 순수 함수를 이용하여 자료를 처리하고 가능한 상태와 가변 데이터를 멀리하는 패러다임. 즉, 불변성을 추구한다.

불변성과 아키텍처

 아키텍처를 고려할때 왜 불변성이 중요한가? 왜 변수의 가변성을 염려하는가? 바로 경합조건, 교착상태, 동시 업데이트 문제 때문이다. 애초에 경합조건이나 동시 업데이트가 일어날 수가 없고, 락 또한 불변이기 때문에 교착상태도 일어나지 않는다.

 그렇다면 불변성을 가진 아키텍처는 정말 실현이 가능한가? 가능하다. 단, 저장공간과 프로세스 처리 속도가 무한해야한다. 실현 가능하겠지만 타협을 해야한다.

 가장 주요한 타협 중 하나는 가변-불변 컴포넌트로 분리하는 일이다. 불변 컴포넌트에선 순수하게 함수형 방식으로만 작업을 처리하고 어떤 가변 변수도 이용하지 않는다. 따라서, 실제 시스템에서는 하나 이상의 가변 컴포넌트와 이용되어야 한다.

이벤트 소싱

 하지만 상태 변경은 동시성 문제에 노출된다. 이 문제를 트랜잭션 메모리와 같은 방법으로 해결할 수 있다. (상태 변경 과정을 트랜잭션이라 하며 초기값에서 저장된 트랜잭션을 순서대로 수행하여 상태를 가져온다.) 하지만 문제가 있다. 변경 시도가 매우 많다면 상태를 가져올때마다 많은 자원을 필요로 할 것이다. 트랜잭션 수가 늘어날수록 계산에 필요한 컴퓨팅 자원은 걷잡을 수 없이 커진다. 그래서 이 전략이 실현 가능하려면 무한한 저장공간과 처리능력이 필요하다.

 하지만 이러한 전략이 영원히 동작하게 할 필요는 없다. 단순히 어플리케이션의 수명주기 동안만 문제없이 동작하게 하면 충분할 것이다. 그런 이후 트랜잭션을 수행하여 상태를 재저장하면 될 일이다. 동작 중 필요한 트랜잭션 저장소는 엄청나게 많을테지만 현재에 와서는 충분히 확보할 수 잇다.

 중요한 점은 저장소에서 삭제, 변경이 일어나지 않는것이다. CRUD 중 CR만 수행하는 것이다. 따라서 동시 업데이트 문제가 발생하지 않는다. 잘 이해가 되지 않는다면 git 같은 소스 코드 버전 관리 시스템을 생각해보자.

결론

함수형 프로그래밍은 우리에게 가변성을 빼앗아 변수 할당에 대한 규칙을 부과한다.


챕터 마무리

 세 패러다임 모두 우리에게 하지 말아야 할것을 알려주며 소프트웨어가 생각보다 급격하게 변한 기술이 아니라는 것을 알 수 있다.1946년 앨런 튜링이 최초의 코드를 작성할 때 사용한 소프트웨어 규칙은 지금과 전혀 다르지 않다. 도구는 달라졌고 하드웨어도 변했지만, 핵심은 여전히 그대로다.

  소프트웨어, 즉 컴퓨터 프로그램은 순차, 분기, 반복, 참조로 구성되며 그 이상 그 이하도 아닌다.

profile
안녕하세요!
post-custom-banner

0개의 댓글