
생성 수정
IBM * 레드햇 AX 과정 · 17강
의미의 지도와 연결의 뼈대 — 온톨로지와 토폴로지
2026년 6월 22일(월) ~ 6월 26일(금)
이 장을 다 읽으면
- 온톨로지(Ontology)와 토폴로지(Topology)를 각각 한 문장으로 정의하고, 둘이 무엇으로 갈리는지 말할 수 있다.
- 두 개념이 던지는 질문이 서로 다르다는 것을 — "어떤 의미로 이어지는가" 대 "어떤 통로로 이어지는가" — 예를 들어 설명할 수 있다.
- 그래프가 노드(Node)와 엣지(Edge)로 이루어지며, 학문마다 이름만 다를 뿐 같은 개념임을 안다.
- 스타 토폴로지가 곧 허브 앤 스포크(Hub-and-Spoke) 모델임을 알고, 장점(장애 격리)과 단점(단일 장애점)을 짝으로 설명할 수 있다.
- GitHub의 main-branch 구조가 왜 스타 토폴로지와 같은 메커니즘인지 네 가지 항목으로 대응시킬 수 있다.
- 앞에서 배운 헥사고날 아키텍처가 스타 토폴로지의 한 형태임을 설명하고, 6주차의 Facade와 어떻게 다른지 구별할 수 있다.
- 모듈러 모놀리식 위에 허브(
apps/star_craft)와 스포크를 지정하는 규칙을 쓰고, 스포크 간 순환 참조를 왜 금지하는지 말할 수 있다. - FastAPI를 중앙 허브로 두고 MCP를 인바운드, n8n을 아웃바운드 어댑터로 붙이는 4단계를 순서대로 구현할 수 있다.
- 린터·포매터·pre-commit이 "하네스의 센서"라는 것을 알고, 워크스페이스 전체에 규칙을 강제하는 방법을 안다.
- 파이토치 GPU 가상환경과 플러터 프로젝트를 만들고, 웹뷰가 아닌 네이티브 앱이어야 하는 이유를 설명할 수 있다.
0. 이 장의 판별 축
새 용어를 만나면 비유를 찾기 전에 아래 축 중 어디에 꽂히는지부터 정하세요. 이번 주의 용어들은 유난히 비유가 예쁩니다(별, 바퀴살, 지도). 그래서 더 위험합니다. 바퀴살 그림을 먼저 외우면 "그럼 온톨로지도 바퀴살인가?" 에서 무너집니다.
| # | 축 | 왼쪽 (0) | 오른쪽 (1) | 이 축으로 갈리는 것 |
|---|---|---|---|---|
| 1 | 무엇을 묻는가 | 의미의 관계 | 연결의 구조 | 온톨로지 ↔ 토폴로지 |
| 2 | 어디로 모이는가 | 중앙 집중 | 분산 | 스타(허브) ↔ 메시 / 모놀리식 ↔ MSA |
| 3 | 누구를 거치는가 | 직접 통신 가능 | 반드시 허브 경유 | 메시 ↔ 스타 / 스포크 간 직접 호출 ↔ 허브 중계 |
| 4 | 고장이 어디까지 가는가 | 장애 격리 | 단일 장애점(SPOF) | 스포크 장애 ↔ 허브 장애 |
| 5 | 규칙을 누가 지키는가 | 사람이 지킴 | 도구가 강제함 | 코드 리뷰 ↔ 린터·pre-commit |
축 1이 이 장의 중심입니다. 이 축 하나로 이번 주에 나온 거의 모든 단어가 두 줄로 갈립니다.
축 2·3·4는 사실상 한 덩어리입니다. 중앙으로 모으면(축 2) 직접 통신이 막히고(축 3), 그 대가로 장애는 격리되지만 중앙이 죽으면 전부 죽습니다(축 4). 하나를 고르면 나머지 둘이 따라옵니다. 이것이 이 장에서 가장 자주 쓰게 될 사고 도구입니다.
축 2·3·4는 이전 장에서 이어집니다. 3주차의 모놀리식 ↔ MSA, 6주차의 Facade(한 곳에 몰기) ↔ CoR(체인으로 넘기기) 가 같은 축의 다른 이름이었습니다. 이번 주에 그 축이 이름을 얻습니다. 토폴로지입니다.
1. 온톨로지와 토폴로지 — 두 장의 지도
1.1 왜 이 둘이 같은 주에 나오는가
발음이 비슷해서가 아닙니다. 둘 다 "지도"이기 때문입니다.
같은 시스템을 놓고 두 장의 지도를 그릴 수 있습니다.
- 한 장은 무엇이 무엇과 어떤 의미로 이어지는지를 그립니다 → 온톨로지
- 다른 한 장은 무엇이 무엇과 어떤 통로로 이어지는지를 그립니다 → 토폴로지
지하철을 생각하면 정확합니다. "강남역은 서초구에 있고, 2호선과 신분당선이 지나가며, 상권으로 분류된다"는 의미의 지도입니다. "강남역에서 홍대입구역으로 가려면 2호선을 타고 몇 정거장을 간다"는 연결의 지도입니다. 둘 다 강남역을 설명하지만 묻는 질문이 다릅니다.
1.2 정의
정의는 이 책의 형식대로 유(類, 상위 범주) + 종차(種差, 그 안에서의 차이) 로 씁니다.
- ⭐ 온톨로지(Ontology) — [지식을 정형화한 지도]인데, [데이터와 지식 사이의 의미(Semantic)와 관계를 컴퓨터가 처리할 수 있게 정의한다]는 점에서 토폴로지와 구별된다.
- ⭐ 토폴로지(Topology) — [연결을 정형화한 지도]인데, [시스템·장치·노드가 물리적·논리적으로 배치되어 연결된 구조와 형태를 정의한다]는 점에서 온톨로지와 구별된다.
1.3 온톨로지 — 의미의 지도
핵심 요소는 셋입니다.
| 요소 | 영문 | 하는 일 | 예 |
|---|---|---|---|
| 개념 | Class | 같은 성질의 것을 묶은 범주 | 사람, 주문, 배달원, 지역 |
| 속성 | Property | 그 개념이 가지는 특징 | 이름, 주소, 주문 시각 |
| 관계 | Relation | 개념과 개념을 잇는 의미의 선 | is-a, part-of, orders |
묻는 질문:
"이 데이터와 저 데이터는 서로 어떤 의미적 연관성을 가지는가?"
주요 활용:
- 시맨틱 웹(Semantic Web)
- AI 지식 그래프(Knowledge Graph)
- 연관 검색 시스템
여기서 눈치채야 할 것이 하나 있습니다. 개념(Class) · 속성(Property) 이라는 단어를,
우리는 이미 3주차에서 만났습니다. 클래스와 필드입니다.
그리고 관계(Relation) 역시 3주차의 IS-A / HAS-A / USES-A 로 만났습니다.
⭐ 온톨로지는 객체지향을 "코드 밖"으로 꺼낸 것입니다. 클래스·속성·관계라는 똑같은 세 단어를, 한 프로그램 안이 아니라 조직 전체의 지식에 적용한 것이 온톨로지입니다. 그래서 객체지향을 이해한 사람은 온톨로지를 새로 배울 필요가 거의 없습니다. 범위만 넓히면 됩니다.
1.4 토폴로지 — 연결의 지도
핵심 요소는 하나입니다.
| 요소 | 영문 | 하는 일 |
|---|---|---|
| 연결성 | Connectivity | 구성 요소들이 실제 거리·크기와 무관하게 어떻게 이어져 있는지 |
"실제 거리나 크기와 무관하다"는 말이 토폴로지의 전부입니다. 지하철 노선도를 떠올리세요. 노선도의 역 간 간격은 실제 거리와 전혀 다릅니다. 그런데도 우리는 노선도로 길을 찾습니다. 거리가 아니라 연결만 맞으면 되기 때문입니다.
묻는 질문:
"A 노드에서 B 노드로 가기 위해 어떤 통로(구조)로 연결되어 있는가?"
주요 활용:
- 컴퓨터 네트워크 망 구성
- 공장 설비 및 센서 데이터 흐름 회로 설계
1.5 한 표로
| 구분 | 온톨로지 (Ontology) | 토폴로지 (Topology) |
|---|---|---|
| 그리는 것 | 의미(Semantic)와 관계 | 구조(Structure)와 형태 |
| 핵심 요소 | 개념(Class) · 속성(Property) · 관계(Relation) | 연결성(Connectivity) |
| 묻는 질문 | "어떤 의미로 이어지는가" | "어떤 통로로 이어지는가" |
| 무관한 것 | 물리적 배치 | 실제 거리·크기 |
| 대표 활용 | 시맨틱 웹, 지식 그래프, 연관 검색 | 네트워크 망 구성, 센서 데이터 흐름 |
| 한 단어로 | 뜻 | 길 |
1.6 반례 — 이건 아닙니다
미국식 분류의 마지막 단계는 반례입니다. 아닌 것을 알아야 경계가 굳습니다.
| 문장 | 판정 | 이유 |
|---|---|---|
| "서버 A와 B가 스위치를 통해 연결돼 있다" | 토폴로지 ⭕ / 온톨로지 ❌ | 통로 이야기입니다. 의미가 없습니다 |
| "배달원은 사람의 한 종류다" | 온톨로지 ⭕ / 토폴로지 ❌ | is-a 관계. 통로가 아니라 뜻입니다 |
| "이 API는 저 API를 호출한다" | 토폴로지 ⭕ | 호출 경로 = 연결 구조 |
| "주문은 고객에 속한다" | 온톨로지 ⭕ | part-of 관계 |
| "노드가 100개 있다" | 둘 다 아님 ❌ | 개수는 지도가 아닙니다. 관계도 연결도 말하지 않았습니다 |
| "이 폴더 구조가 예쁘다" | 둘 다 아님 ❌ | 취향입니다 |
⚠️ 가장 흔한 오해: "온톨로지는 어려운 말이고 토폴로지는 네트워크 용어다." ❌ 둘 다 지도의 종류입니다. 난이도로 나뉘는 게 아니라 묻는 질문으로 나뉩니다.
1.7 한국어 번역어가 만드는 함정
이 책은 번역어가 원 개념을 왜곡한 곳을 반드시 짚습니다. 이번 주에는 두 개나 있습니다.
| 원어 | 흔한 한국어 번역 | 문제 | 이 책에서 쓰는 뜻 |
|---|---|---|---|
| Ontology | 존재론 | 철학 용어로 읽혀서 "무엇이 존재하는가"라는 형이상학 논쟁처럼 들립니다 | 지식 지도 — 개념·속성·관계를 기계가 읽게 정형화한 것 |
| Topology | 위상수학 | 수학 과목 이름으로 읽혀서 "나는 저거 안 배웠는데"가 됩니다 | 연결 구조 — 거리를 지우고 이어짐만 남긴 형태 |
실무에서 이 둘을 말할 때는 번역하지 않는 편이 낫습니다. 그래서 이 책도 온톨로지·토폴로지로 씁니다.
💡 어원을 알면 오히려 쉬워집니다.
- Onto- (존재하는 것) + -logy (학문·체계) → 있는 것들을 체계로 정리한 것
- Topo- (장소·자리) + -logy → 자리들이 어떻게 놓여 있는지 정리한 것
Topology의 topo-는 "장소"이지 "수학"이 아닙니다. 번역이 어렵게 만든 것뿐입니다.
2. 그래프 — 두 지도가 공유하는 재료
2.1 그래프의 정의
두 지도는 서로 다른 것을 그리지만, 그릴 때 쓰는 재료는 같습니다. 그래프입니다.
⭐ 그래프(Graph) = 노드(Node) + 엣지(Edge)
| 구성요소 | 다른 이름 | 뜻 |
|---|---|---|
| 노드 | Vertice(정점), Node(마디), 점 | 무언가 하나. 사람·서버·문서·개념 |
| 엣지 | Edge(모서리), Link(연결), 선 | 노드와 노드를 잇는 것 |
엣지에는 방향성이 있을 수도, 없을 수도 있습니다.
| 구분 | 표기 | 뜻 | 예 |
|---|---|---|---|
| 무방향 그래프 | A — B | 서로 대등하게 이어짐 | "A와 B는 친구다" |
| 유방향 그래프 | A → B | 한쪽에서 다른 쪽으로 | "A가 B를 호출한다", "A는 B의 한 종류다" |
⚠️ 방향은 장식이 아닙니다. 6주차에서 "제어의 흐름과 의존성의 방향은 반대일 수 있다" 를 배웠습니다. 그때 그린 것이 이미 유방향 그래프였습니다. 화살표를 잘못 그리면 아키텍처 설명 전체가 틀립니다.
2.2 이름만 다를 뿐, 같은 개념입니다
여기가 이번 주에 가장 마음이 편해지는 대목입니다.
| 분야 | 부르는 이름 | 노드를 뭐라고 부르나 |
|---|---|---|
| 컴퓨터공학 | 그래프 자료구조 | Node |
| 수학 (통계학) | 그래프 | Vertex / Vertice |
| 그래프이론 | 그래프 | Vertex |
| 네트워크이론 | 네트워크 | Node |
⭐ 컴퓨터공학·수학(통계학)·그래프이론·네트워크이론에서 이름만 다를 뿐 개념은 동일합니다.
이것을 알아야 하는 실질적인 이유가 있습니다. 검색을 하면 어떤 문서는 "vertex"라 하고 어떤 문서는 "node"라 합니다. 다른 걸 배워야 하나 싶어서 멈추는 순간, 그 주의 공부는 거기서 끝납니다. 같은 것입니다. 계속 읽으세요.
💡 그래프 용어를 헷갈리게 하는 것이 하나 더 있습니다. "그래프"라는 말 자체입니다. 엑셀의 막대 그래프도 graph, 지금 말하는 노드-엣지 구조도 graph입니다. 영어권에서는 전자를 보통 chart, 후자를 graph라고 구별해 부릅니다.
2.3 그래프 위에서 두 지도가 갈라진다
같은 그래프를 놓고, 엣지에 무엇을 적느냐에 따라 지도의 종류가 갈립니다.
[배달원] ────────── [주문]
엣지
이 엣지에 무엇을 적느냐가 전부입니다.
| 엣지에 적는 것 | 되는 것 | 읽는 법 |
|---|---|---|
배달한다, is-a, part-of | 온톨로지 | 의미 |
HTTP POST, 초당 3건, TCP 8000번 포트 | 토폴로지 | 통로 |
⭐ 노드와 엣지는 재료이고, 온톨로지와 토폴로지는 그 재료로 만든 두 가지 요리입니다. 그래서 "그래프 DB를 쓰면 온톨로지가 되나요?" 라는 질문의 답은 아니오입니다. 그래프 DB는 재료를 담는 그릇입니다. 엣지에 의미를 적어야 온톨로지가 됩니다.
앞으로 다시 만납니다 이 그래프 재료 위에 그래프 DB(Neo4j) 와 벡터 DB 를 얹고, 온톨로지 폴더를 프로젝트 어디에 둘 것인가를 정하는 일이 다음 주에 이어집니다. 그때 결론은
apps/star_craft/domain/ontology/가 되는데, 왜 하필 거기인지는 이번 주에 배우는 허브·스포크 규칙(6장)을 알아야 이해됩니다.
2.4 옵시디언 그래프뷰 — 손안의 그래프
개념으로만 배우면 잘 안 남습니다. 이번 주에 실제로 그래프를 눈으로 봅니다. 옵시디언의 그래프뷰(Graph View) 는 내 노트를 노드로, 노트 사이의 링크를 엣지로 그려 줍니다.
문제는 노트가 쌓이면 화면이 털뭉치가 된다는 것입니다. 노드가 너무 많으면 아무것도 안 보입니다.
제외 설정으로 정리하기
좌측 하단 톱니바퀴(설정) → Files and links → Manage
여기서 그래프에 넣지 않을 폴더·파일 유형을 제외합니다.
_docs 안의 자동 생성 문서, 첨부 이미지, 빌드 산출물 같은 것들이 제외 대상입니다.
💡 이 작업 자체가 토폴로지 설계입니다. 어떤 노드를 지도에 남길지 정하는 일이니까요. 그래프뷰가 예뻐지는 게 목적이 아니라, "내 지식이 실제로 어떤 모양으로 이어져 있는지" 를 보는 게 목적입니다. 털뭉치라면 그건 그래프뷰의 문제가 아니라 노트 구조의 문제입니다.
3. 스타 토폴로지 = 허브 앤 스포크
3.1 정의
⭐ 스타 토폴로지(Star Topology) — [네트워크 토폴로지]인데, [중앙의 핵심 장치를 중심으로 모든 주변 장치가 1:1로 직접 연결된다]는 점에서 다른 토폴로지와 구별된다.
한국어로는 성형(星形) 구조, 즉 별 모양입니다.
구조적 모델의 이름이 따로 있습니다.
⭐ 스타 토폴로지 = 허브 앤 스포크(Hub-and-Spoke) 모델
두 이름은 같은 것을 가리킵니다. 네트워크 쪽에서는 스타 토폴로지, 물류·항공·아키텍처 쪽에서는 허브 앤 스포크라고 부릅니다. 시험이나 면접에서 둘 중 하나만 알고 있으면 같은 질문을 다른 문제로 착각합니다.
3.2 허브와 스포크
| 구성요소 | 영문 | 정의 | 위치 |
|---|---|---|---|
| 허브 | Hub | 데이터 교환과 흐름을 제어하고 중계하는 중앙 중심축 | 한가운데 |
| 스포크 | Spoke | 허브에 연결되어 데이터를 주고받는 바퀴살 형태의 주변부 단말기 | 바깥쪽 |
Spoke는 자전거 바퀴의 바퀴살입니다. 바퀴살은 서로 닿지 않습니다. 전부 가운데 축(허브)에만 붙어 있습니다. 이 그림 하나가 스타 토폴로지의 규칙 전체입니다.
[스포크 B]
│
│
[스포크 A] ───[허 브]─── [스포크 C]
│
│
[스포크 D]
⚠️ A와 C 사이에는 선이 없습니다. 그릴 수 없습니다.
A가 C에게 말하려면 반드시 허브를 거칩니다.
3.3 장점과 단점은 같은 뿌리에서 나옵니다
| 내용 | 왜 | |
|---|---|---|
| 장점 | 특정 스포크의 회선 장애가 전체에 영향을 주지 않음 (장애 격리 우수) | 스포크끼리 안 이어져 있으니 옆으로 번질 길이 없음 |
| 장점 | 확장 및 유지보수 용이 | 새 스포크는 허브에 선 하나만 꽂으면 끝. 기존 스포크는 건드릴 필요 없음 |
| 단점 | 중앙 허브가 고장 나면 전체 네트워크가 마비됨 (단일 장애점, Single Point of Failure) | 모든 통신이 허브를 지나므로 |
⭐ 장애 격리와 단일 장애점은 반대말이 아니라 같은 사실의 두 얼굴입니다. 모든 길이 가운데를 지나기 때문에 옆으로 번지지 않고, 모든 길이 가운데를 지나기 때문에 가운데가 죽으면 전부 죽습니다. 하나를 얻으면 하나를 잃습니다. 3주차에서 배운 트레이드오프(Trade-off) 의 교과서적인 예입니다.
⚠️ 자주 하는 착각: "장애 격리가 되니까 안전한 구조다." ❌ 안전한 게 아니라 위험이 한 곳에 모인 구조입니다. 그래서 허브에는 다른 곳보다 훨씬 많은 방어를 겁니다.
3.4 형제 개념과의 경계선
스타만 알면 스타가 왜 스타인지 모릅니다. 형제들을 세워 놓고 봐야 경계가 생깁니다.
| 토폴로지 | 연결 방식 | 스포크 간 직접 통신 | 단일 장애점 | 확장 비용 |
|---|---|---|---|---|
| 스타 (Star) | 전부 중앙 허브에 1:1 | ❌ 불가 (허브 경유) | ⭕ 허브 | 낮음 (선 하나) |
| 버스 (Bus) | 하나의 공용 회선에 전부 매달림 | ⭕ 가능 | ⭕ 회선 자체 | 낮으나 혼잡 |
| 링 (Ring) | 원형으로 옆 사람에게 전달 | △ 옆으로만 | ⭕ 한 노드가 끊기면 | 중간 |
| 메시 (Mesh) | 노드끼리 서로 다 이어짐 | ⭕ 가능 | ❌ 없음 | 매우 높음 (선이 n²로 증가) |
| 트리 (Tree) | 계층적으로 갈라짐 | ❌ 상위 경유 | ⭕ 상위 노드 | 중간 |
축 2·3·4로 읽으면:
- 메시는 축 2에서 분산, 축 3에서 직접 통신 가능, 축 4에서 단일 장애점 없음. 대신 선이 폭발합니다.
- 스타는 정확히 그 반대입니다. 선이 노드 수만큼만 늘어납니다.
왜 현실은 대부분 스타인가 노드 10개를 메시로 이으면 선이 45개 필요합니다. 100개면 4,950개입니다. 스타는 100개면 선도 100개입니다. 관리 가능한 복잡도를 사는 대가로 중앙 의존을 받아들인 것입니다. 3주차의 MSA 논의와 같은 결론입니다 — 구조는 좋고 나쁨이 아니라 규모에 대한 선택입니다.
3.5 단일 장애점을 줄이는 방법
허브가 죽으면 다 죽는다는 것을 알고도 스타를 씁니다. 대신 허브를 이렇게 지킵니다.
| 방법 | 무엇을 하는가 |
|---|---|
| 허브 이중화 | 허브를 두 대 두고 하나가 죽으면 다른 하나가 받는다 (Active-Standby) |
| 허브를 얇게 유지 | 허브에 비즈니스 로직을 많이 넣지 않는다. 중계와 라우팅만 시킨다 |
| 헬스 체크 | 허브의 상태를 주기적으로 확인해 죽기 전에 안다 |
| 스포크의 자체 대기열 | 허브가 잠깐 죽어도 스포크가 요청을 쌓아 두었다가 다시 보낸다 |
💡 두 번째 항목이 소프트웨어에서 특히 중요합니다.
허브가 두꺼워질수록 단일 장애점의 크기가 커집니다. 6장에서 star_craft 허브의 책임을
세 가지(온톨로지 인덱스·라우팅·전역 상태)로 못 박는 이유가 바로 이것입니다.
4. GitHub의 main-branch 구조는 스타 토폴로지다 ⭐
4.1 이 절이 이 장의 중심입니다
여기서 이번 주의 두 흐름이 만납니다. 1주차부터 써 온 GitHub와, 이번 주에 처음 배운 스타 토폴로지가 같은 메커니즘이라는 것입니다.
⭐ 깃허브의
Main-Branch구조와 스타 토폴로지의Hub-Spoke구조는 "중앙 집중축을 중심으로 주변부가 결합한 구조" 라는 관점에서 메커니즘이 일치합니다.
4.2 네 항목 대응표
| 비교 항목 | GitHub 아키텍처 | 스타 토폴로지 (네트워크) |
|---|---|---|
| 중앙 집중축 | 메인 (Main) | 허브 (Hub) |
| 주변 확장부 | 브랜치 (Branch) | 스포크 (Spoke) |
| 통신/흐름 방식 | 브랜치 간 직접 교환 불가, 반드시 메인을 거쳐 병합(Merge) | 스포크 간 직접 통신 불가, 반드시 허브를 거쳐 중계(Switching) |
| 장애 격리성 | 특정 브랜치의 코드 오류가 메인이나 타 브랜치에 영향을 주지 않음 | 특정 스포크의 회선 장애가 허브나 타 스포크에 영향을 주지 않음 |
| 구조적 목적 | 주변부(Branch)의 작업물을 중앙으로 동기화·누적 | 중앙 장치(Hub)를 통해 주변부 간의 데이터를 중계·전송 |
4.3 한 줄씩 뜯어보기
① 중앙 집중축
main 브랜치는 "가장 중요한 브랜치"가 아니라 구조상 가운데에 있는 브랜치입니다.
기술적으로 main은 다른 브랜치와 완전히 동등합니다. 우리가 가운데로 정했을 뿐입니다.
허브도 마찬가지입니다. 허브 장비가 특별히 비싼 게 아니라, 가운데 꽂았기 때문에 허브인 것입니다.
② 통신/흐름 방식 — 이게 핵심입니다
❌ 이렇게 못 합니다
feature/login ──────────────▶ feature/payment
⭕ 이렇게 합니다
feature/login ──merge──▶ main ──merge──▶ feature/payment
깃 명령으로는 브랜치끼리 직접 머지하는 것이 기술적으로는 가능합니다. 그런데 팀 규칙에서는 금지합니다. 왜일까요? 그렇게 하는 순간 메시 토폴로지가 되기 때문입니다. 브랜치 4개가 서로 직접 머지하기 시작하면 선이 6개가 되고, 어느 코드가 어디까지 갔는지 아무도 모릅니다.
⭐ "반드시 중앙을 거친다"는 규칙은 기술적 제약이 아니라 구조적 선택입니다. 네트워크의 스포크는 물리적으로 서로 못 이어져 있어서 못 하는 것이고, 깃 브랜치는 할 수 있는데 안 하기로 정한 것입니다. 그리고 그 이유가 같습니다.
③ 장애 격리성
브랜치에서 코드를 아무리 망가뜨려도 main은 무사합니다.
이것이 브랜치를 따는 진짜 이유입니다. 브랜치는 "각자 작업 공간"이 아니라 격리 장치입니다.
④ 구조적 목적 — 이 한 칸이 대응표에서 유일하게 다릅니다
| GitHub | 스타 토폴로지 | |
|---|---|---|
| 중앙이 하는 일 | 동기화·누적 (Synchronize & Accumulate) | 중계·전송 (Switch & Transfer) |
| 데이터가 중앙에 | 쌓입니다 | 지나갑니다 |
| 시간이 지나면 | main이 점점 커집니다 | 허브는 그대로입니다 |
⚠️ 여기를 놓치면 비유가 어긋납니다.
스타 토폴로지의 허브는 데이터를 보관하지 않습니다. 받아서 넘길 뿐입니다.
GitHub의 main은 보관합니다. 그것이 존재 이유입니다.
구조는 같고 목적은 다릅니다. 이 구별을 못 하면 "허브에 데이터를 다 넣으면 되겠네"라는
잘못된 설계로 갑니다.
4.4 그래서 팀 브랜치 전략이 그렇게 생겼습니다
앞에서 배운 팀 프로젝트 설정을 다시 보면 이제 구조가 보입니다.
- 콜라보레이터 4명에게 각각 브랜치 할당 → 스포크 4개
- 리드(PM)가 머지 → 허브가 중계
- 각자 자기 브랜치만 클론 →
git clone -b pm https://github.com/example/repo.git
# 스포크 하나를 받아 가는 명령 — -b 뒤가 자기 브랜치
git clone -b pm https://github.com/example/repo.git
리드가 병목이 되는 것이 불편해 보이지만, 그것이 의도된 단일 창구입니다. 리드가 자리를 비우면 머지가 멈춥니다 — 그게 바로 단일 장애점입니다. 그래서 실무에서는 리뷰어를 복수로 두거나 CI를 자동화해서 허브를 이중화합니다(3.5절).
5. 앞에서 배운 것들이 사실 전부 별이었다
5.1 헥사고날 아키텍처 = 스타 토폴로지의 한 형태 ⭐
6주차에서 헥사고날 아키텍처를 배웠습니다. 그림을 다시 떠올려 보세요.
[인바운드 어댑터] [아웃바운드 어댑터]
REST / MCP ─┐ ┌─ DB / n8n / 외부 API
│ │
[인바운드 어댑터]─▶ [ 코 어 ] ◀─[아웃바운드 어댑터]
CLI / 배치 ─┘ 비즈니스 └─ 파일 / 메일
로직
중앙에 하나가 있고, 바깥의 여럿이 전부 중앙에만 붙어 있습니다. 그리고 바깥끼리는 서로 직접 이야기하지 않습니다.
이것을 지금 배운 말로 다시 쓰면 이렇게 됩니다.
| 헥사고날 용어 | 스타 토폴로지 용어 |
|---|---|
| 코어(Core) / 도메인 | 허브(Hub) |
| 어댑터(Adapter) | 스포크(Spoke) |
| 포트(Port) | 허브가 스포크를 받는 규격화된 꽂는 자리 |
| "어댑터는 코어를 통해서만 연결된다" | "스포크 간 직접 통신 불가" |
| "DB를 바꿔도 코어는 안 바뀐다" | "스포크 장애가 다른 스포크에 안 번진다" |
⭐ 헥사고날 아키텍처는 코드 안에 그린 스타 토폴로지입니다. 6주차에서 "왜 육각형인가 — 좌우 대칭이 핵심"이라고 했던 그 그림은, 사실 육각형이 중요한 게 아니라 중심이 하나라는 것이 중요했습니다. 오각형이든 팔각형이든 상관없습니다. 바깥이 전부 안쪽 하나에만 붙어 있으면 됩니다.
그리고 헥사고날의 트레이드오프도 그대로 따라옵니다. 코어가 죽으면 전부 죽습니다. 그래서 코어에는 외부 기술을 절대 넣지 않습니다. 코어가 얇고 순수해야 안 죽기 때문입니다. 3.5절의 "허브를 얇게 유지" 와 정확히 같은 말입니다.
5.2 6주차의 Facade와는 어떻게 다른가
6주차에서 gateway가 Facade 패턴이라고 배웠습니다. 복잡한 내부 앞에 창구 하나를 세우는 것이었죠.
Facade도 "여럿을 하나로 모은다"는 점에서 별처럼 보입니다. 그런데 축이 다릅니다.
| Facade (6주차) | 허브 (9주차) | |
|---|---|---|
| 소속 범주 | 디자인 패턴 (GoF 구조 패턴) | 토폴로지 (연결 구조의 형태) |
| 방향 | 바깥 → 안 (들어오는 입구를 하나로) | 모든 방향 (오가는 것 전부를 중계) |
| 하는 일 | 단순화 — 복잡한 내부를 감춘다 | 중계 — 누구에게 보낼지 정한다 |
| 뒤에 있는 것 | 서브시스템 여러 개 | 스포크 여러 개 |
| 없어지면 | 호출하는 쪽이 내부를 다 알아야 함 | 통신 자체가 불가능 |
| 다루는 단위 | 호출(Call) | 노드(Node)와 연결(Edge) |
한 줄로 구별하는 법:
Facade는 "입구"이고, 허브는 "교환대"입니다. Facade는 들어오는 사람에게 편의를 제공하고, 허브는 안에 있는 것들끼리 서로 못 만나게 하면서 대신 전달합니다.
그러면 둘은 무슨 사이일까요? 겹칩니다.
외부 요청 ──▶ [ gateway (Facade) ] ──▶ [ 허브 코어 (Hub) ] ──▶ [ 스포크들 ]
입구를 하나로 중계를 하나로
⭐ gateway는 허브의 "얼굴"이고, 코어는 허브의 "몸"입니다. 실제 프로젝트에서는 이 둘이 같은 곳에 붙어 있어서 하나처럼 보입니다. 그래도 질문이 다르다는 것은 기억해야 합니다. Facade에게는 "밖에서 들어오기 쉬운가"를 묻고, 허브에게는 "안에서 서로 못 만나게 하는가"를 묻습니다.
5.3 6주차의 CoR과 함께 놓고 보기
6주차 판별 축 5는 Facade(한 곳에 몰기) ↔ CoR(체인으로 넘기기) 였습니다. 이제 이 축에 토폴로지 이름을 붙일 수 있습니다.
| 6주차 표현 | 9주차 표현 | 토폴로지 |
|---|---|---|
| Facade — 한 곳에 몰기 | 허브로 모으기 | 스타 |
| CoR — 체인으로 넘기기 | 옆으로 전달하기 | 링 / 버스에 가까움 |
"바깥에서는 창구를 하나로 모으고(Facade), 그 창구 안쪽에서는 일을 여러 손으로 나눈다(CoR)" — 6주차의 이 문장은 사실 "바깥은 스타로, 안쪽은 체인으로" 라는 토폴로지 설계였습니다.
5.4 프랙탈 — 같은 별이 크기를 바꿔 반복된다
3주차에서 배운 프랙탈(Fractal), 즉 같은 구조가 크기만 바꿔 반복된다는 원리가 이번 주에 아주 선명하게 드러납니다.
| 크기(스케일) | 허브 | 스포크 |
|---|---|---|
| 네트워크 장비 | 스위치·라우터 | 단말기 |
| 저장소 | main 브랜치 | feature 브랜치 |
| 서비스 아키텍처 | FastAPI 서버 | n8n · MCP 클라이언트 · DB |
| 애플리케이션 내부 | apps/star_craft | apps/titanic, apps/silicon_valley … |
| 헥사고날 | 코어(도메인) | 어댑터들 |
| 문서 | 루트 CLAUDE.md | 각 프로젝트의 _docs |
| 팀 | 리드(PM) | 팀원 |
⭐ 일곱 개의 스케일에서 같은 모양이 반복됩니다. 이것이 이 책이 계속 강조하는 것입니다 — 하나를 제대로 이해하면 아직 안 배운 것도 추론됩니다. 새 기술을 만났을 때 "여기서 허브는 무엇이고 스포크는 무엇인가"만 물으면 절반은 파악됩니다.
6. 모듈러 모놀리식 위에 별을 그리다
6.1 전제 — FastAPI 백엔드는 모듈러 모놀리식이다
3주차에서 배운 분류를 다시 꺼냅니다.
| 배포 단위 | 내부 구조 | |
|---|---|---|
| 모놀리식 | 하나 | 뭉쳐 있음 |
| 모듈러 모놀리식 | 하나 | 모듈로 나뉘어 있음 |
| MSA | 여러 개 | 서비스별로 나뉨 |
우리 백엔드는 모듈러 모놀리식입니다. 서버는 한 대인데, 안에 apps/ 아래로 앱이 여러 개 있습니다.
fastapi/
├── CLAUDE.md ← 규칙을 여기에 명시
└── apps/
├── star_craft/ ← 허브 (Hub)
├── titanic/ ← 스포크 (Spoke)
├── silicon_valley/ ← 스포크 (Spoke)
└── ... ← 스포크 (Spoke)
⚠️ 모듈러 모놀리식에는 함정이 하나 있습니다. 배포가 하나이므로 아무 모듈이나 아무 모듈을 그냥 import 할 수 있습니다. MSA라면 네트워크가 막아 주지만, 한 프로세스 안에서는 아무것도 안 막아 줍니다. 그래서 규칙을 사람이 세워야 합니다. 그 규칙이 바로 스타 토폴로지입니다.
6.2 fastapi/CLAUDE.md 에 명시하는 규칙
⭐
fastapi/apps/star_craft를 허브(Hub) 로, 나머지를 스포크(Spoke) 로 설정한다.
이 한 줄을 fastapi/CLAUDE.md 에 씁니다. 왜 CLAUDE.md 인가 — AI와 사람이 같은 규칙을 보게 하기 위해서입니다.
앞에서 배운 하네스 엔지니어링의 가이드(Feedforward) 에 해당합니다.
규칙을 머릿속에만 두면 AI가 코드를 생성할 때 그냥 스포크끼리 import 해 버립니다.
6.3 허브의 세 가지 책임
허브가 무엇이든 다 하게 두면 안 됩니다(3.5절). 그래서 딱 셋만 줍니다.
| # | 책임 | 무엇을 하는가 | 왜 허브에 있어야 하는가 |
|---|---|---|---|
| 1 | 온톨로지 인덱스 | 전체 시스템의 개념·속성·관계 지도를 보관 | 지식 지도는 하나여야 합니다. 앱마다 다른 지도를 들면 같은 단어가 다른 뜻이 됩니다 |
| 2 | 라우팅 | 어떤 요청을 어느 스포크로 보낼지 결정 | 스포크는 다른 스포크의 존재를 몰라야 하므로, 아는 쪽이 하나 필요합니다 |
| 3 | 전역 상태 | 여러 스포크가 공유해야 하는 상태 관리 | 상태가 두 곳에 있으면 반드시 어긋납니다 |
💡 1번이 온톨로지, 2번이 토폴로지입니다. 허브는 의미의 지도와 연결의 지도를 동시에 들고 있는 자리입니다. 이 장의 제목이 "의미의 지도와 연결의 뼈대"인 이유가 여기 있습니다. 두 개념은 따로 배우지만 구현할 때는 같은 자리에서 만납니다.
6.4 스포크 규칙 — 이 두 줄이 전부입니다
⭐ 스포크는 반드시 허브를 통해서만 상호작용한다. ⭐ 스포크 간 순환 참조는 엄격히 금지한다.
⭕ 허용 ❌ 금지
titanic ──▶ star_craft titanic ──▶ silicon_valley
silicon_valley ──▶ star_craft silicon_valley ──▶ titanic
(그리고 이 둘이 서로를 부르면 = 순환 참조)
의존성 방향은 spoke → hub 한 방향만 허용됩니다.
왜 그렇게까지 하는가
| 스포크끼리 직접 부르면 생기는 일 | 결과 |
|---|---|
| A가 B를 import하고 B가 A를 import | 순환 참조 — 파이썬에서 ImportError 또는 부분 초기화된 모듈 |
| 앱이 4개면 가능한 연결이 12개 | 메시가 됨 — 아무도 전체 그림을 못 그림 |
| B를 고치면 A가 깨짐 | 한 앱을 떼어낼 수 없음 = 모듈이 아님 |
| 테스트할 때 A만 못 띄움 | B도 같이 띄워야 함. 3주차의 "테스트 용이성" 상실 |
⚠️ 순환 참조는 파이썬에서 특히 고약합니다. 에러가 import 시점에 나는 게 아니라 실행 중 특정 경로를 탈 때 납니다. 그리고 에러 메시지가 원인을 안 가리킵니다. 그래서 구조로 막는 것이 유일한 해법입니다.
이 규칙이 지켜지는지 사람이 확인하지 마세요
규칙을 CLAUDE.md에 써 놨어도 지켜졌는지는 별개입니다. 의존성 정적 분석 도구로 기계가 확인하게 합니다.
| 언어 | 도구 | 하는 일 |
|---|---|---|
| JavaScript / TypeScript | dependency-cruiser | import 그래프를 그리고 금지 규칙 위반을 잡아냄 |
| Python | import-linter | 계층·독립성 계약(contract)을 선언하고 위반 시 실패 |
⭐ 이 도구들이 하는 일이 정확히 "토폴로지 검사"입니다. 코드의 import 관계는 유방향 그래프이고, 이 도구들은 그 그래프가 스타 모양인지 메시가 되었는지를 판정합니다. 2장에서 배운 그래프가 여기서 실제 도구로 돌아옵니다.
7. 별의 바깥 — FastAPI + MCP + n8n
7.1 전체 그림
6장이 한 서버 안의 별이었다면, 7장은 서버 바깥까지 뻗는 별입니다.
⭐ 구조: 스타 토폴로지 = 중앙 허브(FastAPI, 비즈니스 로직) + 방사형 외부 연동
[ MCP 클라이언트 ]
(LLM · AI 도구)
│
│ MCP 요청 (인바운드)
▼
[ 외부 API ] ◀──── ┌──────────────┐ ────▶ [ n8n 워크플로우 ]
│ FastAPI │ (아웃바운드 어댑터)
[ DB ] ◀──── │ 중앙 허브 │ ────▶ [ 알림 · 메일 · 시트 ]
│ 비즈니스 로직 │
└──────────────┘
▲
│
[ 웹 프론트엔드 ]
두 가지 역할을 못 박고 갑니다.
- ⭐ n8n = 아웃바운드 어댑터(Outbound Adapter)
- ⭐ MCP = LLM이 도구·데이터를 규격에 맞춰 쓰게 하는 프로토콜
7.2 MCP — LLM 쪽에서 들어오는 문
MCP(Model Context Protocol) 는 LLM이 외부 도구와 데이터를 정해진 규격으로 쓰게 만드는 프로토콜입니다.
여기서 중요한 단어는 프로토콜입니다. 라이브러리도, 서비스도, 회사 이름도 아닙니다. 약속(규격) 입니다. 그래서 이 책의 분류로는 포트(Port)에 대한 표준에 가깝습니다.
계속 헷갈리는 삼총사를 한 번에 정리합니다.
| 구분 | CLI | API | MCP |
|---|---|---|---|
| 쉬운 말 | 사람이 명령어 치는 창 | 프로그램끼리 약속한 호출 규격 | AI가 도구를 쓰는 연결 규칙 |
| 주 사용자 | 사람 | 프로그램 | AI 에이전트 |
| 대표 예 | claude, npm, python | REST 엔드포인트, SDK | 파일·GitHub·검색·DB·캘린더 연결 |
| 장점 | 빠르고 직접적 | 언어 무관하게 통함 | 여러 도구를 AI가 스스로 활용 |
| 주의점 | 삭제·배포 명령 조심 | 버전 호환 | 권한·보안 설정이 중요 |
셋은 경쟁 관계가 아닙니다. 사람이 CLI로 AI 도구를 실행하고, AI 도구는 MCP로 외부 도구를 연결해 일합니다.
사람 → CLI 실행 → AI 도구 → MCP 연결 → 파일/GitHub/검색/DB → 결과 확인
MCP 용어 넷
| 용어 | 뜻 |
|---|---|
| Tool | AI가 사용할 수 있는 기능 단위 (파일 읽기, 메일 보내기, 검색) |
| Connector | 특정 앱·서비스에 연결해 주는 어댑터 |
| API Key | 서비스를 사용할 수 있게 해주는 '열쇠' |
| 권한 | AI가 무엇을 할 수 있는지 제한하는 규칙 |
안전하게 쓰는 4원칙
- 최소 권한 원칙 — 필요한 기능만 허용
- 읽기 전용부터 시작 — 익숙해진 뒤 쓰기, 삭제는 초보자에게 비추천
- API Key 안전 관리 — 키는 공유하지 않기
- 사용자 통제 — 보내기·삭제·수정은 사람이 마지막에 승인
⚠️ 4원칙은 예의가 아니라 단일 장애점 관리입니다. MCP는 허브로 들어오는 문이고, 문이 헐거우면 허브 전체가 위험해집니다.
MCP 툴 파일은 인바운드 어댑터 자리에 둡니다.
apps/silicon_valley/adapter/inbound/mcp/piper_henricks_ceo_tools.py
└──────┬──────┘
인바운드 = 들어오는 쪽
6주차의 헥사고날 일곱 칸을 떠올리세요. REST 라우터가 있던 자리 옆에 mcp/ 폴더가 하나 더 생긴 것입니다.
들어오는 문이 하나 늘었을 뿐, 코어는 아무것도 바뀌지 않습니다. 이것이 헥사고날의 이점입니다.
⚠️ mcp.server.fastmcp 임포트 에러
from mcp.server.fastmcp import ... 에서 ModuleNotFoundError 가 나면
패키지가 설치되지 않은 것입니다. requirements.txt 에 추가해서 해결합니다.
# requirements.txt 에 추가한 뒤
pip install -r requirements.txt
7.3 n8n — 바깥으로 나가는 문
n8n은 노드를 선으로 이어 자동화 워크플로우를 만드는 도구입니다. Zapier, Make와 같은 계열이며, 직접 설치해서 쓸 수 있다는 점이 다릅니다.
npm install -g n8n
⭐ 우리 아키텍처에서 n8n의 신분은 "아웃바운드 어댑터"입니다.
이 한 줄을 정확히 이해해야 합니다.
| ❌ 잘못된 이해 | ⭕ 올바른 이해 |
|---|---|
| n8n이 우리 시스템을 조종한다 | 우리 FastAPI가 n8n을 호출한다 |
| n8n에 비즈니스 로직을 넣는다 | 로직은 허브(FastAPI) 에 있고, n8n은 실행만 한다 |
| n8n이 중심이다 | 스포크입니다. 언제든 다른 것으로 교체 가능해야 합니다 |
💡 n8n 화면 자체가 그래프입니다. 노드를 놓고 엣지로 잇습니다. n8n을 쓰면서 "이건 유방향 그래프구나" 라고 생각하면, 2장이 손으로 체득됩니다.
7.4 구현 4단계
1단계 — n8n Webhook 준비
- n8n에서 새 워크플로우 생성
- Webhook 노드 추가
- HTTP Method 를
POST로 설정 - Webhook URL 복사
이 URL이 허브에서 스포크로 나가는 선입니다.
2단계 — 아웃바운드 클라이언트 작성
파일 위치: adapter/outbound/client/n8n_client.py
class N8nClient:
def __init__(self, webhook_url: str):
self.webhook_url = webhook_url
async def send_event(self, payload: Dict[str, Any]) -> bool:
async with httpx.AsyncClient() as client:
response = await client.post(self.webhook_url, json=payload)
return response.status_code == 200
이 코드에서 확인할 것 세 가지
| 확인할 것 | 왜 |
|---|---|
위치가 adapter/outbound/ | 나가는 쪽이므로. 6주차 일곱 칸 그대로입니다 |
async def 를 썼다 | 네트워크 I/O 대기이므로. CPU 작업이면 일반 def 였을 것입니다 |
webhook_url 을 생성자로 받는다 | 하드코딩하지 않습니다. 3주차의 DI 원칙 |
3단계 — 도메인·의존성 연결
⭐ 어댑터를 직접 부르지 않습니다. 반드시 도메인·유스케이스를 거칩니다.
파일 위치: dependencies/providers.py
def get_n8n_client() -> N8nClient:
return N8nClient(webhook_url=N8N_WEBHOOK_URL)
⚠️ N8N_WEBHOOK_URL 은 코드에 적지 않고 os.environ 에서 조회합니다.
import os
def get_n8n_client() -> N8nClient:
return N8nClient(webhook_url=os.environ["N8N_WEBHOOK_URL"])
이 함수가 6주차에서 배운 의존성 조립소(DIP 팩토리) 입니다.
라우터는 N8nClient 를 만드는 법을 모르고, 조립소만 그것을 압니다.
4단계 — 엔드포인트 + MCP 툴
@app.post("/tools/trigger-automation")
async def trigger_automation(
data: dict,
n8n_client: N8nClient = Depends(get_n8n_client),
):
# 도메인에서 가공·검증한 결과
processed_data = ...
await n8n_client.send_event(processed_data)
경로가 /tools/... 인 것이 힌트입니다. MCP의 Tool로 노출될 엔드포인트라는 뜻입니다.
7.5 데이터 흐름 한 줄
⭐
AI/MCP Client→ (MCP 요청) →FastAPI 허브(app: 접수 / domain: 가공·검증) → (DI로 어댑터 호출) →n8n_client.py→ (HTTP POST Webhook) →n8n 워크플로우
이 한 줄을 별 그림 위에 얹으면 이렇게 읽힙니다.
스포크(MCP 클라이언트) ──▶ 허브(FastAPI) ──▶ 스포크(n8n)
들어옴 중계·가공 나감
MCP 클라이언트와 n8n은 서로를 모릅니다. 알 필요도 없습니다. LLM은 n8n이 뭔지 몰라도 되고, n8n은 요청이 LLM에서 왔는지 웹에서 왔는지 몰라도 됩니다. ⭐ 이것이 스포크 간 직접 통신 금지의 실질적 이익입니다.
7.6 헥사고날 일곱 칸에 얹어 보기
6주차에서 외운 표에 이번 주에 배운 것을 채워 넣으면 이렇게 됩니다.
| 칸 | 6주차에서 배운 것 | 이번 주에 추가된 것 |
|---|---|---|
| Front end | Next.js 페이지 | 플러터 앱 (taper) |
| inbound | REST 라우터 | MCP 툴 (adapter/inbound/mcp/) |
| input (port) | UseCase 인터페이스 | 그대로 |
| use case | Interactor | 그대로 — 아무것도 안 바뀝니다 |
| output (port) | Repository 인터페이스 | 이벤트 발행 포트 |
| outbound | PG Repository | N8nClient (adapter/outbound/client/) |
| database | PostgreSQL | 그대로 |
⭐ 가운데 세 칸(input · use case · output)이 하나도 안 바뀌었다는 것이 이 표의 요점입니다. LLM 연동과 자동화 도구라는 완전히 새로운 세계를 붙였는데 비즈니스 로직은 그대로입니다. 6주차에서 "기술 교체 유연성"이라고 추상적으로 배운 것이, 이번 주에 실제로 증명됩니다.
앞으로 다시 만납니다 이 n8n 스포크에 실제 업무를 얹는 일이 곧 이어집니다. 웹훅을 받아 메일을 자동 발송하고, 받은 메일을 LLM이 필터링·요약하고, 캘린더까지 묶는 자동화가 다음에 나옵니다. 나가는 통로가 메일 말고도 하나 더 생기는데 텔레그램 봇입니다. 봇 토큰과 Chat ID를
.env에 넣고sendMessage로 POST 하는 방식인데, 그 역시 아웃바운드 어댑터 스포크 하나가 늘어나는 것일 뿐입니다. 구조를 먼저 잡아 두면, 새 도구는 선 하나 꽂는 일이 됩니다(3.3절 "확장 용이").
LangTailor