클린 아키텍처

클린 아키텍처

로버트 C. 마틴의 시스템 설계 교과서를 장별로 정리한 노트. 좋은 아키텍처란 결정을 미루고 세부사항을 격리해 선택지를 열어두는 기술이며, 그 도구는 경계선 하나뿐이라는 이야기를 따라간다.

로버트 C. 마틴의 『클린 아키텍처 — 소프트웨어 구조와 설계의 원칙』을 읽으며 장 단위로 정리합니다. 클린 코드가 문장 수준, 리팩터링이 변형 수준, TDD가 작업 리듬, 오브젝트가 객체 설계였다면 이 책은 그 한 층 위 — 시스템 수준입니다. 클래스와 컴포넌트를 어떤 선으로 갈라놓아야, 프레임워크와 데이터베이스와 웹이 바뀌어도 업무 규칙이 흔들리지 않는가.

이 책이 처음부터 끝까지 붙들고 있는 주장은 하나입니다.

  • 아키텍처의 목표는 시스템을 만드는 데 드는 인력을 최소로 유지하는 것이다. 기능이 아니라 유지 비용이 설계의 성패를 가른다.
  • 좋은 아키텍처는 결정을 최대한 미룰 수 있게 해준다. 데이터베이스·웹·프레임워크는 업무 규칙에 꽂았다 뺐다 하는 세부사항(플러그인)일 뿐, 그 선택을 빨리 확정할수록 손해다.
  • 그 모든 것을 가능케 하는 유일한 도구는 경계선이다. 어디에 선을 긋고 무엇을 어느 쪽에 두느냐 — 이 책은 결국 선긋기에 관한 책이다.

패러다임(3부)에서 SOLID 원칙(설계·컴포넌트)을 거쳐 아키텍처(5부)와 세부사항(6부)으로 내려가는 동안, 노트에서는 원칙이 코드로 구체화되는 순간을 스텝 플레이어의 diff로 기록합니다.

Contents

  1. 01
    1장 설계와 아키텍처란?설계와 아키텍처는 다른 것이 아니라 하나의 연속체다. 릴리스를 거듭할수록 개발자는 늘어도 생산성이 0으로 수렴하는 그래프를 앞에 놓고, '일단 돌게 하고 나중에 정리하자'는 신화를 해부한다. 좋은 아키텍처는 나중이 아니라 지금 지불해야 하는 비용이다.
  2. 02
    2장 두 가지 가치에 관한 이야기소프트웨어는 행위와 구조라는 두 가치를 갖는다. 급하게 우는 쪽은 늘 행위지만, 진짜 값어치는 변경 비용을 형태가 아니라 범위에 비례하게 만드는 구조에 있다. 아이젠하워의 긴급/중요 격자로 그 역설을 읽는다.
  3. 03
    3장 패러다임 개요구조적·객체지향·함수형이라는 세 패러다임은 프로그래머에게 새 능력을 얹어 주지 않는다. 오히려 goto와 함수 포인터와 할당이라는 능력을 하나씩 빼앗는다. 프로그래밍의 역사는 무엇을 더 할 수 있게 되었는가가 아니라 무엇을 하지 못하게 규율했는가의 역사다.
  4. 04
    4장 구조적 프로그래밍다익스트라는 프로그램도 수학처럼 증명할 수 있으리라 믿었지만, 그가 남긴 것은 증명이 아니라 반증이었다. goto를 앗아가 순차·분기·반복 세 구조로 프로그램을 반증 가능한 단위로 쪼갠 것 — 테스트는 버그의 존재만 보일 뿐 없음을 증명하지 못한다는 이 겸손이 아키텍트에게는 무기가 된다.
  5. 05
    5장 객체 지향 프로그래밍OO의 진짜 힘은 캡슐화·상속이 아니라 다형성으로 소스 의존성을 제어 흐름과 반대로 뒤집는 능력이다. 급여 계산 규칙이 DB와 콘솔에 직접 매달리던 코드를, 인터페이스를 사이에 끼우고 생성을 바깥으로 밀어내 플러그인 아키텍처로 역전시킨다.
  6. 06
    6장 함수형 프로그래밍세 번째 패러다임인 함수형 프로그래밍이 빼앗는 것은 할당이다. 변수를 두 번 쓰지 않는다는 규율 하나가 경쟁·교착·동시 갱신 같은 모든 동시성 문제의 뿌리를 잘라낸다. 가변성을 좁은 곳으로 격리하고, 상태 대신 트랜잭션을 저장하는 이벤트 소싱으로 가변 상태 자체를 지워 버리는 데까지 나아간다.
  7. 07
    7장 SRP: 단일 책임 원칙SRP는 '한 가지 일만 하라'가 아니라 '하나의 액터에 대해서만 책임지라'다. 급여·근무시간·저장이 회계·인사·DBA 세 액터의 코드인데도 한 Employee 클래스에 뭉쳐 있을 때, 우발적 중복과 병합 충돌이 어떻게 터지는지, 그리고 데이터와 기능을 갈라 파사드로 다시 묶어 그 사슬을 끊는 과정을 단계별로 따라간다.
  8. 08
    8장 OCP: 개방-폐쇄 원칙확장에 열리고 수정에 닫힌다는 원칙을 클래스 수준의 격언에서 아키텍처의 도구로 끌어올린다. 웹에 뿌리던 재무 보고서를 그대로 PDF로 인쇄하라는 요구를 예로, 변경을 컴포넌트 계층으로 격리하고 의존성 방향으로 상위 정책을 하위 세부로부터 보호하는 법을 본다.
  9. 09
    9장 LSP: 리스코프 치환 원칙정사각형은 직사각형인가. setW/setH의 규약이 깨지는 순간을 스텝으로 짚어, 상속이 아니라 치환 가능성이 계약임을 보인다. 그리고 택시 디스패치처럼 LSP가 인터페이스 규약으로 확장되면, 단 하나의 위반이 시스템 곳곳을 특수 케이스와 조건문으로 오염시킨다.
  10. 10
    10장 ISP: 인터페이스 분리 원칙필요 없는 것에 의존하지 마라. 정적 타입 언어에서는 쓰지도 않는 메서드가 바뀌어도 그것을 문법적으로 아는 쪽이 재컴파일·재배포를 강제당한다. 살찐 인터페이스 하나에 여럿이 매달린 구조를, 실제로 쓰는 연산만 담은 인터페이스로 갈라 불필요한 결합을 끊는다. 아키텍처의 교훈: 불필요한 것에 대한 의존이 예상 못 한 트러블을 부른다.
  11. 11
    11장 DIP: 의존성 역전 원칙유연한 시스템은 소스 의존성이 변동성 큰 구체가 아니라 안정적인 추상에만 걸린다. 주문 처리 규칙이 런타임에 배송 라벨을 new로 찍어내며 벤더 구현에 매달리던 코드를, 추상 팩토리로 생성마저 경계 밖으로 밀어내 제어 흐름과 소스 의존성이 반대로 갈라지는 곡선을 만든다.
  12. 12
    12장 컴포넌트컴포넌트는 배포 단위다 — .jar, .dll, gem처럼 시스템에 끼우고 뺄 수 있는 조각. 재배치 가능 바이너리에서 링킹 로더를 거쳐 로드타임·런타임 플러그인에 이르는 반세기의 이야기를 따라가며, 컴포넌트가 어떻게 '독립적으로 배포되고 개발되는 플러그인'이 되었는지를 읽는다.
  13. 13
    13장 컴포넌트 응집도어떤 클래스를 어떤 컴포넌트에 담을 것인가. REP·CCP·CRP 세 원칙은 각자 옳지만 서로 반대 방향으로 당기며, 셋을 동시에 만족시킬 수는 없다. 프로젝트가 성숙하며 그 삼각형의 무게중심이 재사용성에서 개발 편의로 옮겨 가는 이야기.
  14. 14
    14장 컴포넌트 결합컴포넌트를 잇는 화살표를 어디로 그을 것인가. 순환을 금하는 ADP, 안정성의 방향을 따르라는 SDP, 안정적일수록 추상적이어야 한다는 SAP — 세 원칙이 만나 안정성-추상성 평면 위의 주계열(main sequence)을 그린다. 그 선에서 멀어진 컴포넌트가 떨어지는 곳이 고통의 구역과 쓸모없는 구역이다.
  15. 15
    15장 아키텍처란?아키텍트는 코드를 놓은 사람이 아니라 여전히 프로그래머다. 좋은 아키텍처의 목적은 시스템의 개발·배포·운영·유지보수를 떠받치고, 무엇보다 결정을 최대한 미뤄 선택지를 열어 두는 것이다. 1960년대 장치 독립성의 교훈으로 그 절제를 읽는다.
  16. 16
    16장 독립성좋은 아키텍처는 유스케이스·운영·개발·배포를 동시에 지원해야 한다. 그 지지대는 변경의 축을 따라 계층과 유스케이스를 갈라 두는 결합 분리에서 온다. 진짜 중복과 우발적 중복을 구별하고, 소스·배포·서비스라는 세 결합 분리 모드가 시간에 따라 옮겨 간다는 것이 이 장의 골자다.
  17. 17
    17장 경계 - 선긋기아키텍처는 결국 선을 긋는 일이다. 그 선은 지금 결정하지 않아도 될 것(프레임워크·DB·웹)을 나중으로 미룰 수 있게 시스템을 갈라 놓는다. GUI와 DB를 업무 규칙에 꽂았다 뺐다 하는 플러그인으로 만들고, 선은 취향이 아니라 진짜 변경축·가치축 위에 긋는다.
  18. 18
    18장 경계 해부학경계를 횡단한다는 것은 결국 한쪽 함수가 다른 쪽 함수를 부르는 런타임 호출일 뿐이다. 문제는 소스 의존성의 방향과, 그 경계를 얼마나 물리적으로 갈라놓을 것인가다. 모놀리식·배포형·스레드·로컬 프로세스·서비스로 이어지는 결합 분리의 스펙트럼을 따라, 각 단계가 무엇을 사고 무엇을 지불하는지 값을 매긴다.
  19. 19
    19장 정책과 수준프로그램은 정책의 집합이고, 정책은 같은 이유·같은 시점에 바뀌는 것끼리 묶인다. 수준이란 입력과 출력으로부터의 거리이며, 좋은 아키텍처는 소스 의존성이 늘 수준 높은(입출력에서 먼) 쪽을 향하게 배치한다. 문자를 읽어 암호화해 내보내는 작은 프로그램이 그 규칙을 통째로 보여준다.
  20. 20
    20장 업무 규칙은행 대출 이자 예시. 이자를 계산하는 핵심 업무 규칙과 대출이라는 핵심 업무 데이터를 엔티티로 세우고, '이자를 청구한다'는 애플리케이션 특화 규칙을 유스케이스로 분리한다. 엔티티는 유스케이스를 모르고, 요청·응답 모델은 어디에도 의존하지 않는다 — 무엇이 더 높은 수준인가를 가르는 장.
  21. 21
    21장 소리치는 아키텍처건축 도면이 그 건물의 용도를 한눈에 외치듯, 소프트웨어의 최상위 구조도 시스템이 무엇을 하는 시스템인지를 외쳐야 한다. 웹과 프레임워크와 데이터베이스는 그 외침의 주인공이 아니라 나중으로 미뤄도 좋을 전달 메커니즘이며, 그렇게 미룬 아키텍처는 덤으로 테스트 용이성까지 돌려준다.
  22. 22
    22장 클린 아키텍처동심원 네 겹과 의존성 규칙 — 소스 의존성은 오직 안쪽을 향한다. 웹 요청이 컨트롤러에서 유스케이스로, 다시 프레젠터를 거쳐 뷰로 흐르는 경계 횡단을, 유스케이스가 프레젠터를 직접 부르는 결합에서 출력 경계 인터페이스로 역전시키는 스텝으로 보여준다.
  23. 23
    23장 프레젠터와 험블 객체험블 객체 패턴은 테스트하기 어려운 것과 쉬운 것을 가른다. 뷰에 포매팅·판단이 뒤섞인 코드를, 뷰 모델을 만드는 프레젠터와 그것을 그대로 출력하는 험블 뷰로 쪼갠다. 어디를 테스트할 수 있느냐가 곧 경계가 어디에 있어야 하는지를 드러낸다.
  24. 24
    24장 부분적 경계완전한 경계는 양방향 인터페이스와 입출력 데이터 구조, 양쪽 컴포넌트를 모두 요구하는 값비싼 물건이다. 그 값을 다 치를 여유가 없을 때를 위해 마틴은 세 가지 절충안을 내놓는다 — 마지막 분리 단계를 건너뛰기, 일차원 경계, 그리고 파사드. 각각이 무엇을 깎아 값을 낮추고, 그 대가로 무엇을 잃는지를 저울질한다.
  25. 25
    25장 계층과 경계가장 단순한 텍스트 게임 Hunt the Wumpus에도 UI·언어·저장소라는 경계가 숨어 있다. 모든 경계는 공짜가 아니라 비용을 부르므로, 지금 그을지 미룰지는 과잉과 과소 엔지니어링 사이에서 내리는 판단이다. 숨은 경계를 언제 실체화할지 지켜보는 것도 설계다.
  26. 26
    26장 메인 컴포넌트Main은 시스템에서 가장 낮은 수준의 정책, 궁극의 세부사항이다. 시스템의 진입점에서 팩토리·전략·구체 구현을 생성해 상위 컴포넌트에 주입하고, 더러운 초기 구성을 자기 안에 격리하는 플러그인이다. 5장에서 생성 책임을 밀어냈던 그 Main을 정면으로 들여다본다.
  27. 27
    27장 크고 작은 모든 서비스마이크로서비스로 쪼갠다고 아키텍처가 생기지는 않는다. 서비스 경계는 결합 분리·독립 개발·독립 배포를 저절로 주지 않으며, 공유 데이터와 횡단 관심사가 서비스들을 다시 하나로 묶는다. 택시 집계라는 새 기능 하나가 모든 서비스를 가로지르며 그 사실을 폭로한다.
  28. 28
    28장 테스트 경계테스트는 시스템 바깥에 있는 검사 도구가 아니라 시스템의 일부이자 가장 바깥 원이며, 의존성 규칙을 그대로 따른다. 테스트가 시스템의 공통 구조와 UI에 결합하면 사소한 변경 하나가 수백 개의 테스트를 깨뜨리는 취약한 테스트 문제가 생긴다. 해법은 테스트를 위한 전용 API로 그 결합을 끊는 것이다.
  29. 29
    29장 클린 임베디드 아키텍처임베디드 코드는 돌아가는 순간 끝난 것처럼 보이지만, 하드웨어라는 가장 잘 바뀌는 세부사항에 묶인 채 가장 빨리 썩는다. 소프트웨어와 펌웨어를 가르고, HAL과 OS 추상 계층으로 하드웨어를 경계 밖으로 밀어내 '하드웨어가 바뀌어도 사는 코드'를 남기는 이야기.
  30. 30
    30장 데이터베이스는 세부사항이다데이터베이스는 아키텍처의 중심이 아니라 회전 자기 디스크라는 우연에서 태어난 유틸리티다. 아키텍처적으로 중요한 것은 데이터의 모델이지 DBMS·스키마·SQL이 아니며, 성능은 업무 규칙 바깥의 저수준에 캡슐화할 관심사다. 무엇을 중심에 두고 무엇을 세부로 미룰지를 가르는 장.
  31. 31
    31장 웹은 세부사항이다웹은 아키텍처가 아니라 전달 메커니즘이다. 중앙 집중과 분산 사이를 오간 컴퓨팅의 진자 운동을 되짚으며, GUI는 IO 장치일 뿐이고 웹은 세부사항이므로 업무 규칙과 경계로 갈라야 함을 논증한다. UI와 업무 규칙 사이에는 언제나 추상이 있다.
  32. 32
    32장 프레임워크는 세부사항이다프레임워크 저자와 나의 관계는 비대칭이다 — 그는 내 사정을 신경 쓰지 않는다. 그러니 프레임워크와 결혼하지 말고 데이트만 하라. 핵심 업무 객체가 프레임워크를 상속하거나 의존하는 순간, 버전과 방향 전환의 리스크가 시스템의 심장까지 파고든다.
  33. 33
    33장 사례 연구: 비디오 판매비디오 판매 소프트웨어를 실제로 설계해 본다. 액터를 세어 유스케이스를 뽑고, 유스케이스마다 컨트롤러·인터랙터·프레젠터·뷰·엔티티를 세우고, 그 클래스들을 액터의 변경축을 따라 컴포넌트로 가른다. 앞서 나온 SOLID·컴포넌트·경계 원칙이 한 장의 다이어그램으로 모이는 종합 무대.
  34. 34
    34장 빠뜨린 장원칙을 실제 코드로 옮기면 결국 패키지와 디렉터리를 어떻게 가르느냐의 문제가 된다. 계층·기능·포트와 어댑터·컴포넌트라는 네 조직화 전략을 나란히 세워, 각각이 캡슐화와 순환 의존에 무엇을 지불하고 무엇을 사는지 저울질한다. 그리고 마지막으로, 어떤 다이어그램도 컴파일러의 접근 제어 앞에서만 진짜라는 사실을 확인한다.