---
layout: post
title: '독후감 #1 클린 아키텍처'
subtitle: 'by 로버트 C. 마틴'
date: 2022-05-02 11:55:00 +0900
categories: [Reddit]
---

## 첫 개시, 첫 게시

![실물](https://i.postimg.cc/5N80twpP/Kakao-Talk-20220419-113127818.jpg)  
멋진 구도가 딱히 생각이 안나서 차라리 매니악하게
{: .center}

양심고백, 3월 초 책을 다 읽고 11일 경에 독후감을 작성해야지 다짐하며 목차를 작성해 뒀으나, 그로부터 한 달이 훌쩍 넘어 두 달 째가 가까워진 상태에서 글을 시작하고 있다. 블로그 관련해서 들었던 조언 중에 "텀이 길어도 되니까 주기적으로 글을 작성하라"는 말씀이 있었는데... 아무튼 그 덕택에 기억이 온전하진 않다. 양해를 미리 구하고 시작하겠다.

## 첫 책

독후감을 어떻게 써야 할 지 상상만 하던 때 부터, 그 시작은 왜 이 책을 선택하였는가에 대해 정리하는 것으로 하고싶었다. 왜냐하면, 지금 내가 가진 책이 좀 많다. 좋은 기회가 있어서 개발 관련 서적을 잔뜩 구매할 수 있었고, 사실 좀 충동적으로 지른게 많긴 해. 그렇다면 프론트/백엔드/인프라/AI 장르 불문 다양한 책들 중에서 나는 왜 하필 클린 아키텍처를 첫 번째 책으로 결정했나. 정리해보자.

![야그니](https://i.postimg.cc/CKpQg523/yagni.png)  
최근 개인 프로젝트 하면서도 꾸준히 이러고 있다.
{: .center}

첫 번째, [의존성 주입을 주제로 한 글](/hack/2022/03/07/hack-the-terms-1.html)에서도 언급했지만, 내가 소프트웨어 개발이란 길을 걸으면서 조금이나마 관심을 가지고 있다고 말할 수 있을만한 분야는 **소프트웨어 공학**이다. 그 덕에 나는 어느 정도 이쁜 설계의 필요성에 대해 자각은 가지고 있는 것 같다. 그런데 내가 항상 무언가를 만들면서 머리를 싸매고 고민하는 것은, **"이게 좋은 패턴인가?"** 라는 걱정이다. 타고난 기질이 혼자 조용히 깨작대는 것인 나는, 지금 내가 만들어놓은 이 코드 덩어리가 보기에 이쁜 모양인지에 대해 객관적인 판단을 하는데에 많은 어려움을 겪고 있는 상황인 것이다.

둘째로, 나는 이것저것 하는걸 좋아한다. 소프트웨어 공학을 좋아하는 이유도 그런 점이라고 생각한다. 개발, 정확히는 **소프트웨어 개발** 이라는 것이 GUI가 달린 클라이언트 어플리케이션을 만드는 것일 수도 있고, 백엔드에서 동작할 서버 어플리케이션을 만드는 것일 수도 있다. 혹은 꼭 코드를 많이 작성하는 것 뿐만이 아니라 서비스를 위한 인프라를 구성하는 것도 개발일 수 있고, 데이터베이스를 주로 다루는 개발자도 있겠지. 아니면 다른 단위로 묶어볼 수도 있다. 인공지능, 블록체인, 게임, 웹서비스, 데이터 처리, 등등등등 이런 수많은 가능성이 있는 것이 개발이 아닐까. 그리고 이 가능성들의 최상위 공통점이 바로 "소프트웨어"라고 생각한다.  

나는 소프트웨어 개발자(지망생)로써 여러 개발 분야들을 이것저것 찔러보는 편이다. 웹 프론트도 해보고, 백엔드도 해보고, 작년엔 데이터 처리도 했고, 버킷리스트 중 하나는 게임 개발이고, 지금은 개인 프로젝트로 모바일 어플리케이션을 개발하고 있다. 이렇게 줏대없이 여러가지 해왔다 보니 일반적인 소프트웨어 설계에 대한 호기심이 생겨난 것 같다. 뭘 만들든 일단 소프트웨어인데, 소프트웨어 관점에서 잘 설계된 구조는 무엇일까? 싶었던 것이다.

[![밥아저씨](https://i.postimg.cc/htqb7DHY/robert.png)](https://www.google.com/search?q=Uncle+bob)
{: .center}

그리고 마지막으로, 내가 산 책 중에서 가장 유명했다. 잘 모르겠을땐 역시 유명한 쪽으로.

## 옛날 이야기

![자바](https://i.postimg.cc/yxGj19Ym/java.jpg)  
{: .center}

고등학생 시절 프로그래머가 되고 싶다고 주변 친구들에게 말하면 다음과 같은 놀림을 받을 수 있었다.

*"요즘 프로그래머가 얼마나 힘든줄 알아? 어디 인력사무소 같은 곳에서 드럼통 모닥불 쬐며 기다리고 있다가 봉고차 한 대 와가지고 "2명 타요!" 하면 거기 실려가는 처지라더라!"*   

물론 저 때가 2010년대 초였고, 알파고니 비트코인이니 하는 단어들, 그리고 디지털 트랜스포메이션 어쩌구 하는 흐름들은 왠만하면 접할 수 없던 시대였으니 어느 정도 시대적 상황을 감안해서 받아들일 만한 농담이라고 생각한다. 지금은 취업도 잘된다고 하고, 초봉도 높다고 하고 상황이 많이 좋아지긴 했다. 그럼에도 불구하고 저 *"Java 두명 타요!"* 조크가 자조적으로 쓰이는 것을 아직도 가끔씩 발견할 수 있다. 시대가 아무리 발전하고 이 시장에 아무리 젖과 꿀이 흐르고 있다고 한들, 사실 프로그래머의 역할이 크게 바꼈다는 생각은 안든다. 이 책의 초반에서는 지금까지 이뤄진 프로그래밍 패러다임의 변화를 설명한 끝에 이렇게 표현하고 있다.

> ... 우리는 환영받지 못할 사실, 즉 소프트웨어는 급격히 발전하는 기술이 아니라는 진실과 마주하게 된다. ... 소프트웨어, 즉 컴퓨터 프로그램은 순차, 분기, 반복, 참조로 구성된다. 그 이상도 이하도 아니다.

안타까운 현실, 프로그래머로써의 우리는 언제든 대체될 수 있다. 좀 더 젊고, 손이 빠르고, 순간순간의 문제에서 더 효율적으로 처리할 수 있는 사람들에게. 주제넘은 소리겠지만, 가끔씩은 여기에 어떠한 두려움이 존재한다고 느껴진다. 나는 그저 "코더"에 불과할 지도 모른다. 어느 순간부터 나는 스스로를 프로그래머라고 소개하기 보단 소프트웨어 개발자라고 칭하는 것에 익숙해지고 있다. 프로그램(좀 더 코드에 가까운 뉘앙스로 다가온다)을 짜는 사람이 아니라, 좀 더 고차원적인 것을 만드는 사람이 되고싶은 것은 아닐까.

## 아키텍트

![아키텍트](https://i.postimg.cc/SQ9PZjGp/a.jpg)  
매트릭스도 이제 오래되긴 했어...
{: .center}

이 상황에서 우리의 동경을 불러일으키는 단어가 있으니, 바로 **아키텍트**이다. **시스템의 설계자**라는 칭호는 단순히 더 젊은 두뇌만으로 차지할 수 있을 만한 것이 아닌 것 처럼 느껴진다. 수많은 경험과 그 결과로 얻어낸 지식을 바탕으로 될 수 있는 존재가 **아키텍트**인 것이다. 하지만 앞서 말했듯이 소프트웨어는 생각보다 급격히 발전하는 기술이 아니다. 지긋한 백발의 현인들이 가진 지식은 글로 쓰여저 전수되는 중이고, 여전히 유효하다. 우리는 그 수많은 시간이 필요한 경험들을 굳이 우리 생에 걸쳐 겪지 않아도 단편적으로나마 체험할 수 있는 것이다. 그 경험의 끝에서 그들이 검증한 좋은 설계, 즉 아키텍처가 도출되며, 엉클 밥의 **클린 아키텍처**가 바로 그 중 하나이다.

## 코드, 정책Policy 그리고 세부사항Detail

이렇게까지 이야기 했지만, 정작 책에서 일깨워주는 중요한 사실은 바로 **아키텍트는 여전히 프로그래머**라는 것이다. 마치 아키텍트는 코드와는 전혀 관계없는 고수준의 논의를 해야할 것 처럼 느껴지겠지만, 아키텍트는 여전히 코드를 다뤄야 한다. 그들의 경험이란 코드이다. 클린 아키텍처는 여기서 시작하는 것 같다. 소프트웨어 아키텍처의 목표가 그저 "시스템을 제대로 동작하도록 만드는데"에 멈춰있어선 안되는 것이다. 물론 저 목표도 옳은 말이긴 하지만, 저 목표엔 코드가 포함되어있지 않다.

**소프트웨어 아키텍처**는 소프트웨어 시스템을 이루는 컴포넌트(재사용 가능한 각각의 독립된 모듈)들이 나타내는 형태이고, 그 형태는 소프트웨어 시스템이 쉽게 개발, 배포, 운영, 유지보수될 수 있는 방향으로 만들어져야 한다.

아키텍처는 기계어 코드의 덩어리로써 존재할 소프트웨어에 대한 청사진이다. 그리고 그 청사진을 본 사람들은 **소프트웨어(Software)를 부드럽게(Soft) 받아들일 수 있어야 한다.** 소프트웨어가 부드럽게 유지되기 위해서는 **세부사항에 대한 선택을 가능한 한 많이, 가능한 한 오랫동안 열어둬야 한다**고 책에서는 설명한다. 모든 소프트웨어 시스템은 **정책**과 **세부사항**으로 분해할 수 있는데, 이렇게 분해한 후 세부사항은 뒤로 미뤄둔 채 고수준의 정책을 만들 수 있도록 해야 한다는 말이다. 그 결과 세부사항에 대한 결정을 오랫동안 참아가며 더 많은 정보를 얻을 수 있고, 그 정보를 토대로 더욱 제대로 된 결정을 내릴 수 있게 만든다는 것이다.

> - 개발 초기에는 데이터베이스 시스템을 선택할 필요가 없다.  
>   고수준의 정책은 어떤 종류의 데이터베이스를 사용하는지 신경 써서는 안 된다.
> - 개발 초기에는 웹 서버를 선택할 필요가 없다.  
>   고수준의 정책은 자신이 웹을 통해 전달된다는 사실을 알아서는 안 된다.
> - 개발 초기에는 REST를 적용할 필요가 없다.  
>   고수준의 정책은 외부 세계로의 인터페이스에 대해 독립적이어야 하기 때문이다.
> - 개발 초기에는 의존성 주입 프레임워크를 적용할 필요가 없다.  
>   고수준의 정책은 의존성을 해석하는 방식에 대해 신경 써서는 안 된다.

> **좋은 아키텍트는 결정되지 않은 사항의 수를 최대화한다.**

## 프레임워크와 결혼하지 마라

![결혼하지마](https://i.postimg.cc/0Q1k2DYp/image.jpg)
{: .center}

자, 다시. 아키텍처는 정책과 세부사항으로 이루어진다. 그렇다면 그 둘을 통해 아키텍처는 무엇을 말하는가. **아키텍처는 그 스스로 소리쳐야 된다.**

> 이 아키텍처는 "헬스 케어 시스템이야" 또는 "재고 관리 시스템이야"라고 소리치는가? 아니면 "레일스야", "스프링/하이버네이트야", 아니면 "ASP야"라고 소리치는가?

아키텍처는 그 자체로 존재하고, 스스로 본인의 존재를 증명한다. 아키텍처의 정책들은 유스케이스의 형태로 발현되어 이것이 어떤 서비스에 대한 아키텍처인지 다방면으로 설명해줘야 한다. 세부사항은 그 정책들이 어떠한 방식으로 구현될지에 대해 설명할 것이다. 이 어떠한 방식들에 React, Spring 따위의 프레임워크들이 들어갈 수 있는 것이다. 즉 아키텍처는 특정 프레임워크로부터 제공받을 수 없는 것이다. 아키텍처를 위해 프레임워크라는 도구가 존재하는 것이지, 프레임워크 그 자체가 아키텍처가 될 수는 없고, 그래서는 안된다. 그걸 **프레임워크와 결혼하지 말라**는 표현으로 말하는 것이다. 현대 프레임워크들은 종종 사용자에게 너무 강한 결합(Coupling)을 요구해서 우리가 그와 결혼까지 고민해야 하도록 만들곤 한다. 얼머나 자주 우리 스스로를 "리액트 개발자" 내지는 "스프링 개발자" 라고 소개하게 되는지 떠올려보자.

[얼마 전 이런 글을 봤다.](https://news.hada.io/topic?id=6395)  
{: .middle-big .center}
물론 [원글](https://medium.com/codex/the-javascript-framework-war-is-over-bd110ddab732)의 댓글을 보면 알겠지만 상당히 반발이 심하긴 하다...
{: .center}

요약하자면, React가 자바스크립트 프레임워크 전쟁의 승자로 뽑힐 수 있는 이유는 스스로를 사용자 코드에서부터 제거하기 때문이라고 한다. 여러모로 이 클린 아키텍처의 내용이 생각나는 글이었다.

조금은 이상적이고 추상적이기만 한 이야기처럼 들릴 수도 있겠단 생각이 든다. 하지만 이 논의들의 결과로 아키텍처가 정책을 설명하는 유스케이스를 최우선으로 두고, 프레임워크와는 적당한 거리를 두도록 만듦으로써, 프레임워크를 전혀 준비하지 않더라도 유스케이스 전부에 대해 단위 테스트를 할 수 있는 아키텍처가 탄생하는 것이다. 즉, **테스트하기 쉬운 아키텍처**가 탄생하는 것이다.

## 클린 아키텍처Clean Architecture

아키텍처가 가져야 할 특징을 이제 다음과 같이 정리해볼 수 있다.

- 테스트 용이성  
  업무 규칙은 UI, 웹 서버, DB 등의 여타 외부 요소가 없이도 테스트 가능해야 한다.
- 프레임워크 독립성  
  아키텍처는 프레임워크의 존재 여부에 의존하지 않는다. 프레임워크가 가진 제약사항 안으로 시스템을 욱여 넣도록 강제하지 않는다.
- 모든 외부 에이전시에 대한 독립성(UI, DB 모두 포함해서)  
  시스템의 나머지 부분을 변경하지 않고도 UI를 쉽게 변경할 수 있어야 한다.  
  시스템의 나머지 부분을 변경하지 않고도 DBMS를 쉽게 변경할 수 있어야 한다.  
  즉, 업무 규칙은 외부 세계와의 인터페이스에 대해 전혀 알 필요가 없다.

이 특징들을 한 마디로 표현하자면, **관심사의 분리Separation of Concerns, SoC**이다. 클린 아키텍처 뿐만 아니라 다른 아키텍처와 관련된 아이디어들 또한 추구하는 목표가 바로 SoC이고, 그들은 그 목표를 달성하기 위해 **소프트웨어를 계층으로 분리**하는 방법을 사용한다.

![클린 아키텍처](https://i.postimg.cc/MTJVCzDd/Clean-Architecture.jpg)
{: .center}

엉클 밥의 **클린 아키텍처Clean Architecture**{: .middle-big}는 아키텍처에 대한 이전의 제안들을 하나의 아이디어로 통합하는 시도이다.

- 전사적인 핵심 업무 규칙 **엔티티**
- 애플리케이션에 특화된 업무 규칙 **유스케이스**
- 데이터를 유스케이스와 엔티티에게 가장 편리한 형식에서  
  데이터베이스나 웹 같은 외부 에이전시에게 가장 편리한 형식으로 변환하는  
  어댑터들의 묶음 **인터페이스 어댑터**
- 모든 세부사항이 위치하는 곳 **프레임워크와 드라이버**

안쪽 원은 고수준의 정책에 가까워지고, 바깥쪽 원은 세부적인 메커니즘에 가까워진다. 여기에 한 가지 제약사항을 가미한다.

> 소스 코드 의존성은 반드시 안쪽으로, 고수준의 정책을 향해야 한다.  
> 내부의 원에 속한 요소는 외부의 원에 속한 어떤 것도 알지 못한다. 내부의 원에 속한 코드는 외부의 원에 선언된 어떤 것에 대해서도 그 이름을 언급해서는 절대 안된다.

그럼에도 불구하고 각 계층의 경계 사이를 데이터는 횡단해야 한다, 각 경계 사이엔 제어의 흐름이 존재해야 한다. 서비스가 동작하려면 당연히 그래야 한다. 여기서 SOLID의 D, 의존성 역전 원칙DIP을 사용한다. 비로소 우리는 아키텍처가 진정 코드 수준의 문제를 다룬다는 점 또한 확인할 수 있다.

## 경계

![우아하게 넘어가기](https://i.postimg.cc/hjjcXZxW/border.jpg)
{: .center}

앞 문단에서 미리 정리하였듯, 이제 우리의 눈길은 경계로 이동한다. 아키텍처의 각 경계들을 어떻게 우아하게 넘어갈 것인가? 우아하게 넘어갈 수 있는 경계들을 어떻게 만들 것인가? 클린 아키텍처는 **험블 객체** 구현체들로 가득 차 있다. 험블 객체 패턴은 테스트 하기 쉬운 가장 기본적 본질들과 테스트하기 어려운 행위들을 분리하는 디자인 패턴이다. 험블 객체가 클린 아키텍처의 각 경계 속에 숨어있는 것이다.  

GUI를 예를 들어보자. GUI를 두 개의 클래스로 분리한다. 프레젠터와 뷰. 프레젠터는 테스트하기 쉬운 객체이다. 프레젠터는 애플리케이션으로부터 데이터를 받아 화면에 표현할 수 있는 포맷으로 만드는 역할을 한다. 그리고 뷰는 테스트하기 어려운 행위들이 분리된 험블 객체이다. 뷰는 데이터를 직접 처리하지 않고, 데이터를 GUI로 이동시키기만 한다. 둘 사이에 뷰 모델이라는 간단한 데이터 구조를 놔두고, 뷰 모델을 통해 둘은 데이터를 전달하고 전달 받도록 만들면 된다.

## Mo Problems Mo Money

![YAGNI2](https://i.postimg.cc/Jn6zrDTq/image.png)  
**야**, **그**거 **니**한테 필요 없을껄?
{: .center}

아 저렇게 경계를 처리하면 되는구나? 라고 하기엔 생각보다 계층과 경계를 다루는 일은 사실 쉽지 않다. 정확히는, 품이 많이 든다. 많은 경계는 많은 지출을 야기할 수 있다. 하지만 그렇다고 이걸 무시할 순 없다. YAGNI만큼 유명한 말이 **기술 부채Technical Debt**다. 그럼 어쩌란 말이야?

> ... 아키텍처 **경계가 어디에나 존재한다**는 사실을 보여주기 위함이다 ... **경계를 제대로 구현하려면 비용이 많이 든다**는 사실도 인지하고 있어야 한다. 이와 동시에 이러한 **경계가 무시되었다면 나중에 다시 추가하는 비용이 크다**는 사실도 알아야 한다. ... 자, 바로 이것이다. 오! 소프트웨어 아키텍트여, **당신은 미래를 내다봐야만 한다.**

조금은 무책임하게 느껴질 수도 있겠단 생각이 든다. 결국 "적절히" 잘 판단하라는 것 아닌가? 하지만 어쩌면 우리는 클린 아키텍처 또한 바위에 새겨진 진리는 아니라는 사실을 받아들여야 할 지도 모른다는 생각이 든다. 사실, 세상 대부분의 가치들이 그런 적이 많더라. 하지만 그 중에서 분명 훌륭한 방향을 제시하는 것들이 존재한다는 사실을 무시할 순 없다. 다시 사실, 진짜 가치있는 조언이란 평은 그런 쪽에 더 어울린다. 강조하고 싶은 메세지는 이것이다.

> 목표를 달성하려면 빈틈없이 지켜봐야 한다.

클린 아키텍쳐라는 특정 방법론을 넘어서서 아키텍트가 되고싶은 사람이라면 가져야 할 자세라고 생각한다.

## 좀 더 감상적으로 마무리

어쩌다보니 마지막 부분은 좀 감상적으로 전개가 된 것 같다. 보통 기술 블로그라고 하면 좀 더 실용적이고 코드 예제도 많이 보여주고, 3줄 요약도 잘해주고 그러는데... 그런 의미에서 사실 다음 레퍼런스들을 참고해가며 글을 써볼까 생각도 해보긴 했다.

- [주니어 개발자의 클린 아키텍처 맛보기](https://techblog.woowahan.com/2647/)
- [리액트 w/ 클린 아키텍처](https://github.com/falsy/react-with-clean-architecture/blob/master/readme-ko.md)

하지만 내공이 부족한 것은 어쩔 수 없나보다. 은근 문과에 더 어울리는 것 같다는 이야기 들어본 적도 있고... 그래도 개인적으로 클린 아키텍처 이 책을 읽은게 아무 수확이 없었던 것은 아니라는 생각도 드는데, 현재 개발 중인 개인 프로젝트에 이 책에서 제안된 가치들을 부분적으로 사용해보기도 하였기 때문이다. 물론 그게 잘 따른 건지는 모르겠다. 자세히는 민망하니 말로만 조금 설명하자면, 리액트 개발 중 백엔드 API를 호출해야 하는데, 컴포넌트에 달린 로직이 그 API가 무엇인지 구체적으로 알게 되는 형태가 마음에 안들어서 API 호출쪽 코드에 인터페이스를 정의해두고 컴포넌트는 그 인터페이스를 사용하도록 만들었다. 그리고 구체적인 백엔드(지금 상황에서는 Firebase)에 의존하는 코드(Firebase SDK 사용하는 코드)를 분리했다.

그래서 실행 되는 환경에 따라서 
```typescript
let ResourceClient: ResourceAPI;
if (process.env.NODE_ENV !== "test")
  ResourceClient = new FirebaseResourceClient();
else
  ResourceClient = new TestApiClient();
```
요로코롬 인스턴스가 제공되도록 만들었다.

아무튼 이건 그냥 잡담이고, 클린 아키텍처 아주 좋은 책이었습니다. 위에 있는 링크 두개는 사실 좀 실용적인 혹은 실증적인 레퍼런스가 있으면 좋겠다는 생각에 첨부한 것이기도 하다. 이 글로는 부족할 터이니 만약 관심이 생겼다면 저 링크들을 보면서 시각을 좀 더 넓혀보는 것도 좋을 것이다.

다음 책으로는 켄트 벡의 [테스트 주도 개발](http://www.yes24.com/Product/Goods/98857178)을 읽고있다. 아마 이건 좀 더 코드 많이 써가면서 독후감을 쓸 수 있을지도 모르겠다. 그 목적으로 읽는건 물론 아니고. 다만 얼마나 걸릴지는 모르겠다. 최근엔 계속 하루가 너무 짧다는 생각 밖에 안들어서 책 읽는데 시간 할당이 잘 안되더라. 그 외에 5월 중에 다른 글을 좀 더 써보려고 노력은 해볼 생각이다. 글은 안써도 주제는 계속 쌓아두고 있긴 하거든. 누군가 읽어줬으면 좋겠다는 바램 보다는 역시 이 블로그의 핵심 가치 "안적으면 까먹는다"에 따라.