좋은 아키텍처를 설명할 때 레이어 그림부터 그리면 중요한 절반이 빠진다. 실제 프로젝트에서 구조를 망가뜨리는 건 폴더 이름 하나가 아니라, 사람들이 시간이 없을 때 자연스럽게 어떤 선택을 하게 되는가다.
2026년 5월 12일 matklad가 쓴 Learning Software Architecture는 이 지점을 잘 찌른다. 글은 소프트웨어 설계를 책이나 강의보다 실제 책임 속에서 배운다고 말하고, rust-analyzer 사례를 통해 기술 구조와 사회적 구조가 어떻게 맞물리는지 보여준다. GeekNews 요약에서도 이 글의 핵심을 Conway’s Law, 기여자 흐름, Karpenter가 아니라 rust-analyzer의 구조 선택으로 잘 압축했다.
실무용으로 한 줄만 남기면 이렇다. 아키텍처는 예쁜 박스 다이어그램이 아니라, 제한된 시간과 서로 다른 기여자들이 덜 위험한 선택을 하게 만드는 운영 환경이다.
이 글은 “아키텍처 책 추천” 글이 아니다. 팀 프로젝트, 오픈소스, AI 에이전트 하네스, 사내 자동화 도구를 굴릴 때 핵심 spine과 독립 기능을 어떻게 나눌지 보는 체크리스트다. 그림은 나중에 그려도 된다. 먼저 사람들이 어느 경로로 코드를 바꾸게 되는지 봐야 한다.
지금 결론
| 설계 질문 | 나쁜 출발점 | 더 나은 출발점 |
|---|---|---|
| 아키텍처를 어디서 배우나 | 강의, 책, 패턴 이름부터 외운다 | 실제 책임이 있는 프로젝트에서 실패 비용을 본다 |
| 구조가 왜 생기나 | 좋은 개발자가 좋은 코드를 짠다 | 조직과 인센티브가 코드 구조를 밀어낸다 |
| 기여자 수준이 다르면 | PR 기준을 하나로 통일한다 | 핵심 spine과 독립 기능의 기준을 다르게 둔다 |
| 실험 코드는 어떻게 다루나 | 나중에 정리하면 된다고 둔다 | 장기 현실로 굳을 위험을 의사결정 로그에 남긴다 |
| 좋은 설계의 목표 | 깔끔한 계층을 만든다 | 올바른 선택이 쉬운 환경을 만든다 |
matklad의 핵심 주장은 단순하다. 소프트웨어 설계는 책임이 붙은 실제 프로젝트에서 깊어진다. 대학 수업에서 아키텍트 역할을 맡는 것보다, IntelliJ Rust처럼 설계 문제가 자기 일이 되는 순간 배움이 시작됐다는 이야기다.
이건 TECHTAEK 글감으로 꽤 중요하다. AI 코딩 에이전트, 하네스, 자동화 팀을 만들 때도 같은 문제가 반복된다. 프롬프트를 잘 짜는 것보다 더 중요한 건, 나중에 사람이 피곤할 때도 위험한 변경이 핵심 경로로 번지지 않게 만드는 구조다.
Conway’s Law를 코드 밖 이야기로만 두면 놓친다
Conway’s Law는 보통 “조직 구조가 소프트웨어 구조를 닮는다” 정도로 소비된다. 그런데 이 말을 진짜로 쓰려면 조직도 그림이 아니라 인센티브를 봐야 한다. 누가 어떤 압박을 받는가, 누가 유지보수 책임을 지는가, 누가 빠르게 논문이나 기능을 내야 하는가가 코드의 모양을 만든다.
matklad는 산업 소프트웨어와 과학 코드의 차이도 단순한 지식 차이가 아니라 인센티브 구조에서 생길 수 있다고 본다. 3개월 안에 논문을 내야 하는 상황과, 몇 년 동안 프로덕션 장애를 감당해야 하는 상황은 완전히 다른 코드를 만든다. 코드는 손가락으로 쓰지만, 손가락을 움직이는 건 마감과 책임이다.
팀에서도 똑같다. “우리 팀은 테스트를 중요하게 생각합니다”라고 말해도, 테스트가 40분 걸리고 실패 원인이 불명확하면 사람들은 우회한다. “문서를 꼭 씁시다”라고 해도, 문서 업데이트가 리뷰 기준에 없으면 오래 못 간다. 시스템이 허용하는 방향으로 사람은 흐른다.
그래서 아키텍처 리뷰에서 물어야 할 질문은 “계층이 맞나?” 하나가 아니다. 새 기능을 추가하는 사람이 어디를 만질지 쉽게 알 수 있는가, 잘못 만졌을 때 빨리 깨지는가, 실험 기능이 핵심 데이터 모델을 오염시킬 수 있는가를 같이 봐야 한다.
rust-analyzer가 보여준 두 겹 구조
rust-analyzer 사례에서 흥미로운 부분은 프로젝트가 깊고 넓다는 점이다. 컴파일러라는 깊은 영역은 헌신적이고 실력이 높은 기여자를 끌어들인다. 반대로 IDE 기능은 폭이 넓고, Rust를 배우는 사람이 주말에 자기 불편을 해결하려고 한두 시간을 투자하기 좋다.
matklad는 이 두 종류의 기여자를 같은 문으로 밀어넣지 않았다. rust-analyzer는 rustc 빌드를 요구하지 않고, stable에서 빌드되며, C 의존성이 없고, 전체 테스트가 몇 초 안에 끝나도록 고집했다. 이것은 단순한 개발 편의가 아니라 고효율 기여자가 다른 것에 신경 쓰지 않고 핵심 문제에 집중하게 만드는 설계다.
동시에 독립 기능은 catch_unwind로 보호했다. 기능 PR의 기준은 “happy path가 동작하고 테스트가 있음”까지 낮출 수 있었지만, 그 코드가 크래시하더라도 품질 문제가 기능 안에 격리되고 사용자에게 보이지 않아야 했다. 불변 스냅샷으로 동작해 데이터를 오염시키지 못하게 한 것도 같은 맥락이다.
반대로 core spine은 훨씬 엄격하게 봤다. 기능 개발은 낙관적으로 받아들이되, 기능을 떠받치는 공통 구조는 조심스럽게 다룬 것이다. 이 차등 기준이 중요하다. 모든 코드에 같은 리뷰 강도를 걸면 속도가 죽고, 모든 코드에 낮은 기준을 걸면 핵심이 썩는다.
내 프로젝트에 옮기면 이렇게 나뉜다
작은 팀이나 개인 프로젝트에서도 이 구조는 그대로 쓸 수 있다. 먼저 core spine을 정한다. 데이터 모델, 권한 경계, 작업 큐, 상태 저장, 복구 루프, 배포 파이프라인처럼 망가지면 전체가 흔들리는 부분이다.
그 다음 독립 기능을 나눈다. 리포트 템플릿, 알림 문구, UI 보조 패널, 특정 소스용 importer, 실험적인 agent skill처럼 실패해도 격리 가능한 부분이다. 이런 영역은 PR 기준을 조금 낮추고, 대신 실패가 밖으로 번지지 않는 장치를 둔다.
AI 에이전트 하네스라면 core spine은 더 명확하다. 실행 명령, 상태 파일, 재시작 정책, 승인 게이트, 로그 보존, 작업 큐가 spine이다. 반대로 개별 리서치 스킬, 요약 템플릿, 채널별 SNS 문구는 기능 영역이다.
이 구분이 없으면 자꾸 이상한 일이 생긴다. 실험용 스킬이 상태 파일 형식을 바꾸고, 작은 편의 기능이 실행 권한을 넓히고, 임시 자동화가 정식 배포 루트가 된다. 처음엔 빠른데 나중엔 누가 어디를 고쳐도 무서운 프로젝트가 된다.
프로토타입은 장기 현실이 될 수 있다
matklad가 던지는 경고 중 제일 아픈 부분은 실험적 구조가 오래 남는다는 점이다. rust-analyzer의 원래 동기는 IntelliJ Rust 안에 또 하나의 병렬 컴파일러를 쓰는 일을 피하고, LSP를 위한 더 나은 아키텍처를 프로토타입으로 검증해 그 배움을 rustc로 되돌리는 것이었다.
그런데 결과적으로 또 하나의 컴파일러를 유지하게 됐다. 이건 “실험 실패”라기보다 소프트웨어에서 자주 일어나는 일이다. 임시 코드가 고객을 만나고, 고객이 의존하고, 의존이 생기면 임시는 운영 자산이 된다. tmp_final_v2_real 같은 이름이 폴더 어딘가에서 박수를 치는 순간이다.
그래서 프로토타입을 만들 때도 최소한의 장기화 방지 장치는 필요하다. 데이터 모델을 임시로 잡았다면 언제 바꿀지, 어떤 API가 실험인지, 어떤 로그가 남아야 되돌릴 수 있는지 써야 한다. “나중에 정리”는 일정표가 아니라 기도문이 되기 쉽다.
특히 AI 자동화 프로젝트는 이 위험이 더 크다. 결과가 빨리 나오기 때문에 임시 스크립트가 곧 운영 루틴이 된다. 처음엔 한 번 돌려보려고 만든 파일이 다음 달에는 매일 아침 브리핑을 책임진다. 그러니 프로토타입에도 최소한 이름, 입력, 출력, 실패 시 복구 방법은 남겨야 한다.
좋은 아키텍처 체크리스트
| 체크 항목 | 질문 | 위험 신호 |
|---|---|---|
| 핵심 spine | 망가지면 전체가 흔들리는 부분이 명확한가 | 모든 파일이 다 중요하다고 말함 |
| 독립 기능 | 실패해도 격리 가능한 기능 영역이 있는가 | 작은 기능이 공통 상태를 직접 수정함 |
| 기여자 마찰 | 빌드/테스트/실행이 빠르고 단순한가 | 첫 기여자가 환경 세팅에서 탈락함 |
| 리뷰 기준 | 핵심과 주변부의 기준이 다른가 | 모든 PR이 같은 속도 또는 같은 엄격함 |
| 실패 격리 | 크래시와 데이터 오염이 분리되는가 | 기능 실패가 전역 상태를 망가뜨림 |
| 임시 구조 | 프로토타입의 만료 조건이 있는가 | 실험 API가 조용히 정식 API가 됨 |
이 표를 보면 아키텍처가 갑자기 덜 고상해진다. 하지만 실무에서는 이 정도로 내려와야 쓸모가 있다. “헥사고날 아키텍처를 적용했습니다”보다 “이 importer가 실패해도 작업 큐와 원본 노트는 오염되지 않습니다”가 훨씬 강한 문장이다.
내가 새 프로젝트를 본다면 첫날에는 폴더 구조보다 실행 경로를 먼저 본다. setup부터 테스트까지 얼마나 걸리는지, 실패하면 어디에 기록되는지, 한 기능이 어느 파일까지 건드리는지 본다. 이 흐름이 답답하면 아키텍처 그림이 예뻐도 오래 버티기 어렵다.
아키텍처 책을 읽을 때도 관점이 달라져야 한다
matklad는 단일한 진리의 책은 없다고 말한다. 대신 Gary Bernhardt의 Boundaries, How to Test, ZeroMQ guide, Pieter Hintjens의 글, Jamii의 회고, Ted Kaminski 블로그, Software Engineering at Google, Ousterhout의 The Philosophy of Software Design 등을 언급한다.
이 목록을 “필독서 모음”으로만 보면 재미가 줄어든다. 더 좋은 사용법은 각 자료를 하나의 질문에 붙이는 것이다. Boundaries는 경계와 의존성 질문에, How to Test는 테스트가 설계를 드러내는 질문에, Software Engineering at Google은 조직 규모와 장기 유지보수 질문에 붙인다.
책을 읽고 바로 패턴 이름을 코드에 붙이는 건 조심해야 한다. 추상화는 문제를 줄이기도 하지만, 원하는 것이 얼마나 단순한지 숨기기도 한다. 좋은 자료를 읽었다면 “우리 프로젝트의 어떤 마찰을 줄일까?”로 번역해야 한다.
TECHTAEK식으로 말하면, 아키텍처 학습은 독서 기록이 아니라 운영 체크리스트로 끝나야 한다. 오늘 배운 개념이 다음 PR에서 어떤 리뷰 질문으로 바뀌는지, 다음 장애에서 어떤 로그를 더 남기게 하는지, 다음 기능에서 어떤 경계를 더 선명하게 하는지가 진짜 성과다.
실무에 바로 적용하는 5단계
첫째, 현재 프로젝트에서 core spine을 5줄로 적는다. 예를 들면 작업 큐, 상태 저장, 권한 승인, 로그/리포트, 배포 루트처럼 적는다. 이 5줄을 못 적으면 설계 토론은 금방 추상화 구름 위로 올라간다.
둘째, 독립 기능 목록을 적는다. 실패해도 사용자 데이터나 공통 상태를 망가뜨리지 않는 영역을 따로 표시한다. 이런 영역은 기여자 경험을 좋게 만들고, 작은 PR을 빨리 받을 수 있게 설계한다.
셋째, 빌드와 테스트 시간을 본다. rust-analyzer 사례에서 몇 초짜리 테스트가 중요한 이유는 친절해서가 아니라 기여자가 집중할 수 있는 시간을 지켜주기 때문이다. 테스트가 느리면 사람은 테스트를 덜 믿고, 덜 믿으면 설계도 같이 약해진다.
넷째, 임시 구조에 만료 조건을 붙인다. “이번 달 실험”, “데이터 100건까지”, “공개 API 아님”, “마이그레이션 로그 필요”처럼 적어둔다. 임시 구조가 오래 살 가능성을 인정해야 나중에 덜 다친다.
다섯째, 리뷰 질문을 바꾼다. “코드가 예쁜가?”보다 “이 변경이 core spine을 건드리는가?”, “실패가 격리되는가?”, “다음 기여자가 올바른 위치를 찾을 수 있는가?”를 묻는다. 이 질문이 반복되면 팀의 선택 경로가 조금씩 바뀐다.
TECHTAEK식 판단
이 글이 좋은 이유는 아키텍처를 신비화하지 않는다는 점이다. 좋은 구조는 천재가 칠판 앞에서 만든 정답이 아니라, 시간이 없는 사람들이 덜 나쁜 선택을 하게 만드는 장치다. 조금 덜 낭만적이지만, 실제로는 이쪽이 오래 간다.
AI 코딩 시대에는 이 관점이 더 중요해진다. 에이전트는 코드를 빨리 만든다. 그래서 core spine과 독립 기능을 안 나눠두면, 빠른 변경이 빠르게 핵심을 건드린다. 자동화가 빨라질수록 아키텍처는 더 느긋한 그림이 아니라 더 엄격한 경계가 된다.
내가 이 글을 운영 체크리스트로 바꾼다면 딱 세 문장이다. 핵심 경로는 느리게 리뷰한다. 독립 기능은 빨리 실험하게 한다. 실험 구조는 오래 남을 수 있다고 가정하고 로그와 만료 조건을 남긴다.
이 정도만 지켜도 아키텍처 이야기는 훨씬 실용적이 된다. 폴더 이름 논쟁이 줄고, “왜 이 코드는 여기에 있어야 하는가”를 설명하기 쉬워진다. 결국 좋은 설계는 코드가 똑똑해 보이게 만드는 일이 아니라, 사람이 덜 위험한 선택을 하게 만드는 일이다.
FAQ
소프트웨어 아키텍처는 책보다 프로젝트로 배워야 하나?
책은 필요하지만 충분하지 않다. matklad의 핵심도 실전 책임이 붙을 때 설계 문제가 자기 일이 되고, 그때 학습이 깊어진다는 쪽에 가깝다. 책은 질문을 주고, 프로젝트는 대가를 보여준다.
core spine은 어떻게 찾나?
망가지면 전체 시스템이 흔들리는 부분을 먼저 찾으면 된다. 데이터 모델, 권한 경계, 상태 저장, 작업 큐, 복구 루프, 배포 루트, 공통 API가 보통 spine 후보가 된다. 변경이 어렵고 영향 범위가 넓을수록 더 엄격하게 봐야 한다.
모든 코드에 높은 품질 기준을 적용하면 더 안전하지 않나?
겉보기엔 그렇지만 속도가 크게 죽을 수 있다. rust-analyzer 사례처럼 핵심 spine에는 엄격한 기준을 두고, 독립 기능에는 실패 격리 장치를 둔 뒤 낮은 기준을 허용하는 방식이 더 현실적일 수 있다.
프로토타입을 만들 때 가장 먼저 남길 것은?
입력, 출력, 상태 저장 위치, 실패 시 복구 방법, 정식 전환 또는 폐기 조건을 남기는 게 좋다. 특히 데이터 모델과 외부 API는 임시라고 적어도 오래 남을 수 있으니 기록이 필요하다.
AI 코딩 에이전트 프로젝트에도 이 관점이 필요한가?
오히려 더 필요하다. 에이전트는 주변 기능을 빠르게 늘릴 수 있지만, 권한, 상태, 로그, 승인 게이트 같은 spine이 약하면 실패도 빠르게 퍼진다. 자동화 속도가 올라갈수록 경계 설계가 더 중요해진다.
아키텍처 리뷰에서 바로 쓸 질문은 무엇인가?
이 변경이 core spine을 건드리는가, 실패가 격리되는가, 다음 기여자가 올바른 위치를 찾을 수 있는가, 테스트가 빠르게 실패를 알려주는가, 임시 구조가 장기 현실로 굳어질 때의 비용이 기록되어 있는가를 물으면 된다.
공식 출처
- Learning Software Architecture – matklad
- 소프트웨어 아키텍처 배우기 – GeekNews
- rust-analyzer GitHub repository
- Conway’s law – Wikipedia
- TIGER_STYLE