건축가에게 도면 한 장을 건네면 그는 곧바로 안다. 이건 단독주택이고, 저건 도서관이며, 또 저건 병원이다. 도면에는 그렇게 적혀 있지 않아도, 방의 배치와 동선과 크기가 그 건물이 무엇을 위한 곳인지를 소리쳐 알려 준다. 도서관의 도면 한가운데에는 열람실과 서가가 자리 잡고, 현관과 화장실과 배관은 그 주변으로 물러나 있다. 도면을 보고 “이건 벽돌로 지었군”이라고 먼저 읽는 사람은 없다. 벽돌은 세부사항이고, 도서관이라는 용도가 주인공이다.
마틴이 이 장에서 던지는 질문은 여기서 온다 — 당신 시스템의 최상위 디렉터리를 열었을 때, 그것은 무엇을 외치는가. 폴더 이름들이 Controllers, Views, Models, Config, 그리고 프레임워크 이름들로 채워져 있다면, 그 아키텍처가 외치는 것은 “나는 스프링으로 지은 웹 애플리케이션입니다”이다. 정작 이 시스템이 의료 기록을 관리하는지, 회계를 처리하는지, 재고를 추적하는지는 어디에도 소리치지 않는다. 도서관인지 병원인지 알 수 없는 도면 — 그것이 오늘날 대부분의 소프트웨어가 가진 문제다.
아키텍처는 유스케이스를 외쳐야 한다
좋은 아키텍처는 그 시스템의 의도를 중심에 세운다. 건강 관리 시스템을 열면 최상위에 진료·처방·환자관리 같은 유스케이스가 보여야 하고, 재고 시스템을 열면 입고·출고·재고조정이 보여야 한다. 이것들은 이 시스템이 존재하는 이유이며, 이해관계자가 돈을 지불하고 사려는 바로 그 행위다. 아키텍처의 형태는 이 유스케이스를 지원하는 데서 나와야 하지, 도구를 담는 상자의 모양에서 나와서는 안 된다.
20장에서 우리는 업무 규칙과 유스케이스를 시스템의 고수준 정책으로 끌어올렸다. 21장은 그 정책이 아키텍처의 표면에까지 드러나야 한다고 말한다. 안쪽에 유스케이스를 두는 것으로 끝이 아니라, 시스템을 처음 열어 본 사람이 디렉터리 구조만 보고도 “아, 이건 대출 심사 시스템이구나”라고 읽어낼 수 있어야 한다. 구조가 의도를 숨기면, 그 의도는 코드 곳곳에 흩어져 아무도 전체 그림을 갖지 못한다.
마틴이 즐겨 쓰는 비유가 있다. 어느 훌륭한 건축가의 설계도를 보면 그 건물이 무엇인지 소리치지만, 그 도면 어디에도 “이 벽은 콘크리트인가 나무인가”는 당장 결정되어 있지 않다. 재료는 마지막까지 열어 둔 선택지다. 마찬가지로 좋은 소프트웨어 아키텍처는 유스케이스를 소리치되, 그것이 웹으로 배달될지 데스크톱 앱으로 배달될지, 데이터가 오라클에 담길지 몽고DB에 담길지는 아직 크게 외치지 않는다. 그 결정들은 미뤄 둔 채로도 시스템의 정체성은 또렷하다.
판단 기준: 시스템의 최상위 디렉터리 이름만 훑었을 때, 프레임워크 이름이 아니라 이 시스템이 하는 일(유스케이스)이 먼저 읽히면 아키텍처가 제 목소리를 내고 있는 것이다. 함정: 프레임워크가 권하는 표준 폴더 구조(controllers/, models/, views/)를 그대로 최상위에 얹으면, 아키텍처는 자기 정체성이 아니라 도구의 정체성을 외치게 된다 — 편해서 따랐을 뿐인데 시스템의 얼굴을 도구에게 내준 셈이다.
웹은 전달 메커니즘이다
웹이 등장했을 때 많은 이가 그것을 아키텍처의 중심으로 착각했다. 하지만 웹은 입출력 장치의 한 종류일 뿐이다. 1960년대에 콘솔이, 그다음에 데스크톱 GUI가 그랬듯, 웹도 사용자에게 기능을 전달하는 방식 중 하나다. 마틴의 단어를 빌리면 웹은 **전달 메커니즘(delivery mechanism)**이며, 전달 메커니즘은 세부사항이다.
이 구별이 왜 중요한가. 만약 업무 규칙이 “이것은 웹 요청으로 들어온다”는 사실에 얽매여 짜여 있다면, 내일 같은 규칙을 배치 작업으로, 혹은 모바일 앱의 로컬 처리로, 혹은 다른 시스템이 호출하는 API로 제공하려 할 때 규칙 자체를 뜯어고쳐야 한다. 반대로 업무 규칙을 웹으로부터 완전히 갈라 두었다면, 웹은 규칙 바깥에 꽂힌 얇은 어댑터가 되고, 전달 방식이 바뀌어도 규칙은 미동도 하지 않는다. 은행의 대출 심사 로직이 HTTP 요청 객체를 직접 읽고 있어서는 안 된다. 그 로직은 “얼마를, 몇 개월, 어떤 신용등급”이라는 순수한 입력만 알아야 하고, 그 입력이 웹에서 왔는지 파일에서 왔는지는 몰라야 한다.
31장에서 마틴은 이 주제를 “웹은 세부사항이다”라는 제목으로 다시 깊이 파고든다. 21장에서는 그 결론을 아키텍처의 형태라는 관점에서 미리 심어 둔다 — 시스템을 웹 애플리케이션으로 설계하지 마라. 유스케이스를 지원하는 시스템으로 설계하고, 웹은 그 시스템에 붙이는 창구 하나로 다뤄라.
판단 기준: 업무 규칙 코드에서 HTTP 요청·세션·라우팅 같은 웹 개념이 등장한다면, 전달 메커니즘이 규칙 안으로 새어 들어온 것이다 — 그 경계를 다시 그어야 한다. 함정: “우리는 웹 서비스니까 웹이 곧 우리 아키텍처”라는 생각. 웹은 오늘의 창구일 뿐이고, 창구의 모양을 건물의 골조로 착각하면 창구가 바뀔 때 건물째 무너진다.
프레임워크는 열어 둘 선택지이지 아키텍처가 아니다
프레임워크는 강력한 도구다. 그러나 프레임워크 저자와 당신의 관계는 비대칭이다. 그는 당신의 시스템 사정을 알지 못하고 알 이유도 없다. 그가 권하는 사용법은 대개 “우리 프레임워크를 상속하라, 우리 기반 클래스를 확장하라, 우리 애너테이션으로 당신의 핵심 객체를 장식하라”이다. 그 권유를 그대로 따르는 순간, 당신의 업무 객체는 프레임워크에 결혼해 버린다.
문제는 결혼에는 이혼 비용이 따른다는 것이다. 프레임워크의 새 버전이 호환을 깨거나, 더 나은 대안이 나타나거나, 그 프레임워크가 당신이 정말 필요한 방향으로는 끝내 진화하지 않을 때, 핵심 업무 규칙이 프레임워크를 상속하고 있다면 규칙을 통째로 들고 나올 수 없다. 그래서 마틴의 처방은 단호하다 — 프레임워크를 쓰되, 그것과 결혼하지 마라. 프레임워크를 아키텍처의 바깥 원에 밀어 두고, 핵심 업무 객체가 그것을 상속하거나 의존하지 않게 하라. 프레임워크는 미뤄 둘 결정, 즉 열어 둔 선택지여야지 시스템의 뼈대가 되어서는 안 된다.
15장에서 아키텍처의 목적 중 하나가 “선택지를 최대한 열어 두는 것”이라고 했던 것을 떠올려 보자. 프레임워크를 아키텍처의 중심에 앉히는 것은 그 반대다 — 아직 되돌릴 수 있었던 결정을, 되돌리기 어려운 골조로 굳혀 버리는 일이다. 좋은 아키텍트는 프레임워크 선택을 최대한 늦게, 그리고 최대한 얕게 만든다. 얕게란, 프레임워크가 시스템의 표면 한 겹에만 닿고 핵심까지 스며들지 않게 한다는 뜻이다.
판단 기준: 핵심 업무 객체가 프레임워크의 기반 클래스를 상속하거나 프레임워크 애너테이션 없이는 동작하지 않는다면, 열어 둬야 할 선택지를 골조로 굳힌 것이다. 함정: 프레임워크가 제공하는 “빠른 시작”의 편의는 초기에 달콤하지만, 그 편의는 대개 당신의 핵심 코드를 프레임워크 안으로 끌어들이는 대가로 주어진다 — 속도를 샀다고 생각한 자리에서 실은 결합을 산 것이다.
미뤄 둔 결정이 테스트 용이성을 돌려준다
유스케이스를 중심에 세우고 웹·프레임워크·데이터베이스를 바깥으로 밀어내면, 뜻밖의 보상이 따라온다. 시스템이 테스트하기 쉬워진다. 이것은 우연이 아니라 구조의 필연적 결과다.
핵심 업무 규칙이 웹 서버 없이는, 데이터베이스 연결 없이는, 프레임워크의 실행 컨텍스트 없이는 돌아가지 않는다면, 그 규칙을 검증하려면 매번 그 무거운 장치들을 다 띄워야 한다. 테스트는 느려지고 불안정해지며, 결국 아무도 자주 돌리지 않는다. 반대로 유스케이스가 순수한 입력을 받아 순수한 출력을 내는 독립된 객체라면, 웹도 DB도 없이 그 자리에서 즉시 테스트할 수 있다. 대출 심사 규칙에 “3천만 원, 24개월, 신용등급 2등급”을 넣으면 승인/거절이 나온다 — 서버를 띄울 필요도, 테스트용 DB를 준비할 필요도 없다.
마틴은 이 점을 아키텍처의 시금석으로 삼는다. 당신의 유스케이스를 프레임워크와 무관하게 단위 테스트할 수 있는가. 그렇다면 프레임워크는 제대로 바깥에 있는 것이다. 그렇지 못하다면, 프레임워크가 이미 핵심 안으로 스며들었다는 신호다. 이런 의미에서 테스트 용이성은 좋은 아키텍처를 얻기 위해 따로 노력해야 하는 별도의 덕목이 아니라, 유스케이스를 소리치는 구조가 이미 갖춰졌는지를 비추는 거울이다. 23장에서 다룰 험블 객체 패턴이 이 거울을 더 정교하게 다듬지만, 그 씨앗은 여기 21장에 있다.
판단 기준: 핵심 유스케이스를 웹 서버·데이터베이스·프레임워크 없이 단위 테스트로 검증할 수 있으면, 세부사항이 제대로 바깥에 밀려나 있는 것이다 — 테스트 용이성은 아키텍처 상태를 읽는 진단 도구다. 함정: 테스트가 매번 느린 통합 환경을 요구한다면, 그것을 “원래 테스트란 무거운 것”이라 받아들이지 말 것 — 그 무거움은 세부사항이 핵심에 엉겨 붙었다는 증상이지 테스트의 본성이 아니다.
그래서, 무엇을 소리쳐야 하는가
정리하면 이 장은 아키텍처를 향한 하나의 태도를 요구한다. 시스템을 지을 때 “무엇으로 짓는가”(웹, 프레임워크, DB)를 먼저 정하고 그 위에 기능을 얹지 말고, “무엇을 하는가”(유스케이스)를 먼저 세우고 도구는 나중에, 바깥에, 미뤄 둔 채로 꽂아라. 도서관의 도면이 벽돌이 아니라 도서관을 소리치듯, 당신의 아키텍처는 스프링이 아니라 당신이 푸는 문제를 소리쳐야 한다.
- 좋은 아키텍처는 프레임워크가 아니라 유스케이스를 중심에 세운다 — 최상위 구조가 시스템이 무엇을 하는 시스템인지를 외쳐야 한다.
- 웹은 전달 메커니즘, 즉 입출력 장치의 한 종류일 뿐이다. 업무 규칙을 웹으로부터 갈라 두면 창구가 바뀌어도 규칙은 흔들리지 않는다.
- 프레임워크는 열어 둘 선택지이지 골조가 아니다. 핵심 객체가 프레임워크를 상속·의존하지 않게 하여 결혼을 피하고, 결정을 미룬다.
- 유스케이스를 중심에 세우고 세부사항을 미룬 구조는 덤으로 테스트 용이성을 돌려준다 — 웹도 DB도 없이 규칙을 검증할 수 있는지가 아키텍처의 시금석이다.
이렇게 유스케이스를 소리치는 구조를 손에 넣었다면, 남은 질문은 그것을 어떤 형태로 조직하느냐다. 안쪽에는 규칙을, 바깥쪽에는 세부사항을 두라는 원칙을 하나의 그림으로 그려낼 수 있을까. 다음 장은 그 그림 — 동심원과 의존성 규칙 — 을 통해 이 장의 태도를 완성된 설계도로 옮긴다.
다음장으로 22장