← doubleon study hub

A Field Guide to the Canon · 정독본

소프트웨어 아키텍처를 관통하는 것들

수십 년간 반복 인용되는 원리들은 사실 하나의 문제를 각도만 바꿔 공격한다 — 복잡도를 모듈 경계로 다스리는 법. 이 자료는 그 핵심 20%를, 처음부터 끝까지 한 번에 훑도록 짰다.

분량약 2시간 정독 구성7개 장 · 11개 도해 대상전체 지형을 빠르게 잡고 싶은 사람 이후장별 원전으로 드릴다운
00 · 시작하며 · 8분

왜 대부분의 원리가 결국 같은 이야기인가

소프트웨어 아키텍처를 공부하려고 책과 논문 목록을 펼치면 압도된다. Parnas, Brooks, Dijkstra, Fielding, Evans, Ousterhout, "Clean Architecture", "Domain-Driven Design", "Out of the Tar Pit"… 서로 다른 시대·언어·문제에서 나온 수십 편의 정전(canon)이 있다. 하지만 그것들을 나란히 놓고 읽으면 이상한 일이 벌어진다. 거의 전부가 같은 적을 다른 무기로 상대하고 있다.

그 적은 복잡도다. 그리고 인류가 지금까지 찾아낸 가장 강력한 무기는 하나다 — 경계를 긋고, 그 경계 뒤로 결정을 숨기는 것. 무엇을 숨길지, 무엇을 함께 두고 무엇을 떼어낼지, 경계를 어디에 그을지, 그 경계가 왜 조직·시간과 얽히는지. 정전들은 이 질문들의 서로 다른 좌표에 서 있을 뿐이다.

이 자료를 관통하는 한 문장

소프트웨어 설계의 거의 모든 원리는 “복잡도를 모듈 경계로 다스린다”는 하나의 문제를, 각도만 바꿔 공격한다. 이 한 줄을 축으로 잡으면 개별 개념들이 흩어지지 않는다.

먼저 · 아키텍처란 무엇인가

두 정의를 겹쳐 두면 충분하다. 구조적으로(Bass·Clements·Kazman, SEI): 시스템을 이루는 요소, 요소 간 관계, 그리고 외부에 드러나는 속성의 집합. 실용적으로(Fowler·Booch): “나중에 바꾸기 비싼 결정들” — 되돌리기 어려운 선택일수록 아키텍처에 속한다. 이 자료의 모든 원리는 결국 그 비싼 결정을 어디에·어떻게 내릴 것인가에 대한 답이다.

문제의 복잡도 (raw) 경계를 긋는다 숨긴다 · 나눈다 질서 잡힌 모듈들 (designed) 인터페이스비밀
FIG. 1설계란 뒤엉킨 복잡도(왼쪽)를 경계로 잘라, 각자 하나의 “비밀”을 숨기고 얇은 인터페이스만 내미는 모듈들(오른쪽)로 바꾸는 일이다. 이후의 모든 장은 이 그림의 세부다.

아래 7개 장은 이 그림을 따라 흐른다. 먼저 적의 정체(복잡도)를 규정하고 → 그 적을 가두는 기본 도구(모듈)를 익히고 → 경계를 어디에 그을지 판단하는 법을 배우고 → 코드 바깥의 힘(사람·시간)을 이해하고 → 완성된 구조를 기술·평가하는 언어를 갖춘 뒤 → 전체를 하나의 지도로 묶는다.

한 줄 요약정전은 많지만 문제는 하나다. 복잡도를 경계로 다스리는 법.
01 · 적을 규정하다 · 15분

복잡도 — 무엇과 싸우는가

싸움을 시작하기 전에 적을 정의해야 한다. 소프트웨어에서 “복잡하다”는 건 “코드가 길다”가 아니다. John Ousterhout는 복잡도를 이렇게 정의한다: 시스템을 이해하거나 수정하기 어렵게 만드는 모든 것. 규모가 아니라 변경 비용이 기준이다.

Ousterhout · 복잡도의 3가지 증상
복잡도는 이렇게 모습을 드러낸다

복잡도는 추상적이지만, 그것이 남기는 발자국은 구체적이다. Ousterhout는 세 가지 증상을 꼽는다. 코드를 볼 때 이 셋 중 하나가 느껴진다면 복잡도가 쌓이고 있는 것이다.

Symptom 1

변경 증폭

사소한 요구 하나를 바꾸는 데 여러 곳을 동시에 고쳐야 한다. 하나의 결정이 코드 곳곳에 흩어져 있다는 신호.

Symptom 2

인지 부하

무언가를 하려면 알아야 할 것이 너무 많다. 개발자가 머릿속에 담아야 할 상태·규칙·예외가 넘친다.

Symptom 3

모르는 줄도 모름

가장 위험한 증상. 무엇을 알아야 안전하게 고칠 수 있는지조차 알 수 없다. 지뢰밭 위에서 코딩하는 상태.

Brooks 1986 · No Silver Bullet
본질적 복잡도 vs 우발적 복잡도

그런데 모든 복잡도가 같은 종류는 아니다. Fred Brooks는 1986년 논문 “No Silver Bullet”에서 결정적인 구분을 세운다.

  • 본질적(essential) 복잡도 — 문제 자체에 내재한다. 항공 관제 시스템이 다뤄야 할 규칙의 수는 우리가 어떤 언어를 쓰든 줄지 않는다. 제거 불가능하다.
  • 우발적(accidental) 복잡도 — 문제가 아니라 우리가 선택한 도구·방식 때문에 생긴다. 어설픈 추상화, 중복된 상태, 얽힌 의존성. 제거 가능하다.

Brooks의 논증은 이 구분 위에 선다. 소프트웨어의 본질적 어려움(복잡성·순응성·가변성·비가시성)은 도구로 없앨 수 없으므로, “생산성을 10배로 올리는 은탄환(silver bullet)”은 존재하지 않는다. 우리가 실제로 공략할 수 있는 전장(戰場)은 우발적 복잡도뿐이다.

복잡도 총량 우발적 본질적 현실의 시스템 우발적↓ 본질적 좋은 설계의 목표 본질은 그대로 여기만 줄일 수 있다
FIG. 2설계의 목표는 본질적 복잡도(파랑)를 건드리는 게 아니라 — 그건 문제에 속하므로 줄지 않는다 — 우발적 복잡도(빨강)를 최대한 0으로 미는 것이다. 모듈·경계·추상화는 모두 우발적 복잡도를 억제하는 도구다.

Moseley & Marks 2006 · Out of the Tar Pit
우발적 복잡도의 최대 원천: 상태와 제어

Brooks가 지도를 그렸다면, “Out of the Tar Pit”은 그 지도에서 가장 큰 늪이 어디인지 짚었다. 저자들은 우발적 복잡도의 두 주범을 지목한다:

  • 상태(state) — 시스템의 어느 부분이 지금 어떤 값을 갖고 있는지가 경우의 수를 폭발시킨다. 가변 상태가 많을수록 “지금 무슨 일이 일어날 수 있는가”를 추론하기 어렵다.
  • 제어(control) — 실행 순서·흐름에 대한 불필요한 명세. “무엇을” 원하는지가 아니라 “어떻게·어떤 순서로” 하라고 일일이 지시할수록 얽힌다.

이들의 처방은 상태와 제어를 최소화하는 방향 — 함수형·선언형 사고, 그리고 본질과 우발을 아키텍처 수준에서 분리하는 것이다. 이 논문이 오늘날까지 설계 토론에서 가장 많이 인용되는 현대 고전인 이유는, “복잡도를 줄여라”라는 구호를 “상태와 제어를 줄여라”라는 실행 가능한 지침으로 번역했기 때문이다.

“단순함을 얻기 어려운 게 아니다. 우리가 스스로 만들어낸 복잡함을 걷어내기가 어려울 뿐이다.”

— Out of the Tar Pit의 문제의식을 요약하며
한 줄 요약복잡도 = 변경을 어렵게 하는 모든 것. 본질은 못 줄이고, 우발(특히 상태·제어)만 줄일 수 있다.
02 · 기본 무기 · 22분

모듈 — 복잡도를 가두는 상자

우발적 복잡도와 싸우는 근본 전략은 분해(decomposition)다. 큰 문제를 독립적으로 이해·교체·재사용할 수 있는 단위로 쪼갠다. 이 단위가 모듈이다. 하지만 “쪼갠다”는 말은 위험하다. 잘못 쪼개면 조각 수만 늘고 복잡도는 오히려 커진다. 이 장의 개념들은 전부 “어떻게 잘 쪼개는가”에 대한 답이다.

Parnas 1972 · 이 분야의 원전
정보 은닉 — 무엇을 기준으로 쪼갤 것인가

David Parnas의 1972년 논문 “On the Criteria To Be Used in Decomposing Systems into Modules”는 이 질문에 처음으로 정확한 답을 줬고, 이후 거의 모든 모듈 이론이 그 후손이다. 핵심 주장은 반직관적이다.

대부분의 사람은 시스템을 처리 순서(flowchart)로 나눈다: “입력을 읽는 모듈 → 처리하는 모듈 → 출력하는 모듈”. Parnas는 이게 나쁜 분해라고 말한다. 왜냐하면 각 단계가 서로의 자료구조를 알아야 하고, 그래서 한 결정(예: 데이터 저장 포맷)이 바뀌면 여러 모듈이 동시에 무너지기 때문이다.

좋은 분해의 기준은 “바뀔 가능성이 높은 설계 결정”이다. 각 모듈은 그런 결정 하나 — Parnas의 용어로 비밀(secret) — 을 감추고, 바깥에는 안정적인 인터페이스만 내민다. 그러면 그 결정이 바뀌어도 한 모듈만 고치면 된다. 이것이 정보 은닉(Information Hiding)이다.

✕ 나쁨 · 순서(flowchart)로 분해 읽기 처리 쓰기 공유 자료구조 (모두가 알아야 함) → 포맷 하나 바뀌면 셋 다 무너짐 ✓ 좋음 · 비밀(secret)로 분해 비밀 A 비밀 B 비밀 C → 결정이 바뀌어도 한 모듈에 갇힘
FIG. 3같은 시스템, 두 가지 분해. 왼쪽은 처리 단계로 나눠 모두가 공유 자료구조에 묶인다 — 변경이 전파된다. 오른쪽은 “바뀔 결정”별로 나눠 각 모듈이 자기 비밀을 숨긴다 — 변경이 한 상자 안에 갇힌다.

정보 은닉은 이후 등장한 거의 모든 것의 뿌리다. 객체지향의 캡슐화, 추상 데이터 타입, 아래에서 볼 깊은 모듈의존성 규칙까지 — 전부 “변할 것을 안정된 인터페이스 뒤에 숨긴다”는 이 아이디어의 변주다.

Ousterhout · A Philosophy of Software Design
깊은 모듈 — 정보 은닉의 정량화

그렇다면 “좋은 모듈”을 어떻게 알아볼까? Ousterhout는 시각적인 은유를 준다. 모듈을 직사각형으로 그리자. 윗변은 인터페이스(다른 코드가 이 모듈을 쓰기 위해 알아야 하는 것), 넓이 전체는 기능(구현)이다.

  • 깊은(deep) 모듈 — 윗변이 좁고(인터페이스가 작고) 몸통이 깊다(구현이 많다). 적게 알려주고 많이 해준다. 좋은 모듈.
  • 얕은(shallow) 모듈 — 윗변이 넓고 몸통이 얕다. 알아야 할 건 많은데 감춰주는 건 거의 없다. 인터페이스 비용만 물리는 나쁜 모듈.
구현 (가치) 넓고 깊게 ← 좁은 인터페이스 깊은 모듈 ✓ 얕은 구현 넓은 인터페이스 (전부 비용) 얕은 모듈 ✕ 모듈의 가치 = 기능 − 인터페이스 비용
FIG. 4인터페이스는 비용(모두가 배워야 한다), 구현은 가치(대신 해준다). 그래서 좋은 모듈은 인터페이스는 작게, 구현은 깊게 만든다. 유닉스 파일 I/O가 고전적 예 — open/read/write/close 네 개 뒤에 방대한 구현이 숨어 있다.

이 관점은 실무 결정을 뒤집는다. “함수를 잘게 쪼개라”는 통념과 달리, 잘게 쪼갠 얕은 함수가 수십 개면 오히려 인터페이스 비용만 폭증한다. Ousterhout는 여기에 두 가지를 덧붙인다. 전술적 vs 전략적 프로그래밍 — 당장 돌아가게만 하는(tactical) 대신 좋은 설계에 조금씩 투자하라(strategic). 그리고 복잡도는 점진적으로 쌓이므로 무관용(zero-tolerance)으로 대하라 — 작은 지저분함 하나하나가 이자까지 쳐서 갚는 기술 부채(technical debt, Cunningham)가 되고, 그 방치된 극단이 바로 정전적 안티패턴 “큰 진흙 공(Big Ball of Mud)”이다.

보태기 · 추상화와 그 한계

깊은 모듈이 작동하는 근본 이유는 좋은 추상화 덕분이다 — “무엇을 하는가”만 남기고 “어떻게”를 지운 단순화. 다만 모든 추상화는 어느 지점에서 샌다(leaky abstraction, Spolsky): 성능·오류·한계처럼 감춰둔 세부가 결국 새어 나온다. 그래서 추상화에 기대되, 그 아래에서 무슨 일이 벌어지는지도 알고 있어야 한다.

두 축 · 좋은 구조의 척도
결합도와 응집도

정보 은닉과 깊은 모듈이 “한 모듈을 어떻게 만드는가”라면, 결합도(coupling)응집도(cohesion)는 “모듈들의 관계가 건강한가”를 재는 두 축이다. 1970년대 구조적 설계(Constantine·Yourdon, Structured Design)에서 정식화된 개념이다. 규칙은 짧다:

  • 낮은 결합도 — 모듈 사이의 의존은 적고 얇게. 한 모듈을 바꿔도 다른 모듈이 흔들리지 않아야 한다.
  • 높은 응집도 — 한 모듈 안의 요소들은 하나의 목적을 향해 모여 있어야 한다. “여기 있어야 할 이유”가 분명해야 한다.
✕ 높은 결합 · 낮은 응집 모두가 서로 엉킴 ✓ 낮은 결합 · 높은 응집 안은 촘촘, 밖은 얇게
FIG. 5왼쪽은 모든 노드가 서로 얽혀(높은 결합) 목적도 흐릿하다(낮은 응집). 오른쪽은 관련된 것끼리 모여 촘촘히 연결되고(높은 응집), 모듈 사이는 얇은 선 하나로만 이어진다(낮은 결합). — 정보 은닉을 잘하면 자연히 이 그림이 나온다.

Dijkstra · 관심사의 분리
한 번에 하나의 관심사

이 모든 것의 상위 개념이 관심사의 분리(Separation of Concerns)다. Edsger Dijkstra가 1974년 정립한 이 원칙은 “서로 다른 관심사(예: 비즈니스 로직 / 화면 표현 / 데이터 저장)를 섞지 말고, 한 번에 하나에만 집중할 수 있게 나누라”는 것이다. 정보 은닉이 “무엇을 숨길까”라면, 관심사의 분리는 “애초에 무엇과 무엇을 같은 자리에 두지 말까”를 말한다. MVC, 계층형 아키텍처, 클린 아키텍처가 전부 이 원칙의 구체화다.

언어가 원칙을 품을 때 · POP

Swift의 프로토콜 지향 프로그래밍(POP)은 이 장의 원칙들을 언어 차원에서 구현한 한 사례다. 클래스 상속(강한 결합)을 피하고, 작은 프로토콜 + 값 타입의 조합으로 기능을 짓는다. “구현이 아니라 능력(인터페이스)에 의존하라”는 결합도·정보 은닉의 원칙이 문법이 된 형태다.

주의 · 경계도 비용이다

경계는 공짜가 아니다. 잘못 그은 경계는 없느니만 못하다 — 서로 얽힌 것을 억지로 갈라놓으면 인터페이스만 두꺼워지고 변경이 경계를 넘나든다(얕은 모듈, 나노서비스가 그 예다). 조기 추상화도 같다. 아직 변화의 축이 안 보이면 성급히 나누기보다 뭉쳐 두고 기다리는 편이 낫다. “언제 나눌지”는 “어떻게 나눌지”만큼 중요하다.

한 줄 요약바뀔 결정을 숨겨(정보 은닉) 좁은 인터페이스·깊은 구현(깊은 모듈)으로 만들면, 낮은 결합·높은 응집이 따라온다.
03 · 판단 · 20분

경계를 어디에 긋나

모듈이 좋은 도구라는 걸 알았다. 하지만 진짜 어려운 질문이 남았다: 선을 정확히 어디에 그을 것인가? 같은 시스템도 경계를 어디에 두느냐에 따라 천국과 지옥이 갈린다. 이 장의 세 개념은 “경계선을 긋는 세 가지 판단 기준”이다.

Evans · Domain-Driven Design
바운디드 컨텍스트 — 용어가 통하는 범위

대규모 시스템에서 가장 흔한 함정: “고객(Customer)”이라는 하나의 단어를 온 시스템이 공유하려 든다. 하지만 영업팀의 “고객”과 배송팀의 “고객”은 전혀 다른 것이다. 영업의 고객엔 신용등급·계약이, 배송의 고객엔 주소·수령 가능 시간이 중요하다. 이걸 하나의 거대 모델로 통합하려 하면 아무도 만족 못 하는 괴물이 태어난다.

Eric Evans의 바운디드 컨텍스트(Bounded Context)는 이렇게 답한다: 모델과 용어가 일관되게 통하는 명시적 경계를 그어라. 경계 안에서 “고객”은 하나의 뜻만 갖는다. 경계를 넘을 때는 번역(context map)을 거친다. 즉 경계는 물리적 코드 분할이기 전에 “말이 통하는 범위”다.

영업 컨텍스트 고객 (Customer) · 신용 등급 · 계약 이력 · 영업 담당자 배송 컨텍스트 고객 (Customer) · 배송 주소 · 수령 가능 시간 · 부재 시 정책 번역 context map
FIG. 6같은 단어 “고객”이 두 컨텍스트에서 완전히 다른 모델을 가리킨다. 억지로 합치지 않고, 각자 일관된 경계를 유지한 채 경계에서만 번역한다. 마이크로서비스의 서비스 경계도 대개 이 컨텍스트 경계를 따른다.

Martin · Clean Architecture · SOLID
의존성 규칙 — 방향이 전부다

경계를 그었다면, 그 경계를 어느 방향으로 넘게 할 것인가가 남는다. Robert C. Martin의 클린 아키텍처는 이를 하나의 규칙으로 압축한다 — 의존성 규칙(Dependency Rule): 소스 코드 의존성은 항상 바깥(세부)에서 안쪽(정책)으로만 향한다.

동심원을 상상하자. 중심엔 가장 안정적이고 추상적인 업무 규칙(엔티티·유스케이스)이, 바깥으로 갈수록 잘 바뀌는 세부(DB·웹 프레임워크·UI)가 있다. 규칙: 안쪽은 바깥쪽의 존재를 몰라야 한다. 데이터베이스나 프레임워크는 “세부사항”일 뿐이며, 핵심 업무 규칙이 그것들에 의존해선 안 된다. 이렇게 하면 DB를 갈아치우거나 웹을 CLI로 바꿔도 중심은 무사하다.

엔티티 핵심 업무 규칙 유스케이스 인터페이스 어댑터 프레임워크 · DB · UI (세부) 의존성은 사방에서 안쪽(정책)으로만 향한다
FIG. 7바깥 원(잘 바뀌는 세부)이 안쪽 원(안정적 정책)에 의존한다. 절대 반대가 아니다. 안쪽은 바깥의 이름조차 모른다 — 그래서 세부를 교체해도 중심 규칙은 그대로다. 이를 지탱하는 게 SOLID, 특히 의존성 역전 원칙(DIP)이다.

이 규칙을 코드에서 지탱하는 도구가 SOLID 5원칙, 그중에서도 의존성 역전(DIP)이다: 구체 구현이 아니라 추상(인터페이스)에 의존하라. 그러면 화살표의 방향을 뒤집어 “안쪽이 정의한 인터페이스를 바깥이 구현”하게 만들 수 있다. 결국 이것도 정보 은닉의 확장 — 세부라는 “비밀”을 안정적 인터페이스 뒤로 숨기는 것이다. 같은 아이디어를 먼저 정식화한 것이 육각형 아키텍처(Ports & Adapters, Cockburn 2005)어니언 아키텍처이며, 클린 아키텍처는 이 계보를 종합한 이름표에 가깝다.

Saltzer, Reed & Clark 1984
종단 간 논증 — 기능을 어느 계층에 둘까

마지막 판단 기준은 네트워크·시스템 설계의 고전 종단 간 논증(End-to-End Argument)이다. 질문은 이렇다: 어떤 기능(예: 오류 검출·재전송)을 낮은 계층(네트워크)에 둘까, 종단(애플리케이션)에 둘까?

논증의 결론: 정확성을 완전히 책임질 수 있는 것은 종단뿐이므로, 그 기능은 종단에 두어야 한다. 중간 계층이 아무리 신뢰성 있게 데이터를 날라도, “내가 보낸 파일이 온전히 도착했는가”는 결국 양 끝의 애플리케이션이 검증해야만 보장된다. 낮은 계층에서 완벽을 추구하는 것은 대개 비용만 늘리고 정확성은 여전히 종단에서 다시 확인해야 한다.

이 원칙은 “기능을 아무 데나 두지 말고, 그것을 온전히 책임질 수 있는 곳에 두라”는 경계 배치의 판단 기준으로 일반화된다. 인터넷이 “멍청한 네트워크 + 똑똑한 종단” 구조로 설계된 사상적 뿌리이기도 하다.

한 줄 요약말이 통하는 범위로 경계를 긋고(바운디드 컨텍스트), 의존성은 정책 쪽으로만 흐르게 하며(의존성 규칙), 기능은 그것을 책임질 수 있는 곳에 둔다(종단 간 논증).
04 · 코드 바깥의 힘 · 18분

사람과 시간이라는 힘

지금까지는 코드 안의 이야기였다. 하지만 아키텍처는 진공에서 만들어지지 않는다. 그것을 만드는 사람(조직)과, 그것이 살아가는 시간이라는 두 힘이 구조를 끊임없이 밀고 당긴다. 이 힘을 모르면 “왜 우리 설계는 자꾸 이 모양이 되는가”를 영원히 이해하지 못한다.

Conway 1968 · Datamation
콘웨이의 법칙 — 조직이 구조를 복사한다

Melvin Conway의 관찰은 반세기가 지나도 무섭도록 정확하다: 시스템을 설계하는 조직은, 그 조직의 소통 구조를 그대로 닮은 시스템을 만든다. 세 팀이 만들면 세 덩어리짜리 시스템이 나온다. 팀 사이의 소통이 어려우면, 그 팀들이 만든 모듈 사이의 인터페이스도 어색하고 두꺼워진다.

조직 (소통 구조) 팀 A 팀 B 팀 C 구조가 복사된다 시스템 (모듈 구조) 서비스 A 서비스 B 서비스 C
FIG. 8팀 간 소통 구조(위)가 시스템 모듈 구조(아래)로 그대로 찍혀 나온다. 그래서 “원하는 아키텍처가 있으면 먼저 조직을 그 모양으로 바꾸라”는 역(逆) 콘웨이 전략이 나온다. 아키텍처 결정은 곧 조직 설계다.

실무적 함의는 강력하다. 원하는 시스템 구조가 있다면 먼저 조직을 그 모양으로 만들어라(inverse Conway maneuver). 마이크로서비스가 유행한 데는 “작고 자율적인 팀”이라는 조직 형태가 그런 시스템을 자연스럽게 낳기 때문이라는 배경이 있다.

Brooks · The Mythical Man-Month
개념적 무결성 — 하나의 마음처럼

여러 사람이 만들면 필연적으로 생기는 문제: 시스템이 여러 개의 서로 다른 아이디어가 기워진 누더기가 된다. Brooks는 이에 맞서 개념적 무결성(Conceptual Integrity)을 최고의 설계 가치로 꼽는다.

“개념적 무결성은 시스템 설계에서 가장 중요한 고려사항이다. 서로 무관한 좋은 아이디어를 여럿 담기보다 — 설령 일부 기능을 덜어내더라도 — 하나의 설계 사상을 일관되게 반영한 시스템이 낫다.”

— Fred Brooks, The Mythical Man-Month (원문 의역)

즉 시스템은 마치 한 사람의 마음에서 나온 것처럼 일관된 개념 모델을 가져야 한다. 이를 위해 Brooks는 소수의 “아키텍트”가 개념적 일관성을 지키고, 나머지는 그 비전을 충실히 구현하는 역할 분담을 제안했다. 사용자가 시스템을 배우기 쉬운 이유의 8할은 기능의 많고 적음이 아니라 이 일관성이다.

Ford, Parsons & Kua
진화적 아키텍처 — 시간을 설계에 넣다

마지막 힘은 시간이다. 과거의 아키텍처는 “처음에 완벽히 설계하고 얼지 말라”는 것이었지만, 요구사항이 끊임없이 바뀌는 현실에서 이는 환상이다. 진화적 아키텍처(Evolutionary Architecture)는 관점을 뒤집는다: 변경을 예외가 아니라 상수(常數)로 받아들이고, 유도된 점진적 변경을 지원하도록 설계하라.

핵심 도구는 적합도 함수(fitness function)다. “이 아키텍처가 지켜야 할 특성”(예: 응답시간 100ms 이하, 서비스 간 순환 의존 0, 보안 규칙 준수)을 자동으로 측정 가능한 테스트로 만들어 CI에 걸어둔다. 그러면 시스템이 진화하면서도 중요한 특성을 잃지 않았는지 계속 검증된다. 아키텍처가 “문서”에서 “계속 실행되는 검증”으로 바뀌는 것이다.

점진적 변경 적합도 함수자동 측정 유도된 방향 피드백 — 특성을 잃지 않았는지 계속 검증
FIG. 9변경 → 적합도 함수로 측정 → 방향 유도 → 다시 변경. 진화적 아키텍처는 아키텍처의 중요한 특성을 실행되는 테스트로 못박아, 시스템이 시간에 따라 안전하게 변하도록 만든다.
한 줄 요약조직이 구조를 낳고(콘웨이), 하나의 일관된 개념이 시스템을 이해 가능하게 하며(개념적 무결성), 적합도 함수가 시간 속 변화를 안전하게 만든다(진화적 아키텍처).
05 · 언어 · 17분

아키텍처를 기술하고 평가하기

구조를 만들 줄 아는 것과, 그 구조를 설명하고 남과 비교·평가할 줄 아는 것은 다른 능력이다. 이 장은 아키텍처를 논하는 공용어 — 스타일과 품질 속성 — 를 준다. 이것이 있어야 “왜 이 구조를 골랐는가”를 근거 있게 말할 수 있다.

Fielding · REST의 뿌리
아키텍처 스타일 — 제약이 품질을 만든다

아키텍처 스타일(Architectural Style)이란 특정 품질을 얻기 위해 선택한 제약(constraint)의 집합이다. 놀라운 통찰은 방향이다: 자유를 늘리는 게 아니라 제약을 걸어서 원하는 특성을 얻는다.

Roy Fielding의 박사 논문이 정의한 REST가 교과서적 예다. Fielding은 “REST란 이런 API다”라고 시작하지 않았다. 대신 “확장성·독립적 진화·캐시 가능성” 같은 원하는 품질에서 출발해, 그것을 얻기 위한 제약(무상태, 통일된 인터페이스, 클라이언트-서버 분리, 캐시 가능성…)을 하나씩 도출했다. REST는 규격이 아니라 제약을 도출하는 방법론인 셈이다.

Style

계층형(Layered)

관심사를 수직 계층으로. 이해·교체 쉬움 ↔ 계층 통과 비용.

Style

파이프-필터

데이터가 필터를 통과. 조합·재사용 쉬움. 유닉스 파이프.

Style

이벤트 기반

느슨한 결합·확장성 ↔ 흐름 추적이 어려움.

Style

마이크로서비스

독립 배포·팀 자율 ↔ 분산의 복잡성·운영 비용.

Style

모놀리스

단순·빠른 개발 ↔ 규모가 커지면 결합·배포 부담.

Style

REST

제약(무상태·통일 인터페이스)에서 확장성·진화성을 도출.

어떤 스타일도 공짜가 아니다. 각 제약은 어떤 품질을 얻는 대신 다른 품질을 포기한다. 그래서 “좋은 스타일”은 없고 “이 맥락에 맞는 스타일”만 있다. 그 맥락을 판단하려면 다음 개념이 필요하다.

분산으로 가면 · 공짜가 아닌 정도가 아니다

서비스를 네트워크로 쪼개는 순간(마이크로서비스), 단일 프로세스엔 없던 근본 제약이 생긴다. CAP 정리(Brewer): 네트워크 분단(Partition)이 불가피한 분산 시스템에선 일관성(Consistency)과 가용성(Availability)을 동시에 완벽히 지킬 수 없다 — 상황에 맞게 하나를 양보해야 한다. 게다가 “네트워크는 신뢰할 수 있다·지연은 0이다·대역폭은 무한하다” 같은 분산 컴퓨팅의 8가지 오류를 무심코 가정하는 순간 시스템은 조용히 깨진다. 분산은 확장성을 사는 대신 이 복잡도를 지불하는 거래다.

SEI · Software Architecture in Practice
품질 속성 — 아키텍처의 진짜 요구사항

흔한 오해: 아키텍처는 “기능”을 위해 존재한다. 아니다. 기능은 대개 어떤 구조로도 구현할 수 있다. 아키텍처를 진짜로 결정짓는 것은 품질 속성(Quality Attributes) — 즉 비기능 요구사항이다. 성능, 가용성, 수정 용이성, 보안, 확장성, 테스트 용이성… SEI(카네기멜런 소프트웨어 공학 연구소)의 정리에 따르면 아키텍처란 이 품질 속성들 사이의 트레이드오프를 내린 결정의 집합이다.

성능 가용성 확장성 수정용이성 테스트 보안 모놀리스 마이크로서비스 같은 문제, 다른 트레이드오프
FIG. 10두 아키텍처는 같은 품질 축 위에서 다른 모양을 그린다. 모놀리스는 성능·단순성에서, 마이크로서비스는 확장성·가용성에서 앞선다(단, 여러 서비스에 걸친 변경은 오히려 더 어렵다). 아키텍처 평가(예: SEI의 ATAM)는 바로 이 트레이드오프가 우리 맥락의 우선순위와 맞는지를 따지는 일이다.

이 관점을 얻으면 설계 토론이 달라진다. “마이크로서비스가 좋아요, 모놀리스가 좋아요?” 같은 질문은 성립하지 않는다. 올바른 질문은 “우리 맥락에서 어떤 품질 속성이 최우선이고, 무엇을 포기할 수 있는가?”다. 아키텍처 결정은 언제나 무언가를 얻고 무언가를 내주는 거래다.

한 줄 요약스타일은 원하는 품질을 얻기 위한 제약의 집합이고(REST가 그 방법론의 예), 아키텍처란 품질 속성 간 트레이드오프를 내린 결정이다.
06 · 종합 · 6분

하나의 지도로 묶기

지금까지 15개의 개념을 지나왔다. 이제 처음의 명제로 돌아가 전체를 하나의 지도로 접는다. 모든 개념은 결국 “복잡도를 모듈 경계로 다스린다”는 중심 문제의 다섯 갈래다.

The one problem
복잡도를 모듈 경계로 다스린다

① 적: 복잡도의 본질

무엇과 싸우는가. 줄일 수 있는 것과 없는 것.

본질 vs 우발 복잡도개념적 무결성

② 모듈 경계의 원리

한 모듈을 어떻게 잘 만드는가.

정보 은닉깊은 모듈결합·응집모듈성관심사 분리POP

③ 경계를 어디에 긋나

선을 어디에, 어느 방향으로.

바운디드 컨텍스트의존성 규칙종단 간 논증

④ 조직과 시간

코드 바깥에서 구조를 미는 힘.

콘웨이의 법칙진화적 아키텍처

⑤ 기술·평가

구조를 설명하고 비교하는 언어.

아키텍처 스타일품질 속성

이 지도의 아름다움은 화살표들이 서로를 가리킨다는 데 있다. 정보 은닉(②)을 잘하면 낮은 결합·높은 응집이 따라오고, 그 경계를 컨텍스트(③)로 그으면 의존성 규칙(③)이 방향을 잡아준다. 그런데 그 경계는 결국 조직 구조(④)를 닮고, 시간이 지나면 적합도 함수(④)로 지켜야 하며, 그 모든 선택은 품질 속성(⑤) 간의 트레이드오프다. 하나를 당기면 나머지가 딸려 온다 — 정전들이 서로를 끊임없이 인용하는 이유다.

한 문장으로 다시

좋은 아키텍처란, 바뀔 것을 경계 뒤에 숨겨 복잡도를 국소화하고, 그 경계를 조직·시간·품질 요구에 맞게 배치한 구조다. 나머지는 전부 이 문장의 각주다.

07 · 다음 걸음 · 6분

더 깊이 — 먼저 읽어야 할 세 원전

이 자료는 지형을 훑는 지도였다. 이제 실제 땅을 밟을 차례다. 60여 편의 정전 중, 딱 세 개만 먼저 읽는다면 이 순서를 권한다 — 나머지 열두 개념의 뿌리이기 때문이다.

01
On the Criteria To Be Used in Decomposing Systems into ModulesParnas, 1972 · 논문 12p

모든 모듈 이론의 원전. “처리 순서가 아니라 바뀔 결정으로 나누라.” 짧아서 실제로 완독할 수 있고, 읽고 나면 세상의 코드가 다르게 보인다. — 2장 정보 은닉의 출처.

02
A Philosophy of Software DesignOusterhout · 단행본

Parnas의 현대적 계승. 깊은 모듈, 복잡도의 3증상, 전술 vs 전략 프로그래밍을 실전 감각으로 풀어낸다. 이 분야에서 가장 실용적인 한 권. — 1·2장의 뼈대.

03
Out of the Tar PitMoseley & Marks, 2006 · 논문

“복잡도를 줄여라”를 “상태와 제어를 줄여라”로 번역한 현대 고전. 설계 토론에서 가장 많이 인용된다. Brooks의 No Silver Bullet과 함께 읽으면 완성된다. — 1장의 핵심.

이 셋을 소화한 뒤엔 관심 갈래를 따라 넓히면 된다: 경계가 궁금하면 Evans의 Domain-Driven Design과 Martin의 Clean Architecture로, 조직·시간이 궁금하면 Team TopologiesBuilding Evolutionary Architectures로, 평가 언어가 궁금하면 SEI의 Software Architecture in Practice로.

읽기와 정리를 함께

새 자료를 읽을 때마다 흩어진 메모로 남기지 말고, 개념 단위로 누적하자. 각 원전이 이 자료의 어느 개념에 속하는지 이미 지도가 있으니, 새로 읽은 사실을 그 개념 위에 덧붙이면 지식이 복리로 쌓인다.