Skip to Content

Step 12. 아키텍처는 완성이 아니라 방어다 — 진화적 아키텍처

만화로 보는 요약 — 먼저 읽어보세요

만화로 보는 요약 — 먼저 읽어보세요

아키텍처는 세워두면 침식되므로, 품질 속성 주장을 적합도 함수로 자동 실행해 경계를 깨지게 만들고 결정을 가능한 늦춘다.

면접 실전 질문: ① 아키텍처 침식이 왜 일어나는가? ② 적합도 함수란 무엇인가? ③ 최종 책임 시점은 왜 중요한가?


이 장의 질문: 여기까지 배운 모든 경계와 규율은 세워둔 그날은 완벽하다. 6개월 뒤에도 그럴까? 그리고 지키는 일을 사람의 선의에 맡기지 않으려면 무엇이 필요한가?


1. 세워둔 날이 정점인 구조물

Part 2에서 우리는 경계를 세우는 법을 배웠다 — 도메인 주위에(헥사고날), 컨텍스트 사이에(DDD), 데이터 소유권에(앞 장). 그런데 이 책의 첫 장에서 이미 배운 불편한 단어 둘이 있다. Perry와 Wolf(1992)가 명명한 침식(erosion) — 구현이 아키텍처의 의도를 조금씩 위반하며 무너지는 것 — 과 표류(drift) — 위반은 아니지만 의도가 흐려지는 것.

침식은 예외가 아니라 기본값이다. 근거는 이미 다 나와 있다. Lehman 제2법칙(Step 05): 복잡성은 줄이는 작업을 따로 하지 않는 한 증가한다. Conway(Step 09): 다이어그램과 조직이 싸우면 방치할 경우 조직이 이긴다. 그리고 현장의 미시 역학: 마감에 몰린 개발자가 “이번만” 도메인에서 인프라를 직접 import한다. 리뷰어는 놓친다. 두 달 뒤 그 코드가 선례가 된다. 여섯 달 뒤 의존성 규칙은 위키에만 존재한다 — 다이어그램은 위반해도 컴파일되기 때문이다.

전통적 방어선 — 아키텍처 문서, 코드 리뷰, 신입 교육 — 의 공통 약점은 전부 사람의 주의력에 기댄다는 것이다. 주의력은 마감 앞에서 가장 먼저 소진되는 자원이다. 필요한 것은 선의가 아니라 깨지는 것 — 위반하면 빌드가 빨간불이 되는, 자동화된 방어선이다.

2. 은유의 교체 — 건축에서 진화로

Step 01에서 예고한 장면이다: 건축 은유에는 유통기한이 있다. 건물은 완공되는 순간이 있지만 소프트웨어에는 없다 — 성공한 소프트웨어일수록 더 많이 바뀐다(Brooks의 변경 가능성). “설계 → 시공 → 완공”이라는 건축의 서사가 소프트웨어에서 성립하지 않는다면, 은유를 바꿔야 한다.

Ford, Parsons, Kua의 『Building Evolutionary Architectures』(2017)가 제안한 은유는 진화다. 그들의 정의:

진화적 아키텍처란, 여러 차원에 걸친 유도된(guided) 점진적(incremental) 변경을 일급 관심사로 지원하는 아키텍처다.

단어 두 개가 하중을 받는다. 점진적 — 빅뱅 재설계가 아니라 작은 변경의 연속으로 진화한다(Step 05의 Piecemeal Growth가 규율을 얻은 것). 그리고 유도된 — 진화는 방향타 없이 일어나지 않는다. 생물의 진화에 자연선택이라는 압력이 있듯, 아키텍처의 진화에는 “무엇이 적합한가”를 판정하는 인공의 선택압이 필요하다. 그 선택압의 이름이 이 장의 핵심 개념이다.

3. 적합도 함수 — 측정 언어의 자동화

적합도 함수(fitness function). 유전 알고리즘에서 빌려온 용어로, Ford와 동료들의 정의는 “아키텍처 특성(들)의 무결성을 객관적으로 평가하는 장치”다. Step 04에서 심어둔 복선이 여기서 회수된다 — 품질 속성 시나리오가 측정 가능한 문장이었다면, 적합도 함수는 그 문장을 자동 실행되는 검사로 만든 것이다.

구체적으로 어떤 모습인가.

  • 의존성 규칙의 테스트. “도메인은 인프라를 import하지 않는다”(헥사고날의 의존성 규칙), “모듈 간 순환 금지”(ADP) — 이런 규칙을 단위 테스트처럼 실행한다. 다음 절의 ArchUnit이 이것이다.
  • 성능 예산. “주문 API p99 500ms”(Step 04의 그 시나리오)를 CI의 부하 테스트나 운영 알림으로 상시 감시한다. 넘으면 빌드/알림이 깨진다.
  • 계약 테스트. 서비스 간 API 계약(소비자 주도 계약, Pact류)을 자동 검증 — 제공자(provider)가 계약을 깨면 소비자의 빌드가 아니라 제공자의 빌드가 깨진다.
  • 카오스 엔지니어링. Netflix의 Chaos Monkey — 운영 환경에서 무작위로 인스턴스를 죽여 “장애를 견딘다”는 가용성 주장을 상시 검증한다. Ford와 동료들이 즐겨 드는 예인데, 분류로는 전체적(holistic)·지속적(continual) 적합도 함수다(개별 규칙을 검사 시점에 확인하는 원자적·트리거드 함수와 대비).

공통 구조를 보라. 전부 “아키텍처에 대한 주장”을 “깨질 수 있는 실행물”로 바꾼 것이다. 문서는 낡지만 테스트는 낡지 않는다 — 낡으면 깨지니까.

4. 실무의 모습 — 아키텍처를 테스트하다

Java 생태계의 대표 도구인 ArchUnit으로, 이 책의 규칙들이 코드가 되는 모습을 보자.

@AnalyzeClasses(packages = "io.github.hyeonqz.settlement") class ArchitectureTest { @ArchTest // 헥사고날의 의존성 규칙: 도메인은 바깥을 모른다 static final ArchRule domain_depends_on_nothing_outside = noClasses().that().resideInAPackage("..domain..") .should().dependOnClassesThat() .resideInAnyPackage("..adapter..", "..infrastructure..", "org.springframework..", "jakarta.persistence.."); @ArchTest // ADP: 모듈 간 순환 금지 static final ArchRule no_cycles = slices().matching("..settlement.(*)..").should().beFreeOfCycles(); }

첫 번째 규칙이 깨졌다는 것은 “도메인이 세부사항의 인질이 되기 시작했다”(Step 06의 병)는 조기 경보이고, 두 번째는 “경계가 소멸하고 있다”(Step 03의 ADP)는 경보다. 마감에 몰린 “이번만”은 이제 리뷰어의 주의력이 아니라 빨간 빌드와 협상해야 한다. 모듈러 모놀리스(Step 10)에서 “경계 위반이 빌드에서 깨진다”고 했던 그 규율의 실체가 이것이다 — Spring Modulith의 모듈 검증(ApplicationModules.of(App.class).verify())도 같은 계열의 도구다.

적합도 함수에 대한 정확한 기대치도 적어두자. 이것은 아키텍처의 하한선(floor)을 지키는 장치이지, 좋은 설계를 만들어주는 장치가 아니다. 어디에 경계를 긋고 무엇을 검사할지는 여전히 Part 1~2의 사고가 정한다 — 적합도 함수는 그 결정이 6개월 뒤에도 살아 있게 할 뿐이다. 그리고 규칙도 진화한다: 경계를 다시 그으면(컨텍스트 재편) 함수도 함께 고쳐야 한다. 함수는 결정의 자동화이지 결정의 화석화가 아니다.

정직한 한계도 하나 적어두자 — 적합도 함수도 꺼질 수 있다. 침식을 부르는 바로 그 마감 압력이 @Disabled와 주석 처리를 부른다. 그래서 최후의 방어선은 다시 규율이다: 함수를 끄는 것 자체를 아키텍처 결정으로 취급하고(기록과 리뷰의 대상), CI에서 우회할 수 없게 둔다.

5. 결정을 미루는 기술 — 그리고 Part 2의 결산

이 책에서 세 번 등장한 문장을 마지막으로 한 번 더 쓰자. 좋은 아키텍처는 되돌리기 비싼 결정을 신중히 먼저 내리고, 바뀔 법한 결정을 최대한 늦게 내릴 수 있게 한다(Step 02). 헥사고날은 DB 결정을 미뤘고(Step 08), 모듈러 모놀리스는 배포 토폴로지 결정을 미뤘다(Step 10). 린(lean) 진영은 이 원칙에 이름을 붙였다 — 최종 책임 시점(last responsible moment): 결정은 “더 미루면 선택지가 사라지는 마지막 순간”까지 미룬다. 일찍 정할수록 아는 것이 적은 채로 정하기 때문이다.

진화적 아키텍처는 이 연기(deferral)를 일회성 기법이 아니라 시스템의 상시 상태로 만든 것이다: 가역성을 구조에 넣고(작은 단위, 명확한 경계), 적합도 함수가 안전망을 치면, 큰 결정도 “나중에 바꿀 수 있는 결정”의 목록으로 내려온다. 되돌리기 비싼 것의 목록을 줄이는 것 — Step 01의 정의를 빌리면, 아키텍처라는 짐 자체를 줄이는 것이다.

이제 Part 2 전체를 결산하자. 우리가 걸어온 길 — 진흙공, 레이어드, DDD, 세 형제, 조직, 분산, 데이터 — 을 한 발 물러나 보면 방향이 하나다. 진화는 언제나 결합을 끊는 방향이었다. 층으로(레이어드), 의미로(컨텍스트), 화살표의 역전으로(헥사고날), 팀으로(Conway), 배포로(마이크로서비스), 소유권으로(데이터). 그러나 목적지는 없었다 — 끊을 때마다 새 고통이 태어났고(분산의 물리학), 진자는 돌아왔다. 정확히 갈라 적으면: 논리적 결합은 단조적으로 끊겨 왔지만, 물리적 배포 결합은 진동했다 — 모듈러 모놀리스는 바로 그 배포 축에서의 의도적 재결합이었다. 그래서 궤적이 원이 아니라 나선인 것이다. 그래서 이 책의 마지막 은유 교체는 이것이다: 아키텍처는 명사가 아니라 동사다. 세우는 것이 아니라 계속 지키고 고쳐 긋는 활동 — 완성이 아니라 방어.


6. 흔한 오해 바로잡기

오해 1: “진화적 아키텍처 = 계획 없이 그때그때.” 정의의 절반을 빠뜨린 것이다 — 유도된(guided) 변경. 적합도 함수라는 명시적 선택압이 없으면 그것은 진화가 아니라 표류(drift)이고, 도착지는 Step 05의 진흙공이다. 진화적 아키텍처는 계획의 부재가 아니라 계획의 형태 변경이다: 청사진 대신 방향타.

오해 2: “적합도 함수는 그냥 테스트의 다른 이름이다.” 대상이 다르다. 단위 테스트는 기능(“취소하면 상태가 CANCELLED”)을, 적합도 함수는 아키텍처 특성(“도메인은 인프라에 의존하지 않는다”, “p99 500ms”)을 검증한다. 기능 테스트가 다 통과해도 아키텍처는 침식될 수 있다 — 그 사각지대가 이 도구의 존재 이유다.

오해 3: “한 번 설정해두면 끝이다.” 함수 자체가 진화 대상이다. 경계를 다시 그으면 규칙도 다시 쓰고, 새 품질 속성이 중요해지면(트래픽 성장으로 성능 예산이 필요해지면) 함수를 추가한다. 낡은 규칙을 방치한 적합도 함수는 낡은 다이어그램과 같은 운명이 된다 — 다만 이쪽은 시끄럽게 낡는다는 점이 낫다(거짓 빨간불은 무시가 아니라 수정을 요구하게 되므로).

오해 4: “카오스 엔지니어링은 무모한 도박이다.” 통제된 실험이다 — 가설(“인스턴스 하나가 죽어도 응답한다”), 최소 폭발 반경, 즉시 중단 스위치. 무모한 쪽은 오히려 “장애를 견딘다”는 주장을 실제 장애가 날 때까지 한 번도 검증하지 않는 것이다.


7. 요약, 그리고 Part 3으로

  • 침식은 기본값이다(Lehman, Conway) — 문서·리뷰·선의는 주의력에 기대고, 주의력은 마감 앞에서 소진된다. 필요한 것은 깨지는 방어선.
  • 은유의 교체: 건축(완공)에서 진화(유도된 점진적 변경)로. 방향타가 적합도 함수 — 품질 속성 시나리오(Step 04)의 자동 실행판.
  • 실무: ArchUnit/Spring Modulith로 의존성 규칙·ADP를 테스트로, 성능 예산·계약 테스트·카오스 실험으로 나머지 특성을. 주장을 깨질 수 있는 실행물로. 단, 하한선 장치이지 설계 대체물이 아니다.
  • 최종 책임 시점: 결정은 마지막 책임 순간까지 미룬다. 진화적 아키텍처는 결정 연기를 상시 상태로 만들어, 되돌리기 비싼 결정의 목록 자체를 줄인다.
  • Part 2 결산: 진화는 언제나 결합을 끊는 방향이었다 — 그러나 목적지는 없다. 아키텍처는 명사가 아니라 동사다.

Part 3 예고: 이론은 끝났다. 이제 손이다. 같은 요구사항(주문/정산)을 레이어드 → 헥사고날 → 클린으로 세 번 구현하고, 변경 시나리오를 때려 diff가 어디서 폭발하는지 본다. 먼저 인정할 것: 작을 땐 레이어드가 이긴다. 그 다음, 언제부터 지기 시작하는지를 눈으로 확인한다 — architecture 모듈에서.


원전

연도문헌이 장에서의 역할
1992Perry & Wolf, Foundations for the Study of Software Architecture침식·표류의 명명 (Step 01 회수)
2003Poppendieck & Poppendieck, Lean Software Development최종 책임 시점(last responsible moment)
2011~Netflix, Chaos Monkey / Simian Army지속·전체적 적합도 함수의 실례
2017/2022Ford, Parsons & Kua, Building Evolutionary Architectures (1판/2판)진화적 아키텍처 정의, 적합도 함수
2017~ArchUnit / Spring Modulith아키텍처 규칙의 테스트화 도구
Last updated on