빨리가는 유일한 방법은 제대로 가는것이다.
-로버트 C 마틴-
🧱아키텍처란?
설계와 아키텍처의 차이는?
- 단순히 설계는 저수준의 아키텍처는 고수준의 무언가를 가르치는데 둘의 차이는 크지 않다. 건축을 할 때 전체와 디테일이 모두 필요하듯이 개발하기전 설계에 있어서도 저/고수준의 설계가 필요하고 중요한 법이다.
- 클린 아키텍처를 위해서는 이러한 전체 설계에서 저/고수준의 설계를 거치며 연속적으로 좋은 의사결정을 해나가는것을 목표로해야 한다.
- 클린 아키텍처의 최종 목표는 필요한 시스템을 만들고 유지보수 비용을 줄임에 있으며, 극단적으로는 시스템의 수명이 다 할 때까지 낮은 비용을 유지할 수 있다면 그것이 좋은 설계이다.
🎃나쁜 설계의 사례
- 회사에서 가파른 성장과함께 개발자의 수는 기하급수적으로 늘어나지만 생산성이 그와 완벽하게 정비례하지 않는 경우이다.
- 쉽게말하면 개발자당 생산성이 낮아지는 사례이다. 이런 경우는 대부분 엉망이된 기존 코드를 수정하는 일에 비용이 소모되기 때문이다. 대부분 르블랑의법칙(코드 수정을 위한 나중은 결코 오지않는다는 말)을 망각한 채 출시를 가장 우선 목표로 코드를 짜기 때문에 이러한 일이 빈번하게 발생한다.
🥇클린 아키텍처의 핵심 가치 - 구조
"행위"는 프로그래머가 이해관계자를 위해 어떠한 요구사항을 구체화하며 이를 기계를 통해 실행시킬수 있게 돕는것이며
"구조"는 소프트 웨어(부드러운 제품)이름에 맞게 요구사항을 더 쉽게 적용할수 있는 쉬운 구조를 지녀야한다.
-
대부분의 경영자와 개발자들은 행위에 초점을 맞추지만, 요구사항이 변경될 때 프로그램이 동작하지 않는다면 그 프로그램은 거의 쓸모가 없어지기 때문에 클린 아키텍처를 위해서는 구조에 초점을 두어야 한다.
(코드의 유통기한Down)
-
아이젠하워 매트릭스에서 중요와 긴급 두가지의 조합으로 우선순위를 매길 때 1.중요,긴급 2.중요 3.긴급 4.둘다X 순이라고 한다. 이 때 행위는 긴급하지만 매번 중요도가 높지는않다. 하지만 구조는 중요하지만 매번 긴급하지 않다. 앞선 매트릭스의 말을 빌려 우선순위를 매긴다하면, 구조가 우선시 되어야함을 알 수 있다.
-
마지막으로 개발자는 이해관계자를 위해 일하지만 자기 자신도 이해관계자임을 깨달아야하며, 소프트웨어를 안전하게 보호해야할 책임이 있으니 좋은 아키텍처를 위해 투쟁해야한다. 명심하자, 아키텍처를 충분히 고려하지 않은 개발은 더 큰 비용을 초래한다.
📽 프로그래밍의 패러다임
프로그래밍의 패러다임을 알아야 언제 어떤 프로그래밍 아키텍처를 사용할지 아는법. 현재까지 정리된 패러다임은 구조적, 객체 지향, 함수형 3종류로 나뉜다고 한다.
- 구조적 프로그래밍 - If, Then, While과 같이 제어 흐름에 대한 직접적인 전환에 대한 규칙을 부과하는 프로그래밍
- 객체 지향 프로그래밍 - 함수 호출이 된 이후 유지되는 특성을 사용해 클래스, 지역변수, 메서드 개념이 생겨났으며, 제어흐름의 간접적인 전환에 대한 규칙을 부과한다.
- 함수형 프로그래밍 - 근래 도입되었으며, 상태변경을 피하며 불변성을 이용해 함수를 조합하여 처리하며, 할당문에 대한 규칙을 부과하는 프로그래밍이다.
각 프로그래밍의 패러다임이 생겨난 이유는 프로그래머에서 권한을 박탈해서 시스템을 유지하기 쉽게 해주는 특징을 지닌다. 이 세가지 패러다임은 1958년부터 10년간 만들어진 규칙이며 이 후 새롭게 등장한 패러다임은 없고, 앞으로도 없을것이라고 클린 아키텍처 저자는 얘기한다.
구조적 프로그래밍
- 초기 프로그래머들은 프로그래밍은 어려웠기에 이를 쉽게하기 위한 구조를 이루어 나갔다. 그 노력으로 순차, 분기, 반복 세가지 구조만 있으면 모든 표현이 가능하다는 사실을 알아냈다. 구조적 프로그래밍은 이렇게 등장하며 다익스트라가 이 순차, 분기, 반복 구조의 신뢰성을 증명해내어 프로그래밍 세계에 불이 붙게되었다.(goto 문 위주의 프로그래밍 -> 구조적 프로그래밍)
- 이 후 프로그램 자체의 신뢰를 증명하려 노력했지만 불가능하다는 것을 알았고, 다익스트라가 말하길 "테스트는 버그가 있음을 보여줄 뿐, 버그가 없음을 보여줄 수는 없다,"라고 했고 이 뜻은 수학과는 다르게 소프트웨어 개발은 과학적인 시도로 증명할 밖에 없음을 말한다.
- 구조적 프로그래밍이 오늘날까지 가치있는 이유는 프로그래밍에서 반증 가능한 단위를 만들어내 프로그램의 신뢰를 증명할 수 있기 때문이다. 따라서 이러한 규칙들을 받아들여 활용해야한다.
객체 지향 프로그래밍
객체지향이란? 현실세계의 개념을 소프트웨어 모델로 추상화 하기 위한 방식으로 캡슐화, 상속, 다형성을 적절하게 조합한것이라고 보면된다.
- 캡슐화란? 데이터를 은닉시키고 일부 함수만 노출시키는 기법으로 사실 C에서도 잘 쓰였고 이후 점차 C++언어 형태로 OO가 자리잡고 객체 안에 멤버변수가 있음을 확인할 수 있어서 캡슐화가 깨지게되었다고 한다. 이를 보완하는 역할이 Public, Private, Protected 인 것. 사실 OO는 캡슐화에 의존한다고 보긴 힘들며 데이터를 우회하여 사용하지 않게 최소한의 가이드라인 역할을 하는것이 캡슐화이다.
- 상속이란? 상속은 어떠한 변수와 함수를 하나의 유효범위로 묶어 재정의하는 일이다. OO가 캡슐화를 약화시켰다면 상속은 살짝 강화시키는 느낌이다. C에서도 비슷하게 쓰였지만 완벽하게 상속이라고 보기는 어려웠다.
- 다형성이란? OO가 있기에 다형성을 더욱 쉽게 사용할 수 있게 하였고, 동일한 이름의 메서드나 속성을 가진 객체를 다양한 타입으로 사용할 수 있는 능력을 높일 수 있었다.
(다형성으로부터 나온 플러그인 아키텍처는 장치에 의존에서 벗어나, 장치의 독립성을 지원해주었다. ex) FILE -> READ, WRITE ...등의 인터페이스 사용)
의존성 역전
OO등장전에는 소프트웨어가 고수준 함수가 저수준 함수를 호출하는 의존성의 방향이 폭포수처럼 제어흐름을 따랐다. OO의 다향성으로 인해 인터페이스 개념을 추가해 소스코드 사이의 방향성을 역전 시킬 수 있었고 모든 소스코드의 의존성을 원하는 방향으로 설정할 수 있게 되었다.(독립성을 보장) 이것이 OO가 지향하는 바이며 소스코드를 독립적으로 배포할 수 있도록 환경을 구성해 주게 되었다.
함수형 프로그래밍
사실 함수형 프로그래밍은 등장한지 오래되었다. 이 패러다임의 핵심은 람다 계산법이다.
- 자바와 C++은 가변변수를 사용하는데 함수형 프로그래밍에서 변수는 변경되지 않는다!(0부터 25를 프린트 시 변수 25개 생성) 즉 가변변수를 사용하지 않는다.
- 아키텍처를 고려할 때 가변성을 염려하는 이유는 Race Condition, DeadLock, Conccurent update등 동시성과 관련한 문제가 모두 가변 변수로부터 생기기 때문이다. 따라서 가변변수를 지양하는것은 구조적으로 도움이 될 수 있다.
- 하지만 우리는 시스템 성능에 제한이 있기 때문에 항상 함수형만 쓸 수 없다. 때문에 타협이 필요하다. 가변 컴포넌트와 불변 컴포넌트를 적절하게 분리하여 사용하면 "성능"과 "교착상태로 부터 보호" 두마리의 토끼를 잡아야 한다. 가능한한 불변 컴포넌트 쪽 비중이 높아야 좋은 코드라 할 수 있다.
이벤트 소싱
특정 상태와 그 이후의 트랜잭션을 저장하는 방식을 이벤트 소싱 기법이라고 한다. 함수형 프로그래밍의 좋은 예시라고 볼 수 있다.
- 계좌 같은 경우 요즘 저장 공간과 처리능력의 한계가 줄어들고 있다해도 계좌 개설부터 현재 잔고가 있기 까지 모든 이벤트를 저장해 트랜잭션(불변) 처리할 수는 없으니 상태는 결국 필요하다. 이 때 가변/불변 컴포넌트를 적절하게 사용하는 기법이 필요한 것이다.
- 생각해보면 우리가 쓰는 Git도 이벤트 소싱과 비슷한 성격을 띄고 있다.
💡결론
- 클린 아키텍처는 필요한 시스템을 만들고 유지보수 비용을 줄이기 위함이며, 프로그램의 동작보다 구조에 초점을 두고 개발하는 습관이 필요하다.
- 소프트웨어의 핵심은 순차, 분기, 반복, 참조 그 이상도 이하도 아니며 앞서말한 패러다임과 함께 해서는 안되는 것을 지키며 아키텍처를 생각해야 한다.
유익한 글이었습니다.