이번 포스트에서는 로버트 마틴의 ‘Clean Architecture’ 을 읽고서 느꼈던 점들을 기반으로, 책에 대한 소개와 추천을 드리고자 합니다.
‘아름다운 코드’ 스터디
이 책은 오랜만에 예전에 했던 스터디를 떠올리게 해주었습니다. 바로 ‘아름다운 코드란 무엇인가?’ 를 주제로 진행했던 스터디 입니다. 이때 다뤘던 책 중의 하나가 로버트 마틴의 ≪클린코드≫ 입니다. 이 책에서는 코드를 어떻게 짜야하는지, 변수와 함수의 네이밍과 함수 간의 순서 등 주로 코드의 가독성을 기본으로 다양한 주제들을 다루고 있기 때문에, 흥미롭게 이야기할 수 있는 주제들이 많습니다. 다만 저자의 스타일이 자신의 주장을 명확하게 밝히는 편이기 때문에 무조건 이렇게 하는게 맞다 라는 관점 보다는 ‘A라는 상황에서는 이런 장점들이 있기 때문에 이렇게 해야 한다고 생각한다’ 에 가깝습니다. 그래서 저자가 제시하는 다양한 상황과 주장에 대해서 서로 어떻게 생각하는지 이야기 해보고 토론해보면서 많은 것들을 배웠던 기억이 납니다.
≪클린 아키텍처≫ 역시, 스타일은 ≪클린코드≫ 와 비슷합니다. 다만 뷰가 조금 다릅니다. 아주 가까이, 코드란 무엇인가 에서 부터 조금씩 Zoom-out을 하면서 프로그래밍 패러다임, 코드 설계 원칙, 컴포넌트, 아키텍처 까지 전반적인 내용들을 다루고 있습니다. 이번에는 혼자서 책을 보았는데, 같이 보면서 의견 나누면서 스터디 진행하면 좋겠다는 생각이 자연스럽게 들었습니다.
예전에 스터디 했을 때와는 다르게, 이제는 어느 정도 현업에서 개발하고 있기 때문에, 이 책을 볼때는 자연스럽게 그 동안의 경험들에 근거해서 바라보게 됩니다. 특히 아래의 말은 공감이 가는 말이기도 합니다.
소프트웨어 개발의 단순한 진리, 빨리 가는 유일한 방법은 제대로 가는 것이다.
- 1장 설계와 아키텍처란? 중에서
그렇다면 아름다운 아키텍처란 무엇일까?
아키텍처가 가지는 의미는 무엇일까요? 건축에서 아키텍처는 다음과 같은 의미로 쓰입니다.
건물이나 다른 구조물을 계획하고 설계하고 건설하는 과정과 그 결과물
CS 에서의 아키텍처 역시, 시스템을 계획하고 설계하는 전반을 포함하고 있다고 생각합니다. 조금 더 분리해서 들여다보면, 시스템을 튼튼하게 받쳐주는 구조를 의미한다고 생각을 합니다. 이 구조가 튼튼할 수록, 쉽게 변경할 수 있을수록 시스템은 무궁무진한 방향으로 발전할 수 있을 것 입니다. 그리고 계획과 설계는 한번에 끝나는 일이 아닌, 계속해서 상황에 따라서 변화해야하는 것이기도 합니다.
아키텍처는 종착지가 아니라 여정에 더 가까우며, 고정된 산출물이 아니라 계속된 탐구 과정에 더 가까움을 이해해야 좋은 아키텍처가 만들어진다.
- 추천사 중에서
좋은 소프트웨어 설계의 목표는?
소프트웨어 아키텍처의 목표는 필요한 시스템을 만들고 유지보수하는 데 투입되는 인력을 최소화하는 데 있다.
- 1장 설계와 아키텍처란? 중에서
저자는 아키텍처의 목표에 대해서 명확한 방향을 제시하고 있다고 생각을 합니다.
‘필요한 시스템을 만들 수 있으면서, 가장 적은 인원으로 대응할 수 있는 것.’
필요한 시스템에는 확장성과 속도, 견고함과 같은 측면들이 포함되어 있다고 보이기 때문에 그 외에 한가지 요소를 더 추가하면 조금 더 명확한 방향이라고 할 수 있을 것 같습니다. 바로 시간입니다.
‘요구되는 기간 안에 필요한 시스템을 만들 수 있으면서, 가장 적은 인원으로 대응할 수 있는 것.’
코드에서 패러다임, 컴포넌트, 아키텍처까지
아래에서는 저자가 이 책에서 이야기하는 다양한 주제에 대해서 다뤄보고자 합니다. 저자가 이 책을 쓴 것에는 다음과 같은 전제가 기본으로 들어가 있습니다.
코드는 여전히 순차 sequence, 분기 selection, 반복 iteration 의 집합체일 뿐이다. … 언어는 조금 발전했다. 도구는 환상적으로 좋아졌다. 하지만 컴퓨터 프로그래밍을 이루는 기본 구성요소는 조금도 바뀌지 않았다.
- 서문 중에서
이를 가장 잘 표현하는 것은 추천사에도 있는 이 말이라고 생각합니다. ‘소프트웨어는 본질적으로 재귀적이고 프랙털구조로 되어 있으며…’ 아래 그림처럼, 일부 작은 조각(기본 구성요소)이 전체(소프트웨어)와 비슷한 형태를 지니는 것.
이제 기본 구성요소를 넘어서 패러다임 부터 간단하게 이야기 해보려고 합니다.
프로그래밍 패러다임
패러다임이란 해당 문제에 접근하는 관점 혹은 방법론을 의미합니다. 프로그래밍 패러다임에는 크게 3가지가 존재합니다. 저자는 이 대표적인 3가지 패러다임을 ‘규제’의 관점으로 바라보고 있습니다. 우리에게 자유를 뺏어 가기 때문이죠.
- 구조적 프로그래밍 : 제어흐름의 직접적인 전환에 부과되는 규율이다. (goto문)
- 객체지향 프로그래밍 : 제어흐름의 간접적인 전환에 부과되는 규율이다. (함수포인터)
- 함수형 프로그래밍 : 변수 할당에 부과되는 규율이다. (할당문)
이렇게 규율을 부과하는 것은 해당 문제를 풀어감에 있어서, 규율이 도움이 되기 때문입니다. 그래서 위의 패러다임들은 배타적인 관계가 아닌, 상호 보완적인 관계에 가깝다고 볼 수 있습니다. 각각의 패러다임이 가지는 가장 큰 강점을 아래와 같이 추려 보았습니다.
구조적 프로그래밍
모든 프로그램을 순차(sequence), 분기(selection), 반복(iteration) 이라는 세 가지 구조만으로 표현할 수 있다는 사실을 증명했다.
(중략)
모듈을 증명 가능한 더 작은 단위로 재귀적으로 분해할 수 있게 되었고, 이는 결국 모듈을 기능적으로 분해할 수 있음을 뜻했다.
- 4장 구조적 프로그래밍 중에서
객체지향 프로그래밍
구현체와 인터페이스 사이의 소스 코드 의존성(상속 관계)이 제어흐름과는 반대인 점을 주목하자. 이는 의존성 역전(dependency inversion)이라고 부르며, 소프트웨어 아키텍처 관점에서 이러한 현상은 심오한 의미를 갖는다.
OO 언어가 다형성을 안전하고 편리하게 제공한다는 사실은 소스 코드 의존성을 어디에서든 역전시킬 수 있다는 뜻이기도 하다.
(중략)
이것이 힘이다! 이것이 바로 OO가 제공하는 힘이다. 그리고 이것이 바로 OO가 지향하는 것이다(최소한 아키텍트의 관점에서는).
- 5장 객체 지향 프로그래밍 중에서
함수형 프로그래밍
아키텍트는 왜 변수의 가변성을 염려하는가? 터무니없게도 대답은 단순하다. 경합(race) 조건, 교착상태(deadlock) 조건, 동시 업데이트(concurrent update) 문제가 모두 가변 변수로 인해 발생하기 때문이다. 만약 어떠한 변수도 갱신되지 않는다면 경합 조건이나 동시 업데이트 문제가 일어나지 않는다. 락(lock)이 가변적이지 않다면 교착상태도 일어나지 않는다.
다시 말해 우리가 동시성 애플리케이션에서 마주치는 모든 문제, 즉 다수의 스레드와 프로세스를 사용하는 애플리케이션에서 마주치는 모든 문제는 가변 변수가 없다면 절대로 생기지 않는다.
아키텍트라면 동시성(concurrency) 문제에 지대한 관심을 가져야만 한다. … 이 질문에 대한 대답은 대체로 긍정적이다. 단, 저장 공간이 무한하고 프로세서의 속도가 무한히 빠르다고 전제한다면 말이다.
- 6장 함수형 프로그래밍 중에서
설계 원칙과 컴포넌트
다음으로는 코드의 설계 원칙을 이야기합니다. 다음과 같은 SOLID를 각 항목 별로 살펴보게 됩니다.
- SRP: 단일 책임 원칙 Single Responsibility Principle
- OCP: 개방-폐쇄 원칙 Open-Closed Priciple
- LSP: 리스코프 치환 원칙 Liskov Substitution Principle
- ISP: 인터페이스 분리 원칙 Interface Segregation Principle
- DIP: 의존성 역전 원칙 Dependency Inversion Principle
각각의 원칙 마다도 이야기하는 것들이 많이 있습니다만, 이정도로 소개만 하고 컴포넌트에 대해서 이야기를 해보려고 합니다.
SOLID 원칙이 벽과 방에 벽돌을 배치하는 방법을 알려준다면, 컴포넌트 원칙은 빌딩에 방을 배치하는 방법을 설명해준다. 큰 빌딩과 마찬가지로 대규모 소프트웨어 시스템은 작은 컴포넌트들로 만들어진다.
- 4부 컴포넌트 원칙 중에서
컴포넌트 역시 SOLID 와 비슷하게 몇가지 원칙들을 소개합니다. 다만 컴포넌트가 가지는 속성을 기반으로 설계 원칙들을 이야기 합니다. 코드가 로직의 구성체라면, 컴포넌트는 코드들의 구성체이면서 아래와 같은 특징들을 가지고 있습니다. 가장 중요하게 이야기되는 특징이 ‘배포’ 와 ‘독립성’ 이라는 것을 아실 수 있을 것 입니다.
컴포넌트는 배포 단위다. 컴포넌트는 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위다. … 컴파일형 언어에서 컴포넌트는 바이너리 파일의 결합체다. 인터프리티형 언어의 경우는 소스 파일의 결합체다. 모든 언어에서 컴포넌트는 배포할 수 있는 단위 입자다. … 컴포넌트가 마지막에 어떤 형태로 배포되든, 잘 설계된 컴포넌트라면 반드시 독립적으로 배포 가능한, 따라서 독립적으로 개발 가능한 능력을 갖춰야 한다.
- 12 장 컴포넌트 중에서
- REP: 재사용/릴리즈 등가 원칙
- CCP: 공통 폐쇄 원칙
- CRP: 공통 재사용 원칙
컴포넌트 결합
- ADP: 의존성 비순환 원칙
- SDP: 안정된 의존성 원칙
- SAP: 안정된 추상화 원칙
아키텍처
마지막으로 전체를 아우르는 아키텍처 입니다. 여기에서도 다양한 사례들을 기반으로 이야기하고, 또 저자가 주장하는 구조가 있습니다. 아마 이 책에서 가장 유명한 다이어그램이 아닐까 싶습니다.
코어인 업무 규칙이 담겨있는 ‘엔티티’, 사용자에 대한 입력과 출력을 기반으로 구성되는 ‘유스케이스’, 그 바깥으로는 컨트롤러와 프레젠터가 있고 마지막 외부 인터페이스들인 (세부사항이라고 표현하기도 하는) 웹, UI, DB 이 있습니다. 의존성은 안쪽을 향해 있으며, 제어흐름 역전을 위해서 DIP가 안에서 사용되는 모습들도 보이고 있습니다.
여기에는 다음과 같은 특징들이 담고 있다고 이야기하고 있습니다.
- 프레임워크 독립성
- 테스트 용이성
- UI 독립성
- 데이터베이스 독립성
- 모든 외부 에이전시에 대한 독립성
위의 클린 아키텍처에 대한 조금 더 자세한 설명을 포함해서 경계와 험블 객체, 등 다양한 주제에 대해서 많은 이야기를 하고 있으니 자세히 읽어보시는 것을 추천드립니다.
정리를 하기 전에 마지막으로 ‘아키텍처’ 에 대한 저자의 생각을 인용하고자 합니다. 저 역시 해당 시스템의 아키텍트는 계속해서 코드를 다뤄야 한다는 점에 동의하기 때문입니다.
무엇보다도 소프트웨어 아키텍트는 프로그래머이며, 앞으로도 계속 프로그래머로 남는다. 소프트웨어 아키텍트라면 코드에서 탈피하여 고수준의 문제에 집중해야 한다는 거짓말에 절대로 속아 넘어가서는 안 된다. 소프트웨어 아키텍트는 코드와 동떨어져서는 안 된다. 소프트웨어 아키텍트는 최고의 프로그래머이며, 앞으로도 계속 프로그래밍 작업을 맡을 뿐만 아니라 동시에 나머지 팀원들이 생산성을 극대화할 수 있는 설계를 하도록 방향을 이끌어 준다. … 프로그래밍 작업을 계속하는 이유는, 발생하는 문제를 경험해보지 않는다면 다른 프로그래머를 지원하는 작업을 제대로 수행할 수 없기 때문이다.
- 15장 아키텍처란? 중에서
끝으로
이 책은 정말 다양한 주제들을 다루고 있습니다. 엉클 밥의 다양한 인사이트 역시 확인하실 수 있을 것입니다. 그리고 서두에 이야기를 한 것처럼, 저자가 던지고 있는 다양한 화두를 살펴보면서 자신은 해당 상황에서 어떻게 생각하고 설계하고 있는지 비교해보면 더 많은 것들을 얻을 수 있을 것이라고 생각합니다. 이 책을 보시면서 설계에 대해서 더 깊이 고민하고, 아름다운 아키텍처를 만드는 아키텍트를 꿈꾸시는 분들에게 추천드리고 싶습니다.
부록
한가지 이야기하고 싶은 점은, 저자의 주 언어는 Java라고 알고 있습니다. 그런 만큼, 구체적인 예시로 들어가면 대부분 객체지향 언어가 많이 부각되기도 합니다. 마지막 장의 패키지를 보면 자바의 Spring에서 많이 보던 구조이기도 합니다. 하지만 처음에 저자가 이야기 한 것처럼 기본 구성요소는 변하지 않기 때문에 문제가 되지는 않을 것이라 생각합니다.
그 외 볼거리