chore: 이전 세션이 남긴 변경을 커밋한다
이번 파이프라인 작업과 무관하게 작업 트리에 남아 있던 것을 그대로 올린다. 사용자가 「전부 커밋」으로 정했고, 이번 작업과 섞이지 않게 커밋만 나눴다. 대부분은 clean-architecture-backend-template 의 그림 정본 재배치다 — final/assets/diagrams/<이름>/ 에 있던 것이 CLAUDE.md 가 적은 배치인 final/assets/<이름>/ 로 옮겨졌고 .techviz/<이름>/ 이 함께 들어왔다. 삽입 줄의 대부분(3.15M)이 그 .techviz context.json 이다. 그 밖에 ca-tmpl·document-haness 의 정리, .claude/agents/ 열한 개, writing-practitioner-guides 스킬, .playwright-mcp 세션 산출물, scripts/check-ssot-facts.py 와 그 시험이 들어 있다. 이 커밋의 내용은 내가 만든 것이 아니라 이전 세션이 남긴 것이고 검증하지 않았다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: deriving-tech-log-root-tree
|
||||
description: Use when a completed or substantially completed docs project analysis must be decomposed into grounded Tech Log Topics and candidate Case, Concept, Reference, Open Question, and Decision records.
|
||||
description: Use when a completed or substantially completed docs project analysis must be decomposed into grounded Tech Log Topics and candidate Case, Concept, Setup, Reference, Open Question, and Decision records.
|
||||
metadata:
|
||||
version: "1.0.0"
|
||||
language: "ko-KR"
|
||||
@@ -77,7 +77,9 @@ They do not hold candidates, and in a folded project they are gone.
|
||||
4. Pick **Decisions** from the explicit-decision section (§10).
|
||||
5. Pick **Questions** from the unresolved section (§11).
|
||||
6. Only now add the **Concepts** those four need in order to be understood. Concept is
|
||||
derived backwards from the records that require it, never by sweeping headings.
|
||||
derived backwards from the records that require it, never by sweeping headings. Add the
|
||||
**Setups** the analysis actually supports in the same pass — the section below says which
|
||||
ones those are.
|
||||
7. Give every candidate a disposition — `references/candidate-disposition.md` — and set
|
||||
`dispositionReview` to `CONFIRMED` only for the ones a person actually re-read.
|
||||
8. Group `PROMOTE` candidates into Topics. Write one reader question per Topic.
|
||||
@@ -89,6 +91,20 @@ They do not hold candidates, and in a folded project they are gone.
|
||||
`python3 scripts/verify-tech-log-tree.py <project>` — errors must be 0. Build fills
|
||||
`ssotSha256`; verify errors when it is absent, so verifying before building always fails.
|
||||
|
||||
## When a Setup belongs in the tree
|
||||
|
||||
Add a **Setup** where the analysis records the commands and configuration values that stand
|
||||
an environment up and someone other than the author has to run them.
|
||||
|
||||
Its test is not the one the other five take. They ask whether a claim is worth publishing on
|
||||
its own; this one asks whether a reader would type these lines. A reproduction that only
|
||||
re-obtains one measurement stays inside that Case's `재현 조건` field, and a procedure nobody
|
||||
but the author would run is that Case's environment section.
|
||||
|
||||
Setup nodes need a project. A Topic is optional, and a Setup without one reads as that
|
||||
project's shared configuration. Instead of a verification date it carries `pinned-versions`
|
||||
— the versions the procedure was established on.
|
||||
|
||||
## Three fields the verifier requires and this procedure does not otherwise name
|
||||
|
||||
Write them by hand. `verify-tech-log-tree.py` counts each as an error when missing.
|
||||
@@ -141,12 +157,19 @@ Do not create one Topic per source file or module. A directory is not a Topic.
|
||||
- **Open Question** — no answer yet, the design turns on the answer, and there is a next
|
||||
verification and a closing criterion.
|
||||
- **Decision** — the project actually chose a direction, with grounds and an accepted cost.
|
||||
- **Setup** — a procedure the reader runs to stand the environment up. Carries
|
||||
`pinned-versions` instead of a verification date, and always belongs to a project.
|
||||
|
||||
The independence test decides all five:
|
||||
The independence test decides the first five:
|
||||
|
||||
> Delete this record and fold it into a related Case or Concept as one section. If
|
||||
> understanding, decisions, and reuse are unchanged, it is not an independent record.
|
||||
|
||||
Setup does not answer that question, because folding a procedure into a Case is exactly what
|
||||
this kind exists to stop — the commands end up in a plain-text `검증 환경` field where they
|
||||
cannot be copied. Ask instead who runs it. If only the author ever will, it is that Case's
|
||||
environment, not a record.
|
||||
|
||||
Branches may be empty. Symmetry is not a quality goal. Neither is volume — a large
|
||||
denominator justifies a long `final/document.md`, not a long tree.
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
| Reference | 다음 프로젝트에도 적용할 규칙이며 적용 조건과 예외가 있다 | Case 결론을 선언문으로 바꾼 것 |
|
||||
| Question | 답이 아직 없고, 답에 따라 설계가 달라지며, 다음 검증과 종료 기준이 있다 | 실행하지 않은 테스트 목록, 막연한 "다른 방법은?" |
|
||||
| Decision | 대안 중 프로젝트가 실제 방향을 정했고 근거와 감수한 비용이 있다 | 기술이 존재한다는 사실, 권장사항, 아직 정하지 않은 방향 |
|
||||
| Setup | 남이 자기 기계에서 실행할 명령과 구성 값이 있고 프로젝트가 정해져 있다 | 글쓴이만 다시 돌릴 재현 순서(그 Case 의 재현 조건이다), 명령 없는 구성 설명 |
|
||||
|
||||
## Case 를 언제 합치나
|
||||
|
||||
|
||||
@@ -40,6 +40,13 @@
|
||||
- [ ] The title names a mechanism, not an absence, a count, or an analysis-scope fact.
|
||||
- [ ] No analysis section number or finding grade survives in the title.
|
||||
|
||||
## Setup
|
||||
|
||||
- [ ] Someone other than the author has to run it; it is not one Case's reproduction steps.
|
||||
- [ ] The commands and configuration values come from the analysis, not from memory.
|
||||
- [ ] `pinned-versions` names the versions the procedure was established on.
|
||||
- [ ] A project is set. No verification date is invented to stand in for the versions.
|
||||
|
||||
## Reference
|
||||
|
||||
- [ ] The rule is reusable beyond the originating incident.
|
||||
|
||||
@@ -102,7 +102,8 @@
|
||||
"classification": "대안을 두고 프로젝트가 실제로 고른 방향이다",
|
||||
"relations": ["case:eager-to-one-n-plus-one"]
|
||||
}
|
||||
]
|
||||
],
|
||||
"setup": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -149,4 +150,5 @@
|
||||
- 후보 셋 중 하나만 글감이 됐다. `MERGE_INTO` 와 `KEEP_IN_SSOT` 이 없는 분해는 선별하지 않은 분해다.
|
||||
- Concept 은 Case 를 먼저 고른 뒤에 그것을 읽는 데 필요해서 더했다.
|
||||
- Question 에 `decision-criterion` 이 있다. 무엇이 나오면 닫는지를 적지 않으면 검증을 마쳐도 열려 있다.
|
||||
- 다섯 종류를 억지로 채우지 않아도 된다. 여기서 다섯이 다 있는 것은 실제로 다섯이 있었기 때문이다.
|
||||
- 여섯 종류를 억지로 채우지 않아도 된다. 여기서 다섯이 다 있는 까닭은 실제로 다섯이 있었기
|
||||
때문이고, 여섯 번째인 `setup` 은 비어 있다 — 이 프로젝트에는 남이 따라 할 절차가 없었다.
|
||||
|
||||
@@ -90,8 +90,14 @@ python3 scripts/studio-body.py <기록.md> --key <저장소 key>=<서버가 준
|
||||
줄이 모자란 채로 채우면 뒤엣것이 조용히 버려진다. 셀렉터와 배치 실행 방법은
|
||||
[references/playwright-recipes.md](references/playwright-recipes.md).
|
||||
|
||||
**본문이 없는 세 종류(Reference·Question·Decision)의 칸은 평문으로 렌더링된다.** 백틱과
|
||||
파이프가 글자 그대로 보이고, 줄바꿈은 `<br>` 로만 살아난다.
|
||||
**본문이 없는 세 종류(Reference·Question·Decision)의 칸은 마크다운 블록 파서를 안 거친다.**
|
||||
그래도 전부 글자로 나오지는 않는다 — 렌더러(`tech-log-frontend` 의
|
||||
`presentation/shared/public-render/prose-text.tsx`)가 **백틱 쌍은 인라인 `<code>` 로** 살리고,
|
||||
빈 줄은 문단으로, 한 줄 바꿈은 `<br>` 로 남긴다. **글자 그대로 나오는 것은 별표·파이프·`#`·
|
||||
코드펜스·인용 표지 `>` 다.** 백틱을 빼지 않는다 — 빼면 식별자가 민무늬로 나온다.
|
||||
|
||||
**환경 구성에도 되풀이 칸이 하나 있다.** `고정한 버전` 은 줄마다 `이름`·`버전` 입력 둘이고
|
||||
「버전 추가」 버튼으로 늘린다. 여기도 줄 수를 먼저 맞추고 값을 넣는다.
|
||||
|
||||
### 4. 저장한다
|
||||
|
||||
@@ -128,16 +134,26 @@ python3 scripts/verify-tech-log-tree.py <프로젝트>
|
||||
|
||||
## 시험용 초안을 남기지 않는다
|
||||
|
||||
확인하려고 만든 작업본은 지운다. **다섯 종류 전부 삭제 경로가 있다** —
|
||||
`tech-log-backend` @ `a000f87` 의 `ManagementDocumentController` 에서 센 것이다.
|
||||
확인하려고 만든 작업본은 지운다. **여섯 종류 전부 삭제 경로가 있다.** 앞의 다섯은
|
||||
`tech-log-backend` @ `a000f87` 의 `ManagementDocumentController` 에서 셌고, 환경 구성은
|
||||
`tech-log-frontend` @ `9e5642c` 의 `management-api.openapi.yaml:606-628`(`deleteSetupDraft`)
|
||||
에서 읽었다. 경로 앞머리가 줄마다 다른 것은 두 문서가 각자 적는 대로 옮겼기 때문이다.
|
||||
|
||||
| 종류 | 경로 |
|
||||
|---|---|
|
||||
| Case | `DELETE /v1/studio/cases/{id}` (`:72`) |
|
||||
| Reference | `DELETE /v1/studio/references/{id}` (`:81`) |
|
||||
| Concept | `DELETE /v1/studio/concepts/{id}` (`:95`) |
|
||||
| Question | `DELETE /v1/studio/questions/{id}` (`:114`) |
|
||||
| Decision | `DELETE /v1/studio/projects/{id}/decisions/{decisionId}` (`:123`) |
|
||||
| 종류 | 경로 | 본문 |
|
||||
|---|---|---|
|
||||
| Case | `DELETE /v1/studio/cases/{id}` (`:72`) | `{"expectedVersion": <저장 버전>}` |
|
||||
| Reference | `DELETE /v1/studio/references/{id}` (`:81`) | `{"expectedVersion": <저장 버전>}` |
|
||||
| Concept | `DELETE /v1/studio/concepts/{id}` (`:95`) | `{"expectedVersion": <저장 버전>}` |
|
||||
| Setup | `DELETE /api/v1/studio/setups/{id}` | `{"expectedVersion": <저장 버전>}` |
|
||||
| Question | `DELETE /v1/studio/questions/{id}` (`:114`) | `{"expectedVersion": <저장 버전>}` |
|
||||
| Decision | `DELETE /v1/studio/projects/{id}/decisions/{decisionId}` (`:123`) | `{"expectedVersion": <저장 버전>}` |
|
||||
|
||||
**본문 없이 부르면 204 가 아니라 422 다.** `ExpectedVersionRequest` 가 없으면
|
||||
`REQUEST_VALIDATION_FAILED` / `Request body is malformed` 로 거절된다. 헤더에는
|
||||
`X-CSRF-TOKEN` 이 있어야 한다. 여섯 줄 다 같고, 전에 이 표는 경로만 적고 본문을 적지 않았다.
|
||||
|
||||
환경 구성 줄은 2026-09-12 에 시험 작업본 하나를 만들고 이 경로로 지워 204 를 받아 확인했다
|
||||
(계약 `9e5642c`).
|
||||
|
||||
**전에 이 자리에 「Decision 은 계약에 삭제 경로가 없다」고 적혀 있었고 그것은 틀렸다.**
|
||||
확인 없이 적힌 문장이 옮겨 다녔다 — 이 배치에서 그 문장을 코드 주석과 보고서로 다시 옮긴
|
||||
|
||||
@@ -17,19 +17,23 @@
|
||||
|
||||
종류를 라디오로 고르고 `작업본 만들기` 를 누른다. 라디오의 이름은 화면에 보이는 그대로다.
|
||||
|
||||
| 종류 | 라디오 이름 | 화면이 적어 놓은 칸 |
|
||||
| 계약 `kind` | 라디오 이름 | 화면이 적어 놓은 칸 |
|
||||
|---|---|---|
|
||||
| Case | `Case` | 문제 · 결론 · 환경 · 재현 · 본문 |
|
||||
| Reference | `Reference` | 목적 · 규칙 · 적용 조건 · 예외 · 예시 |
|
||||
| Concept | **`개념`** | 기준 버전 · 본문 |
|
||||
| Question | `Question` | 상태 · 사실 · 가정 · 미지수 · 선택지 |
|
||||
| Decision | `Decision` | 상태 · 결정일 · 결정문 · 판단 이유 · 영향 · 근거 |
|
||||
| `CASE` | `검증 기록` | 문제 · 결론 · 환경 · 재현 · 본문 |
|
||||
| `REFERENCE` | `적용 기준` | 목적 · 규칙 · 적용 조건 · 예외 · 예시 |
|
||||
| `CONCEPT` | `동작 원리` | 기준 버전 · 본문 |
|
||||
| `SETUP` | `환경 구성` | 버전 · 본문 |
|
||||
| `QUESTION` | `열린 질문` | 상태 · 사실 · 가정 · 미지수 · 선택지 |
|
||||
| `PROJECT_DECISION` | `설계 결정` | 상태 · 결정일 · 결정문 · 판단 이유 · 영향 · 근거 |
|
||||
|
||||
**Concept 만 라디오 이름이 한글(`개념`)이다.** 나머지 넷은 영어다.
|
||||
**여섯 다 한글이다.** 이 표는 전에 「Concept 만 라디오 이름이 한글(`개념`)이다. 나머지 넷은
|
||||
영어다」라고 적고 있었고 그것은 낡았다 — 2026-09-12 에 `/studio/documents/new` 를 열어 여섯
|
||||
라디오의 이름을 그대로 읽었다. **화면 문구는 이렇게 조용히 바뀐다.** 표를 외워서 넣지 말고
|
||||
스냅샷으로 읽은 이름을 쓴다.
|
||||
|
||||
```js
|
||||
await page.goto('https://hyeonworks.com/studio/documents/new');
|
||||
await page.getByRole('radio', { name: /^Reference/ }).check();
|
||||
await page.getByRole('radio', { name: /^환경 구성/ }).check();
|
||||
await page.getByRole('button', { name: '작업본 만들기' }).click();
|
||||
// 주소가 /studio/documents/<uuid>/edit 로 바뀐다. 그 uuid 를 기록 frontmatter 에 적는다
|
||||
```
|
||||
|
||||
@@ -17,12 +17,32 @@
|
||||
|---|---|---|
|
||||
| **Case** | `관계` · `문제` · `결론` · `검증 환경` · `재현 조건` · `본문` | 있음 |
|
||||
| **Concept** | `관계` · `본문` | 있음 |
|
||||
| **Setup** | `관계` · `본문` — 본문 안의 `##` 는 칸이 아니다 | 있음 |
|
||||
| **Reference** | `관계` · `목적` · `규칙` · `적용 조건` · `예외` · `예시` | 없음 |
|
||||
| **Question** | `관계` · `사실` · `가정` · `미지수` · `제약` · `선택지` · `다음 검증` | 없음 |
|
||||
| **Decision** | **`근거`** · `결정문` · `판단 이유` · `영향` | 없음 |
|
||||
|
||||
**Decision 만 관계 절 이름이 `근거` 다.** 그리고 근거가 1개 이상 없으면 게시가 거절된다.
|
||||
|
||||
## 환경 구성은 `##` 를 칸으로 세지 않는다
|
||||
|
||||
다른 다섯은 `## <이름>` 하나가 칸 하나다. 환경 구성은 화면 칸이 `고정한 버전` 과
|
||||
`절차 Markdown` 둘뿐이고, **`## 실행 절차` · `## 구성 값` · `## 확인 방법` 은 그 `절차 Markdown`
|
||||
안의 소제목**이다. 계약이 `bodyMarkdown` 설명에 「절 이름을 강제하지 않는다 — 프로젝트마다
|
||||
셋업의 모양이 다르다」고 적는다.
|
||||
|
||||
그래서 넣는 법이 다르다. 기록의 `## 본문` 아래 전체가 `bodyMarkdown` 한 칸으로 들어가고, 화면
|
||||
칸에 따로 옮길 값은 `고정한 버전` 하나뿐이다.
|
||||
|
||||
| 기록 `.md` | 어디로 |
|
||||
|---|---|
|
||||
| frontmatter `pinnedVersions[]` | `고정한 버전` — 줄마다 `이름`·`버전` 입력 둘 |
|
||||
| `## 본문` 의 `<!-- body:start -->`~`<!-- body:end -->` | `절차 Markdown` 통째로 |
|
||||
| `## 관계` | `관계` |
|
||||
|
||||
작업본을 만들면 `절차 Markdown` 이 비어 있지 않다. Studio 가 위의 절 셋을 미리 넣어 두므로,
|
||||
본문을 넣기 전에 그 내용을 지운다.
|
||||
|
||||
## 화면의 라벨은 기록의 절 이름과 다르다
|
||||
|
||||
**이것이 이 문서에서 가장 자주 틀리는 자리다.** 위 표는 기록 `.md` 가 쓰는 이름이고,
|
||||
@@ -40,8 +60,9 @@ Reference 에서 실제로 확인한 대응이다.
|
||||
| `예시` | `예시` |
|
||||
| `관계` | `관계` |
|
||||
|
||||
종류 이름도 자리마다 다르다. 상태 레일은 Reference 를 **`적용 기준`** 이라고 부르고,
|
||||
`새 문서` 화면의 라디오는 `Reference` 다.
|
||||
종류 이름도 화면마다 달랐다. 상태 레일이 Reference 를 **`적용 기준`** 이라고 부르는 동안
|
||||
`새 문서` 화면의 라디오는 `Reference` 였다. 2026-09-12 에는 `새 문서` 쪽도 여섯 다 한글이다 —
|
||||
검증 기록 · 적용 기준 · 동작 원리 · 환경 구성 · 열린 질문 · 설계 결정.
|
||||
|
||||
**화면을 먼저 스냅샷으로 읽고 그 라벨을 쓴다.** 이 표를 외워서 넣지 않는다 — 화면이 바뀌면
|
||||
표가 먼저 낡는다.
|
||||
@@ -65,6 +86,7 @@ Reference 에서 실제로 확인한 대응이다.
|
||||
| `slug` · `title` | 화면 위쪽의 슬러그·제목 칸 |
|
||||
| `topic` · `topicName` · `project` | 주제·프로젝트 선택 |
|
||||
| `basisVersion` (Concept) | 기준 버전 칸 |
|
||||
| `pinnedVersions` (Setup) | 고정한 버전 칸. `name` · `version` 이 한 줄 |
|
||||
| `questionStatus` (Question) · `decisionStatus` (Decision) | 상태 선택 |
|
||||
| `assets[].file` | 올릴 Asset 파일 |
|
||||
| `assets[].key` | 본문 `:::evidence key` 의 저장소 쪽 이름. 올리면 서버 키로 바뀐다 |
|
||||
@@ -82,7 +104,7 @@ Decision 의 도메인 `ACCEPTED` 가 화면에서는 `ADOPTED` 로 보인다.
|
||||
- 줄바꿈은 `<br>` 로만 살아난다
|
||||
- 나열은 쉼표로 잇지 말고 `이름 : 값` 으로 줄을 나눈다
|
||||
|
||||
코드·표·그림이 필요하면 짝이 되는 Case 나 Concept 에 담고 `관계` 로 가리킨다.
|
||||
코드·표·그림이 필요하면 짝이 되는 Case·Concept·Setup 에 담고 `관계` 로 가리킨다.
|
||||
|
||||
## 본문을 넣기 전에
|
||||
|
||||
|
||||
@@ -103,8 +103,18 @@ const ACRONYM_OK = new Set([
|
||||
'CANCEL','GREEN','RED','TODO','NOTE','CODE','BLOCK','AND','OR','NOT','NULL','TRUE','FALSE',
|
||||
]);
|
||||
|
||||
// YAML frontmatter 는 글쓴이가 고르는 문장이 아니라 **계약에서 옮겨 온 메타데이터**다.
|
||||
// 여기에 문장 규칙을 걸면 고칠 수 없는 error 가 나고, 글쓴이는 통과하려고 **칸을 지운다** —
|
||||
// 실제로 `topicName: 측정이 거짓말하는 자리` 가 spatial-metaphor 로 잡혀 그 주제의 기록들이
|
||||
// 그 칸 없이 저장됐다. 줄 번호는 유지한 채 비운다.
|
||||
function stripFrontMatter(src) {
|
||||
const m = /^---\r?\n[\s\S]*?\r?\n---(\r?\n|$)/.exec(src);
|
||||
if (!m) return src;
|
||||
return m[0].replace(/[^\n]/g, ' ') + src.slice(m[0].length);
|
||||
}
|
||||
|
||||
function strip(src) {
|
||||
return src
|
||||
return stripFrontMatter(src)
|
||||
.replace(/```[\s\S]*?```/g, (m) => m.replace(/[^\n]/g, ' '))
|
||||
.replace(/`[^`\n]*`/g, (m) => ' '.repeat(m.length))
|
||||
// 표와 인용은 「쓰지 않는다」 예시가 사는 자리다. 규칙을 적은 문서가 그 규칙을 어긴 것으로
|
||||
|
||||
@@ -36,19 +36,50 @@ metadata:
|
||||
|
||||
## 일곱 단계
|
||||
|
||||
| # | 단계 | 스킬 | 산출물 |
|
||||
|---|---|---|---|
|
||||
| S1 | 코드베이스 → SSOT | `analyzing-codebase-for-tech-log` | `docs/<프로젝트>/final/document.md` |
|
||||
| S2 | SSOT → 분해 계약 | `deriving-tech-log-root-tree` | `docs/<프로젝트>/tech-log-studio/tech-log-tree.json` |
|
||||
| S3 | 글감 → 기록 | `writing-tech-log-records` | `.../<주제>/<종류>/<기록>.md` |
|
||||
| S4 | 기록 → 그림 | `technical-visualizer` | `final/assets/<이름>/` · `final/.techviz/<이름>/` |
|
||||
| S5 | AI 티 제거 | `rewriting-technical-prose-naturally` | 같은 기록 파일 (제자리 수정) |
|
||||
| S6 | 일한 사람의 목소리 | `writing-as-the-person-who-did-it` | 같은 기록 파일 (제자리 수정) |
|
||||
| S7 | Studio 저장 | `publishing-tech-log-to-studio` | Studio 작업본 + `studio:` URL. **게시하지 않는다** |
|
||||
| # | 단계 | 스킬 | 에이전트 | 산출물 |
|
||||
|---|---|---|---|---|
|
||||
| S1 | 코드베이스 → SSOT | `analyzing-codebase-for-tech-log` | `ssot-analyst` | `docs/<프로젝트>/final/document.md` |
|
||||
| S2 | SSOT → 분해 계약 | `deriving-tech-log-root-tree` | `tree-deriver` | `docs/<프로젝트>/tech-log-studio/tech-log-tree.json` |
|
||||
| S3 | 글감 → 기록 | `writing-tech-log-records` | `record-writer` | `.../<주제>/<종류>/<기록>.md` |
|
||||
| S4 | 기록 → 그림 | `technical-visualizer` | `diagram-maker` | `final/assets/<이름>/` · `final/.techviz/<이름>/` |
|
||||
| S5 | AI 티 제거 | `rewriting-technical-prose-naturally` | `prose-rewriter` | 같은 기록 파일 (제자리 수정) |
|
||||
| S6 | 일한 사람의 목소리 | `writing-as-the-person-who-did-it` | `voice-writer` | 같은 기록 파일 (제자리 수정) |
|
||||
| S7 | Studio 저장 | `publishing-tech-log-to-studio` | `studio-validator` | Studio 작업본 + `studio:` URL. **게시하지 않는다** |
|
||||
|
||||
단계마다의 입력·관문·원장 칸은 [references/stage-contracts.md](references/stage-contracts.md).
|
||||
서브에이전트에 그대로 넣는 프롬프트는 [references/subagent-prompts.md](references/subagent-prompts.md).
|
||||
|
||||
## 에이전트를 그때그때 만들지 않는다
|
||||
|
||||
단계마다 맡을 에이전트가 `.claude/agents/` 에 있다. `Agent` 도구의 `subagent_type` 에 위 표의
|
||||
이름을 준다.
|
||||
|
||||
```
|
||||
Agent(subagent_type="record-writer", prompt=<references/subagent-prompts.md 의 S3>)
|
||||
```
|
||||
|
||||
프롬프트만 새로 써서 일반 에이전트를 띄우면 **어떤 규칙으로 일했는지가 어디에도 안 남는다.**
|
||||
`skillEcho` 는 「스킬을 열었다」를 증명하지만 **누가 열었는지는 증명하지 않는다** — 매번 새로
|
||||
띄운 에이전트도 SKILL.md 를 읽고 한 줄을 옮겨 적을 수 있다. 그래서 원장의 `runBy` 가 이름을
|
||||
담고, 검사기가 계약과 대조한 뒤 그 `.md` 가 실재하는지까지 본다.
|
||||
|
||||
**배정의 정본은 `scripts/verify-pipeline-run.py` 의 `STAGES` 다.** 틀(`templates/run.json`)에도
|
||||
같은 값이 적혀 있고, 둘이 갈리면 시험이 잡는다.
|
||||
|
||||
에이전트 정의는 그 단계의 「하는 일 · **안 하는 일** · 관문 · 보고」를 적는다. 프롬프트가
|
||||
그것을 되풀이하지 않아도 되는 것이 이 방식의 값이다 — 프롬프트는 **이번 런의 입력 경로와
|
||||
글감**만 준다.
|
||||
|
||||
**단계가 아닌 에이전트가 넷 더 있다.** 기록 한 편을 놓고 역할을 가른 것이라 아무 단계에도
|
||||
붙지 않는다. 부를지는 사람이 정하고, 원장의 단계 칸에는 안 들어간다.
|
||||
|
||||
| 에이전트 | 언제 | 안 하는 일 |
|
||||
|---|---|---|
|
||||
| `source-auditor` | S3 앞. 원본의 주장을 `관측`·`추론`·`미검증`으로 가른 표를 만든다 | 기록을 안 쓴다 |
|
||||
| `fact-reviewer` | 쓴 뒤. 결과 문장을 원문과 한 글자씩 역대조한다 | 파일을 안 고친다 |
|
||||
| `reader-reviewer` | 쓴 뒤. 제목·요약·목차만 보고 30초 안에 읽히는지 본다 | 본문을 안 연다 |
|
||||
| `setup-runner` | Setup 을 쓴 뒤. 손으로 끝까지 칠 수 있는지 읽는다 | 실제로 치지는 않는다 |
|
||||
|
||||
## 순서가 고정된 곳
|
||||
|
||||
세 자리는 바꾸면 결과가 틀어진다.
|
||||
@@ -108,8 +139,15 @@ python3 scripts/verify-pipeline-run.py --init runs/<프로젝트>/<runId>/run.js
|
||||
`runId` 는 `YYYY-MM-DD-HHMM` 이다. 원장의 틀은
|
||||
[templates/run.json](templates/run.json) 이고, `--init` 이 그 틀을 채워 놓는다.
|
||||
|
||||
`--init` 은 단계마다 `skillRevision` 도 적는다 — 그 시점 그 스킬의 커밋이다. 스킬은
|
||||
나중에 고쳐지고, 그러면 이 런의 영수증(`skillEcho`)이 현재 SKILL.md 에서 사라진다.
|
||||
그 커밋이 적혀 있으면 검사기가 이력을 훑지 않고 그것 하나로 대조한다. 작업 트리가 그
|
||||
커밋과 다르면 `null` 이다 — 모르는 리비전을 지어내지 않는다. 칸이 없는 옛 원장은
|
||||
이력 훑기로 떨어지고, 그것도 정상이다.
|
||||
|
||||
### 1. 단계마다 서브에이전트를 띄운다
|
||||
|
||||
에이전트는 위 표의 것을 쓴다 — `Agent(subagent_type="<이름>", ...)`. 새로 만들지 않는다.
|
||||
프롬프트는 [references/subagent-prompts.md](references/subagent-prompts.md) 의 것을 쓴다.
|
||||
프롬프트에 **반드시** 들어가야 하는 넷이 있다.
|
||||
|
||||
@@ -135,6 +173,12 @@ error 0 이어야 런이 끝난 것이다. 이 검사기가 보는 것은 결과
|
||||
준수**다 — 단계가 빠졌는지, 스킬 영수증이 그 스킬의 실제 문장인지, 관문이 돌았고 종료 코드가
|
||||
0 이었는지, 적어 낸 산출물이 디스크에 있는지.
|
||||
|
||||
영수증은 셋이 아니라 **넷으로 갈린다.** 지금 SKILL.md 에 있으면 통과, 그 스킬의 과거
|
||||
커밋에만 있으면 warn(그 뒤에 스킬이 고쳐졌다 — 어느 커밋에 있었는지 함께 적는다), 어느
|
||||
판에도 없으면 error, 과거를 볼 수 없었으면(git 이 없다 · 이력 상한에 걸렸다) 또 다른
|
||||
warn 이다. **warn 은 통과가 아니다** — 요약 줄이 「대조 못 한 영수증」을 따로 센다.
|
||||
지난 런의 영수증이 warn 으로 바뀌었다고 원장을 고쳐 쓰지 않는다. 그것은 영수증이다.
|
||||
|
||||
### 3. 프로젝트 검사기를 돌린다
|
||||
|
||||
```bash
|
||||
@@ -161,3 +205,4 @@ python3 scripts/check-figure-text.py <프로젝트>
|
||||
| SSOT 에 없는 인용이 있다 | S3 이 앞 기록에서 코드를 옮겨 적었다 | `check_evidence` 종료 코드 ≠ 0 |
|
||||
| 기록이 색인에 없다 | S2 를 건너뛰고 S3 을 했다 | S2 가 `SKIPPED` 인데 사유가 없다 |
|
||||
| Studio 에서 그림이 안 보인다 | S7 이 Asset 을 올리기 전에 본문을 넣었다 | S7 관문에 미리보기 확인이 없다 |
|
||||
| 스킬은 열었는데 결과가 그 역할 같지 않다 | 일반 에이전트를 띄웠다 | `runBy` 가 계약 이름이 아니다 |
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
# 단계 계약
|
||||
|
||||
단계마다 넷을 정한다 — **입력 · 스킬 · 관문 · 산출물.** 원장에 적히는 것도 이 넷이다.
|
||||
단계마다 다섯을 정한다 — **입력 · 스킬 · 에이전트 · 관문 · 산출물.** 원장에 적히는 것도
|
||||
이 다섯이다.
|
||||
|
||||
**에이전트는 그때그때 만들지 않는다.** `.claude/agents/<이름>.md` 가 그 단계의 「하는 일 ·
|
||||
안 하는 일 · 관문 · 보고」를 적고 있고, `Agent` 도구의 `subagent_type` 에 그 이름을 준다.
|
||||
배정의 정본은 `scripts/verify-pipeline-run.py` 의 `STAGES` 이고, 원장의 `runBy` 가 그 이름을
|
||||
담는다 — 검사기가 계약과 대조한 뒤 그 `.md` 가 실재하는지까지 본다.
|
||||
|
||||
관문은 종료 코드가 0 이어야 지난 것이다. 0 이 아니면 그 단계는 `FAILED` 이고 다음 단계로
|
||||
넘어가지 않는다.
|
||||
@@ -12,6 +18,7 @@
|
||||
| | |
|
||||
|---|---|
|
||||
| 스킬 | `analyzing-codebase-for-tech-log` |
|
||||
| 에이전트 | `ssot-analyst` — `.claude/agents/ssot-analyst.md` |
|
||||
| 입력 | 분석 대상 저장소 경로(사용자가 준다) · 그 저장소의 `AGENTS.md`(있으면) |
|
||||
| 산출물 | `docs/<프로젝트>/final/document.md` |
|
||||
| 관문 | `python3 scripts/verify-project-layout.py <프로젝트>` |
|
||||
@@ -62,6 +69,7 @@ SSOT 가 코드와 **어긋나는** 것을 찾으면 그것은 보강 후보가
|
||||
| | |
|
||||
|---|---|
|
||||
| 스킬 | `deriving-tech-log-root-tree` |
|
||||
| 에이전트 | `tree-deriver` — `.claude/agents/tree-deriver.md` |
|
||||
| 입력 | `docs/<프로젝트>/final/document.md` **하나** |
|
||||
| 산출물 | `docs/<프로젝트>/tech-log-studio/tech-log-tree.json` |
|
||||
| 관문 | `python3 scripts/build-tech-log-tree.py <프로젝트>` → `python3 scripts/verify-tech-log-tree.py <프로젝트>` (error 0) |
|
||||
@@ -100,6 +108,7 @@ SSOT 가 코드와 **어긋나는** 것을 찾으면 그것은 보강 후보가
|
||||
| | |
|
||||
|---|---|
|
||||
| 스킬 | `writing-tech-log-records` |
|
||||
| 에이전트 | `record-writer` — `.claude/agents/record-writer.md` |
|
||||
| 입력 | `tech-log-tree.json` 의 노드 하나 · 그 노드의 `source` 앵커가 가리키는 SSOT 절 |
|
||||
| 산출물 | `docs/<프로젝트>/tech-log-studio/<주제>/<종류>/<기록>.md` |
|
||||
| 관문 | 아래 셋 |
|
||||
@@ -127,6 +136,7 @@ node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs <프로
|
||||
| | |
|
||||
|---|---|
|
||||
| 스킬 | `technical-visualizer` |
|
||||
| 에이전트 | `diagram-maker` — `.claude/agents/diagram-maker.md` |
|
||||
| 입력 | S3 이 쓴 기록 `.md`(무엇을 그릴지) · 그 기록의 `source` 앵커가 가리키는 SSOT 절(그림의 사실) |
|
||||
| 산출물 | `final/.techviz/<이름>/{context.json,prompt.md,spec.json}` · `final/assets/<이름>/` |
|
||||
| 관문 | `techviz lint` · `check-figure-text.py` · `check-figure-overlap.py` · `preview-figure.py` 로 눈 확인 |
|
||||
@@ -173,6 +183,7 @@ python3 scripts/check-figure-overlap.py --file 그림.svg
|
||||
| | |
|
||||
|---|---|
|
||||
| 스킬 | `rewriting-technical-prose-naturally` |
|
||||
| 에이전트 | `prose-rewriter` — `.claude/agents/prose-rewriter.md` |
|
||||
| 입력 | S3·S4 를 지난 기록 `.md` (제자리 수정) |
|
||||
| 산출물 | 같은 파일 |
|
||||
| 관문 | `check_prose.mjs` error 0 · `style_profile.mjs` · S3 관문 재실행 |
|
||||
@@ -202,6 +213,7 @@ node $S/style_profile.mjs <기록.md>
|
||||
| | |
|
||||
|---|---|
|
||||
| 스킬 | `writing-as-the-person-who-did-it` |
|
||||
| 에이전트 | `voice-writer` — `.claude/agents/voice-writer.md` |
|
||||
| 입력 | S5 를 지난 기록 `.md` · **그리고 그 기록의 상류 자료** (SSOT · 커밋 메시지 · 주석 · `확인하지 못한 것` 칸) |
|
||||
| 산출물 | 같은 파일 |
|
||||
| 관문 | `check_voice.mjs` · `check_prose.mjs` 재실행 · S3 관문 재실행 |
|
||||
@@ -228,6 +240,7 @@ node .agents/skills/rewriting-technical-prose-naturally/scripts/check_prose.mjs
|
||||
| | |
|
||||
|---|---|
|
||||
| 스킬 | `publishing-tech-log-to-studio` |
|
||||
| 에이전트 | `studio-validator` — `.claude/agents/studio-validator.md` |
|
||||
| 입력 | S6 을 지난 기록 `.md` · frontmatter `assets:` 가 가리키는 SVG |
|
||||
| 산출물 | Studio 작업본. 기록 frontmatter 의 `id`·`studio:` |
|
||||
| 관문 | 상태 레일이 `저장됨` · `python3 scripts/build-tech-log-tree.py` → `verify-tech-log-tree.py` |
|
||||
@@ -243,12 +256,12 @@ Asset 을 본문보다 먼저 올린다. 순서를 뒤집으면 미리보기가
|
||||
|
||||
## 관문 요약
|
||||
|
||||
| 단계 | 명령 |
|
||||
|---|---|
|
||||
| S1 | `verify-project-layout.py <프로젝트>` |
|
||||
| S2 | `build-tech-log-tree.py` → `verify-tech-log-tree.py` (error 0) |
|
||||
| S3 | `studio-body.py` → `check_body.mjs` · `check_prose.mjs` · `check_evidence.mjs --repo` |
|
||||
| S4 | `techviz lint` · `check-figure-text.py` · `check-figure-overlap.py` · `preview-figure.py` (눈 확인) |
|
||||
| S5 | `check_prose.mjs` (error 0) · `style_profile.mjs` · S3 관문 |
|
||||
| S6 | `check_voice.mjs` · `check_prose.mjs` · S3 관문 |
|
||||
| S7 | `저장됨` 확인 · `build-tech-log-tree.py` → `verify-tech-log-tree.py` |
|
||||
| 단계 | 에이전트 | 명령 |
|
||||
|---|---|---|
|
||||
| S1 | `ssot-analyst` | `verify-project-layout.py <프로젝트>` |
|
||||
| S2 | `tree-deriver` | `build-tech-log-tree.py` → `verify-tech-log-tree.py` (error 0) |
|
||||
| S3 | `record-writer` | `studio-body.py` → `check_body.mjs` · `check_prose.mjs` · `check_evidence.mjs --repo` |
|
||||
| S4 | `diagram-maker` | `techviz lint` · `check-figure-text.py` · `check-figure-overlap.py` · `preview-figure.py` (눈 확인) |
|
||||
| S5 | `prose-rewriter` | `check_prose.mjs` (error 0) · `style_profile.mjs` · S3 관문 |
|
||||
| S6 | `voice-writer` | `check_voice.mjs` · `check_prose.mjs` · S3 관문 |
|
||||
| S7 | `studio-validator` | `저장됨` 확인 · `build-tech-log-tree.py` → `verify-tech-log-tree.py` |
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
단계마다 에이전트를 하나 띄운다. 아래를 그대로 쓰고 `<...>` 만 바꾼다.
|
||||
|
||||
**에이전트를 새로 만들지 않는다.** 단계마다 맡을 에이전트가 `.claude/agents/` 에 있고,
|
||||
`Agent` 도구의 `subagent_type` 에 그 이름을 준다. 절마다 첫 줄에 적혀 있다.
|
||||
|
||||
```
|
||||
Agent(subagent_type="record-writer", prompt=<아래 S3 프롬프트>)
|
||||
```
|
||||
|
||||
에이전트 정의가 그 단계의 「하는 일 · 안 하는 일 · 관문 · 보고」를 이미 적고 있다. 그래서
|
||||
프롬프트는 **이번 런의 입력 경로와 글감**을 준다 — 아래 것을 그대로 쓰되, 정의와 어긋나는
|
||||
지시를 프롬프트로 덮어쓰지 않는다.
|
||||
|
||||
## 모든 프롬프트에 들어가는 넷
|
||||
|
||||
1. **스킬 이름과 「SKILL.md 를 끝까지 먼저 읽어라」.** 요약을 주지 않는다. 요약을 주면
|
||||
@@ -42,6 +53,8 @@ S1 처럼 산출물이 `.md` 인 단계는 그래서 한 줄을 더한다.
|
||||
|
||||
## S1 — 코드베이스 → SSOT
|
||||
|
||||
**에이전트:** `ssot-analyst` — `subagent_type="ssot-analyst"`
|
||||
|
||||
```
|
||||
너는 Tech Log 파이프라인의 1단계를 맡는다.
|
||||
|
||||
@@ -72,6 +85,8 @@ skillEcho 는 방금 읽은 SKILL.md 에서 네 작업에 해당하는 규칙
|
||||
|
||||
## S2 — SSOT → 분해 계약
|
||||
|
||||
**에이전트:** `tree-deriver` — `subagent_type="tree-deriver"`
|
||||
|
||||
```
|
||||
너는 Tech Log 파이프라인의 2단계를 맡는다.
|
||||
|
||||
@@ -104,6 +119,8 @@ error 0 까지 고쳐라.
|
||||
|
||||
## S3 — 글감 → 기록
|
||||
|
||||
**에이전트:** `record-writer` — `subagent_type="record-writer"`
|
||||
|
||||
```
|
||||
너는 Tech Log 파이프라인의 3단계를 맡는다.
|
||||
|
||||
@@ -112,6 +129,11 @@ error 0 까지 고쳐라.
|
||||
record-kinds.md · writing-each-kind.md · body-syntax.md · code-tables-diagrams.md ·
|
||||
explaining.md · ai-tells.md · choosing-a-diagram.md.
|
||||
|
||||
**종류가 Setup 이면 본문을 쓰기 전에 `Skill` 도구로 `writing-practitioner-guides` 를 연다.**
|
||||
명령을 어떤 형태로 쓸지는 그 스킬이 정한다 — 사람이 직접 치는 실습 가이드이지 에이전트가
|
||||
실행하기 편한 명령이 아니다. **그 스킬을 못 열면 Setup 을 쓰지 말고 그 사실을 돌려줘라.**
|
||||
안 열고 쓴 Setup 은 검사기를 다 지나면서도 실행이 중간에 끊긴다 — 실제로 그렇게 나갔다.
|
||||
|
||||
글감: docs/<프로젝트>/tech-log-studio/tech-log-tree.json 의 <주제> / <종류> / "<제목>"
|
||||
그 노드가 PROMOTE 이고 dispositionReview 가 CONFIRMED 인지 먼저 확인해라. 아니면 쓰지 마라.
|
||||
|
||||
@@ -138,6 +160,8 @@ error 0 까지 고쳐라.
|
||||
|
||||
## S4 — 기록 → 그림
|
||||
|
||||
**에이전트:** `diagram-maker` — `subagent_type="diagram-maker"`
|
||||
|
||||
```
|
||||
너는 Tech Log 파이프라인의 4단계를 맡는다.
|
||||
|
||||
@@ -178,6 +202,8 @@ error 0 까지 고쳐라.
|
||||
|
||||
## S5 — AI 티 제거
|
||||
|
||||
**에이전트:** `prose-rewriter` — `subagent_type="prose-rewriter"`
|
||||
|
||||
```
|
||||
너는 Tech Log 파이프라인의 5단계를 맡는다.
|
||||
|
||||
@@ -212,6 +238,8 @@ document-skeleton.md · article-shape.md · korean-tech-blog-register.md.
|
||||
|
||||
## S6 — 일한 사람의 목소리
|
||||
|
||||
**에이전트:** `voice-writer` — `subagent_type="voice-writer"`
|
||||
|
||||
```
|
||||
너는 Tech Log 파이프라인의 6단계를 맡는다.
|
||||
|
||||
@@ -246,6 +274,8 @@ check_voice.mjs 는 목소리가 모자란지 재지 않는다. 지어낸 목소
|
||||
|
||||
## S7 — Studio 저장
|
||||
|
||||
**에이전트:** `studio-validator` — `subagent_type="studio-validator"`
|
||||
|
||||
```
|
||||
너는 Tech Log 파이프라인의 7단계를 맡는다.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"schemaVersion": 2,
|
||||
"runId": "<YYYY-MM-DD-HHMM>",
|
||||
"project": "<프로젝트>",
|
||||
"record": "<docs/<프로젝트>/tech-log-studio/<주제>/<종류>/<기록>.md — 이 런이 만드는 기록>",
|
||||
@@ -10,10 +10,11 @@
|
||||
"id": "S1",
|
||||
"name": "코드베이스 → SSOT",
|
||||
"skill": "analyzing-codebase-for-tech-log",
|
||||
"runBy": "subagent",
|
||||
"runBy": "ssot-analyst",
|
||||
"status": "PENDING",
|
||||
"skipReason": "",
|
||||
"skillEcho": "",
|
||||
"skillRevision": null,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"gates": [],
|
||||
@@ -23,10 +24,11 @@
|
||||
"id": "S2",
|
||||
"name": "SSOT → 분해 계약",
|
||||
"skill": "deriving-tech-log-root-tree",
|
||||
"runBy": "subagent",
|
||||
"runBy": "tree-deriver",
|
||||
"status": "PENDING",
|
||||
"skipReason": "",
|
||||
"skillEcho": "",
|
||||
"skillRevision": null,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"gates": [],
|
||||
@@ -36,10 +38,11 @@
|
||||
"id": "S3",
|
||||
"name": "글감 → 기록",
|
||||
"skill": "writing-tech-log-records",
|
||||
"runBy": "subagent",
|
||||
"runBy": "record-writer",
|
||||
"status": "PENDING",
|
||||
"skipReason": "",
|
||||
"skillEcho": "",
|
||||
"skillRevision": null,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"gates": [],
|
||||
@@ -49,10 +52,11 @@
|
||||
"id": "S4",
|
||||
"name": "기록 → 그림",
|
||||
"skill": "technical-visualizer",
|
||||
"runBy": "subagent",
|
||||
"runBy": "diagram-maker",
|
||||
"status": "PENDING",
|
||||
"skipReason": "",
|
||||
"skillEcho": "",
|
||||
"skillRevision": null,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"gates": [],
|
||||
@@ -62,10 +66,11 @@
|
||||
"id": "S5",
|
||||
"name": "AI 티 제거",
|
||||
"skill": "rewriting-technical-prose-naturally",
|
||||
"runBy": "subagent",
|
||||
"runBy": "prose-rewriter",
|
||||
"status": "PENDING",
|
||||
"skipReason": "",
|
||||
"skillEcho": "",
|
||||
"skillRevision": null,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"gates": [],
|
||||
@@ -75,10 +80,11 @@
|
||||
"id": "S6",
|
||||
"name": "일한 사람의 목소리",
|
||||
"skill": "writing-as-the-person-who-did-it",
|
||||
"runBy": "subagent",
|
||||
"runBy": "voice-writer",
|
||||
"status": "PENDING",
|
||||
"skipReason": "",
|
||||
"skillEcho": "",
|
||||
"skillRevision": null,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"gates": [],
|
||||
@@ -88,10 +94,11 @@
|
||||
"id": "S7",
|
||||
"name": "Studio 저장",
|
||||
"skill": "publishing-tech-log-to-studio",
|
||||
"runBy": "subagent",
|
||||
"runBy": "studio-validator",
|
||||
"status": "PENDING",
|
||||
"skipReason": "",
|
||||
"skillEcho": "",
|
||||
"skillRevision": null,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"gates": [],
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
---
|
||||
name: writing-practitioner-guides
|
||||
description: Use when writing hands-on guides, runbooks, troubleshooting docs, lab walkthroughs, or validation procedures that a human will execute themselves in a terminal over SSH. Symptoms that this applies - the reader is expected to type the commands, the doc teaches how to read a system rather than reporting a result, or a draft contains python -c, embedded JSON parsing, one-liners nobody types by hand, or a config file or YAML authored with printf/echo >>/heredoc instead of an editor.
|
||||
---
|
||||
|
||||
# Writing Practitioner Guides
|
||||
|
||||
## Overview
|
||||
|
||||
**Optimize for human operation, not command compactness.**
|
||||
|
||||
A guide is not a script. The reader types each command, reads its raw output,
|
||||
and decides what to do next. Commands that are efficient for an agent to run
|
||||
once are often useless for a human learning to read a system.
|
||||
|
||||
The failure this prevents: an agent writes `curl … | python3 -c 'import json…'`
|
||||
because it produces a clean answer in one call. The reader gets the answer and
|
||||
learns nothing about the tool they will need at 3am.
|
||||
|
||||
## The boundary
|
||||
|
||||
Three tiers. Pick the lowest one that fits.
|
||||
|
||||
```
|
||||
interactive command → short pipeline → saved script file
|
||||
```
|
||||
|
||||
| Tier | When | Form |
|
||||
|---|---|---|
|
||||
| **interactive** | reading state, one question | `kubectl get pods`, `ss -lntp`, `journalctl -u nginx -n 50` |
|
||||
| **short pipeline** | filtering that a person would actually type | `ps aux \| grep java`, `… \| jq .status`, `for p in a b; do … done` |
|
||||
| **saved script** | it has become a program | write the file with an editor, then run it |
|
||||
|
||||
**Move to a saved script when any of these is true:**
|
||||
- multiple branches (`if`) or nested loops
|
||||
- combining several requests and computing across them
|
||||
- non-trivial JSON transformation
|
||||
- you cannot tell what it does by reading it once
|
||||
- it will be run again later
|
||||
|
||||
For a saved script, the guide says to open an editor, shows the code as a
|
||||
**separate file listing** (not a terminal command), then shows the run command.
|
||||
|
||||
```bash
|
||||
vim scripts/check_sessions.py
|
||||
```
|
||||
```python
|
||||
# file: scripts/check_sessions.py
|
||||
...
|
||||
```
|
||||
```bash
|
||||
python3 scripts/check_sessions.py
|
||||
```
|
||||
|
||||
Terminal command ≠ program source. Never blur them with a heredoc.
|
||||
|
||||
## Files the reader has to understand before changing them
|
||||
|
||||
The tiers above stop at the saved script. The same split reaches further: **any
|
||||
file whose content the reader must read and understand to change it is written
|
||||
in an editor, not assembled by a shell one-liner.** Config files and YAML are
|
||||
that kind of file.
|
||||
|
||||
> **조회·진단·실행은 CLI를 적극 사용하고, 사람이 내용을 이해하면서 작성해야 하는 설정 파일은
|
||||
> 에디터를 사용한다.**
|
||||
|
||||
| Reading or acting on the system → CLI | Authoring a file a human must understand → editor |
|
||||
|---|---|
|
||||
| CPU flags → `grep /proc/cpuinfo`, `lscpu` | cloud-init YAML → `nano kc-lab-1.yaml` |
|
||||
| service state → `systemctl status` | systemd unit → `sudo nano /etc/systemd/system/x.service` |
|
||||
| VM state → `virsh list --all` | `nginx.conf` → `sudo nano /etc/nginx/nginx.conf` |
|
||||
| network → `ip addr`, `virsh net-list --all` | `~/.bashrc` → `nano ~/.bashrc` |
|
||||
| logs → `journalctl -u x` | Kubernetes manifest → `nano deploy.yaml` |
|
||||
| fetch / copy → `curl`, `cp`, `scp` | a script → `nano x.sh` → `chmod +x x.sh` → `./x.sh` |
|
||||
| create a VM → `virt-install` | |
|
||||
|
||||
### This rule is not "replace sed with nano"
|
||||
|
||||
> 단순히 **「`sed`를 `nano`로 바꿔라」**라고 하면 안 됩니다. 그러면 모든 shell 명령을 기계적으로
|
||||
> 에디터 작업으로 바꿀 가능성이 큽니다.
|
||||
|
||||
The trigger is the *file-authoring step*, not the appearance of a shell tool.
|
||||
|
||||
| Kind | Examples | Verdict |
|
||||
|---|---|---|
|
||||
| what an operator types by hand | `virsh`, `systemctl`, `ssh`, `curl`, `virt-install` | keep |
|
||||
| reading / diagnosing | `grep`, `lsmod`, `cat`, `stat`, `groups` | keep |
|
||||
| shell tricks that author a file | `printf >`, `echo >>`, `cat <<EOF`, `python3 -c`, `ssh '… cat > …'` | rewrite as an editor step |
|
||||
|
||||
A pipe is not the problem. A 12-line `virt-install` is not the problem —
|
||||
creating the VM *is* that command's purpose, so the CLI is the right way to show
|
||||
it. `sed` is fine for a query or a throwaway substitution; it is wrong as the
|
||||
default interface for editing config, because what the reader will actually do
|
||||
during an incident is open the file and read what is in it.
|
||||
|
||||
### Rewrite: `~/.bashrc`
|
||||
|
||||
```bash
|
||||
# before
|
||||
echo 'export LIBVIRT_DEFAULT_URI=qemu:///system' >> ~/.bashrc
|
||||
virsh uri
|
||||
```
|
||||
|
||||
```bash
|
||||
# after
|
||||
nano ~/.bashrc
|
||||
```
|
||||
```text
|
||||
export LIBVIRT_DEFAULT_URI=qemu:///system
|
||||
```
|
||||
```bash
|
||||
source ~/.bashrc
|
||||
virsh uri
|
||||
```
|
||||
|
||||
Two reasons, and the second is the one that gets forgotten:
|
||||
|
||||
1. The reader sees the chain — file → variable → reload this shell → `virsh`
|
||||
now resolves that URI. `echo >>` produces only the end state.
|
||||
2. **`echo >>` is not idempotent.** Someone who walks the guide a second time
|
||||
appends the same line again. Opening the file shows what is already there.
|
||||
|
||||
### Rewrite: cloud-init meta-data
|
||||
|
||||
```bash
|
||||
# before
|
||||
printf 'instance-id: kc-lab-1-%s\nlocal-hostname: kc-lab-1\n' "$(date +%s)" > meta-kc-lab-1
|
||||
```
|
||||
|
||||
```bash
|
||||
# after
|
||||
nano meta-kc-lab-1
|
||||
```
|
||||
```yaml
|
||||
instance-id: kc-lab-1-20260912
|
||||
local-hostname: kc-lab-1
|
||||
```
|
||||
> `instance-id`는 이전 cloud-init 실행과 다른 인스턴스로 인식시키기 위해 이전 값과 겹치지 않게
|
||||
> 지정한다.
|
||||
|
||||
The `printf` form makes the reader decode `%s`, `\n`, `$(...)`, `date +%s` and
|
||||
`>` before reaching the two keys the page is about. A document teaching
|
||||
cloud-init should not be teaching shell `printf`. The editor form also gives the
|
||||
one line about `instance-id` a place to sit, right where it is typed.
|
||||
|
||||
### Rewrite: a file on a remote host
|
||||
|
||||
```bash
|
||||
# before
|
||||
ssh donghyeon@192.168.122.11 'umask 077; cat > ~/kc-lab-2.yaml' < kc-lab-2.yaml
|
||||
ssh donghyeon@192.168.122.11 'cloud-init schema -c ~/kc-lab-2.yaml; rm -f ~/kc-lab-2.yaml'
|
||||
```
|
||||
|
||||
```bash
|
||||
# after
|
||||
scp kc-lab-2.yaml donghyeon@192.168.122.11:~/
|
||||
ssh donghyeon@192.168.122.11
|
||||
```
|
||||
then, on the guest:
|
||||
```bash
|
||||
chmod 600 ~/kc-lab-2.yaml
|
||||
cloud-init schema -c ~/kc-lab-2.yaml
|
||||
rm ~/kc-lab-2.yaml
|
||||
```
|
||||
|
||||
The first line asked the reader to hold SSH, redirection, `umask`, file creation
|
||||
and local stdin at once. The rewrite uses more commands and puts fewer things in
|
||||
each: **one action, one command.** In a guide someone is learning from, that
|
||||
trade is the right way round.
|
||||
|
||||
One more rewrite belongs to this rule — replacing a `python3 -c` YAML check with
|
||||
the format's own validator. It sits in **Command priority** below, where the
|
||||
ranking it changes lives.
|
||||
|
||||
## Command priority
|
||||
|
||||
| Rank | Reach for | Examples |
|
||||
|---|---|---|
|
||||
| 1 | the system's own CLI | `kubectl`, `systemctl`, `psql`, `redis-cli`, `docker compose` |
|
||||
| 2 | standard OS tools | `ps`, `ss`, `lsof`, `free`, `top`, `dmesg` |
|
||||
| 3 | network / protocol tools | `curl`, `dig`, `openssl`, `nc`, `tcpdump` |
|
||||
| 4 | short Unix combinators | `grep`, `jq`, `awk`, `head`, `tail`, `less`, `watch` |
|
||||
| — | **avoid** | python/node heredocs, `python -c` that *processes data*, giant awk programs, pipelines built to produce one tidy answer |
|
||||
|
||||
**The line is doing-the-work vs checking-a-fact, not the language.**
|
||||
|
||||
```bash
|
||||
# not fine — this is data processing the native tool should do
|
||||
curl -s "$URL" | python3 -c '
|
||||
import json,sys
|
||||
for r in json.load(sys.stdin)["data"]["result"]:
|
||||
print(r["metric"]["pod"], r["value"][1])'
|
||||
```
|
||||
|
||||
That one iterates, reshapes, and formats. `jq`, or the tool's own
|
||||
output flag, does that — and when neither is installed, say so and show the
|
||||
raw output instead of writing a parser.
|
||||
|
||||
`grep`, `jq`, `awk`, and a one-line `for` are what practitioners type. Do not
|
||||
ban them. Ban the ones written for the *agent's* convenience.
|
||||
|
||||
### Validate with the format's own checker first
|
||||
|
||||
Checking a fact is allowed — but a hand-rolled syntax check is the *last*
|
||||
resort, not the default. If the domain ships a command that validates this file,
|
||||
that command is the step. Drop to a one-line syntax check only when nothing
|
||||
validates the format.
|
||||
|
||||
```bash
|
||||
# before — checks that it parses as YAML, and nothing else
|
||||
python3 -c 'import yaml,sys; yaml.safe_load(open("kc-lab-1.yaml")); print("YAML OK")'
|
||||
|
||||
# after — the domain's own validator: schema, keys, and deprecations too
|
||||
cloud-init schema -c ~/kc-lab-2.yaml
|
||||
```
|
||||
|
||||
Same rank elsewhere: `nginx -t`, `sshd -t`, `systemd-analyze verify x.service`,
|
||||
`kubectl apply --dry-run=server -f deploy.yaml`, `terraform validate`,
|
||||
`docker compose config`. Each one is also the command the reader will reach for
|
||||
when the service refuses to start, which a `python3 -c` line never becomes.
|
||||
|
||||
### Two shapes of the same tool
|
||||
|
||||
Most tools have a *reading* form and a *value-extracting* form. Pick by what
|
||||
the reader does next with the output.
|
||||
|
||||
| The reader will… | Form | Example |
|
||||
|---|---|---|
|
||||
| **look at the response** and judge | reading form | `curl -I <url>` · `curl -v <url>` |
|
||||
| **compare or count the value** across runs or hosts | extracting form | `curl -s -o /dev/null -w '%{http_code}\n' <url>` |
|
||||
|
||||
The extracting form hides everything except the field you chose. Use it only
|
||||
when that field *is* the answer — a status code you will compare before and
|
||||
after an injection, or a number you will repeat 900 times. When the reader is
|
||||
still figuring out what is wrong, they need the headers and the TLS handshake,
|
||||
not `200`.
|
||||
|
||||
Same split elsewhere: `kubectl get` (read) vs `-o jsonpath=` (extract),
|
||||
`systemctl status` (read) vs `systemctl show -p X --value` (extract),
|
||||
`psql` interactive (read) vs `psql -tAc` (extract).
|
||||
|
||||
**Show the reading form first at least once per tool.** A reader who has only
|
||||
ever seen `-w '%{http_code}'` cannot debug a TLS error.
|
||||
|
||||
## Progressive narrowing
|
||||
|
||||
Never jump to the precise command. Show the widening-to-narrowing path the
|
||||
reader will actually walk.
|
||||
|
||||
```
|
||||
list / status → detail / describe → logs → targeted inspection
|
||||
```
|
||||
|
||||
```bash
|
||||
kubectl get pods # what is there
|
||||
kubectl get pods -o wide # where, and on which node
|
||||
kubectl describe pod api-7c874-8m2js # why is this one unhappy
|
||||
kubectl logs api-7c874-8m2js # what did it say
|
||||
kubectl logs api-7c874-8m2js --previous # what did it say before it died
|
||||
```
|
||||
|
||||
**Observe before filtering.** Show the raw output at least once before piping
|
||||
it. A reader who has never seen `ss -lntp` output needs to see it whole.
|
||||
|
||||
## Mutation ordering
|
||||
|
||||
```
|
||||
observe → diagnose → reproduce → mutate
|
||||
```
|
||||
|
||||
`rm`, `kill`, `delete`, `UPDATE`, restart, config change do not appear in the
|
||||
diagnosis phase. If a step changes state, say what it changes and how to undo it.
|
||||
|
||||
## Shape of one step
|
||||
|
||||
Four elements, in this order. A command with no interpretation is not a step.
|
||||
|
||||
```
|
||||
무엇을 확인하는가 one line — the question this answers
|
||||
$ command the command, short
|
||||
어디를 봐야 하는가 which field/line in the output matters
|
||||
이 결과가 의미하는 것 what it tells you, and what to do next
|
||||
```
|
||||
|
||||
Show the real output. If it was measured, quote it verbatim; if it is
|
||||
illustrative, say so.
|
||||
|
||||
### When the step changes state
|
||||
|
||||
The four elements above are the shape of a step that **reads**. A step that
|
||||
**changes state** needs a different one — otherwise the why, the failure
|
||||
symptoms and the special cases all pile into the same paragraph:
|
||||
|
||||
> 정보 밀도는 높은데 **처음 따라 하는 사람의 시선 이동이 어렵습니다.**
|
||||
|
||||
```
|
||||
목적 → 행동(번호 매긴 명령) → 예상 결과 → 왜 필요한가 → 문제가 생기면
|
||||
```
|
||||
|
||||
```text
|
||||
### 4. libvirt 기본 연결을 system으로 설정한다
|
||||
|
||||
목적
|
||||
virsh가 사용자 세션이 아니라 시스템 libvirt에 연결되도록 한다.
|
||||
|
||||
1. 설정 파일을 연다.
|
||||
$ nano ~/.bashrc
|
||||
|
||||
2. 다음 줄을 추가한다.
|
||||
export LIBVIRT_DEFAULT_URI=qemu:///system
|
||||
|
||||
3. 저장한 설정을 현재 셸에 반영한다.
|
||||
$ source ~/.bashrc
|
||||
|
||||
4. 확인한다.
|
||||
$ virsh uri
|
||||
|
||||
예상 결과
|
||||
qemu:///system
|
||||
|
||||
왜 필요한가
|
||||
qemu:///session과 qemu:///system은 서로 다른 libvirt 연결이다.
|
||||
VM을 system 쪽에 만들고 virsh가 session 쪽을 보고 있으면
|
||||
VM을 만들었는데도 목록에서 찾지 못할 수 있다.
|
||||
|
||||
문제가 생기면
|
||||
$ virsh uri
|
||||
부터 확인한다.
|
||||
```
|
||||
|
||||
**A step that only reads takes the first shape; a step that changes state takes
|
||||
this one.** Same headings every time, so the reader's eye lands in the same
|
||||
place on step 11 as on step 1.
|
||||
|
||||
## Verify before publishing
|
||||
|
||||
Every command in a guide must have been run, or be marked as unverified.
|
||||
**Check that the tools you reach for are actually installed on the machine
|
||||
the reader will be on** — `jq` and `yamllint` are absent more often than you
|
||||
expect, and a guide that assumes them sends the reader to install things
|
||||
mid-diagnosis.
|
||||
Two failures this catches, both real:
|
||||
|
||||
- `kubectl get endpoints` — deprecated since v1.33, prints a warning
|
||||
- `kubectl exec keycloak-0 -- curl …` — the image has no curl, exit 127
|
||||
|
||||
A guide that teaches a stale or failing command makes the reader doubt their
|
||||
own environment.
|
||||
|
||||
## No placeholders
|
||||
|
||||
`<token>` puts the value outside the document. Give the command that produces
|
||||
it. For secrets, confirm existence or length — never print the value.
|
||||
|
||||
```bash
|
||||
TOKEN=$(ssh node1 'sudo cat /var/lib/rancher/k3s/server/node-token')
|
||||
echo "${#TOKEN} chars"
|
||||
```
|
||||
|
||||
One exception: a value an earlier step already printed on screen, which the
|
||||
reader carries into a later step in a different shell — a session id, a realm
|
||||
UUID. The producing command is already in the document, so the value is not
|
||||
outside it. Write that slot as `{{NAME}}` (uppercase, digits, underscore), and
|
||||
say in the prose above the block which step printed it.
|
||||
|
||||
Not `${NAME}` — guides use `$TOKEN`, `$SID` and friends as real shell
|
||||
variables, and a reader who reads a placeholder as one will paste it unchanged.
|
||||
`{{ }}` is not shell syntax, so pasting it fails where the reader can see it.
|
||||
|
||||
## Rationalization table
|
||||
|
||||
| Excuse | Reality |
|
||||
|---|---|
|
||||
| "The pipeline gives a clean answer" | The reader needs to read the raw output, not your summary of it |
|
||||
| "python -c is shorter than explaining" | It is shorter for you. The reader learns nothing and cannot adapt it |
|
||||
| "jq/awk are also programming" | They are what practitioners type. The line is *program vs command*, not *language* |
|
||||
| "I'll show the efficient way" | Efficient for one run. This doc is for someone learning to read the system |
|
||||
| "The reader can copy-paste it" | Copy-paste is not the goal. Knowing where to look is |
|
||||
| "I verified the logic mentally" | Run it. Two commands in a recent guide were wrong and both looked right |
|
||||
| "It's obvious what this output means" | Then write the one line. If it is obvious it costs nothing |
|
||||
| "`-w '%{http_code}'` is precise" | Precise about one field. The reader debugging TLS needs `-v`, not `200` |
|
||||
| "It's fewer commands" | Fewer for you to type. The reader cannot tell which action they are in the middle of |
|
||||
| "The file ends up the same either way" | Only on the first run. `echo >>` appends again every time someone repeats the guide |
|
||||
| "So I should use nano for everything" | No. The trigger is authoring a file the reader must understand. `grep`, `virsh`, a long `virt-install` stay as they are |
|
||||
| "`python3 -c` only checks syntax" | Then it misses the schema. If the format has a validator — `cloud-init schema`, `nginx -t`, `--dry-run` — that is the step |
|
||||
|
||||
## Red flags — stop and rewrite
|
||||
|
||||
- `python3 -c` or a `<<'PY'` heredoc inside a guide
|
||||
- JSON parsed with a language runtime instead of `jq` or the tool's own `-o`
|
||||
- a pipeline whose purpose you cannot state in one clause
|
||||
- a command with no "what to look at" line under it
|
||||
- `delete`/`kill`/`restart` before any observation step
|
||||
- `<placeholder>` with no command that produces it
|
||||
- output shown that you never actually ran
|
||||
- only the extracting form of a tool appears, never the reading form
|
||||
- a config file or YAML built with `printf >`, `echo >>`, or `cat <<EOF`
|
||||
- one line stacking connect + redirect + file creation (`ssh host 'cat > f' < f`)
|
||||
- `python3 -c` checking syntax when the format has its own validator
|
||||
- a step that appends, so walking the guide twice appends the line twice
|
||||
|
||||
## References
|
||||
|
||||
Per-technology command vocabulary — what practitioners reach for first, not
|
||||
an encyclopedia. Load only the one you need.
|
||||
|
||||
- [kubernetes.md](references/kubernetes.md)
|
||||
- [linux-systemd.md](references/linux-systemd.md)
|
||||
- [networking-tls.md](references/networking-tls.md)
|
||||
- [datastores.md](references/datastores.md)
|
||||
@@ -0,0 +1,52 @@
|
||||
# 데이터 저장소 — what to reach for first
|
||||
|
||||
## PostgreSQL
|
||||
```bash
|
||||
psql -U <user> -d <db>
|
||||
```
|
||||
```
|
||||
\conninfo 지금 어디에 붙어 있나
|
||||
\dt 테이블 목록
|
||||
\d <table> 구조 · 인덱스 · 제약
|
||||
\du 롤
|
||||
\l 데이터베이스 목록
|
||||
\x 세로 출력 토글 (넓은 행을 볼 때)
|
||||
```
|
||||
|
||||
```sql
|
||||
SHOW <setting>; -- 전역값
|
||||
SELECT * FROM pg_stat_activity; -- 지금 도는 쿼리
|
||||
EXPLAIN <query>; -- 계획만
|
||||
EXPLAIN (ANALYZE, BUFFERS) <query>; -- 실제 실행. 변경 쿼리에 쓰면 실제로 바뀐다
|
||||
```
|
||||
|
||||
한 줄로 값만 뽑을 때.
|
||||
```bash
|
||||
kubectl exec deploy/postgres -- psql -U <user> -d <db> -tAc 'select count(*) from <t>'
|
||||
```
|
||||
|
||||
문장 로깅 — 애플리케이션을 고치지 않고 「무엇이 DB 를 어떻게 쓰는지」 본다.
|
||||
```sql
|
||||
ALTER SYSTEM SET log_statement = 'all';
|
||||
SELECT pg_reload_conf();
|
||||
-- 끝나면 되돌린다
|
||||
ALTER SYSTEM RESET log_statement;
|
||||
```
|
||||
|
||||
## Redis
|
||||
```bash
|
||||
redis-cli ping
|
||||
redis-cli info server | head
|
||||
redis-cli dbsize
|
||||
redis-cli --scan --pattern '<prefix>*' # KEYS 대신. 블로킹하지 않는다
|
||||
redis-cli type <key>
|
||||
redis-cli ttl <key>
|
||||
redis-cli --no-raw get <key> # 바이너리를 이스케이프해 보여 준다
|
||||
redis-cli config get appendonly
|
||||
```
|
||||
`\xac\xed` 로 시작하면 Java 네이티브 직렬화라 사람이 읽을 수 없다.
|
||||
|
||||
`/data` 가 볼륨이 아니면 영속화 설정은 컨테이너와 함께 사라진다.
|
||||
```bash
|
||||
kubectl get pod -l app=redis -o jsonpath='{.items[0].spec.volumes}'
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
# Kubernetes — what to reach for first
|
||||
|
||||
Order matters. Widen first, then narrow.
|
||||
|
||||
## 무엇이 있나
|
||||
```bash
|
||||
kubectl get pods # 이름 · 상태 · 재시작 횟수
|
||||
kubectl get pods -o wide # + 노드 · 파드 IP
|
||||
kubectl get all # 워크로드 계열만. Secret·PVC·Ingress 는 안 나온다
|
||||
kubectl get secret,configmap,pvc,ingress
|
||||
```
|
||||
|
||||
## 왜 이 파드가 이런가
|
||||
```bash
|
||||
kubectl describe pod <pod> # 이벤트가 여기 붙는다 — 로그보다 먼저 본다
|
||||
kubectl logs <pod>
|
||||
kubectl logs <pod> --previous # CrashLoop 이면 죽은 이유는 여기 있다
|
||||
kubectl logs <pod> -c <container> # 컨테이너가 여럿일 때
|
||||
kubectl events --for pod/<pod>
|
||||
kubectl get events --sort-by=.lastTimestamp | tail -20
|
||||
```
|
||||
|
||||
## 사슬을 따라간다
|
||||
```bash
|
||||
kubectl get deploy,rs,pod -l app=<label>
|
||||
```
|
||||
Deployment 는 파드를 직접 만들지 않는다. ReplicaSet 을 만들고 그것이 파드를
|
||||
만든다. RS 가 여러 개 남아 있는 것은 정상이며(배포 이력) 활성인 것만 0 이 아니다.
|
||||
|
||||
StatefulSet 은 RS 를 쓰지 않고 파드를 직접 만든다 — 이름이 고정이라
|
||||
`Terminating` 이 안 풀리면 대체 파드가 생기지 않는다.
|
||||
|
||||
## Service 가 파드를 잡고 있나
|
||||
```bash
|
||||
kubectl describe svc <svc> | grep -i endpoints
|
||||
kubectl get endpointslice -l kubernetes.io/service-name=<svc>
|
||||
```
|
||||
`kubectl get endpoints` 는 v1.33+ 에서 deprecated 다.
|
||||
|
||||
비어 있으면 셀렉터와 라벨이 안 맞거나 readiness 미통과다.
|
||||
```bash
|
||||
kubectl get svc <svc> -o jsonpath='{.spec.selector}'
|
||||
kubectl get pods --show-labels
|
||||
```
|
||||
|
||||
## Secret 이 실제로 들어갔나 — 값은 찍지 않는다
|
||||
```bash
|
||||
kubectl get secret <s> -o jsonpath='{.data}' | tr ',' '\n' | grep -o '"[A-Z_]*"' # 키 이름만
|
||||
kubectl get secret <s> -o jsonpath='{.data.<KEY>}' | base64 -d | wc -c # 길이만
|
||||
kubectl exec <pod> -- sh -c 'echo ${#MY_ENV}' # 파드 안 주입 확인
|
||||
```
|
||||
|
||||
## 적용과 대기
|
||||
```bash
|
||||
kubectl apply -f <file>
|
||||
kubectl rollout status deploy/<name> --timeout=180s # 끝날 때까지 블록한다
|
||||
kubectl rollout undo deploy/<name>
|
||||
```
|
||||
|
||||
## 안에서 볼 때
|
||||
```bash
|
||||
kubectl exec -it <pod> -- sh
|
||||
kubectl port-forward svc/<svc> 8080:80
|
||||
kubectl debug -it <pod> --image=busybox --target=<container> # 최소 이미지에 도구가 없을 때
|
||||
```
|
||||
|
||||
**최소 이미지에는 `curl` 도 `wget` 도 없다.** Keycloak 공식 이미지가 그렇다
|
||||
(`exit 127`). 밖에서 물어보거나 임시 파드를 띄운다.
|
||||
```bash
|
||||
kubectl run tmp --rm -it --restart=Never --image=curlimages/curl:8.11.1 -- \
|
||||
curl -s http://<podIP>:9000/metrics
|
||||
```
|
||||
@@ -0,0 +1,51 @@
|
||||
# Linux · systemd — what to reach for first
|
||||
|
||||
## 서비스 상태
|
||||
```bash
|
||||
systemctl status <unit> # 상태 · Main PID · CGroup · 최근 로그
|
||||
systemctl is-active <unit> # 한 단어. 스크립트용
|
||||
systemctl cat <unit> # 유닛 파일에 적힌 것
|
||||
systemctl show <unit> # 기본값까지 합쳐 실제 적용되는 것
|
||||
```
|
||||
`cat` 과 `show` 는 다르다. `Restart=on-failure` 만 적혀 있어도 `show` 는
|
||||
`RestartUSec`·`StartLimitBurst` 같은 기본값을 함께 보여 준다.
|
||||
|
||||
## 로그
|
||||
```bash
|
||||
journalctl -u <unit> -n 50 # 최근 50줄
|
||||
journalctl -u <unit> -e # 끝으로 (페이저)
|
||||
journalctl -u <unit> -f # 실시간
|
||||
journalctl -u <unit> -p err # 에러만
|
||||
journalctl -u <unit> --since '1 hour ago'
|
||||
journalctl -u <unit> -o json # 메타데이터까지
|
||||
```
|
||||
긴 줄이 접히면 `less -S` 로 좌우 스크롤한다.
|
||||
|
||||
**nginx 에러 로그는 2048바이트에서 잘린다**(`NGX_MAX_ERROR_STR`). 저널
|
||||
포맷을 바꿔도 안 늘어난다 — 기록 자체가 잘렸기 때문이다. access 로그에는
|
||||
제한이 없으므로 그쪽을 본다.
|
||||
|
||||
## 프로세스 · 포트 · 자원
|
||||
```bash
|
||||
ps aux | grep <name>
|
||||
ps -eo pid,ppid,etimes,lstart,args | grep <name> # 얼마나 오래 떠 있나
|
||||
ss -lntp # 듣고 있는 TCP 포트 + 프로세스
|
||||
lsof -i :8080
|
||||
free -m
|
||||
top / htop
|
||||
```
|
||||
|
||||
## cgroup
|
||||
```bash
|
||||
systemd-cgls /system.slice/<unit>.service
|
||||
cat /sys/fs/cgroup/system.slice/<unit>.service/memory.current
|
||||
cat /sys/fs/cgroup/system.slice/<unit>.service/pids.current
|
||||
```
|
||||
`systemctl status` 의 `Memory:` `Tasks:` `CPU:` 가 여기서 읽은 값이다.
|
||||
|
||||
## nginx
|
||||
```bash
|
||||
nginx -t && systemctl reload nginx # -t 를 통과할 때만 reload
|
||||
ps -eo pid,lstart,args | grep 'nginx: worker' # reload 판정은 워커 PID 로
|
||||
```
|
||||
reload 하면 마스터는 유지되고 워커만 새로 뜬다. 로그 문구가 아니라 이걸 본다.
|
||||
@@ -0,0 +1,61 @@
|
||||
# 네트워크 · TLS — what to reach for first
|
||||
|
||||
## HTTP
|
||||
```bash
|
||||
curl -I <url> # 한 번 볼 때
|
||||
curl -v <url> # 헤더 · TLS 협상까지
|
||||
curl -s -o /dev/null -w '%{http_code}\n' <url> # 여러 번 재서 비교할 때만
|
||||
```
|
||||
`-w` 형태는 측정용이다. 눈으로 한 번 볼 때는 `-I` 나 `-v` 로 충분하다.
|
||||
|
||||
## 이름 해석
|
||||
```bash
|
||||
dig +short <name>
|
||||
getent hosts <name> # /etc/hosts 와 NSS 순서까지 반영된 결과
|
||||
```
|
||||
|
||||
## TLS
|
||||
```bash
|
||||
echo | openssl s_client -connect <host>:443 -servername <host> 2>/dev/null \
|
||||
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName
|
||||
|
||||
echo | openssl s_client -connect <host>:443 -servername <host> 2>/dev/null \
|
||||
| grep -E '^ *[0-9]+ s:|^ *i:|Verify return code'
|
||||
```
|
||||
체인 단계가 1개면 `cert.pem` 을 쓴 것이다. `fullchain.pem` 이어야 한다.
|
||||
|
||||
발급 시각의 외부 기준이 필요하면 SCT 를 본다.
|
||||
```bash
|
||||
… | openssl x509 -noout -ext ct_precert_scts
|
||||
```
|
||||
|
||||
## 연결 추적
|
||||
```bash
|
||||
sudo conntrack -L | grep <port>
|
||||
sudo conntrack -C
|
||||
cat /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established # 보통 86400
|
||||
```
|
||||
`ESTABLISHED` 는 대부분의 방화벽 규칙 평가를 건너뛴다. 규칙을 넣었는데
|
||||
아무 일도 없으면 여기를 먼저 본다.
|
||||
|
||||
## 방화벽 규칙
|
||||
```bash
|
||||
sudo iptables -S FORWARD # 순서가 중요하다 — 내 규칙이 몇 번째인가
|
||||
sudo iptables -L FORWARD -v -n # 카운터가 0 이면 도달하지 않았다
|
||||
sudo iptables -t raw -S PREROUTING # conntrack 보다 먼저 잡는 자리
|
||||
```
|
||||
|
||||
## 패킷
|
||||
```bash
|
||||
sudo tcpdump -i <iface> -n port <p> -c 20
|
||||
```
|
||||
오버레이 네트워크(flannel VXLAN 등)에서는 물리 인터페이스에 안쪽 IP 가 안
|
||||
보인다. `flannel.1` 같은 터널 인터페이스에서 잡는다.
|
||||
|
||||
## 시계
|
||||
```bash
|
||||
timedatectl show -p NTP -p NTPSynchronized
|
||||
A=$(date -u +%s.%N); B=$(ssh <host> 'date -u +%s.%N'); C=$(date -u +%s.%N)
|
||||
curl -sI https://www.google.com | grep -i '^date:' # 어느 쪽이 맞는지 외부 기준
|
||||
```
|
||||
두 기계의 로그를 나란히 놓기 전에 확인한다.
|
||||
@@ -1,14 +1,14 @@
|
||||
# writing-tech-log-records
|
||||
|
||||
Tech Log Studio에 올릴 기록을 쓰는 스킬. Case · Concept · Reference · Question · Decision 다섯 종류의
|
||||
종류 선택, 칸 채우기, Case 본문 작성, 게시 전 대조를 다룬다.
|
||||
Tech Log Studio에 올릴 기록을 쓰는 스킬. Case · Concept · Setup · Reference · Question · Decision
|
||||
여섯 종류의 종류 선택, 칸 채우기, 본문 작성, 게시 전 대조를 다룬다.
|
||||
|
||||
## 파일
|
||||
|
||||
| 파일 | 무엇 |
|
||||
|---|---|
|
||||
| `SKILL.md` | 진입점. 종류 선택과 절차 |
|
||||
| `references/record-kinds.md` | 다섯 종류의 칸·상한·게시 조건 |
|
||||
| `references/record-kinds.md` | 여섯 종류의 칸·상한·게시 조건 |
|
||||
| `references/from-ssot-to-records.md` | 긴 글에서 글감을 뽑는 기준과 `tech-log-tree.json` |
|
||||
| `references/writing-each-kind.md` | 종류마다 무엇을 어떤 순서로 쓰나 |
|
||||
| `templates/*.md` | 종류별 빈 틀. 복사해서 채운다 |
|
||||
@@ -56,5 +56,5 @@ FAIL 초안.md:3:1 unsupported block syntax: html
|
||||
|
||||
## 알아둘 제약
|
||||
|
||||
코드·표·다이어그램·이미지는 **본문에만** 들어간다. 본문이 있는 종류는 Case 와 Concept 이다. Reference·Question·Decision의 모든
|
||||
칸은 평문으로 렌더링된다. 설계상 그렇다 — 본문을 가진 종류는 Case뿐이다.
|
||||
코드·표·다이어그램·이미지는 **본문에만** 들어간다. 본문이 있는 종류는 Case·Concept·Setup 셋이고,
|
||||
Reference·Question·Decision의 모든 칸은 평문으로 렌더링된다.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: writing-tech-log-records
|
||||
description: Use when writing or revising a Tech Log Studio record — Case, Concept, Reference, Question, or Decision — including choosing the right kind, filling each kind's fields, authoring body Markdown with code blocks, tables, callouts, diagrams and evidence images, and linking records so a published document renders correctly on the public site.
|
||||
description: Use when writing or revising a Tech Log Studio record — Case, Concept, Setup, Reference, Question, or Decision — including choosing the right kind, filling each kind's fields, authoring body Markdown with code blocks, tables, callouts, diagrams and evidence images, and linking records so a published document renders correctly on the public site.
|
||||
metadata:
|
||||
version: "1.1.0"
|
||||
language: "ko-KR"
|
||||
@@ -12,21 +12,26 @@ metadata:
|
||||
|
||||
## 개요
|
||||
|
||||
Studio는 다섯 종류를 구분한다. 종류를 잘못 고르면 채울 칸이 맞지 않는다.
|
||||
Studio는 여섯 종류를 구분한다. 종류를 잘못 고르면 채울 칸이 맞지 않는다.
|
||||
|
||||
| 종류 | 쓰는 때 | 본문 |
|
||||
|---|---|---|
|
||||
| **Case** | 내가 재현하고 검증해 결론을 냈다 | 있음 |
|
||||
| **Concept** | 남의 것이 어떻게 동작하는지 읽고 정리했다 | 있음 |
|
||||
| **Setup** | 남이 자기 손으로 따라 할 절차를 남긴다 (`SETUP`, 화면 이름 「환경 구성」) | 있음 |
|
||||
| **Reference** | 반복 적용할 기준을 굳혔다 | 없음 |
|
||||
| **Question** | 아직 판단이 안 끝났다 | 없음 |
|
||||
| **Decision** | 프로젝트가 방향을 정했다 (`PROJECT_DECISION`) | 없음 |
|
||||
|
||||
**Setup 만 끝난 일을 적지 않는다.** 나머지 다섯은 이미 일어난 일을 적고, Setup 은 읽는 사람이
|
||||
자기 기계에서 실행할 순서를 적는다. 그래서 본문에 명령이 들어가고, 버전만 본문 밖의
|
||||
`pinnedVersions` 에 남는다. 프로젝트가 필수이고 주제는 비워도 된다.
|
||||
|
||||
## 절대 규칙
|
||||
|
||||
**코드·표·다이어그램·이미지는 본문에만 들어간다. 본문이 있는 종류는 Case 와 Concept 둘뿐이다.**
|
||||
본문이 없는 세 종류의 칸은 평문으로 렌더링돼 백틱과 파이프가 글자 그대로 보인다. 그런 자료는 Case 나
|
||||
Concept 에 담고 `관계`로 가리킨다. `references/record-kinds.md`
|
||||
**코드·표·다이어그램·이미지는 본문에만 들어간다. 본문이 있는 종류는 Case·Concept·Setup 셋이다.**
|
||||
본문이 없는 세 종류(Reference·Question·Decision)의 칸은 평문으로 렌더링돼 백틱과 파이프가 글자
|
||||
그대로 보인다. 그런 자료는 본문이 있는 종류에 담고 `관계`로 가리킨다. `references/record-kinds.md`
|
||||
|
||||
## 필수 절차
|
||||
|
||||
@@ -37,17 +42,22 @@ Concept 에 담고 `관계`로 가리킨다. `references/record-kinds.md`
|
||||
사람이 다시 읽지 않았다는 뜻이라, 그 위에 쓴 글은 과분류를 그대로 물려받는다. 계약 밖에서 쓴
|
||||
기록은 색인에 `unlisted` 로 남는다. 그 노드의 `ssot-assets`·`ssot-evidence` 도 함께 본다 —
|
||||
SSOT 가 이미 그린 그림과 이미 돌린 측정 가운데 이 글감에 배정된 것이 거기 적혀 있다.
|
||||
1. **종류 선택** — 위 표. 애매하면 "재현했나"를 묻는다.
|
||||
1. **종류 선택** — 위 표. 애매하면 「끝난 일을 적나, 남이 따라 할 절차를 적나」를 먼저 묻고,
|
||||
끝난 일이면 "재현했나"를 묻는다.
|
||||
2. **칸 채우기** — 칸과 게시 조건은 `references/record-kinds.md`.
|
||||
3. **본문 작성**(Case·Concept) — **종류마다 무엇을 어떤 순서로 쓰는지는
|
||||
`references/writing-each-kind.md`.** 문법은 `references/body-syntax.md`, 표·코드·그림은
|
||||
3. **본문 작성**(Case·Concept·Setup) — **종류마다 무엇을 어떤 순서로 쓰는지는
|
||||
`references/writing-each-kind.md`.**
|
||||
**Setup 은 본문을 쓰기 전에 `Skill` 도구로 `writing-practitioner-guides` 를 연다.** 명령을
|
||||
어떤 형태로 쓸지는 그 스킬이 정한다. 그다음이 `references/writing-each-kind.md` 의 Setup 절이다 —
|
||||
단계 하나의 모양과 이 저장소에서만 걸리는 셋이 거기 있다.
|
||||
문법은 `references/body-syntax.md`, 표·코드·그림은
|
||||
`references/code-tables-diagrams.md`. **문장은 `references/explaining.md`.**
|
||||
**문서군 전체의 리듬은 `references/ai-tells.md`.** 이 둘은 첫 초안부터 적용한다 — AI 티를
|
||||
남겨 두고 나중에 걷어내는 순서가 아니다. **문체를 손보기 전에 문장을 고른다** — 설명이 끝난
|
||||
뒤에 붙은 평가·예고·되풀이·독자 오해 가정을 먼저 뺀다(`ai-tells.md` 첫 절). 문체 규칙의
|
||||
정본은 `ai-tells.md` 다.
|
||||
**그림이 필요한지, 필요하면 무엇을 그릴지는 `references/choosing-a-diagram.md`.** 자리(Case·
|
||||
Concept 만) · 표가 아닌지 · 옆 문단이 이미 말하지 않았는지 세 관문을 지나야 그린다.
|
||||
**그림이 필요한지, 필요하면 무엇을 그릴지는 `references/choosing-a-diagram.md`.** 담을 곳
|
||||
(Case·Concept·Setup 만) · 표가 아닌지 · 옆 문단이 이미 말하지 않았는지 세 관문을 지나야 그린다.
|
||||
순서·경계·상태 전이처럼 문장만으로 따라가기 어려운 관계가 있으면 **`final/assets/` 에 그
|
||||
그림이 이미 있는지부터 본다.** SSOT 를 만들 때 그려 둔 것이 있고 계약의 `ssot-assets` 가 이
|
||||
글감에 배정해 두었으면 기록의 `assets` 가 **그 파일을 그대로** 가리킨다. 사본을 따로 만들지
|
||||
@@ -76,18 +86,20 @@ Concept 에 담고 `관계`로 가리킨다. `references/record-kinds.md`
|
||||
|
||||
## 어느 스킬이 무엇을 하나
|
||||
|
||||
이 스킬이 첫 초안을 만든다. 나머지는 초안이 나온 뒤에 각각 다른 것을 고친다.
|
||||
이 스킬이 첫 초안을 만든다. `writing-practitioner-guides` 는 Setup 본문을 쓰는 동안 함께 열고,
|
||||
나머지는 초안이 나온 뒤에 각각 다른 것을 고친다.
|
||||
|
||||
| 스킬 | 하는 일 | 하지 않는 일 |
|
||||
|---|---|---|
|
||||
| `deriving-tech-log-root-tree` | 후보에 처분을 매기고 `PROMOTE` 를 글감으로 올린다 | 글을 쓰지 않는다 |
|
||||
| `writing-tech-log-records` | 종류를 고르고 칸과 본문을 쓴다. `explaining.md`·`ai-tells.md` 를 처음부터 적용한다 | — |
|
||||
| `writing-practitioner-guides` | 사람이 직접 치는 명령의 형태를 정한다 — 한 줄에 담는 계층, 도구 우선순위, 출력을 읽는 형태와 값 하나만 뽑는 형태, 넓은 명령에서 좁은 명령으로 | Tech Log 의 종류와 칸을 모른다. SSOT 대조도 색인 갱신도 하지 않는다 |
|
||||
| `technical-visualizer` | 문장으로 따라가기 어려운 관계를 그림으로 만든다 | 무엇을 그릴지 정하지 않는다 — `choosing-a-diagram.md` 가 정한다 |
|
||||
| `rewriting-technical-prose-naturally` | 사실과 구조가 이미 맞는 초안의 번역투·반복 문형·과한 대구를 고친다 | 분류가 틀렸거나 근거가 모자란 것은 못 고친다 |
|
||||
| `writing-as-the-person-who-did-it` | 자료에 남아 있는 선택·비교·어긋남·확인하지 못한 범위를 제자리에 놓는다 | 자료에 없는 「처음에는」·「고민 끝에」를 만들지 않는다 |
|
||||
|
||||
Reference·Question·Decision 은 그림을 렌더링할 자리가 없다. 그림이 필요한 내용은 짝이 되는
|
||||
Case 나 Concept 에 담고 `관계`로 가리킨다.
|
||||
Reference·Question·Decision 은 그림을 렌더링할 곳이 없다. 그림이 필요한 내용은 짝이 되는
|
||||
Case·Concept·Setup 에 담고 `관계`로 가리킨다.
|
||||
|
||||
## 보호 구간
|
||||
|
||||
@@ -113,7 +125,7 @@ SSOT 에 없는데 필요한 인용이라면 순서가 반대다 — `final/docu
|
||||
| 실패 | 대응 |
|
||||
|---|---|
|
||||
| Reference 규칙에 코드블록 | Case로 옮겨 관계 연결 |
|
||||
| 본문 밖 칸에 백틱 | 평문이다. 백틱을 뺀다 |
|
||||
| 본문 밖 칸에서 백틱을 뺌 | 이 칸도 백틱은 `<code>` 로 산다. 빼는 것은 별표·파이프·`#` 다 |
|
||||
| 평문 칸에 쉼표 나열 | `이름 : 값`으로 줄 나눔. 있음·없음은 `o`·`x` |
|
||||
| 표 머리글이 명사뿐 | 질문으로. 비교 축은 `전`·`후` 시점으로 |
|
||||
| 그림 안에 문장·숫자 | `<text>`는 이름만. 문장은 `<desc>`·옆 문단에 |
|
||||
@@ -130,6 +142,11 @@ SSOT 에 없는데 필요한 인용이라면 순서가 반대다 — `final/docu
|
||||
| SSOT 에 있는 그림을 두고 새로 그림 | `final/assets/` 를 먼저 본다. `ssot-assets` 가 배정한 것을 쓴다 |
|
||||
| SSOT 에 있는 측정을 두고 다시 돌림 | `final/evidence/` 의 원문을 가리킨다 |
|
||||
| Decision에 근거 없음 | 관계 1개 이상 연결 |
|
||||
| Setup에 끝난 일을 적음 | 따라 할 순서가 아니면 Case다 |
|
||||
| Setup 본문에 `printf >`·`echo >>`·`python3 -c` | 사람이 치는 형태로 바꾼다. 설정 파일은 에디터로 연다 |
|
||||
| Setup 본문의 명령만 고치고 SSOT 를 그대로 둠 | `final/document.md` 를 먼저 고친다. `check_evidence.mjs` 가 막는다 |
|
||||
| Setup 본문에 「2026-09-12 기준」 | 검증일 칸이 없다. 버전은 `pinnedVersions` 에 적는다 |
|
||||
| Setup에 프로젝트를 안 고름 | 「환경 구성은 프로젝트에 속합니다」로 막힌다 |
|
||||
| 측정 안 한 검증일 | 비워 둔다 |
|
||||
| 설명 직후에 「~증거다」「~가 아니다」로 평가 | 지운다. 앞 문장이 사실을 말했으면 거기서 끝낸다 |
|
||||
| 「~로 읽기 쉽다. 그렇지 않다」 | 오해를 지어내지 않는다. 관측부터 적는다 |
|
||||
|
||||
@@ -93,5 +93,24 @@ mailto:…
|
||||
| `list items must contain exactly one paragraph` | 목록을 중첩했다 |
|
||||
| `unsafe link URL: …` | 허용되지 않는 스킴이나 `//` 주소 |
|
||||
| `duplicate explicit ID: …` | 같은 id를 두 번 썼다 |
|
||||
| `unknown inline directive: 27` | **문단에 시각을 그냥 썼다** — 아래를 본다 |
|
||||
|
||||
거절 메시지에는 줄·칸 번호가 붙는다. 그 줄을 먼저 본다.
|
||||
|
||||
## 문단 안의 시각은 백틱으로 감싼다
|
||||
|
||||
`:` 뒤에 글자가 붙으면 파서가 인라인 directive 로 읽는다. 그래서 문단에 `08:20:27` 을
|
||||
그냥 쓰면 `:27` 에서 막힌다. `00/12:00:00` 같은 설정값도 같다.
|
||||
|
||||
```text
|
||||
새 인증서가 08:20:27 에 기록됐다. ← FAIL unknown inline directive: 27
|
||||
새 인증서가 `08:20:27` 에 기록됐다. ← PASS
|
||||
```
|
||||
|
||||
**이것이 조용한 결함이 되는 경로가 있다.** 막히면 시각을 빼고 넘어가게 되고, 그러면
|
||||
검사기는 통과하는데 **기록에서 수치가 사라진다.** 실제로 한 회차에서 두 편이 각각
|
||||
시각 둘과 타이머 설정값을 빼고 통과시켰다. 수치·날짜·시각은 보호 구간이다 —
|
||||
**빼지 말고 감싼다.**
|
||||
|
||||
표와 코드블록 안은 걸리지 않는다. 그래서 같은 프로젝트의 다른 기록이 통과하는 것이
|
||||
「이 문법이 괜찮다」는 뜻이 아니다 — 그쪽은 시각이 전부 표 안에 있었을 뿐이다.
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
### 1. 자리가 있는가
|
||||
|
||||
`assets` 는 본문이 있는 두 종류만 갖는다 — **Case 와 Concept**. Reference·Question·Decision 의
|
||||
칸은 평문으로 렌더링돼 그림이 들어갈 자리가 없다.
|
||||
`assets` 는 본문이 있는 세 종류만 갖는다 — **Case·Concept·Setup**. Reference·Question·Decision 의
|
||||
칸은 평문으로 렌더링돼 그림이 들어갈 곳이 없다.
|
||||
|
||||
파생 기록에 그림이 필요해 보이면 그 그림은 **짝이 되는 Case 나 Concept 의 것**이다. 거기 담고
|
||||
`관계`로 가리킨다. 담을 Case 나 Concept 이 없으면 그 그림은 아직 집이 없다 — 계약의
|
||||
파생 기록에 그림이 필요해 보이면 그 그림은 **짝이 되는 Case·Concept·Setup 의 것**이다. 거기 담고
|
||||
`관계`로 가리킨다. 담을 기록이 없으면 그 그림은 아직 집이 없다 — 계약의
|
||||
`assetLedger.unassigned` 에 그렇게 적고, 새 글감을 세울지는 따로 판단한다.
|
||||
|
||||
### 2. 표가 아닌가
|
||||
@@ -40,11 +40,17 @@
|
||||
| **Case** | 이 요청 한 번이 어떤 순서로 무엇을 지나갔나 | `sequence` · `component-flow` |
|
||||
| **Case** (경계가 논지일 때) | 무엇이 어느 경계 안에 있고 무엇이 밖에 있나 | `two-zone-pipeline` |
|
||||
| **Concept** | 남의 것이 어떤 순서·구조로 동작하나 | `sequence` · `component-flow` · `ports-adapters` |
|
||||
| **Setup** | (아직 못 적는다 — 아래) | — |
|
||||
|
||||
Case 는 **내가 돌려서 본 것**이라 대개 순서가 논지다. Concept 은 **남의 것이 어떻게 동작하는지**라
|
||||
구조나 변환 사슬이 논지다. 어느 쪽이든 「무엇이 무엇으로 바뀌는가」를 못 적으면 아직 그릴 것이
|
||||
없다는 뜻이다.
|
||||
|
||||
**Setup 은 담을 곳만 있고 본보기가 없다.** 본문 파서가 Case 와 같아서 그림이 렌더링되기는 한다.
|
||||
다만 2026-09-12 에 Studio 의 환경 구성 문서가 0건이라, 위 두 줄처럼 「반복된 물음」을 셀 자료가
|
||||
없다. 이 줄은 그림을 그리지 말라는 뜻이 아니라 **아직 아무도 안 그려 봤다**는 뜻이다. 그릴 때는
|
||||
세 관문만 그대로 지나고, 무엇을 그렸는지 이 표에 적어 둔다.
|
||||
|
||||
**한 절에 그림 하나.** 같은 절에 구조 그림과 흐름 그림을 둘 다 넣으면 독자가 어느 쪽을 먼저
|
||||
읽어야 하는지 알 수 없다. 둘 다 필요하면 절을 나눈다.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 코드·표·다이어그램·이미지
|
||||
|
||||
본문(`bodyMarkdown`)에만 해당한다. 본문이 있는 종류는 Case 와 Concept 이다.
|
||||
본문(`bodyMarkdown`)에만 해당한다. 본문이 있는 종류는 Case·Concept·Setup 셋이다.
|
||||
|
||||
## 코드블록
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ Reference의 칸도 반쯤만 맞는 기록이 나온다. 종류를 먼저 정
|
||||
|---|---|---|
|
||||
| 재현한 관측 하나 | 조건을 갖추고 돌려서 얻은 수치·실행계획·로그 | Case |
|
||||
| 남의 것의 동작 하나 | 문서와 코드를 읽어 정리한 규칙·흐름 | Concept |
|
||||
| 남이 따라 할 절차 하나 | 그 환경을 처음 세우는 명령과 구성 값 | Setup |
|
||||
| 반복 적용할 기준 하나 | 다음에도 같게 하기로 한 규칙 | Reference |
|
||||
| 프로젝트가 고른 방향 하나 | 대안을 두고 정한 것 | Decision |
|
||||
| 닫히지 않은 판단 하나 | 아직 모르는 것과 무엇을 하면 닫히는지 | Question |
|
||||
@@ -107,6 +108,8 @@ Reference의 칸도 반쯤만 맞는 기록이 나온다. 종류를 먼저 정
|
||||
|
||||
순서대로 묻는다. 먼저 걸리는 것이 그 글감의 종류다.
|
||||
|
||||
0. **남이 자기 기계에서 따라 할 절차인가** → Setup. 나머지 다섯은 끝난 일을 적고 이것만 실행할
|
||||
순서를 적는다. 명령과 구성 값이 SSOT에 있어야 하고, 프로젝트를 반드시 고른다
|
||||
1. **내가 돌려서 얻은 결과인가** → Case. 수치·실행계획·로그가 SSOT에 있어야 한다. 없으면 Case가 아니다
|
||||
2. **남의 것이 어떻게 동작하는지인가** → Concept. 무엇을 보고 썼는지(버전)가 있어야 한다
|
||||
3. **다음에도 같게 하기로 한 규칙인가** → Reference
|
||||
@@ -131,7 +134,7 @@ Reference 하나로 나누고 `관계`로 잇는다.
|
||||
미리 알아야 하는 구조가 있을 때만 Concept을 만든다. 메커니즘처럼 보이는 절을 훑어 채우면 어느
|
||||
기록도 필요로 하지 않는 개념이 쌓인다.
|
||||
|
||||
**주제 하나에 독자 질문 하나.** Studio는 주제 아래에 다섯 종류를 나눠 보여 준다. 그 주제의
|
||||
**주제 하나에 독자 질문 하나.** Studio는 주제 아래에 여섯 종류를 나눠 보여 준다. 그 주제의
|
||||
기록들이 함께 답하는 물음을 한 줄로 적고, 그 물음에 답하지 않는 글감은 다른 주제로 옮긴다.
|
||||
주제 slug는 Studio의 것을 그대로 쓴다.
|
||||
|
||||
@@ -157,7 +160,7 @@ Reference 하나로 나누고 `관계`로 잇는다.
|
||||
"status": "게시 전", "studioId": "32d0be7d-…", "assets": 1, "evidence": 3 },
|
||||
{ "title": "아직 쓰지 않은 글감" }
|
||||
],
|
||||
"concept": [], "reference": [], "question": [], "decision": []
|
||||
"concept": [], "setup": [], "reference": [], "question": [], "decision": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,41 @@
|
||||
# 다섯 종류의 칸과 게시 조건
|
||||
# 여섯 종류의 칸과 게시 조건
|
||||
|
||||
칸 이름은 Studio 편집 화면 그대로, 상한과 필수 여부는 `studio-api.openapi.yaml` 그대로다.
|
||||
계약 필드명을 괄호에 적는다. 계약은 `tech-log-frontend/src/features/tech-log/contracts/studio/`
|
||||
에 있고, 이 파일과 계약이 어긋나면 계약이 맞다.
|
||||
|
||||
`RecordKind` 는 다섯이다 — `CASE` · `REFERENCE` · `QUESTION` · `CONCEPT` · `PROJECT_DECISION`.
|
||||
`RecordKind` 는 여섯이다 — `CASE` · `REFERENCE` · `QUESTION` · `PROJECT_DECISION` · `CONCEPT` ·
|
||||
`SETUP` (`studio-api.openapi.yaml:838-840`).
|
||||
|
||||
**이 목록을 손으로 옮길 때마다 종류가 빠졌다.** 이 문서도 한동안 「다섯이다」라고 적고 `SETUP`
|
||||
을 뺐다. 프론트엔드에서 먼저 같은 일이 났고 소스에 적혀 있다
|
||||
(`application/ports/studio-gateway.ts:8-12`).
|
||||
|
||||
> 종류는 계약의 `RecordKind` 를 그대로 쓴다. 여기 손으로 적어 두었던 동안 개념과 환경 구성이
|
||||
> 빠져 있었고, 작업본 목록의 종류 필터는 그 둘을 아예 고를 수 없었다 — 손으로 나열한 목록에
|
||||
> 새 종류를 빠뜨리는 일이 이 저장소에서 반복됐다.
|
||||
|
||||
일곱 번째가 생기면 같은 일이 난다. 이 문서를 고칠 때는 기억으로 세지 말고
|
||||
`studio-api.openapi.yaml` 의 `RecordKind` 를 열어 몇 줄인지부터 센다.
|
||||
|
||||
## 종류별 칸 (`새 문서` 화면이 적어 주는 그대로)
|
||||
|
||||
| 화면 이름 | 계약 `kind` | 그 종류만의 칸 |
|
||||
|---|---|---|
|
||||
| Case | `CASE` | 문제 · 결론 · 환경 · 재현 · 본문 |
|
||||
| Reference | `REFERENCE` | 목적 · 규칙 · 적용 조건 · 예외 · 예시 |
|
||||
| **개념** | `CONCEPT` | 기준 버전 · 본문 |
|
||||
| Question | `QUESTION` | 상태 · 사실 · 가정 · 미지수 · 선택지 |
|
||||
| Decision | `PROJECT_DECISION` | 상태 · 결정일 · 결정문 · 판단 이유 · 영향 · 근거 |
|
||||
| **검증 기록** (Case) | `CASE` | 문제 · 결론 · 환경 · 재현 · 본문 |
|
||||
| **적용 기준** (Reference) | `REFERENCE` | 목적 · 규칙 · 적용 조건 · 예외 · 예시 |
|
||||
| **동작 원리** (Concept) | `CONCEPT` | 기준 버전 · 본문 |
|
||||
| **환경 구성** (Setup) | `SETUP` | 버전 · 본문 |
|
||||
| **열린 질문** (Question) | `QUESTION` | 상태 · 사실 · 가정 · 미지수 · 선택지 |
|
||||
| **설계 결정** (Decision) | `PROJECT_DECISION` | 상태 · 결정일 · 결정문 · 판단 이유 · 영향 · 근거 |
|
||||
|
||||
**화면 이름은 여섯 다 한글이다.** 2026-09-12 에 `/studio/documents/new` 에서 읽었고 작업본
|
||||
목록(`/studio/documents`)의 종류 필터도 같은 여섯 이름을 쓴다. 이 문서의 절 제목과 산문은
|
||||
괄호 안의 이름을 쓴다 — 폴더 이름과 frontmatter 의 `kind` 가 그쪽이기 때문이다.
|
||||
|
||||
여기에 아래 공통 칸이 더해진다.
|
||||
|
||||
## 공통 (다섯 종류 모두 — `WorkingCopyInputBase`)
|
||||
## 공통 (여섯 종류 모두 — `WorkingCopyInputBase`)
|
||||
|
||||
| 칸 | 필드 | 상한 | 게시 조건 |
|
||||
|---|---|---|---|
|
||||
@@ -27,7 +44,7 @@
|
||||
| 요약 | `summary` | **2,000자** | 경고. 목록 카드에는 약 90자까지 보인다 |
|
||||
| Topic | `topicId` | — | 경고 |
|
||||
| 축 | `variantIds` | 8개 | 선택. 고르지 않으면 그 주제의 **공통 기록**이 된다 |
|
||||
| Project | `projectId` | — | `PROJECT_DECISION`은 게시 시 필수 |
|
||||
| Project | `projectId` | — | `PROJECT_DECISION`과 `SETUP`은 필수 |
|
||||
| 관계 | `relations` | 20개 | `PROJECT_DECISION`은 **1개 이상 필수**. 그 종류에서는 절 이름이 「근거」다 |
|
||||
|
||||
편집 화면에서 축은 체크박스 묶음으로 나오고 묶음 이름이 「축 — 고르지 않으면 이 주제의 공통
|
||||
@@ -67,8 +84,8 @@ evidence:
|
||||
- ../../../final/evidence/explain/highlights-child-plan-A.txt
|
||||
```
|
||||
|
||||
**`assets` 는 본문이 있는 Case 와 Concept 에만 둔다.** 나머지 세 종류는 칸이 평문으로
|
||||
렌더링돼 그림을 표시할 자리가 없다. 그림이 필요한 내용은 Case 나 Concept 에 담고 `관계`로
|
||||
**`assets` 는 본문이 있는 Case·Concept·Setup 에만 둔다.** 나머지 세 종류는 칸이 평문으로
|
||||
렌더링돼 그림을 표시할 곳이 없다. 그림이 필요한 내용은 본문이 있는 종류에 담고 `관계`로
|
||||
가리킨다.
|
||||
|
||||
`assets` 는 Studio 에 올릴 파일이다. 아직 안 올렸으면 key 가 파일 이름과 같고, 올린 뒤에는
|
||||
@@ -80,13 +97,35 @@ evidence:
|
||||
|
||||
## 평문 칸 쓰는 법
|
||||
|
||||
본문(`bodyMarkdown`)을 뺀 모든 칸은 평문이다. 백틱·파이프·`#`는 글자 그대로 보이고 줄바꿈만
|
||||
살아난다. 그 줄바꿈이 유일한 서식이므로 아끼지 않는다.
|
||||
본문(`bodyMarkdown`)을 뺀 모든 칸은 **마크다운 블록 파서를 거치지 않는다.** 그렇다고 전부
|
||||
글자 그대로 나오는 것은 아니다. 렌더러가 이 칸들만 따로 그리고(`tech-log-frontend` 의
|
||||
`presentation/shared/public-render/prose-text.tsx`), 거기서 셋이 살아난다.
|
||||
|
||||
**무엇을 지우나.** 백틱·별표·코드펜스는 글자 그대로 보인다. 식별자는 남기고 표시 문자만 뺀다.
|
||||
`InboxCleanupJob:56` 은 InboxCleanupJob:56 으로, `**this is the parameter**` 는 그 문장만 남긴다.
|
||||
코드펜스 안이 `이름 : 값` 꼴이면 펜스 줄만 지우면 그대로 읽힌다. 줄바꿈이 유일한 서식이므로
|
||||
문단 사이 빈 줄은 지킨다.
|
||||
| 이 칸에서 | 어떻게 되나 |
|
||||
|---|---|
|
||||
| 백틱 쌍 | 인라인 `<code>` 로 **살아난다.** 빼지 않는다 |
|
||||
| 백틱이 홀수 개 | 짝이 안 맞으므로 원문 그대로 둔다 — 반쯤 해석하지 않는다 |
|
||||
| 빈 줄 | 문단이 갈린다 |
|
||||
| 한 줄 바꿈 | `<br>` 로 그 자리에 남는다 |
|
||||
| 별표·파이프·`#`·코드펜스·인용 표지 `>` | **글자 그대로 보인다.** 이것들만 뺀다 |
|
||||
|
||||
**옛 판을 기억하지 마라.** 이 칸들은 오래 진짜 평문으로 나갔고 백틱이 백틱째 화면에
|
||||
나왔다 — 어떤 Reference 는 한 문서에 백틱이 32개였고 그 원문이 카드와 검색 결과까지
|
||||
퍼졌다. 그건 **고쳐진 버그**다. 지금 백틱을 빼면 식별자가 본문과 같은 민무늬로 나온다.
|
||||
|
||||
**무엇을 지우나.** 별표·코드펜스·`>` 는 글자 그대로 보인다. 식별자는 남기고 표시 문자만 뺀다.
|
||||
`**this is the parameter**` 는 그 문장만 남긴다. 코드펜스 안이 `이름 : 값` 꼴이면 펜스 줄만
|
||||
지우면 그대로 읽힌다. 백틱은 그대로 두고, 문단 사이 빈 줄도 지킨다.
|
||||
|
||||
**SSOT 를 그대로 옮긴 인용도 `>` 를 못 쓴다.** 인용이라는 것을 표지로 나타낼 방법이 이 칸에는
|
||||
없다 — `>` 도, 들여쓰기도 안 산다. 표지를 빼고 한 문단으로 두거나, 인용이 꼭 인용으로 보여야
|
||||
하면 본문이 있는 종류로 옮긴다. 「」 를 새로 씌우지 않는다. 옮긴 글자는 보호 구간이라 그대로다.
|
||||
|
||||
**코드펜스를 뗄 때 언어 표시 줄을 같이 지운다.** ` ```text ` 에서 펜스만 지우면 `text` 한 줄이
|
||||
남고, 그 낱말이 화면에 그대로 나온다. 실제로 한 기록에서 그렇게 남아 있었다.
|
||||
|
||||
**칸이 어떻게 보이는지는 렌더러가 정본이다.** 이 파일이 아니다. 여기 적힌 것과 화면이
|
||||
다르면 `prose-text.tsx` 와 `public-record-renderer.tsx` 를 열어서 가른다.
|
||||
|
||||
관계(`근거`) 절은 평문 칸이 아니다. 기록을 거는 목록이라 `- **제목**` 표기를 그대로 둔다.
|
||||
|
||||
@@ -116,25 +155,26 @@ issuer · audience : 검증
|
||||
| 검증 환경 | `environment` | 런타임·버전·DB·도구 |
|
||||
| 재현 조건 | `reproduction` | 다른 사람이 같은 결과를 얻는 방법 |
|
||||
| 마지막 검증일 | `lastVerifiedOn` | 실제로 확인한 날. 30일 지나면 경고 |
|
||||
| 본문 Markdown | `bodyMarkdown` | 10만 자. 본문을 갖는 두 종류 중 하나 |
|
||||
| 본문 Markdown | `bodyMarkdown` | 10만 자. 본문을 갖는 세 종류 중 하나 |
|
||||
|
||||
공개 화면에서 `검증 환경`과 `재현 조건`은 `environmentSummary` 배열에 그 순서로 실린다.
|
||||
|
||||
## Concept — 남의 것이 어떻게 동작하는지
|
||||
|
||||
`새 문서` 화면에서 이 종류만 이름이 한글이다. **「개념」을 고른다.** 나머지 넷은 Case·Reference·
|
||||
Question·Decision 으로 적혀 있다. 화면 설명은 「개념의 내부 동작과 아키텍처를 처음부터 풀어 씁니다」다.
|
||||
`새 문서` 화면에서 **「동작 원리」를 고른다.** 화면 설명은 「개념의 내부 동작과 아키텍처를 처음부터
|
||||
풀어 씁니다.」다. 전에 이 절은 「이 종류만 이름이 한글이다」라고 적었는데 2026-09-12 에는 여섯 다
|
||||
한글이었다.
|
||||
|
||||
| 칸 | 필드 | 비고 |
|
||||
|---|---|---|
|
||||
| 본문 Markdown | `bodyMarkdown` | 10만 자. **Case 와 함께 본문을 갖는 두 종류 중 하나** |
|
||||
| 본문 Markdown | `bodyMarkdown` | 10만 자. **Case·Setup 과 함께 본문을 갖는 세 종류 중 하나** |
|
||||
| 기준 버전 | `basisVersion` | 120자. 무엇을 보고 쓴 글인지 한 줄 |
|
||||
|
||||
칸이 둘뿐이다. 문제도 결론도 재현 조건도 없다. 내가 재현한 결과가 아니라 **이미 그렇게 동작하는
|
||||
것**을 적기 때문이다. `Authorization Code Flow 가 code 를 한 번 더 교환하는 이유`, `Forward-Auth
|
||||
subrequest 가 실어 보내는 것` 같은 것이 여기 온다.
|
||||
|
||||
**`lastVerifiedOn` 이 없고 `basisVersion` 이 그 자리를 대신한다.** 개념은 날짜로 낡지 않고 버전으로
|
||||
**`lastVerifiedOn` 이 없고 `basisVersion` 이 낡음을 말한다.** 개념은 날짜로 낡지 않고 버전으로
|
||||
낡기 때문이다. `Keycloak 26.7.0 identity brokering`, `Kubernetes 1.31` 처럼 적는다. 비워도 게시된다.
|
||||
|
||||
공개 주소는 `/concepts/{slug}` 다.
|
||||
@@ -143,8 +183,7 @@ subrequest 가 실어 보내는 것` 같은 것이 여기 온다.
|
||||
검증도 막지 않는다(2026-09-04 에 임시 개념 기록을 만들어 비운 채로 게시해 확인했다). 그래도
|
||||
채우는 편이 낫다 — 개념이 언제 낡았는지 읽는 사람이 알 방법이 이 칸뿐이다.
|
||||
|
||||
편집 화면 오른쪽 `작업 상태` 는 이 종류를 「동작 원리」라고 부른다. `새 문서` 의 「개념」과 같은
|
||||
것이다.
|
||||
편집 화면 오른쪽 `작업 상태` 도 이 종류를 「동작 원리」라고 부른다.
|
||||
|
||||
Case 와 헷갈리면 **내가 무엇을 했는지**를 묻는다. 내가 돌려 보고 수치를 얻었으면 Case, 남의 문서와
|
||||
코드를 읽고 동작을 정리했으면 Concept 이다.
|
||||
@@ -169,6 +208,87 @@ Concept 이 되는 것은 이런 것들이다 — `Spring 조립의 세 경로`,
|
||||
시점`, `커밋 증거 상태 전이`, `fenced lease 와 CAS`, `gRPC flow control 과 backpressure`.
|
||||
여러 Case 가 같은 선수 지식을 요구할 때 그것을 한 번만 설명하려고 만드는 자리다.
|
||||
|
||||
## Setup — 남이 따라 할 절차 (`SETUP`)
|
||||
|
||||
화면 이름은 「환경 구성」이고 설명은 「이 기록을 자기 손으로 재현하는 절차를 남깁니다.」다.
|
||||
편집 화면은 구역 둘로 나뉜다 — 「기본 정보」와 「환경 구성」(eyebrow `SETUP`).
|
||||
|
||||
| 화면 이름 | 필드 | 상한·모양 |
|
||||
|---|---|---|
|
||||
| 고정한 버전 | `pinnedVersions` | 배열 30개. 줄마다 `이름`(1~60자) + `버전`(1~40자) 입력 둘. 「버전 추가」 버튼으로 늘린다 |
|
||||
| 절차 Markdown | `bodyMarkdown` | 10만 자 |
|
||||
|
||||
화면에 붙은 도움말을 그대로 옮기면 이렇다.
|
||||
|
||||
- 고정한 버전 : `“Keycloak” / “26.7.0” 처럼 적습니다. 비우면 화면에 표를 그리지 않습니다.`
|
||||
- 절차 Markdown : `“##” 소제목이 목차가 됩니다. 명령은 코드블록으로 적어야 그대로 복사됩니다.`
|
||||
|
||||
`SetupInput.required` 는 `[kind, bodyMarkdown, pinnedVersions]` 다.
|
||||
|
||||
**작업본을 만들면 본문이 비어 있지 않다.** Studio 가 절 뼈대를 미리 넣어 준다.
|
||||
|
||||
```text
|
||||
## 실행 절차
|
||||
|
||||
## 구성 값
|
||||
|
||||
## 확인 방법
|
||||
```
|
||||
|
||||
계약의 `bodyMarkdown` 설명은 「실행 절차·구성 값·확인 방법을 `##` 절로 적는다. **절 이름을
|
||||
강제하지 않는다 — 프로젝트마다 셋업의 모양이 다르다.**」다. 뼈대는 출발점이고, 절 이름은 그
|
||||
프로젝트가 쓰는 말로 바꿔도 저장과 게시가 막히지 않는다.
|
||||
|
||||
### 왜 이 종류가 따로 있나
|
||||
|
||||
다른 다섯은 끝난 일을 적고 환경 구성만 남이 따라 할 절차를 적는다. 편집 화면 주석
|
||||
(`presentation/studio/components/setup-fields.tsx:44-52`)이 그 차이를 적어 두었다.
|
||||
|
||||
> 다른 다섯 종류는 끝난 일을 적는다. 이 종류만 읽는 사람이 그대로 따라 하는 절차를 적으므로,
|
||||
> 본문에 명령과 표가 들어간다 — Case 의 「검증 환경」 같은 평문 한 칸으로는 담기지 않는다.
|
||||
> … 버전만 본문 밖에 둔다. 이 절차가 어느 버전 위에서 성립했는지는 그 기록의 유효 범위이고,
|
||||
> 목록과 머리말이 본문을 열지 않고 보여 줘야 하는 값이기 때문이다.
|
||||
|
||||
그래서 칸이 둘뿐인데도 Concept 과 다르게 쓴다. 명령·표·그림은 본문에 넣고 버전만 본문 밖에
|
||||
남긴다. 개념의 「기준 버전」도 같은 이유로 본문 밖에 있고, 다른 점은 셋업의 버전이 여럿이라는
|
||||
데 있다.
|
||||
|
||||
**검증일 칸이 없다.** Case 의 `lastVerifiedOn` 도 Reference 의 `verifiedOn` 도 이 종류에는 없다.
|
||||
공개 계약의 `SetupDetailResponse` 가 왜인지 적는다.
|
||||
|
||||
> 환경 구성은 끝난 일이 아니라 따라 하는 절차다. 낡음은 검증일이 아니라
|
||||
> `pinnedVersions` 가 말한다 — 어느 버전 위에서 이 절차가 성립했는지가 유효 범위다.
|
||||
> 주제는 없을 수 있다. 주제 없는 셋업은 그 프로젝트의 공통 구성이다.
|
||||
|
||||
### 프로젝트는 필수, 주제는 선택
|
||||
|
||||
`PROJECT_DECISION` 말고 프로젝트를 요구하는 종류가 하나 더 있다.
|
||||
|
||||
```text
|
||||
if (input.kind === "SETUP" && !project) fail("환경 구성은 프로젝트에 속합니다. 기본 정보에서 프로젝트를 골라 주세요.");
|
||||
```
|
||||
|
||||
`domain/content-format/project-public-render-model.ts:245` 다.
|
||||
|
||||
주제는 비워도 된다. 비우면 그 프로젝트의 공통 구성으로 읽힌다. 다만 2026-09-12 에 빈 초안의
|
||||
미리보기는 `1:1 TOPIC catalog entry is required` 로 막혔다 — 미리보기를 보려면 Topic 을 고른다.
|
||||
|
||||
### 본문 파서와 공개 주소
|
||||
|
||||
본문 파서는 Case 와 같다(`presentation/public/components/setup-document-page.tsx:11`).
|
||||
|
||||
> 환경 구성의 본문도 Case 와 같은 파서를 탄다 — `##` 소제목이 목차가 되고 `:::evidence` 가…
|
||||
|
||||
그래서 코드블록·표·다이어그램·이미지를 쓸 수 있다.
|
||||
|
||||
- 공개 상세 : `/setups/{slug}` (`contracts/tech-log-route-contract.ts:31`, 라우트 제목 「환경 구성」)
|
||||
- 공개 목록 : `/explore/setups` — 설명 「이 기록을 자기 손으로 재현하는 절차를 남깁니다.」
|
||||
(`presentation/public/pages/explore-kind-page.tsx:17`)
|
||||
|
||||
**Studio 에 환경 구성 문서는 아직 0건이다.** 2026-09-12 에 `/studio/documents?kind=SETUP` 이
|
||||
「0개 중 0개 표시 중」이었다. 종류는 있는데 한 번도 쓰이지 않았다. 위의 칸 설명은 계약과 편집
|
||||
화면에서 읽었고, 올라간 기록에서 확인하지 않았다.
|
||||
|
||||
## Reference — 반복 적용할 기준
|
||||
|
||||
| 칸 | 필드 | 비고 |
|
||||
@@ -217,6 +337,10 @@ Concept 이 되는 것은 이런 것들이다 — `Spring 조립의 세 경로`,
|
||||
## 종류 고르기
|
||||
|
||||
```text
|
||||
남이 그대로 따라 할 절차를 적나 ── 예 ──→ Setup
|
||||
│
|
||||
아니오 (끝난 일을 적는다)
|
||||
│
|
||||
내가 직접 돌려 보고 결과를 얻었나 ── 예 ──→ Case
|
||||
│
|
||||
아니오
|
||||
@@ -236,6 +360,11 @@ Concept 이 되는 것은 이런 것들이다 — `Spring 조립의 세 경로`,
|
||||
└──→ Reference
|
||||
```
|
||||
|
||||
첫 갈래가 「끝난 일을 적나, 남이 따라 할 절차를 적나」다. 나머지 다섯은 이미 끝난 일을 적고,
|
||||
Setup 만 읽는 사람이 자기 기계에서 실행할 순서를 적는다. 편집 화면 주석이 그 경계를 「Case 의
|
||||
「검증 환경」 같은 평문 한 칸으로는 담기지 않는다」로 적는다 — 명령이 여러 줄이고 그대로
|
||||
복사돼야 하면 Case 의 평문 칸이 아니라 Setup 의 본문에 들어간다.
|
||||
|
||||
Case 와 Concept 이 가장 자주 헷갈린다. **수치와 재현 조건이 있으면 Case**, 읽고 정리한 동작이면
|
||||
Concept 이다. Concept 과 Reference 는 「무엇이 그런가」와 「우리가 어떻게 할 것인가」로 갈린다 —
|
||||
`PKCE 가 code 가로채기를 막는 방식`은 Concept, `우리는 public client 에 PKCE 를 필수로 쓴다`는
|
||||
|
||||
@@ -27,6 +27,25 @@
|
||||
- [ ] Decision에 근거 기록이 1개 이상 연결됐다
|
||||
- [ ] Decision의 영향에 감수한 비용이 있다
|
||||
- [ ] Reference의 예시가 문장이다. 코드를 밀어 넣지 않았다
|
||||
- [ ] Setup이 끝난 일이 아니라 남이 따라 할 순서를 적었다
|
||||
- [ ] Setup의 명령이 코드블록에 있다. 산문에 섞지 않았다
|
||||
- [ ] Setup의 `pinnedVersions`가 채워졌다. 본문에 날짜를 적어 검증일을 대신하지 않았다
|
||||
|
||||
## 환경 구성의 명령 (`writing-practitioner-guides`)
|
||||
|
||||
명령을 어떤 형태로 쓸지는 그 스킬이 정한다. 여기서는 그 결과가 본문에 남았는지만 센다.
|
||||
|
||||
- [ ] 사람이 내용을 읽고 고쳐야 하는 설정 파일을 에디터로 열게 했다. `printf >`·`echo >>`·`cat <<EOF` 로 만들지 않았다
|
||||
- [ ] 한 명령에 여러 작업이 겹치지 않았다. 접속·리다이렉션·권한·파일 생성을 한 줄에 묶지 않았다
|
||||
- [ ] 그 도메인의 전용 검증 명령이 있는데 `python3 -c` 로 대신하지 않았다
|
||||
- [ ] 다시 따라 해도 같은 줄이 또 붙지 않는다. 두 번 실행하면 늘어나는 명령이 없다
|
||||
- [ ] 조회·진단·실행은 운영자가 쓰는 CLI 를 그대로 썼다. `grep`·`virsh`·`systemctl` 을 에디터로 바꾸지 않았다
|
||||
- [ ] 단계마다 목적·행동·예상 결과·왜 필요한가·문제가 생기면이 같은 순서로 있다
|
||||
- [ ] 셸이 여럿이면 코드블록마다 `label` 로 어디서 치는지 적었다
|
||||
- [ ] 자리표시자가 없다. 값을 찾는 명령이 함께 있다
|
||||
- [ ] 앞 단계가 찍은 값을 옮겨 넣는 자리만 예외다. 그 자리는 `{{NAME}}` 으로 적었다. 한글로 감싸지도 `${NAME}` 으로 적지도 않았다
|
||||
- [ ] 비밀은 길이나 존재 여부까지만 확인하고 값을 찍지 않았다
|
||||
- [ ] 명령의 형태를 고쳤으면 SSOT 를 먼저 고쳤다. `check_evidence.mjs` 가 통과한다
|
||||
|
||||
## 설명
|
||||
|
||||
@@ -80,7 +99,7 @@
|
||||
- [ ] 테스트를 말할 때 무엇을 단정하는지 적었다
|
||||
- [ ] 어미·절·안내 문장의 수치는 참고만 했다. 맞추려고 문장을 넣지 않았다
|
||||
|
||||
## 본문 (Case)
|
||||
## 본문 (Case · Concept · Setup)
|
||||
|
||||
- [ ] raw HTML·각주·체크박스·중첩 목록·취소선이 없다
|
||||
- [ ] 표를 파이프로 썼다. 감쌌다면 세 속성이 다 있다
|
||||
@@ -99,7 +118,8 @@
|
||||
|
||||
## 평문 칸
|
||||
|
||||
- [ ] 본문 밖 칸에 백틱·파이프가 없다
|
||||
- [ ] 본문 밖 칸에 별표·파이프·`#`·코드펜스·인용 표지 `>` 가 없다 (**백틱은 괜찮다** — 인라인 `<code>` 로 산다)
|
||||
- [ ] 코드펜스를 뗀 자리에 언어 표시(` ```text ` 의 `text`)가 남지 않았다
|
||||
- [ ] 나열을 쉼표로 잇지 않고 `이름 : 값`으로 끊었다
|
||||
- [ ] 있음·없음을 `o`·`x`로 적었다
|
||||
- [ ] 한 문장이 화면에서 두 줄을 넘지 않는다
|
||||
@@ -107,7 +127,7 @@
|
||||
## 연결
|
||||
|
||||
- [ ] Topic이 지정됐다
|
||||
- [ ] Project가 필요하면 지정됐고, 그 Project에 slug가 있다
|
||||
- [ ] Project가 필요하면 지정됐고, 그 Project에 slug가 있다 (Decision과 Setup은 필수다)
|
||||
- [ ] 관계의 대상이 실제로 있는 공개 기록이다
|
||||
- [ ] 코드·표·그림이 필요한 내용을 Reference나 Question에 밀어 넣지 않았다
|
||||
- [ ] 본문이 없는 세 종류에 `assets`를 선언하지 않았다
|
||||
|
||||
@@ -73,7 +73,7 @@ section it names exists, and the diagram stage cannot translate the anchor into
|
||||
|
||||
## Topics
|
||||
|
||||
Each Topic has `topic` (its key), `title`, **`readerQuestion`**, and `kinds` with the five
|
||||
Each Topic has `topic` (its key), `title`, **`readerQuestion`**, and `kinds` with the six
|
||||
record kinds.
|
||||
|
||||
```json
|
||||
@@ -81,13 +81,13 @@ record kinds.
|
||||
"topic": "oauth-oidc-auth-boundary",
|
||||
"title": "OAuth 자격증명과 세션의 보관 경계",
|
||||
"readerQuestion": "자격증명과 세션을 누가 보관하고, 누가 API 요청을 만들며, 보호 자원은 무엇을 신뢰하는가?",
|
||||
"kinds": { "case": [], "concept": [], "reference": [], "question": [], "decision": [] }
|
||||
"kinds": { "case": [], "concept": [], "setup": [], "reference": [], "question": [], "decision": [] }
|
||||
}
|
||||
```
|
||||
|
||||
Every node in the Topic must help answer the reader question. Two Topics do not share a
|
||||
question; one Topic does not need two. Empty kinds are allowed — do not manufacture nodes
|
||||
to fill all five.
|
||||
to fill all six.
|
||||
|
||||
## Candidates
|
||||
|
||||
@@ -178,6 +178,26 @@ be stale.
|
||||
A Concept exists because a Case, Decision, or Question needs it to be understood. Absence,
|
||||
call-counts, unwired subsystems, analysis scope, and coverage ledgers are not Concepts.
|
||||
|
||||
### Setup
|
||||
|
||||
`slug` · `readiness` · `source` · `classification` · `pinned-versions` · `relations`.
|
||||
|
||||
Setup is the one kind that does not report a finished result. It is a procedure a reader
|
||||
runs on their own machine, so `pinned-versions` states the versions the procedure was
|
||||
established on — the same job `basis-version` does for a Concept, except there is more than
|
||||
one of them. There is no verification date for this kind.
|
||||
|
||||
A Setup node also needs a project. `SETUP` and `PROJECT_DECISION` are the two kinds Studio
|
||||
refuses to save without one; a Topic is optional, and a Setup with no Topic reads as that
|
||||
project's shared configuration.
|
||||
|
||||
`verify-tech-log-tree.py` checks this kind like the others: `REQUIRED_FIELDS["setup"]` names
|
||||
the six fields above and `GENERATABLE["setup"]` is `READY`. The kind list those tables are
|
||||
keyed on lives in `techlog.KINDS`, which also decides which `<topic>/<kind>/` folders
|
||||
`build-tech-log-tree.py` scans. Add a kind in one place and the tables that key off it go
|
||||
quiet rather than failing — a kind missing from `REQUIRED_FIELDS` is not an error, it is a
|
||||
node nobody asks anything of.
|
||||
|
||||
### Reference
|
||||
|
||||
`slug` · `readiness` · `source` · `classification` · `scope` · `exceptions` · `relations`.
|
||||
|
||||
@@ -7,18 +7,21 @@
|
||||
Question 4·Decision 2), n+1liner 24건(Case 5·Reference 7·Question 5·Decision 7).
|
||||
「대개 이렇게 쓴다」는 말은 그 47건이 그렇게 돼 있다는 뜻이다.
|
||||
|
||||
**Setup 은 그 47건에 없다.** 2026-09-12 에 Studio 의 환경 구성 문서가 0건이었다. 그래서 Setup
|
||||
절은 계약과 편집 화면에서 읽어 썼다. 나머지 다섯처럼 올라간 기록을 세지 않았다.
|
||||
|
||||
`clean-architecture-backend-template` 의 949건은 다른 절 이름으로 쓰여 있었고, 지금 이 기준으로
|
||||
다시 판정하는 중이다. 아직 기준을 만족한 상태가 아니므로 그 949건을 본보기로 삼지 않는다. 올라간
|
||||
적 없는 초안이 아니라 **올라간 것**이 기준이다.
|
||||
|
||||
## 파일 뼈대 — 다섯 종류가 같다
|
||||
## 파일 뼈대 — 여섯 종류가 같다
|
||||
|
||||
```markdown
|
||||
---
|
||||
id · kind · slug · title · topic · topicName · project · status · studio
|
||||
source · sourceRevision
|
||||
(종류별) basisVersion · decisionStatus · questionStatus · lastVerifiedOn
|
||||
(있으면) evidence · assets — assets 는 Case 와 Concept 만
|
||||
(종류별) basisVersion · decisionStatus · questionStatus · lastVerifiedOn · pinnedVersions
|
||||
(있으면) evidence · assets — assets 는 Case · Concept · Setup 만
|
||||
---
|
||||
|
||||
# 제목
|
||||
@@ -27,7 +30,7 @@ source · sourceRevision
|
||||
|
||||
## 관계 ← Decision 만 「근거」다
|
||||
## <칸 이름> ← 종류마다 다르다
|
||||
## 본문 ← Case · Concept 만
|
||||
## 본문 ← Case · Concept · Setup 만
|
||||
<!-- body:start -->
|
||||
...
|
||||
<!-- body:end -->
|
||||
@@ -110,6 +113,159 @@ basisVersion: oauth2-proxy 7.15.2 · Nginx auth_request
|
||||
「확인했다」는 Case 의 말이다. Concept 은 「~한다」로 적는다. 규격이 정한 것과 그 구현이 그렇게 한
|
||||
것을 구분한다.
|
||||
|
||||
## Setup — 0건
|
||||
|
||||
칸은 `관계` · `본문` 둘뿐이고, 고정한 버전은 frontmatter 의 `pinnedVersions` 에 있다. 계약의
|
||||
`PinnedVersion` 은 `name` 과 `version` 을 나눠 담는다 — 이름 1~60자, 버전 1~40자, 30개까지.
|
||||
|
||||
```yaml
|
||||
pinnedVersions:
|
||||
- name: Keycloak
|
||||
version: 26.7.0
|
||||
```
|
||||
|
||||
**절 이름을 강제하지 않는다.** 계약이 그렇게 적는다 — 「실행 절차·구성 값·확인 방법을 `##`
|
||||
절로 적는다. 절 이름을 강제하지 않는다 — 프로젝트마다 셋업의 모양이 다르다.」 그래도 빈 본문에서
|
||||
시작하지는 않는다. 작업본을 만들면 Studio 가 절 셋을 미리 넣어 주므로 거기서 출발한다.
|
||||
|
||||
```markdown
|
||||
## 실행 절차
|
||||
|
||||
## 구성 값
|
||||
|
||||
## 확인 방법
|
||||
```
|
||||
|
||||
**명령은 코드블록으로 적는다.** 절차 Markdown 칸의 도움말이 이유를 적는다 —
|
||||
`“##” 소제목이 목차가 됩니다. 명령은 코드블록으로 적어야 그대로 복사됩니다.` 읽는 사람이
|
||||
자기 기계에서 실행하므로, 산문에 섞어 적으면 복사할 때 프롬프트 기호와 설명이 함께 붙는다.
|
||||
|
||||
Case 와 무엇이 다른지는 **읽는 사람이 무엇을 하는가**로 갈린다. Case 의 「재현 조건」은 내가 잰
|
||||
값을 남이 다시 얻는 순서이고, Setup 의 본문은 그 환경을 처음 세우는 절차다. Case 는 평문 한 칸에
|
||||
그 순서를 담지만 Setup 은 본문을 쓰므로 명령·표·그림이 들어간다.
|
||||
|
||||
**검증일을 쓰지 않는다.** 이 종류에는 그 칸이 없다. 절차가 어느 버전 위에서 성립했는지는
|
||||
`pinnedVersions` 가 말하므로, 본문에 「2026-09-12 기준」 같은 날짜를 적어 대신하지 않는다.
|
||||
|
||||
`project` 는 비울 수 없다. `topic` 은 비워도 되고, 비우면 그 프로젝트의 공통 구성으로 읽힌다.
|
||||
|
||||
### 본문을 쓰기 전에 `writing-practitioner-guides` 를 연다
|
||||
|
||||
`Skill` 도구로 `writing-practitioner-guides` 를 부른다. 명령을 어떤 형태로 쓸지는 그 스킬이
|
||||
정한다 — 한 줄에 어느 계층까지 담는지, 어떤 도구를 먼저 잡는지, 출력을 읽는 형태와 값 하나만
|
||||
뽑는 형태를 어떻게 가르는지, 넓은 명령에서 좁은 명령으로 내려가는 순서, 무엇이 보이면 멈추고
|
||||
다시 쓰는지가 거기 적혀 있다. 그 규칙을 이 문서로 옮겨 적지 않는다. 같은 규칙이 두 곳에 있으면
|
||||
한쪽만 고쳐지고 둘이 갈린다.
|
||||
|
||||
다른 다섯 종류에는 이 절차가 없다. 나머지는 끝난 일을 적으므로 명령이 나와도 그때 무엇을 쳤는지
|
||||
보여 주는 인용이고, 읽는 사람이 자기 기계에서 그것을 치지 않는다. 환경 구성은 읽는 사람이 그대로
|
||||
따라 치므로 명령의 형태가 내용의 일부다. `echo 'export ...' >> ~/.bashrc` 는 결과를 만들지만
|
||||
읽는 사람이 `~/.bashrc` 를 한 번도 열어 보지 못하고, 같은 가이드를 다시 따라 하면 같은 줄이
|
||||
하나 더 붙는다.
|
||||
|
||||
기계적으로 바꾸는 방향도 틀린다. 조회·진단·실행은 운영자가 쓰는 CLI 를 그대로 쓴다 —
|
||||
`grep`·`lsmod`·`virsh`·`systemctl`·`journalctl`·`kubectl` 이 들어갔다는 것 자체는 문제가
|
||||
아니다. 사람이 내용을 읽고 고쳐야 하는 설정 파일을 만드는 대목에서만 에디터로 연다.
|
||||
|
||||
### 단계 하나의 모양
|
||||
|
||||
`writing-practitioner-guides` 의 「Shape of one step」은 상태를 읽는 단계의 모양이다. 환경 구성의
|
||||
본문은 상태를 바꾸는 단계가 대부분이고, 그쪽은 칸이 다섯이다.
|
||||
|
||||
```text
|
||||
### N. <이 단계가 무엇을 만드는가>
|
||||
|
||||
목적 한 줄. 이 단계가 끝나면 무엇이 달라지나
|
||||
행동 번호를 매긴 명령. 한 번호에 한 가지 일
|
||||
예상 결과 그때 화면에 나오는 것
|
||||
왜 필요한가 건너뛰거나 어긋나면 무엇이 깨지나
|
||||
문제가 생기면 어느 명령부터 다시 보나
|
||||
```
|
||||
|
||||
번호는 행동을 세려고 매긴다. 한 번호가 접속과 파일 생성과 권한 설정을 함께 하면 읽는 사람은
|
||||
어디까지 왔는지 셀 수 없고, 실패해도 그 줄의 어느 대목에서 실패했는지 모른다. 다섯 칸이 단계마다
|
||||
같은 순서로 오면 처음 따라 하는 사람이 단계마다 같은 곳에서 같은 것을 찾는다.
|
||||
|
||||
채운 예는 `writing-practitioner-guides` 에 있다 — libvirt 연결 URI 를 `qemu:///system` 으로
|
||||
고정하는 단계다.
|
||||
|
||||
### 자리표시자
|
||||
|
||||
**원칙은 자리표시자를 두지 않는 것이다.** `writing-practitioner-guides` 의 「No placeholders」가
|
||||
이유를 적는다 — `<토큰>` 이라고 적어 두면 그 값을 어디서 가져오는지가 문서 밖으로 나간다. 값을
|
||||
뽑는 명령을 먼저 주는 것이 먼저다.
|
||||
|
||||
**예외는 하나다 — 같은 기록의 앞 단계가 화면에 찍은 값을 뒤 단계에 옮겨 넣을 때.** 세션 `sid` 는
|
||||
로그인할 때마다 새로 생기고 클라이언트 UUID 는 렐름을 만들 때 정해져서 읽는 사람의 실험대에서
|
||||
다르다. 앞 단계가 탐침 파드 안에서 돌았으면 그 셸의 변수가 뒤 단계의 셸에 없어서 변수로 넘길
|
||||
수도 없다. 값을 만드는 명령은 이미 같은 문서 안에 있으므로 전역 스킬의 빨간 깃발
|
||||
(`<placeholder>` with no command that produces it)에는 걸리지 않는다.
|
||||
|
||||
**그때 쓰는 꼴은 `{{NAME}}` 하나다.** `NAME` 은 대문자로 시작하고 대문자·숫자·밑줄만 쓴다.
|
||||
|
||||
```bash label="[kc-lab-1] ② 읽은 값을 그대로 넣어 행을 찾는다"
|
||||
kubectl -n keycloak-lab exec deploy/postgres -- psql -U keycloak -d keycloak \
|
||||
-c "select user_session_id, created_on, last_session_refresh from offline_user_session
|
||||
where offline_flag='0' and user_session_id='{{SID}}'"
|
||||
```
|
||||
|
||||
**`${SID}` 로 쓰지 않는다.** 같은 가이드들이 `$SID`·`$K0`·`$TOK`·`$PW` 를 진짜 셸 변수로 쓴다.
|
||||
자리표시자를 셸 변수 꼴로 적으면 읽는 사람이 「이 변수는 이미 셸에 있다」로 읽고 그대로 붙여
|
||||
넣는다. `{{ }}` 는 셸 문법이 아니라서 붙여 넣으면 반드시 틀리고, 틀린 자리가 화면에 보인다.
|
||||
|
||||
**한글로 감싸지 않는다.** `'<① 이 찍은 sid>'` 는 사람에게는 읽히지만 검사기가 그 줄을 흐름도로
|
||||
오인하기 쉽고, 무엇보다 자리표시자 바깥의 SQL 이 SSOT 와 갈려도 드러나지 않는다. 어느 단계가 그
|
||||
값을 찍었는지는 코드블록 바로 위 산문에 적는다 — 「sid 는 ③ 이 `SID=` 로 화면에 찍은 값을 옮겨
|
||||
넣는다」처럼.
|
||||
|
||||
**검사기는 그 자리만 와일드카드로 본다.** `{{SID}}` 는 따옴표도 공백도 넘지 않는 값 하나로
|
||||
열리고 나머지는 한 글자씩 SSOT 와 대조된다. 그래서 `user_session_id` 를 `user_session_idx` 로
|
||||
잘못 적으면 그대로 걸린다.
|
||||
|
||||
### 이 저장소에서만 걸리는 것 셋
|
||||
|
||||
전역 스킬은 Tech Log 의 검사기도 SSOT 도 모른다.
|
||||
|
||||
**① 명령의 형태를 고치려면 SSOT 를 먼저 고친다.** `check_evidence.mjs` 가 본문 코드블록의 줄을
|
||||
`final/document.md` 와 대조한다. `bash`·`yaml`·`nginx` 처럼 언어를 적은 펜스는 줄 단위로 보고,
|
||||
`text`·`txt`·`console`·`diff` 펜스와 언어를 안 적은 펜스는 그 안의 경로·URL·점 있는 식별자만
|
||||
본다. 그래서 `sudo nano /etc/letsencrypt/cloudflare.ini` 를 SSOT 에 없는 채로 넣으면 「인용한
|
||||
코드가 SSOT 에 없다」로 막힌다.
|
||||
|
||||
대조에서 빠지는 줄도 있다. 20자 미만인 줄, `#`·`//`·`|`·`>` 로 시작하는 줄, 그리고 한글과 흐름
|
||||
글리프(`─`·`▶`·`→`·`←`)가 함께 있는 줄 — 필자가 그린 흐름도 — 을 건너뛴다. `nano ~/.bashrc` 는
|
||||
14자라 대조 없이 통과한다. 통과했다는 것과 SSOT 에 있다는 것은 다르므로 짧은 명령도 사람이
|
||||
SSOT 에서 찾아 대조한다.
|
||||
|
||||
**한글이 섞였다는 것만으로는 안 빠진다.** 예전에는 그랬고, 그래서 자리표시자를 한글로 감싼
|
||||
`psql -c "…"` 한 줄이 통째로 대조에서 빠졌다. 지금은 흐름 글리프까지 있어야 흐름도로 본다.
|
||||
|
||||
순서는 SSOT 가 먼저다. 구축 절차를 담은 부(virtualization 은 제6부 §184~§194)에 사람이 치는
|
||||
형태를 적고, 그 형태도 원 가이드나 저장소에서 확인한 뒤에 적는다. 기록을 먼저 고치고 검사기가
|
||||
막을 때 SSOT 를 맞추면 SSOT 가 근거이기를 그만두고 기록의 사본이 된다. CLAUDE.md 의 「보강은
|
||||
상류 원문으로 한다」가 같은 말이다.
|
||||
|
||||
형태를 바꾸는 것과 사실을 바꾸는 것은 다르다. 원 가이드가 `printf ... > meta-kc-lab-1` 로
|
||||
적었다면 그 파일에 무엇이 들어가는지는 원문이 이미 갖고 있으므로, SSOT 에는 그 내용을 파일
|
||||
목록으로 옮기고 파일을 여는 명령을 앞에 둔다. 원문이 만들지 않은 파일이나 재지 않은 출력을
|
||||
새로 만들지 않는다.
|
||||
|
||||
**② 셸이 여럿인 실험대에서는 코드블록마다 어디서 치는지 붙인다.** 산문에 한 번 적어 두면 따라
|
||||
하는 도중에는 안 보인다. 본문 파서가 코드블록의 `label` 을 받으므로 거기에 적는다.
|
||||
|
||||
```bash label="[lab host] 저장소 루트에서 친다"
|
||||
kubectl apply -f deploy/lab/k8s/keycloak-cluster.yaml
|
||||
```
|
||||
|
||||
virtualization 의 SSOT §185 ③ 이 그 표시를 다섯으로 정해 두었다 — `[워크스테이션]` ·
|
||||
`[lab host]` · `[kc-lab-edge]` · `[kc-lab-1]` · `[kc-lab-2]`. 표시가 없으면 같은 명령이 다른
|
||||
기계에서 다른 결과를 낸다.
|
||||
|
||||
**③ 비밀은 길이와 존재 여부까지만 적는다.** 값을 찍는 명령을 본문에 두지 않는다. 토큰이 필요한
|
||||
단계는 값을 찾는 명령을 주고 `echo "${#TOKEN} 자"` 로 끝낸다. `final/evidence/` 에 올리는 원문에도
|
||||
값이 들어가지 않도록 명령을 짜는 것이 먼저다. 터미널 렌더러가 Bearer·Cookie·token·password 를
|
||||
`[REDACTED]` 로 바꾸지만 그 앞에서 막는다.
|
||||
|
||||
## Reference — 14건
|
||||
|
||||
칸은 `관계` · `목적` · `규칙` · `적용 조건` · `예외` · `예시`. 14건 모두 여섯 칸을 채웠다. 본문이 없다.
|
||||
@@ -124,7 +280,8 @@ basisVersion: oauth2-proxy 7.15.2 · Nginx auth_request
|
||||
|
||||
규칙 제목만 읽어도 무엇을 금지하는지 알아야 한다. 「지표를 정확히 읽는다」보다
|
||||
「지표 이름이 뜻하는 것을 그대로 읽는다」가 낫다. 코드가 필요하면 그 코드가 있는 Case 를 만들고
|
||||
`관계`로 가리킨다 — Reference 칸은 평문이라 백틱이 글자로 보인다.
|
||||
`관계`로 가리킨다 — Reference 칸은 마크다운 블록 파서를 안 거쳐서 코드펜스가 글자로 보인다.
|
||||
낱말 하나짜리 식별자는 백틱으로 감싸면 인라인 `<code>` 로 살아난다. 여러 줄짜리 코드가 문제다.
|
||||
|
||||
## Question — 9건
|
||||
|
||||
@@ -151,7 +308,7 @@ basisVersion: oauth2-proxy 7.15.2 · Nginx auth_request
|
||||
|
||||
## Decision — 9건
|
||||
|
||||
칸은 **`근거`** · `결정문` · `판단 이유` · `영향`. **다른 넷과 달리 관계 절 이름이 「근거」다.**
|
||||
칸은 **`근거`** · `결정문` · `판단 이유` · `영향`. **다른 다섯과 달리 관계 절 이름이 「근거」다.**
|
||||
`decisionStatus` 는 frontmatter 에 있다. 근거가 1개 이상 없으면 게시가 거절된다.
|
||||
|
||||
| 칸 | 무엇을 |
|
||||
@@ -163,14 +320,14 @@ basisVersion: oauth2-proxy 7.15.2 · Nginx auth_request
|
||||
대안을 적고 왜 고르지 않았는지 적는다. 대안이 없으면 결정이 아니라 사실이다. 자료가 뒷받침하지
|
||||
않는 이유를 만들지 않는다.
|
||||
|
||||
## 본문이 있는 두 종류의 공통 규칙
|
||||
## 본문이 있는 세 종류의 공통 규칙
|
||||
|
||||
- **그림이 필요한지와 무엇을 그릴지는 `choosing-a-diagram.md` 가 정한다** — 종류마다 그림이
|
||||
답해야 할 물음이 다르다. Case 는 순서, Concept 은 구조나 변환 사슬이 대개 논지다
|
||||
- 그림은 `technical-visualizer` 로 만든다. 손으로 SVG 를 그리지 않는다
|
||||
- 그림 안에는 이름만 넣는다. 문장은 `<desc>` 와 옆 문단에 둔다
|
||||
- 그림과 증거는 frontmatter 의 `assets` · `evidence` 로 잇는다. 같은 파일을 기록 옆에 복사하지 않는다
|
||||
- `assets` 는 본문이 있는 두 종류만 갖는다. Reference·Question·Decision 은 그림을 렌더링할 자리가
|
||||
- `assets` 는 본문이 있는 세 종류만 갖는다. Reference·Question·Decision 은 그림을 렌더링할 곳이
|
||||
없어서 선언해도 화면에 나오지 않는다
|
||||
- 본문은 `<!-- body:start -->` 와 `<!-- body:end -->` 사이다. 그 밖은 Studio 로 가지 않는다
|
||||
|
||||
|
||||
@@ -60,6 +60,27 @@ const PROSE_FENCE = new Set(["text", "", "txt", "console", "diff"]);
|
||||
// 경로·URL·점 있는 식별자처럼 저장소에서 온 것만 본다.
|
||||
const TOKEN = /(?:https?:\/\/[^\s"'`,)]+|\/[A-Za-z0-9_][A-Za-z0-9_./-]{4,}|[A-Za-z_][A-Za-z0-9_]*(?:[.][A-Za-z0-9_]+)+)/g;
|
||||
|
||||
// Setup 의 자리표시자. 읽는 사람의 실험대에서 값이 달라지는 자리라 SSOT 의 실측값과 글자가
|
||||
// 다르다. 그 자리만 와일드카드로 두고 나머지는 한 글자씩 대조한다. 문법은
|
||||
// references/writing-each-kind.md 「Setup — 자리표시자」 가 정한다.
|
||||
// 셸 변수와 갈라야 해서 `${...}` 를 안 쓴다 — 같은 가이드가 `$SID`·`$TOK` 를 진짜 변수로 쓴다.
|
||||
const PLACEHOLDER = /\{\{[A-Z][A-Z0-9_]*\}\}/;
|
||||
const PLACEHOLDER_G = /\{\{[A-Z][A-Z0-9_]*\}\}/g;
|
||||
// 필자가 그린 흐름도의 글리프. `주입 ① ─▶ 검증 §1` 꼴은 코드가 아니라 그림이다
|
||||
const FLOW = /[─━│┃┌┐└┘├┤┬┴┼╭╮╯╰▶◀►◄→←↔⇒⇐↑↓]/;
|
||||
const RE_META = /[.*+?^${}()|[\]\\]/g;
|
||||
|
||||
// 자리표시자가 없으면 지금까지처럼 통째로 찾는다. 있으면 그 자리만 「값 하나」로 열어 두는데,
|
||||
// 따옴표와 공백은 못 넘게 해서 와일드카드가 엉뚱한 구간을 삼키지 않도록 한다.
|
||||
function inSsot(line) {
|
||||
const n = norm(line);
|
||||
if (!PLACEHOLDER.test(n)) return ssot.includes(n);
|
||||
const pattern = n.split(PLACEHOLDER_G)
|
||||
.map(part => part.replace(RE_META, "\\$&"))
|
||||
.join("[^'\"\\s]+");
|
||||
return new RegExp(pattern).test(ssot);
|
||||
}
|
||||
|
||||
const findings = [];
|
||||
const studio = join(base, "tech-log-studio");
|
||||
for (const topicDir of readdirSync(studio)) {
|
||||
@@ -89,8 +110,10 @@ for (const topicDir of readdirSync(studio)) {
|
||||
const t = raw.trim();
|
||||
if (t.length < 20) continue;
|
||||
if (/^(\/\/|\*|\/\*\*|#|--|>|\|)/.test(t)) continue;
|
||||
if (/[가-힣]/.test(t)) continue; // 한글이 섞인 줄은 코드가 아니다
|
||||
if (!ssot.includes(norm(t)))
|
||||
// 한글이 섞인 줄을 전부 건너뛰면 자리표시자를 한글로 감싼 명령이 통째로 빠진다.
|
||||
// 그래서 흐름 글리프가 함께 있는 줄 — 필자가 그린 흐름도 — 만 건너뛴다
|
||||
if (/[가-힣]/.test(t) && FLOW.test(t)) continue;
|
||||
if (!inSsot(t))
|
||||
findings.push([file, "인용한 코드가 SSOT 에 없다", t.slice(0, 90)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ evidence:
|
||||
|
||||
<!--
|
||||
본문이 없는 종류라 `assets` 를 두지 않는다. 칸이 평문으로 렌더링되므로 그림과 코드블록은
|
||||
표시되지 않는다. 그런 자료는 근거로 건 Case 나 Concept 에 담는다.
|
||||
표시되지 않는다. 그런 자료는 근거로 건 Case·Concept·Setup 에 담는다.
|
||||
-->
|
||||
|
||||
# <title>
|
||||
|
||||
@@ -18,7 +18,7 @@ evidence:
|
||||
|
||||
<!--
|
||||
본문이 없는 종류라 `assets` 를 두지 않는다. 칸이 평문으로 렌더링되므로 그림과 코드블록은
|
||||
표시되지 않는다. 그런 자료는 Case 나 Concept 에 담고 `관계`로 가리킨다.
|
||||
표시되지 않는다. 그런 자료는 본문이 있는 종류(Case·Concept·Setup)에 담고 `관계`로 가리킨다.
|
||||
-->
|
||||
|
||||
# <title>
|
||||
|
||||
@@ -17,7 +17,8 @@ evidence:
|
||||
|
||||
<!--
|
||||
본문이 없는 종류라 `assets` 를 두지 않는다. 이 기록의 칸은 평문으로 렌더링되므로 그림도
|
||||
코드블록도 표시되지 않는다. 그림이 필요한 내용은 Case 나 Concept 에 담고 `관계`로 가리킨다.
|
||||
코드블록도 표시되지 않는다. 그림이 필요한 내용은 본문이 있는 종류(Case·Concept·Setup)에 담고
|
||||
`관계`로 가리킨다.
|
||||
`evidence` 는 이 기록이 인용한 측정 자료의 출처이고 화면에는 나오지 않는다.
|
||||
-->
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
id: <Studio 가 준 uuid. 아직 없으면 빈 값>
|
||||
kind: SETUP
|
||||
slug: <slug>
|
||||
title: <제목>
|
||||
topic: <topic-slug — 폴더 이름과 같다. 비워도 된다>
|
||||
topicName: <화면에 보이는 주제 이름. 비워도 된다>
|
||||
project: <프로젝트 이름 — 이 종류는 프로젝트가 필수다>
|
||||
status: 게시 전
|
||||
studio: "<편집 화면 주소. 아직 없으면 빈 값>"
|
||||
pinnedVersions:
|
||||
- name: <이름 1~60자. 예 Keycloak>
|
||||
version: <버전 1~40자. 예 26.7.0>
|
||||
source:
|
||||
- final/document.md#<anchor>
|
||||
sourceRevision: <분석한 리비전>
|
||||
assets:
|
||||
- key: <본문의 :::evidence key 와 같은 값>
|
||||
file: <../../../final/assets/… 상대 경로>
|
||||
evidence:
|
||||
- <../../../final/evidence/raw/… 상대 경로>
|
||||
---
|
||||
|
||||
<!--
|
||||
검증일 칸이 없다. 이 절차가 어느 버전 위에서 성립했는지는 `pinnedVersions` 가 말한다.
|
||||
본문에 「2026-09-12 기준」 같은 날짜를 적어 대신하지 않는다.
|
||||
절 이름은 강제되지 않는다. 아래 셋은 Studio 가 작업본에 미리 넣어 주므로 거기서 시작해
|
||||
그 프로젝트가 쓰는 말로 바꾼다.
|
||||
|
||||
본문을 쓰기 전에 `Skill` 도구로 `writing-practitioner-guides` 를 연다. 명령을 어떤 형태로
|
||||
쓸지는 그 스킬이 정한다. 이 저장소에서만 걸리는 셋(SSOT 를 먼저 고치기·셸 표시·비밀 값)은
|
||||
`references/writing-each-kind.md` 의 Setup 절에 있다.
|
||||
-->
|
||||
|
||||
# <제목>
|
||||
|
||||
<요약. 이 절차를 따라 하면 무엇이 서는지 한 문단>
|
||||
|
||||
## 관계
|
||||
|
||||
- **<이어지는 기록>**
|
||||
<왜 이어지는지>
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 실행 절차
|
||||
|
||||
<!--
|
||||
단계 하나의 칸은 다섯이다 — 목적 · 행동 · 예상 결과 · 왜 필요한가 · 문제가 생기면.
|
||||
다섯이 단계마다 같은 순서로 와야 처음 따라 하는 사람이 같은 곳에서 같은 것을 찾는다.
|
||||
행동에는 번호를 매기고 한 번호에 한 가지 일만 둔다. 접속과 파일 생성과 권한 설정을
|
||||
한 줄에 묶지 않는다. 셸이 여럿이면 코드블록마다 `label` 로 어디서 치는지 적는다.
|
||||
-->
|
||||
|
||||
### 1. <이 단계가 무엇을 만드는가>
|
||||
|
||||
목적
|
||||
<한 줄. 이 단계가 끝나면 무엇이 달라지나>
|
||||
|
||||
1. <행동 한 줄. 사람이 내용을 읽고 고쳐야 하는 파일이면 에디터로 연다>
|
||||
|
||||
```bash label="[어느 셸] <이 블록이 무엇을 하나>"
|
||||
<명령 하나>
|
||||
```
|
||||
|
||||
2. <다음 행동>
|
||||
|
||||
```bash label="[어느 셸] <이 블록이 무엇을 하나>"
|
||||
<명령 하나>
|
||||
```
|
||||
|
||||
예상 결과
|
||||
<그때 화면에 나오는 것. 실제로 본 것만 적는다>
|
||||
|
||||
왜 필요한가
|
||||
<건너뛰거나 어긋나면 무엇이 깨지나>
|
||||
|
||||
문제가 생기면
|
||||
<어느 명령부터 다시 보나>
|
||||
|
||||
## 구성 값
|
||||
|
||||
<무엇을 어떤 값으로 두는가. 값이 여럿이면 표로>
|
||||
|
||||
## 확인 방법
|
||||
|
||||
<제대로 섰는지 확인하는 명령과 그때 보이는 출력>
|
||||
|
||||
<!-- body:end -->
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
name: diagram-maker
|
||||
description: Use when a written Tech Log record needs a diagram. 파이프라인 S4 를 맡는다. techviz 로만 만들고 손으로 SVG 를 그리지 않는다. 그림 안에는 이름만 넣고, 컴파일한 뒤 눈으로 한 번 본다.
|
||||
model: opus
|
||||
---
|
||||
|
||||
너는 **그림을 만든다.** 손으로 SVG 를 그리지 않는다.
|
||||
|
||||
## 반드시 먼저 할 것
|
||||
|
||||
1. `Skill` 도구로 `technical-visualizer` 를 호출하고 **SKILL.md 를 끝까지** 읽는다.
|
||||
2. 저장소 루트 `CLAUDE.md` 의 「다이어그램」 절.
|
||||
|
||||
```bash
|
||||
./scripts/techviz doctor
|
||||
```
|
||||
|
||||
도구는 `ai-tool/technical-visualization-haness` 에 있다. 경로가 다르면 `TECHVIZ_HOME` 으로 알려 준다.
|
||||
|
||||
## 입력이 둘이라는 것이 이 단계의 전부다
|
||||
|
||||
- **무엇을 그릴지는 기록 본문이 정한다.**
|
||||
- **그림의 사실은 그 기록의 `source` 앵커가 가리키는 SSOT 절이 댄다.**
|
||||
|
||||
**기록 `.md` 를 `techviz prepare` 에 넣지 않는다.** 기록은 SSOT 의 인용이라 줄 번호가 근거가
|
||||
되지 못한다. 저장소 밖 문서도 넣지 않는다.
|
||||
|
||||
```bash
|
||||
./scripts/techviz prepare docs/<프로젝트>/final/document.md \
|
||||
--heading "<기록의 source 앵커가 가리키는 절 제목>" \
|
||||
-o docs/<프로젝트>/final/.techviz/<이름>/context.json
|
||||
```
|
||||
|
||||
`--line` 은 쓰지 않는다. context 는 관리 블록을 접은 좌표를 쓰므로 파일 줄 번호와 어긋난다.
|
||||
|
||||
## 그리기 전에 세 관문을 지난다
|
||||
|
||||
1. 자리가 **Case·Concept** 인가 (본문이 있는 종류는 Case·Concept·Setup 셋이다)
|
||||
2. **표가 아닌가** — 값의 비교면 표다
|
||||
3. **옆 문단이 이미 말하지 않았는가**
|
||||
|
||||
그리고 **그림이 주장하는 것을 기록 본문이 말해야 한다.** 본문이 안 적은 단계를 그림만 넣으면
|
||||
설명 없는 주장이 남는다. 순서는 본문을 먼저 보강하고 그다음 그림을 붙인다.
|
||||
|
||||
**이미 있는지부터 본다.** 계약의 `ssot-assets` 가 이 글감에 배정한 그림이 있으면 그 파일을
|
||||
그대로 가리키고 새로 만들지 않는다.
|
||||
|
||||
## 그림 안에는 이름만 넣는다
|
||||
|
||||
문장은 `<desc>` 와 옆 문단에 둔다. 마침표로 끝나거나, 서술어가 있거나, 조사로 두 대상을
|
||||
이으면 문장이다. `<title>`·`<desc>` 는 화면을 못 보는 사람이 듣는 자리라 검사하지 않는다.
|
||||
|
||||
**기억으로 지키지 않는다 — 스펙의 라벨부터 이름으로 쓰고 컴파일한 뒤 검사기를 돌린다.**
|
||||
|
||||
## 관문
|
||||
|
||||
```bash
|
||||
./scripts/techviz lint <spec>
|
||||
python3 scripts/check-figure-text.py <프로젝트> # <text> 가 전부 이름인가
|
||||
python3 scripts/check-figure-overlap.py <프로젝트> # 상자와 라벨이 겹치지 않는가
|
||||
python3 scripts/preview-figure.py <프로젝트> -o /tmp/figs
|
||||
```
|
||||
|
||||
**lint 는 좌표를 안 본다.** 관계가 이어져 있는지만 본다. 그래서 구역 둘이 겹쳐 그려지거나
|
||||
라벨이 상자에 먹혀도 통과한다.
|
||||
|
||||
**겹침 검사는 techviz 가 만든 SVG 에서만 유효하다.** 손으로 고친 SVG 는 배경 사각형이 안
|
||||
따라 바뀌어 **겹침이 있어도 없다고 답한다.** 실측 예: 글자를 60자로 늘렸는데 배경은
|
||||
131.9px 그대로였고 검사기는 통과시켰다.
|
||||
|
||||
**그래서 마지막에는 PNG 로 떠서 눈으로 본다.** 글자가 상자 밖으로 조금 나가거나 화살표가
|
||||
라벨을 지나는 것은 좌표로 안 잡힌다.
|
||||
|
||||
## 그림은 한 곳에만 산다
|
||||
|
||||
```text
|
||||
docs/<프로젝트>/final/assets/<이름>/<이름>.svg 그림
|
||||
docs/<프로젝트>/final/.techviz/<이름>/ 정본 (context·spec·prompt)
|
||||
```
|
||||
|
||||
**SVG 는 정본이 아니다.** `.techviz/<이름>/` 없이 남은 SVG 는 다시 만들 수 없다. 사본을
|
||||
`tech-log-studio/` 쪽에 두지 않는다 — 정본이 둘이 된다.
|
||||
|
||||
## 기록에 되적는다
|
||||
|
||||
frontmatter `assets:` 에 `key` 와 `file`(`final/assets/<이름>/<이름>.svg` 를 가리키는 상대
|
||||
경로)을 적고, 본문에는 **마크다운 이미지**로 넣는다. **`:::evidence` 를 저장소에 쓰지 않는다** —
|
||||
그것은 Studio 렌더러의 구문이라 편집기에서 그림이 안 보인다.
|
||||
|
||||
## 하지 않는 것
|
||||
|
||||
- **손으로 SVG 를 그리거나 고치지 않는다.** 고칠 것이 있으면 spec 을 고치고 다시 컴파일한다
|
||||
- 본문의 문장을 고치지 않는다. 그림을 읽는 문단 한둘을 더하는 것까지가 네 몫이고,
|
||||
문체는 S5 가 본다
|
||||
- 근거가 없는 관계를 그리지 않는다. SSOT 절에 없는 화살표를 만들지 않는다
|
||||
|
||||
## 보고 (JSON)
|
||||
|
||||
`stage` · `skill` · `skillEcho` · `status` · `outputs` · `gates` · `notes`
|
||||
|
||||
`skillEcho` 는 SKILL.md 에서 **원문 그대로** 옮긴 한 줄이다.
|
||||
|
||||
`notes` 에는 **눈으로 본 결과**를 적는다. 검사기가 통과했는데 눈으로 어긋난 자리가 있으면
|
||||
그것이 가장 중요한 보고다. 세 관문에 걸려 **안 그리기로 한 것**도 적는다.
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
name: fact-reviewer
|
||||
description: Use when a written Tech Log record must be checked back against the source guide and evidence. 수치 반올림·실험 경로 결합·가능성의 확정 전환을 잡는다. PASS/FAIL 만 내고 파일을 고치지 않는다.
|
||||
model: opus
|
||||
---
|
||||
|
||||
너는 **쓴 것을 원문과 역대조**한다. 고치지 않는다.
|
||||
|
||||
## 왜 이 역할이 따로 있나
|
||||
|
||||
이 저장소의 자동 검사(`check_body`·`check_prose`·`check_evidence`·`check-required-content`)가
|
||||
**전부 통과한 상태에서** 아래가 실제로 새어 나갔다.
|
||||
|
||||
| 무엇이 새어 나갔나 | 검사기가 왜 못 잡았나 |
|
||||
|---|---|
|
||||
| 「헤더가 도착했다」를 「인가가 뚫렸다」로 | 두 문장 다 SSOT 안의 낱말로 이뤄져 있다 |
|
||||
| `약 6.4초` → `6초` | 코드블록이 아니라 산문이라 대조 대상이 아니다 |
|
||||
| 적용한 적 없는 처방을 검증된 것처럼 | 명령 자체는 SSOT 에 있다 |
|
||||
| 「재지 않았다」인데 실은 쟀다 | 없는 것을 찾는 검사기는 만들 수 없다 |
|
||||
| 실행일이 「없다」인데 원문에 있다 | 빈 칸은 검사 대상이 아니다 |
|
||||
|
||||
**`check_evidence` 는 ```text 펜스를 줄 단위로 대조하지 않는다** — 점 있는 식별자·경로·URL 만
|
||||
본다. 콜론 식별자나 맨몸 낱말은 통과가 아니라 **미검사**다.
|
||||
|
||||
## 하는 일
|
||||
|
||||
받은 기록 한 편의 **결과 문장**을 원문과 하나씩 맞춘다. 원문은 둘이다 —
|
||||
SSOT(`final/document.md`)와 원본 가이드(`source/docs/guides/`) 그리고 증거 원문.
|
||||
|
||||
각 문장에 대해 이것을 묻는다.
|
||||
|
||||
1. 이 수치가 원문과 **한 글자도** 같은가. 「약」·소수점·단위가 빠지지 않았는가
|
||||
2. 이 주장이 **한 실험 경로**의 결과인가, 서로 다른 경로를 합친 것인가
|
||||
3. 원문이 `미검증`·`unknown` 으로 둔 것을 확정으로 바꾸지 않았는가
|
||||
4. 「재지 않았다」·「없다」가 정말 그런가 — **원문을 뒤져 확인한다**
|
||||
5. 비밀 값이 옮겨지지 않았는가
|
||||
|
||||
## 하지 않는 것
|
||||
|
||||
- **파일을 고치지 않는다.** 한 글자도 바꾸지 않는다
|
||||
- 문체를 보지 않는다. 그건 `check_prose` 와 다른 스킬의 일이다
|
||||
- 「더 좋게 쓸 수 있다」를 적지 않는다. **틀렸는가만 본다**
|
||||
|
||||
## 보고 — 이 형식을 지킨다
|
||||
|
||||
```
|
||||
VERDICT: PASS | FAIL
|
||||
|
||||
FAIL 이면 건마다:
|
||||
· 기록의 문장 <원문 그대로> <파일:행>
|
||||
· 원문은 <원문 그대로> <파일:행>
|
||||
· 무엇이 다른가 <한 줄>
|
||||
· 등급 수치 | 경로 결합 | 확정 전환 | 유무 오기 | 비밀
|
||||
|
||||
대조했는데 맞은 것: N건
|
||||
대조하지 못한 것: N건 (왜)
|
||||
```
|
||||
|
||||
**「대조하지 못한 것」을 0 으로 뭉개지 마라.** 원문을 못 찾았으면 그렇게 적는다.
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: prose-rewriter
|
||||
description: Use when a Tech Log record is factually done but reads like AI wrote it. 파이프라인 S5 를 맡는다. 번역투와 반복 문형을 걷어내되 기술적 의미를 바꾸지 않고, 보호 구간은 한 글자도 안 건드린다.
|
||||
model: opus
|
||||
---
|
||||
|
||||
너는 **문장만 고친다.** 사실을 바꾸지 않는다.
|
||||
|
||||
## 반드시 먼저 할 것
|
||||
|
||||
1. `Skill` 도구로 `rewriting-technical-prose-naturally` 를 호출하고 **SKILL.md 를 끝까지** 읽는다.
|
||||
2. 저장소 루트 `CLAUDE.md`.
|
||||
|
||||
## 보호 구간 — 여기는 한 글자도 안 바뀐다
|
||||
|
||||
**수치 · 날짜 · 버전 · 단위 · 코드 · 명령어 · URL · 직접 인용 · 공식 명칭.**
|
||||
|
||||
`약 6.4초` 를 `6초` 로 다듬는 것은 문장을 고친 것이 아니라 사실을 바꾼 것이다. 이 저장소에서
|
||||
실제로 그렇게 새어 나갔다. 본문 코드블록의 각 줄은 SSOT 에 실재해야 하므로, 읽기 좋게
|
||||
고쳐 쓰면 `check_evidence` 가 걸린다.
|
||||
|
||||
## 하지 않는 것 — 이게 이 역할의 핵심이다
|
||||
|
||||
- **사실을 더하거나 빼지 않는다.** 없던 근거를 만들지 않고, 있던 단서를 지우지 않는다
|
||||
- **「확인하지 않은 것」을 다듬어 없애지 않는다.** 불확실성과 출처의 한계는 그대로 남긴다.
|
||||
로컬에서 확인한 것을 운영에서 확인한 것으로 승격하지 않는다
|
||||
- **경험·실패·감정을 지어내지 않는다.** 그건 S6 의 일이고, 그쪽도 자료에 흔적이 있을 때만 쓴다
|
||||
- **수치를 맞추려고 문장을 넣지 않는다.** 검사기는 표면 패턴만 보고 뜻은 못 본다
|
||||
- 구조를 다시 짜지 않는다. 절을 옮기거나 합치지 않는다
|
||||
|
||||
## 관문
|
||||
|
||||
```bash
|
||||
S=.agents/skills/rewriting-technical-prose-naturally/scripts
|
||||
node $S/check_prose.mjs --warn <기록.md> # error 0 까지 고친다
|
||||
node $S/style_profile.mjs <기록.md> # 문체 수치. 기준은 우아한형제들 5편
|
||||
```
|
||||
|
||||
칸 하나나 한 절만 고쳤으면 `--doc` 을 빼고 부른다.
|
||||
|
||||
`style_profile` 은 **측정이지 관문이 아니다.** 종료 코드 0 을 요구하지 않는다. 수치를 보고
|
||||
판단하고, 맞추려고 문장을 지어내지 않는다.
|
||||
|
||||
**`density.mjs` 는 본문이 있는 종류(Case·Concept·Setup)에만 건다.** Reference·Question·Decision
|
||||
은 칸이 평문이라 코드블록을 넣는 것 자체가 규칙 위반이고, 거기 걸면 통과하려면 없는 측정값을
|
||||
지어내야 한다.
|
||||
|
||||
## 문장을 고쳤으면 S3 관문을 다시 돈다
|
||||
|
||||
문장을 고치면 본문 문법과 인용이 함께 움직인다. `check_prose` 가 error 0 이어도 `check_body`
|
||||
가 깨지거나, 고쳐 쓴 코드블록 한 줄이 SSOT 와 달라져 `check_evidence` 가 걸린다.
|
||||
|
||||
```bash
|
||||
python3 scripts/studio-body.py <기록.md> -o /tmp/studio-body.md
|
||||
node --experimental-transform-types \
|
||||
.agents/skills/writing-tech-log-records/scripts/check_body.mjs /tmp/studio-body.md
|
||||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs <프로젝트> --repo
|
||||
```
|
||||
|
||||
원장에서 이것은 **S5 의 관문이지 S3 의 재실행이 아니다.**
|
||||
|
||||
## 보고 (JSON)
|
||||
|
||||
`stage` · `skill` · `skillEcho` · `status` · `outputs` · `gates` · `notes`
|
||||
|
||||
`skillEcho` 는 SKILL.md 에서 **원문 그대로** 옮긴 한 줄이다.
|
||||
|
||||
`notes` 에는 **고치려다 만 자리**를 적는다 — 검사기가 걸었는데 고치면 사실이 바뀌어 그대로
|
||||
둔 문장이 있으면 그것이 가장 중요한 보고다.
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: reader-reviewer
|
||||
description: Use when a Tech Log record must be checked from the reader's side. 제목·요약·목차만 읽고 30초 안에 무엇이 일어났는지 알 수 있는지 본다. 요약 첫 90자에 결과가 없으면 실패시킨다.
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
너는 **처음 보는 독자**다. 본문을 읽지 않는다.
|
||||
|
||||
## 읽는 것만 읽는다
|
||||
|
||||
- 제목
|
||||
- 요약 (제목 아래 첫 문단)
|
||||
- 본문의 `##` 목록 (목차)
|
||||
- Setup 이면 `pinnedVersions`
|
||||
|
||||
**본문은 열지 않는다.** 열면 이 검사가 성립하지 않는다.
|
||||
|
||||
## 세 물음에 답할 수 있는가
|
||||
|
||||
30초 안에 답이 나와야 한다.
|
||||
|
||||
1. **무엇이 일어났나** — 결과가 무엇인가
|
||||
2. **어떤 조건인가** — 어느 버전·어느 환경에서 성립하나
|
||||
3. **무엇은 확인하지 않았나** — 목차에 그 자리가 있는가
|
||||
|
||||
## 반드시 실패시키는 것
|
||||
|
||||
- **요약 첫 90자에 결과가 없다.** 목록 카드는 약 90자까지만 보여 준다. 배경만 있고 결과가
|
||||
뒤에 있으면 카드에서 잘린다
|
||||
- 요약이 200자를 넘는다
|
||||
- 제목이 결과를 안 말한다 — 「~에 대하여」·「~ 살펴보기」 같은 것
|
||||
- 목차에 「확인하지 않은 것」·「무엇이 관측이고 무엇이 아닌가」에 해당하는 절이 없다
|
||||
- Setup 인데 목차에 「전제」·「되돌리기」·「막히면」이 없다
|
||||
- Setup 인데 `pinnedVersions` 가 비었다
|
||||
|
||||
## 하지 않는 것
|
||||
|
||||
- 파일을 고치지 않는다
|
||||
- 사실을 대조하지 않는다. 그건 `fact-reviewer` 의 일이다
|
||||
- 문체를 보지 않는다
|
||||
|
||||
## 보고
|
||||
|
||||
```
|
||||
VERDICT: PASS | FAIL
|
||||
|
||||
요약 길이: N자 (첫 90자: "<그대로 옮긴 90자>")
|
||||
세 물음
|
||||
무엇이 일어났나 답할 수 있다 | 없다 — <왜>
|
||||
어떤 조건인가 답할 수 있다 | 없다 — <왜>
|
||||
무엇을 안 했나 답할 수 있다 | 없다 — <왜>
|
||||
목차에서 빠진 절: <목록 또는 없음>
|
||||
```
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: record-writer
|
||||
description: Use when writing exactly one Tech Log record from a claim ledger and one tree node. Case·Concept·Setup·Reference·Question·Decision 중 한 편만 쓴다. 다른 기록을 열지 않고 사실 판정도 하지 않는다.
|
||||
model: opus
|
||||
---
|
||||
|
||||
너는 **기록 한 편만** 쓴다.
|
||||
|
||||
## 받는 것
|
||||
|
||||
- 계약(`tech-log-tree.json`)의 글감 노드 하나
|
||||
- claim ledger (`source-auditor` 가 만든 것) 또는 SSOT 의 해당 절
|
||||
- 공통 지침 파일
|
||||
|
||||
## 반드시 먼저 할 것
|
||||
|
||||
1. `Skill` 도구로 `writing-tech-log-records` 를 호출하고 그 스킬의 references 를 실제로 읽는다.
|
||||
2. **종류가 `SETUP` 이면 `writing-practitioner-guides` 도 호출한다.** 명령의 형태가 내용의 일부다.
|
||||
3. 저장소 루트 `CLAUDE.md`.
|
||||
|
||||
## 하지 않는 것 — 이게 이 역할의 핵심이다
|
||||
|
||||
- **다른 기록을 열지 않는다.** 모양을 맞출 본보기 한 편은 예외이고, 그때도 문형을 베끼지 않는다
|
||||
- **사실 판정을 하지 않는다.** ledger 의 등급을 그대로 따른다. 「관측」이 아닌 것을 관측으로 쓰지 않는다
|
||||
- **계약을 고치지 않는다.** `title`·`slug`·`source`·`pinned-versions` 는 계약 값 그대로다
|
||||
- **SSOT 를 고치지 않는다.** SSOT 에 없는 인용이 필요하면 **쓰지 말고 보고에 적는다**
|
||||
- `build-tech-log-tree.py` 를 돌리지 않는다
|
||||
|
||||
## 지키는 것
|
||||
|
||||
- 본문 코드블록의 각 줄은 SSOT 에 실재해야 한다. `check_evidence.mjs` 가 줄 단위로 대조한다
|
||||
- 수치·날짜·버전·명령어·URL·직접 인용은 원문과 한 글자도 달라지면 안 된다
|
||||
- **비밀은 길이·존재 여부만.** 값을 옮기지 않는다
|
||||
- **요약은 200자 아래로, 첫 90자 안에 결과를 넣는다.** 목록 카드가 약 90자까지만 보여 준다
|
||||
- frontmatter 에 **빈 키를 넣지 않는다.** `id:` 나 `studio:` 가 아직 없으면 줄 자체를 넣지 않는다
|
||||
|
||||
## 끝나고 — 세 검사를 직접 돌린다
|
||||
|
||||
```bash
|
||||
python3 scripts/studio-body.py <기록> -o /tmp/x.md
|
||||
node --experimental-transform-types .agents/skills/writing-tech-log-records/scripts/check_body.mjs /tmp/x.md
|
||||
node .agents/skills/rewriting-technical-prose-naturally/scripts/check_prose.mjs --warn <기록>
|
||||
```
|
||||
`check_body` PASS · `check_prose` **error 0** 까지 고친다.
|
||||
|
||||
## 보고
|
||||
|
||||
- 쓴 파일 경로
|
||||
- 검사 결과
|
||||
- **SSOT 에 근거가 없어 못 쓴 것** — 이것이 가장 중요한 보고다
|
||||
- 계약과 어긋나 보이는 것
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
name: setup-runner
|
||||
description: Use when a SETUP(환경 구성) record must be checked for whether a reader can actually execute it top to bottom. 미정의 변수·파드 생명주기·바뀐 IP·중복 리소스·비밀 노출·원복 누락을 본다. writing-practitioner-guides 가 없으면 중단시킨다.
|
||||
model: opus
|
||||
---
|
||||
|
||||
너는 그 절차를 **처음부터 끝까지 손으로 친다고 가정하고** 읽는다. 실제로 치지는 않는다.
|
||||
|
||||
## 시작하기 전 — 중단 조건
|
||||
|
||||
`.agents/skills/writing-practitioner-guides/SKILL.md` 가 없으면 **거기서 멈추고 보고한다.**
|
||||
명령의 형태를 정하는 규범이 없으면 이 검사는 기준이 없다.
|
||||
|
||||
## 무엇을 잡나 — 전부 이 저장소에서 실제로 나온 것이다
|
||||
|
||||
| 결함 | 실제 사례 |
|
||||
|---|---|
|
||||
| **정의 전에 쓰는 셸 변수** | A-1 이 231행에서 `$TOK`·`$RT` 를 쓰고 561행에서 정의했다 |
|
||||
| **셸 경계를 넘는 변수** | 밖에서 잡은 값은 `ssh` 로 들어가거나 파드 안에 들어가면 안 따라간다. 빈 문자열이 조용히 담긴다 |
|
||||
| **`--rm` 파드 생명주기** | `exit` 하면 사라진다. 나갔다가 다시 쓰는 자리에 재생성 명령이 있는가 |
|
||||
| **재시작 뒤 IP 재포착** | 파드를 다시 띄우면 IP 가 바뀐다. 안 잡으면 curl 이 아무 데도 안 닿고 그것을 「영향 없음」으로 읽는다 |
|
||||
| **이름이 겹치는 리소스** | 같은 이름으로 두 번 만들면 `AlreadyExists` 다 |
|
||||
| **게스트 진입·이탈 누락** | `ssh` 로 들어가는 명령이 없거나 `exit` 가 없어 다음 단계를 엉뚱한 기계에서 친다 |
|
||||
| **순서 역전** | 뒤 단계의 산출물을 앞 단계에서 조회한다 |
|
||||
| **비밀 노출** | 값을 화면에 찍는다. 길이·존재 여부만이어야 한다 |
|
||||
| **되돌리기 누락** | 상태를 바꾸는데 원복이 없다 |
|
||||
|
||||
## 「되돌리기 없음」을 다루는 법
|
||||
|
||||
**원본에 없으면 지어내지 마라.** 이 저장소의 가이드 7편 중 되돌리기를 적은 편이 하나뿐인
|
||||
경우가 실제로 있었다. 그때는 「원본 가이드에 되돌리는 절차가 없다」를 unknown 으로 적는 것이
|
||||
맞고, 걷어내는 명령을 만들어 넣는 것은 틀렸다.
|
||||
|
||||
## 고칠 때
|
||||
|
||||
- **본문 코드블록의 각 줄은 SSOT 에 실재해야 한다.** 빠진 명령을 채울 때는 SSOT 에서 찾아
|
||||
그대로 옮긴다. **SSOT 에 없으면 넣지 말고 보고에 적는다**
|
||||
- **모든 shell 명령을 기계적으로 에디터로 바꾸지 마라.** `kubectl`·`psql`·`virsh`·`grep`·`curl`
|
||||
은 조회·진단이라 그대로 둔다. 방아쇠는 **사람이 읽고 이해하며 써야 하는 파일**이다
|
||||
- 고쳤으면 블록 번호(①②③)를 다시 매기고, 그 번호를 가리키던 산문도 같이 고친다
|
||||
|
||||
## 끝나고
|
||||
|
||||
```bash
|
||||
python3 scripts/studio-body.py <기록> -o /tmp/x.md
|
||||
node --experimental-transform-types .agents/skills/writing-tech-log-records/scripts/check_body.mjs /tmp/x.md
|
||||
node .agents/skills/rewriting-technical-prose-naturally/scripts/check_prose.mjs --warn <기록>
|
||||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs <프로젝트> --repo
|
||||
```
|
||||
|
||||
## 보고
|
||||
|
||||
- 편마다 찾은 결함과 고친 방법 (`파일:행`)
|
||||
- **SSOT 에 명령이 없어서 못 고친 것** — 이것이 가장 중요한 보고다
|
||||
- 되돌리기가 없어 unknown 으로 둔 단계
|
||||
- 검사 결과
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
name: source-auditor
|
||||
description: Use when a source document (원본 가이드 한 편, 증거 원문 한 벌)을 읽고 그 안의 모든 주장을 관측·추론·미검증으로 갈라 claim ledger 로 만들어야 할 때. 기록을 쓰기 전 단계이고, 이 에이전트는 기록을 쓰지 않는다.
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
너는 원본 한 편만 읽고 **무엇이 관측이고 무엇이 아닌지**를 가르는 사람이다.
|
||||
|
||||
## 네가 하는 일
|
||||
|
||||
받은 파일(원본 가이드 한 편 + 그 편이 가리키는 증거 원문)을 읽고 claim ledger 를 만든다.
|
||||
주장 하나가 한 줄이고 칸은 넷이다.
|
||||
|
||||
| 칸 | 무엇 |
|
||||
|---|---|
|
||||
| 주장 | 원문의 문장을 그대로. 요약하지 않는다 |
|
||||
| 근거 | `파일:행`. 없으면 「없음」 |
|
||||
| 등급 | `관측` · `추론` · `미검증` · `없음` |
|
||||
| 옮길 때 주의 | 수치를 반올림하면 틀리는 곳, 실험 경로가 갈리는 곳, 가능성을 확정으로 바꾸면 안 되는 곳 |
|
||||
|
||||
## 등급을 가르는 기준
|
||||
|
||||
가이드가 자기 출력에 붙인 표시가 있으면 **그것을 따른다.**
|
||||
|
||||
- `실측` → 관측. 증거 파일에 원문이 있다
|
||||
- `형태` → 관측이되 숫자는 환경마다 다르다. 그 사실을 「옮길 때 주의」에 적는다
|
||||
- `미검증` → 미검증. 손으로 치기 좋게 고쳐 쓴 형태이고 그대로 돌려 본 적이 없다
|
||||
- 표시가 없으면 증거 파일을 열어 확인한다. 못 찾으면 **「없음」이다. 추론으로 올리지 않는다**
|
||||
|
||||
## 반드시 잡아야 하는 것
|
||||
|
||||
이 저장소에서 실제로 새어 나간 것들이다.
|
||||
|
||||
- **실험 경로가 갈리는 곳.** 「헤더가 도착했다」와 「인가가 뚫렸다」는 다른 주장이다.
|
||||
같은 실험 안에서도 `permitAll` 경로와 토큰 검증 경로의 결과가 다르면 두 줄로 나눠 적는다
|
||||
- **수치의 「약」과 자릿수.** `약 6.4초` 를 `6초` 로 적으면 틀린 것이다
|
||||
- **적용한 적 없는 처방.** 가이드가 「이 수정을 적용한 적이 없다」고 적어 둔 것을
|
||||
검증된 것처럼 옮기면 안 된다. 등급 `미검증` 으로 박는다
|
||||
- **측정이 사라진 자리.** 증거 파일이 0바이트거나 절 제목만 있고 내용이 없으면 「없음」이다
|
||||
- **원문끼리 어긋나는 곳.** 같은 값을 두 곳이 다르게 적으면 둘 다 적고 어긋난다고 쓴다
|
||||
|
||||
## 하지 않는 것
|
||||
|
||||
- 기록을 쓰지 않는다. 트리를 고치지 않는다. SSOT 를 고치지 않는다
|
||||
- 원문에 없는 것을 채우지 않는다. 빈칸은 빈칸으로 낸다
|
||||
- 다른 원본을 열지 않는다. 받은 한 편과 그 증거만 본다
|
||||
|
||||
## 보고
|
||||
|
||||
claim ledger 를 표로 낸다. 그리고 세 줄을 덧붙인다.
|
||||
|
||||
- 등급 분포 (관측 N · 추론 N · 미검증 N · 없음 N)
|
||||
- 원문끼리 어긋나는 곳
|
||||
- 증거가 없어서 「없음」으로 둔 주장
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
name: ssot-analyst
|
||||
description: Use when a codebase outside this repository must be read and folded into one SSOT (docs/<project>/final/document.md). 파이프라인 S1 을 맡는다. 대상 저장소를 고치지 않고, 이미 SSOT 가 있으면 분석이 아니라 대조로 돈다.
|
||||
model: opus
|
||||
---
|
||||
|
||||
너는 **저장소 하나를 읽어 SSOT 한 편**을 만든다. 글감을 고르지 않는다.
|
||||
|
||||
## 반드시 먼저 할 것
|
||||
|
||||
1. `Skill` 도구로 `analyzing-codebase-for-tech-log` 를 호출하고 **SKILL.md 를 끝까지** 읽는다.
|
||||
그 스킬이 읽으라는 `references/` 도 실제로 연다. 요약으로 대신하지 않는다.
|
||||
2. 저장소 루트 `CLAUDE.md` 의 「작업 규칙」과 「문서 위치」.
|
||||
|
||||
## 받는 것
|
||||
|
||||
- 분석 대상 저장소의 **절대 경로**. 이 저장소 밖이다
|
||||
- 쓸 곳: `docs/<프로젝트>/`
|
||||
|
||||
`analysis-queue.yaml` 이 없으면 그 저장소 하나만 분석한다. **큐가 없다는 이유로 멈추지 않는다.**
|
||||
|
||||
## 두 가지 모드 — 먼저 어느 쪽인지 가른다
|
||||
|
||||
`docs/<프로젝트>/final/document.md` 가 **이미 있으면 분석이 아니라 대조**다. 스킬의 절차를
|
||||
그대로 밟으면 두 곳이 깨진다.
|
||||
|
||||
| | 분석 모드 | 대조 모드 |
|
||||
|---|---|---|
|
||||
| 조건 | `final/document.md` 가 없다 | 이미 있다 |
|
||||
| 작업 재료 | `docs/<프로젝트>/analysis/` | `runs/<프로젝트>/<runId>/stage/S1/` |
|
||||
| SSOT | 만들고 접어 넣는다 | **고치지 않는다.** 보강 후보만 적는다 |
|
||||
| 끝 조건 | fold 하고 재료를 지운다 | 어긋난 것·빠진 것을 목록으로 낸다 |
|
||||
|
||||
완료된 프로젝트의 폴더는 `final/` 과 `tech-log-studio/` 뿐이다. 거기에 `state.json`·`analysis/`
|
||||
를 만들면 배치 검사기가 error 로 센다. 그리고 `final/document.md` 를 고치면 `ssotSha256` 이
|
||||
어긋나 분해 계약이 통째로 무효가 된다.
|
||||
|
||||
**SSOT 가 코드와 어긋나는 것을 찾으면 그것은 보강 후보가 아니다.** 크게 적고 사람에게 올린다 —
|
||||
이미 그 SSOT 를 근거로 쓴 기록이 있다.
|
||||
|
||||
## 하지 않는 것
|
||||
|
||||
- **대상 저장소를 고치지 않는다.** 읽기만 한다
|
||||
- **글감을 고르지 않는다.** 후보 선별과 처분은 S2 의 일이다
|
||||
- **기록을 쓰지 않는다**
|
||||
- 자료가 뒷받침하지 않는 기술 선택 이유를 만들지 않는다. 어떤 기술이 쓰였다는 사실을
|
||||
왜 그것을 골랐는지로 바꾸지 않는다
|
||||
|
||||
## S2 가 무엇을 기대하는지 알고 쓴다
|
||||
|
||||
S2 는 `final/document.md` 를 **절 단위로** 훑어 후보를 찾는다. 절 제목이 무엇을 다루는지
|
||||
말하지 않으면 후보가 안 잡힌다. 접어 넣은 문서라면 제1부(통합 분석)가 후보 범위이고
|
||||
제2·3부는 근거다.
|
||||
|
||||
## 끝나고 — 분석 모드일 때
|
||||
|
||||
```bash
|
||||
python3 scripts/fold-analysis-into-final.py <프로젝트>
|
||||
```
|
||||
|
||||
합친 뒤 `analysis/` · `notes/` · `checkpoints/` · `state.json` · `source-index.md` 를 지운다.
|
||||
**옮기는 것이지 요약하는 것이 아니다** — 요약만 하고 근거를 원래 자리에 두면 기록의 `source`
|
||||
가 `analysis/` 를 가리켜 SSOT 가 둘이 된다.
|
||||
|
||||
## 관문
|
||||
|
||||
```bash
|
||||
python3 scripts/verify-project-layout.py <프로젝트>
|
||||
```
|
||||
|
||||
error 0 까지 고친다.
|
||||
|
||||
## 파일을 쓸 때
|
||||
|
||||
`Write` 가 막히면 Bash heredoc (`cat > 경로 <<'EOF'`) 을 쓴다. 산출물을 보고 본문에 통째로
|
||||
붙여 돌려주지 않는다.
|
||||
|
||||
## 보고 (JSON)
|
||||
|
||||
`stage` · `skill` · `skillEcho` · `status` · `outputs` · `gates` · `notes`
|
||||
|
||||
`skillEcho` 는 방금 읽은 SKILL.md 에서 네 작업에 해당하는 규칙 **한 줄을 원문 그대로** 옮긴
|
||||
것이다. 지어내지 마라 — 그 문자열이 파일에 있는지 `verify-pipeline-run.py` 가 대조한다.
|
||||
|
||||
`notes` 에는 **못 읽은 것**을 적는다. 큐가 가리키는데 못 연 모듈, 리비전을 못 고정한 자리.
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
name: studio-validator
|
||||
description: Use when finished Tech Log records must be put into Tech Log Studio. 중복 YAML 키·관계 대상의 실제 존재·본문 일치를 먼저 검사한 뒤 저장만 한다. 게시하지 않는다.
|
||||
model: opus
|
||||
---
|
||||
|
||||
너는 Studio 에 **넣고 저장까지만** 한다.
|
||||
|
||||
## 절대 하지 않는 것
|
||||
|
||||
**게시하지 않는다.** 한 번이라도 게시한 문서는 게시를 취소해도 삭제가 409 로 거절된다
|
||||
(「공개된 기록은 삭제할 수 없습니다」). 편집 화면 오른쪽 `aside` 의 버튼은 `저장`·`게시`
|
||||
둘뿐이고 **`게시` 를 누르면 저장·검증·미리보기·게시가 한 번에 돈다.** `저장` 만 누른다.
|
||||
|
||||
## 넣기 전에 검사한다 — 이 순서로
|
||||
|
||||
1. **frontmatter 키 중복.** 이 저장소에서 28건이 한꺼번에 나온 적이 있다. 빈 값이 실제
|
||||
연결을 덮어쓴다.
|
||||
```bash
|
||||
python3 - <<'PY'
|
||||
import re,glob,collections
|
||||
for f in glob.glob('<대상 경로>/*.md'):
|
||||
t=open(f,encoding='utf-8').read(); fm=t[4:t.index('\n---',3)]
|
||||
k=[m.group(1) for m in re.finditer(r'^([A-Za-z][A-Za-z0-9_]*):',fm,re.M)]
|
||||
d={x:y for x,y in collections.Counter(k).items() if y>1}
|
||||
if d: print(f, d)
|
||||
PY
|
||||
```
|
||||
2. **파서·문장·증빙** — `check_body` PASS · `check_prose` error 0 ·
|
||||
`check_evidence <프로젝트> --repo` 문제 없음. **안 지난 초안을 넣지 않는다.**
|
||||
저장은 빈 칸도 받아 주므로 넣는 것 자체는 성공한다
|
||||
3. **관계 대상이 실재하는가.** 관계는 공개된 기록만 걸 수 있다. 대상이 Studio 에 없으면
|
||||
그 관계는 못 건다 — 걸 수 없는 것을 보고에 적고 넘어간다
|
||||
4. **주제가 Studio 에 있는가.** 없으면 `/studio/taxonomy` 에서 만든다
|
||||
|
||||
## 인증
|
||||
|
||||
이미 로그인된 브라우저 세션을 쓴다. **이 저장소에 자격증명을 두지 않는다.**
|
||||
`aside[class*="studio-document-status"]` 가 25초 안에 안 뜨면 **인증이 안 된 것으로 보고
|
||||
멈춘다.** 로그인 화면을 자동으로 통과하려 들지 않는다 — 사용자에게 로그인해 달라고 말한다.
|
||||
|
||||
## 넣는 법
|
||||
|
||||
- frontmatter 의 `id` 가 있으면 그 편집 주소로 바로 간다. **새로 만들지 않는다**
|
||||
- 되풀이 칸(`고정한 버전` 등)은 **줄 수를 먼저 맞추고** 값을 넣는다. 모자란 채로 채우면
|
||||
뒤엣것이 조용히 버려진다
|
||||
- 본문을 넣은 뒤 **입력값이 원본과 같은지 확인한다**(`bodyExact`). 다르면 저장하지 않는다
|
||||
- `저장` 을 누르고 같은 `aside` 의 글자가 `저장됨` 으로 바뀔 때까지 기다린다(최대 40초).
|
||||
**못 보면 실패다. 그 `aside` 글자를 그대로 읽어 보고한다. 버튼을 다시 누르지 않는다**
|
||||
- 버전이 올랐는지 `aside` 의 첫 `dd` 로 전후 확인한다
|
||||
|
||||
## 끝나고
|
||||
|
||||
새로 만들었으면 받은 uuid 와 편집 주소를 기록 frontmatter 에 되적는다.
|
||||
그다음 `python3 scripts/build-tech-log-tree.py <프로젝트>` 와
|
||||
`python3 scripts/verify-tech-log-tree.py <프로젝트>` 를 돌려 error 0 을 확인한다.
|
||||
|
||||
## 보고
|
||||
|
||||
- 편마다 `id` · 버전 전→후 · `저장됨` 여부 · 본문 바이트 일치 여부
|
||||
- 걸지 못한 관계와 그 까닭
|
||||
- 넣기 전 검사에서 걸린 것
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: tree-deriver
|
||||
description: Use when an SSOT (final/document.md) must be decomposed into Tech Log 글감 and written into tech-log-tree.json. 파이프라인 S2 를 맡는다. 후보마다 처분을 적고 PROMOTE 만 글감으로 올린다. 기록은 쓰지 않는다.
|
||||
model: opus
|
||||
---
|
||||
|
||||
너는 SSOT 를 읽고 **무엇을 글로 쓸지 고른다.** 글은 쓰지 않는다.
|
||||
|
||||
## 반드시 먼저 할 것
|
||||
|
||||
1. `Skill` 도구로 `deriving-tech-log-root-tree` 를 호출하고 **SKILL.md 를 끝까지** 읽는다.
|
||||
`references/candidate-disposition.md` 와 `references/decomposition-checklist.md` 도 읽는다.
|
||||
2. 출력 계약은 `.agents/skills/writing-tech-log-records/references/tech-log-tree-contract.md` 다.
|
||||
3. 저장소 루트 `CLAUDE.md`.
|
||||
|
||||
## 입력은 하나다
|
||||
|
||||
`docs/<프로젝트>/final/document.md` — **이것 하나다.**
|
||||
|
||||
**`analysis/**` 를 후보를 찾으려고 열지 않는다.** 분석에만 있는 자료를 발견하면 트리에 바로
|
||||
넣지 말고 `final/document.md` 를 먼저 보강한다. 그러지 않으면 모듈 문서마다 정본 노릇을 하고
|
||||
트리는 그 절 수의 합만큼 자란다.
|
||||
|
||||
## 출력
|
||||
|
||||
`docs/<프로젝트>/tech-log-studio/tech-log-tree.json` — 분해 계약이자 색인이고 **이 파일이 정본이다.**
|
||||
|
||||
디렉터리를 훑어 주제를 만들지 않는다. 폴더가 정본이면 계약에서 뺀 주제가 파일이 남아 있다는
|
||||
이유만으로 되살아난다.
|
||||
|
||||
## 검사기가 요구하는데 스킬 절차가 안 적는 칸 — 손으로 채운다
|
||||
|
||||
| 칸 | 무엇 |
|
||||
|---|---|
|
||||
| `candidateScope` | 후보를 찾은 범위. 적지 않으면 모듈 분석 절 제목이 전부 글감이 된다 |
|
||||
| `candidateScope.excludedAnchorPattern` | 「범위 밖 글감」 검사가 이 칸으로 판정한다. 없으면 그 검사가 통째로 꺼진다 |
|
||||
| `sourceRepository` | 분석한 저장소의 경로·리비전·그렇게 판단한 근거. **모르면 `null`, 지어내지 않는다** |
|
||||
| `ssotSha256` | `build-tech-log-tree.py` 가 채운다. 그래서 build 를 먼저 돌리고 verify 를 돌린다 |
|
||||
| `ssot-assets` · `ssot-evidence` | SSOT 가 이미 그린 그림과 이미 돌린 측정을 글감에 배정한다 |
|
||||
|
||||
## 선별이 이 역할의 전부다
|
||||
|
||||
**제외가 0 건인 분해는 선별하지 않은 분해다.** 후보마다 처분을 적는다.
|
||||
|
||||
`PROMOTE` · `MERGE_INTO` · `KEEP_IN_SSOT` · `NEEDS_EVIDENCE` · `NEEDS_DECISION`
|
||||
|
||||
`KEEP_IN_SSOT` 은 버린 것이 아니라 **분석에 남기고 독립 기록으로 만들지 않기로 한 것**이고,
|
||||
그것도 정상적인 결과다.
|
||||
|
||||
처분과 글감은 양쪽으로 맞아야 한다 — `PROMOTE` 인데 글감이 없는 것도, 글감인데 그것을 낳은
|
||||
`PROMOTE` 후보가 없는 것도 error 다. **다시 읽은 후보만 `dispositionReview: CONFIRMED`** 로
|
||||
둔다. `PENDING` 이 남아 있으면 error 다.
|
||||
|
||||
주제마다 **독자 질문을 한 줄** 적는다.
|
||||
|
||||
## 앵커는 한 형식으로 통일한다
|
||||
|
||||
`source` 앵커의 형식은 어디에도 적혀 있지 않다. 검사기는 SSOT 경로를 포함하는지만 보고
|
||||
실재하는 heading 으로 풀지 않는다. **한 프로젝트 안에서는 한 형식으로** 쓴다 — S4 가
|
||||
`--heading` 값을 이 앵커에서 옮긴다.
|
||||
|
||||
## 검사기가 안 보는 것 — 그래서 사람이 본다
|
||||
|
||||
**검사기는 「후보 ↔ 글감」만 본다. 「SSOT ↔ 후보」는 안 본다.** SSOT 에 있는 재료를 후보
|
||||
대장에 올리지도 않고 지나쳐도 error 가 0 이다. 범위의 절을 끝까지 읽는 것은 네 일이다.
|
||||
|
||||
## 하지 않는 것
|
||||
|
||||
- **기록을 쓰지 않는다.** `<주제>/<종류>/*.md` 를 만들지 않는다
|
||||
- **SSOT 를 고치지 않는다.** 보강이 필요하면 보고에 적는다
|
||||
- 종류가 요구하는 칸을 비워 두고 `PROMOTE` 하지 않는다
|
||||
|
||||
## 관문
|
||||
|
||||
```bash
|
||||
python3 scripts/build-tech-log-tree.py <프로젝트>
|
||||
python3 scripts/verify-tech-log-tree.py <프로젝트>
|
||||
```
|
||||
|
||||
error 0 까지 고친다. `build` 가 다시 채우는 칸은 `file`·`publication`·`status` 뿐이고
|
||||
나머지는 네가 손으로 적는다.
|
||||
|
||||
## 보고 (JSON)
|
||||
|
||||
`stage` · `skill` · `skillEcho` · `status` · `outputs` · `gates` · `notes`
|
||||
|
||||
`skillEcho` 는 SKILL.md 에서 **원문 그대로** 옮긴 한 줄이다. 지어내지 마라.
|
||||
|
||||
`notes` 에는 **처분 분포**(PROMOTE N · MERGE_INTO N · KEEP_IN_SSOT N · …)와 **근거가 모자라
|
||||
판정을 미룬 후보**를 적는다.
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: voice-writer
|
||||
description: Use when a Tech Log record is accurate and well-ordered but reads like a report produced by nobody. 파이프라인 S6 을 맡는다. S5 다음이다. 자료에 흔적이 없으면 아무것도 넣지 않고 「흔적 없음」으로 끝낸다.
|
||||
model: opus
|
||||
---
|
||||
|
||||
너는 **일한 사람이 골랐던 자리**를 문장에 되살린다. 없는 사람을 만들지 않는다.
|
||||
|
||||
## 반드시 먼저 할 것
|
||||
|
||||
1. `Skill` 도구로 `writing-as-the-person-who-did-it` 을 호출하고 **SKILL.md 를 끝까지** 읽는다.
|
||||
2. 저장소 루트 `CLAUDE.md`.
|
||||
|
||||
## S5 다음이다 — 순서를 뒤집지 않는다
|
||||
|
||||
번역투와 반복 문형을 걷어낸 뒤라야 채울 자리(선택·비교·어긋남)가 보인다. 뒤집으면 S5 가
|
||||
네가 넣은 목소리를 「과한 대구」로 다시 깎는다.
|
||||
|
||||
## 입력이 둘이다
|
||||
|
||||
- S5 를 지난 기록 `.md`
|
||||
- **그리고 그 기록의 상류 자료** — SSOT(`final/document.md`) · 커밋 메시지 · 코드 주석 ·
|
||||
「확인하지 못한 것」 칸 · `final/evidence/` 의 원문
|
||||
|
||||
**상류를 안 열고는 이 단계가 성립하지 않는다.** 흔적을 찾는 것이 이 단계의 일이다.
|
||||
|
||||
## 자료에 흔적이 없으면 거기서 끝난다
|
||||
|
||||
**없는 사람을 만들지 않는다.** 「처음에는」·「고민 끝에」·「놀랍게도」·「예상과 달리」를
|
||||
자료 없이 쓰면 지어낸 것이다.
|
||||
|
||||
그때 원장에는 **`DONE` 에 「흔적 없음」을 적는다 — `SKIPPED` 가 아니다.** 찾아봤다는 것이
|
||||
이 단계의 일이고, 찾아본 결과가 없음이면 그것이 결과다.
|
||||
|
||||
## 무엇이 흔적인가
|
||||
|
||||
| 자료에 있는 것 | 문장에서 되는 것 |
|
||||
|---|---|
|
||||
| 커밋이 같은 자리를 두 번 고쳤다 | 처음 고른 것이 안 맞아 다시 골랐다 |
|
||||
| 「확인하지 못한 것」에 적힌 칸 | 무엇을 못 재고 넘어갔는지의 인정 |
|
||||
| 증거 원문이 예상과 다른 값 | 어긋남. 그 자리를 뭉개지 않는다 |
|
||||
| 대안이 문서에 적혀 있다 | 제약 → 선택 → 이유 → 대안 → 감수한 비용 |
|
||||
|
||||
기술 선택을 설명할 때는 **제약 → 선택 → 이유 → 대안 → 감수한 비용 → 가드레일**을 잇는다.
|
||||
자료에 근거가 있으면 검증 방법과 적용되지 않는 조건도 덧붙인다.
|
||||
|
||||
## 하지 않는 것
|
||||
|
||||
- **1인칭 서술을 자료 없이 만들지 않는다**
|
||||
- **어떤 기술이 쓰였다는 사실을 왜 그것을 골랐는지로 바꾸지 않는다**
|
||||
- 보호 구간(수치·날짜·버전·단위·코드·명령어·URL·직접 인용·공식 명칭)을 건드리지 않는다
|
||||
- 한계를 재고 목록으로 늘어놓지 않는다. 인정은 문장이지 표가 아니다
|
||||
|
||||
## 관문
|
||||
|
||||
```bash
|
||||
node .agents/skills/writing-as-the-person-who-did-it/scripts/check_voice.mjs <기록.md>
|
||||
node .agents/skills/rewriting-technical-prose-naturally/scripts/check_prose.mjs --warn <기록.md>
|
||||
```
|
||||
|
||||
**`check_voice.mjs` 는 목소리가 모자란지 재지 않는다. 지어낸 목소리를 잡는다.** 조용하다고
|
||||
목소리가 생긴 것은 아니다.
|
||||
|
||||
그리고 네가 넣은 문장을 `check_prose` 가 다시 본다. 그래서 재실행이 관문이다.
|
||||
|
||||
## 그리고 S3 관문을 다시 돈다
|
||||
|
||||
```bash
|
||||
python3 scripts/studio-body.py <기록.md> -o /tmp/studio-body.md
|
||||
node --experimental-transform-types \
|
||||
.agents/skills/writing-tech-log-records/scripts/check_body.mjs /tmp/studio-body.md
|
||||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs <프로젝트> --repo
|
||||
```
|
||||
|
||||
## 보고 (JSON)
|
||||
|
||||
`stage` · `skill` · `skillEcho` · `status` · `outputs` · `gates` · `notes`
|
||||
|
||||
`skillEcho` 는 SKILL.md 에서 **원문 그대로** 옮긴 한 줄이다.
|
||||
|
||||
`notes` 에는 **어디를 뒤졌고 무엇이 없었는지**를 적는다. 흔적이 없으면 「흔적 없음」과 함께
|
||||
뒤진 자리를 적는다 — 안 뒤진 것과 뒤졌는데 없는 것은 다르다.
|
||||
@@ -0,0 +1,3 @@
|
||||
[ 417ms] [ERROR] Failed to load resource: the server responded with a status of 401 (Unauthorized) @ https://hyeonworks.com/api/v1/studio/session:0
|
||||
[ 3718ms] [ERROR] Failed to load resource: the server responded with a status of 401 (Unauthorized) @ https://hyeonworks.com/api/v1/studio/documents?size=1:0
|
||||
[ 115663ms] [ERROR] Failed to load resource: the server responded with a status of 401 (Unauthorized) @ https://hyeonworks.com/api/v1/studio/documents?size=1:0
|
||||
@@ -0,0 +1 @@
|
||||
[ 285ms] [ERROR] Failed to load resource: the server responded with a status of 401 (Unauthorized) @ https://hyeonworks.com/api/v1/studio/session:0
|
||||
@@ -0,0 +1 @@
|
||||
[ 70424ms] [ERROR] Failed to load resource: the server responded with a status of 422 (Unprocessable Entity) @ https://hyeonworks.com/api/v1/studio/setups/fad58b44-2742-4772-b184-21a085093178:0
|
||||
@@ -0,0 +1 @@
|
||||
[ 396ms] [ERROR] Failed to load resource: the server responded with a status of 401 (Unauthorized) @ https://hyeonworks.com/api/v1/studio/session:0
|
||||
@@ -0,0 +1 @@
|
||||
[ 382ms] [ERROR] Failed to load resource: the server responded with a status of 401 (Unauthorized) @ https://hyeonworks.com/api/v1/studio/session:0
|
||||
@@ -0,0 +1 @@
|
||||
[ 5724ms] [ERROR] Failed to load resource: the server responded with a status of 401 (Unauthorized) @ https://hyeonworks.com/api/v1/studio/session:0
|
||||
@@ -0,0 +1,5 @@
|
||||
[ 178156ms] [ERROR] Failed to load resource: the server responded with a status of 405 (Method Not Allowed) @ https://hyeonworks.com/api/v1/studio/topics/2ee927ff-217b-429f-b081-d313df65a5de:0
|
||||
[ 2394637ms] [ERROR] Failed to load resource: the server responded with a status of 409 (Conflict) @ https://hyeonworks.com/api/v1/studio/topics/886181b9-5d25-47bf-81a4-813084e3d9d7:0
|
||||
[ 2454206ms] [ERROR] Failed to load resource: the server responded with a status of 422 (Unprocessable Entity) @ https://hyeonworks.com/api/v1/studio/topics/new/variants:0
|
||||
[ 2455034ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ https://hyeonworks.com/studio/taxonomy/topics/new:0
|
||||
[ 2456336ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ https://hyeonworks.com/studio/topics:0
|
||||
@@ -0,0 +1 @@
|
||||
[ 435ms] [ERROR] Failed to load resource: the server responded with a status of 401 (Unauthorized) @ https://hyeonworks.com/api/v1/studio/session:0
|
||||
@@ -0,0 +1,13 @@
|
||||
- generic [ref=e3]:
|
||||
- link "본문으로 건너뛰기" [ref=e4] [cursor=pointer]:
|
||||
- /url: "#main-content"
|
||||
- main [ref=e5]:
|
||||
- generic [ref=e7]:
|
||||
- generic [ref=e8]:
|
||||
- paragraph [ref=e9]: STUDIO
|
||||
- heading "기록을 쓰려면 로그인이 필요합니다" [active] [level=1] [ref=e10]
|
||||
- paragraph [ref=e11]: 로그인하면 방금 열려던 화면으로 돌아옵니다.
|
||||
- generic [ref=e12]:
|
||||
- button "로그인 시작" [ref=e13] [cursor=pointer]
|
||||
- paragraph [ref=e14]: 로그인하면 /studio 로 돌아옵니다.
|
||||
- paragraph [ref=e15]
|
||||
@@ -0,0 +1,13 @@
|
||||
- generic [ref=f4e3]:
|
||||
- link "본문으로 건너뛰기" [ref=f4e4] [cursor=pointer]:
|
||||
- /url: "#main-content"
|
||||
- main [ref=f4e5]:
|
||||
- generic [ref=f4e7]:
|
||||
- generic [ref=f4e8]:
|
||||
- paragraph [ref=f4e9]: STUDIO
|
||||
- heading "세션을 복구하고 있습니다." [level=1] [ref=f4e10]
|
||||
- paragraph [ref=f4e11]: 이전 로그인이 아직 살아 있는지 확인하고 있습니다. 잠시 뒤에도 이 화면이면 다시 시도해 주세요.
|
||||
- generic [ref=f4e12]:
|
||||
- button "세션 복구" [ref=f4e13] [cursor=pointer]
|
||||
- paragraph [ref=f4e14]: 로그인하면 /studio/documents/new 로 돌아옵니다.
|
||||
- paragraph [ref=f4e15]
|
||||
@@ -0,0 +1,65 @@
|
||||
- generic [ref=f6e35]:
|
||||
- link "본문으로 건너뛰기" [ref=f6e36] [cursor=pointer]:
|
||||
- /url: "#main-content"
|
||||
- banner [ref=f6e37]:
|
||||
- generic [ref=f6e38]:
|
||||
- link "TechLog Studio" [ref=f6e39] [cursor=pointer]:
|
||||
- /url: /studio
|
||||
- text: TechLog
|
||||
- generic [ref=f6e40]: Studio
|
||||
- navigation "Studio 주 탐색" [ref=f6e42]:
|
||||
- link "작업본" [ref=f6e43] [cursor=pointer]:
|
||||
- /url: /studio/documents
|
||||
- link "게시 기록" [ref=f6e44] [cursor=pointer]:
|
||||
- /url: /studio/publications
|
||||
- link "새 문서" [ref=f6e45] [cursor=pointer]:
|
||||
- /url: /studio/documents/new
|
||||
- link "주제·프로젝트" [ref=f6e46] [cursor=pointer]:
|
||||
- /url: /studio/taxonomy
|
||||
- link "Asset" [ref=f6e47] [cursor=pointer]:
|
||||
- /url: /studio/assets
|
||||
- link "릴리즈" [ref=f6e48] [cursor=pointer]:
|
||||
- /url: /studio/releases
|
||||
- link "공개 사이트 보기" [ref=f6e49] [cursor=pointer]:
|
||||
- /url: /
|
||||
- button "로그아웃" [ref=f6e50]
|
||||
- main [ref=f6e51]:
|
||||
- generic [ref=f6e52]:
|
||||
- generic [ref=f6e53]:
|
||||
- paragraph [ref=f6e54]: NEW WORKING COPY
|
||||
- heading "새 문서" [level=1] [ref=f6e55]
|
||||
- paragraph [ref=f6e56]: 목적에 맞는 기록 종류를 선택하면 빈 작업본을 만들고 바로 편집을 시작합니다.
|
||||
- generic [ref=f6e57]:
|
||||
- group "문서 종류" [ref=f6e1]:
|
||||
- generic [ref=f6e3] [cursor=pointer]:
|
||||
- radio "검증 기록 문제를 재현하고 검증한 결론을 기록합니다. 문제 · 결론 · 환경 · 재현 · 본문" [ref=f6e4]
|
||||
- strong [ref=f6e5]: 검증 기록
|
||||
- generic [ref=f6e6]: 문제를 재현하고 검증한 결론을 기록합니다.
|
||||
- generic [ref=f6e7]: 문제 · 결론 · 환경 · 재현 · 본문
|
||||
- generic [ref=f6e8] [cursor=pointer]:
|
||||
- radio "적용 기준 반복해서 적용할 기술 기준을 정리합니다. 목적 · 규칙 · 적용 조건 · 예외 · 예시" [ref=f6e9]
|
||||
- strong [ref=f6e10]: 적용 기준
|
||||
- generic [ref=f6e11]: 반복해서 적용할 기술 기준을 정리합니다.
|
||||
- generic [ref=f6e12]: 목적 · 규칙 · 적용 조건 · 예외 · 예시
|
||||
- generic [ref=f6e13] [cursor=pointer]:
|
||||
- radio "동작 원리 개념의 내부 동작과 아키텍처를 처음부터 풀어 씁니다. 기준 버전 · 본문" [ref=f6e14]
|
||||
- strong [ref=f6e15]: 동작 원리
|
||||
- generic [ref=f6e16]: 개념의 내부 동작과 아키텍처를 처음부터 풀어 씁니다.
|
||||
- generic [ref=f6e17]: 기준 버전 · 본문
|
||||
- generic [ref=f6e18] [cursor=pointer]:
|
||||
- radio "환경 구성 이 기록을 자기 손으로 재현하는 절차를 남깁니다. 버전 · 본문" [checked] [active] [ref=f6e19]
|
||||
- strong [ref=f6e20]: 환경 구성
|
||||
- generic [ref=f6e21]: 이 기록을 자기 손으로 재현하는 절차를 남깁니다.
|
||||
- generic [ref=f6e22]: 버전 · 본문
|
||||
- generic [ref=f6e23] [cursor=pointer]:
|
||||
- radio "열린 질문 아직 닫히지 않은 판단과 다음 검증을 관리합니다. 상태 · 사실 · 가정 · 미지수 · 선택지" [ref=f6e24]
|
||||
- strong [ref=f6e25]: 열린 질문
|
||||
- generic [ref=f6e26]: 아직 닫히지 않은 판단과 다음 검증을 관리합니다.
|
||||
- generic [ref=f6e27]: 상태 · 사실 · 가정 · 미지수 · 선택지
|
||||
- generic [ref=f6e28] [cursor=pointer]:
|
||||
- radio "설계 결정 프로젝트가 선택한 방향과 그 근거·영향을 기록합니다. 상태 · 결정일 · 결정문 · 판단 이유 · 영향 · 근거" [ref=f6e29]
|
||||
- strong [ref=f6e30]: 설계 결정
|
||||
- generic [ref=f6e31]: 프로젝트가 선택한 방향과 그 근거·영향을 기록합니다.
|
||||
- generic [ref=f6e32]: 상태 · 결정일 · 결정문 · 판단 이유 · 영향 · 근거
|
||||
- button "작업본 만들기" [ref=f6e59]
|
||||
- paragraph [ref=f6e60]
|
||||
@@ -0,0 +1,250 @@
|
||||
- generic [ref=f6e35]:
|
||||
- link "본문으로 건너뛰기" [ref=f6e36] [cursor=pointer]:
|
||||
- /url: "#main-content"
|
||||
- banner [ref=f6e37]:
|
||||
- generic [ref=f6e38]:
|
||||
- link "TechLog Studio" [ref=f6e39] [cursor=pointer]:
|
||||
- /url: /studio
|
||||
- text: TechLog
|
||||
- generic [ref=f6e40]: Studio
|
||||
- navigation "Studio 주 탐색" [ref=f6e42]:
|
||||
- link "작업본" [ref=f6e43] [cursor=pointer]:
|
||||
- /url: /studio/documents
|
||||
- link "게시 기록" [ref=f6e44] [cursor=pointer]:
|
||||
- /url: /studio/publications
|
||||
- link "새 문서" [ref=f6e45] [cursor=pointer]:
|
||||
- /url: /studio/documents/new
|
||||
- link "주제·프로젝트" [ref=f6e46] [cursor=pointer]:
|
||||
- /url: /studio/taxonomy
|
||||
- link "Asset" [ref=f6e47] [cursor=pointer]:
|
||||
- /url: /studio/assets
|
||||
- link "릴리즈" [ref=f6e48] [cursor=pointer]:
|
||||
- /url: /studio/releases
|
||||
- link "공개 사이트 보기" [ref=f6e49] [cursor=pointer]:
|
||||
- /url: /
|
||||
- button "로그아웃" [ref=f6e50]
|
||||
- main [ref=f6e51]:
|
||||
- generic [ref=f6e61]:
|
||||
- generic [ref=f6e62]:
|
||||
- region [ref=f6e63]:
|
||||
- generic [ref=f6e64]:
|
||||
- paragraph [ref=f6e65]: SETUP · VERSION 1
|
||||
- heading "문서 편집" [level=1] [ref=f6e66]
|
||||
- paragraph [ref=f6e67]: 제목 없는 작업본
|
||||
- navigation "편집 구역" [ref=f6e68]:
|
||||
- list [ref=f6e69]:
|
||||
- listitem [ref=f6e70]:
|
||||
- link "기본 정보" [ref=f6e71] [cursor=pointer]:
|
||||
- /url: "#studio-common-fields-title"
|
||||
- listitem [ref=f6e72]:
|
||||
- link "환경 구성" [ref=f6e73] [cursor=pointer]:
|
||||
- /url: "#studio-setup-fields-title"
|
||||
- region [ref=f6e74]:
|
||||
- generic [ref=f6e75]:
|
||||
- paragraph [ref=f6e76]: DOCUMENT
|
||||
- heading "기본 정보" [level=2] [ref=f6e77]
|
||||
- generic [ref=f6e78]:
|
||||
- generic [ref=f6e79]:
|
||||
- generic [ref=f6e80]: 제목
|
||||
- textbox "제목" [ref=f6e81]
|
||||
- generic [ref=f6e82]:
|
||||
- generic [ref=f6e83]: slug
|
||||
- textbox "slug" [ref=f6e84]:
|
||||
- /placeholder: 비우면 제목에서 만듭니다 (영문 소문자·숫자·하이픈)
|
||||
- generic [ref=f6e85]:
|
||||
- generic [ref=f6e86]: 요약
|
||||
- textbox "요약" [ref=f6e87]
|
||||
- generic [aria-hidden] [ref=f6e88]: 목록 카드에는 약 90자까지 보입니다 · 0 / 2000
|
||||
- generic [ref=f6e89]:
|
||||
- generic [ref=f6e90]: Topic
|
||||
- combobox "Topic" [ref=f6e91]:
|
||||
- option "선택하지 않음" [selected]
|
||||
- option "네트워크 가상화"
|
||||
- option "스토리지 가상화"
|
||||
- option "메모리 가상화"
|
||||
- option "CPU 가상화"
|
||||
- option "JPA 피드 조회 성능"
|
||||
- option "OAuth/OIDC 인증 경계"
|
||||
- generic [ref=f6e92]:
|
||||
- generic [ref=f6e93]: Project
|
||||
- combobox "Project" [ref=f6e94]:
|
||||
- option "미지정" [selected]
|
||||
- option "가상화"
|
||||
- option "Backend Clean Architecture"
|
||||
- option "KeyCloak Patterns"
|
||||
- option "Liner N + 1문제"
|
||||
- status [ref=f6e95]
|
||||
- group "관계" [ref=f6e96]:
|
||||
- paragraph [ref=f6e98]: 연결한 공개 기록이 없습니다.
|
||||
- button "관계 추가" [ref=f6e99]
|
||||
- region [ref=f6e100]:
|
||||
- generic [ref=f6e101]:
|
||||
- paragraph [ref=f6e102]: SETUP
|
||||
- heading "환경 구성" [level=2] [ref=f6e103]
|
||||
- generic [ref=f6e104]:
|
||||
- group "고정한 버전" [ref=f6e105]:
|
||||
- list
|
||||
- button "버전 추가" [ref=f6e107]
|
||||
- generic [ref=f6e108]: “Keycloak” / “26.7.0” 처럼 적습니다. 비우면 화면에 표를 그리지 않습니다.
|
||||
- generic [ref=f6e109]:
|
||||
- generic [ref=f6e110]: 절차 Markdown
|
||||
- group "Markdown 삽입" [ref=f6e111]:
|
||||
- button "코드" [ref=f6e112] [cursor=pointer]
|
||||
- button "표" [ref=f6e113] [cursor=pointer]
|
||||
- button "목록" [ref=f6e114] [cursor=pointer]
|
||||
- textbox "절차 Markdown" [ref=f6e115]: "## 실행 절차 ## 구성 값 ## 확인 방법"
|
||||
- generic [ref=f6e116]: “##” 소제목이 목차가 됩니다. 명령은 코드블록으로 적어야 그대로 복사됩니다.
|
||||
- group [ref=f6e117]:
|
||||
- paragraph [ref=f6e118]: EVIDENCE
|
||||
- heading "본문에 Asset 삽입" [level=3] [ref=f6e119]
|
||||
- paragraph [ref=f6e120]: 목록에서 선택하면 본문 커서 위치에 evidence 구문을 삽입합니다. READY 상태의 Asset만 선택할 수 있습니다.
|
||||
- generic [ref=f6e121]:
|
||||
- generic [ref=f6e122]:
|
||||
- generic [ref=f6e123]: 업로드 종류
|
||||
- combobox "업로드 종류" [ref=f6e124]:
|
||||
- option "이미지"
|
||||
- option "다이어그램" [selected]
|
||||
- option "첨부파일"
|
||||
- button "Asset 업로드" [ref=f6e125]
|
||||
- generic [ref=f6e126]:
|
||||
- search [ref=f6e127]:
|
||||
- generic [ref=f6e128]: Asset 검색
|
||||
- generic [ref=f6e129]:
|
||||
- searchbox "Asset 검색" [ref=f6e130]
|
||||
- button "검색" [ref=f6e131]
|
||||
- generic [ref=f6e132]:
|
||||
- checkbox "삽입할 때 크게 보기 허용" [checked] [ref=f6e133]
|
||||
- generic [ref=f6e134]: 삽입할 때 크게 보기 허용
|
||||
- status [ref=f6e135]: 삽입할 수 있는 Asset 36개
|
||||
- list [ref=f6e136]:
|
||||
- listitem [ref=f6e137]:
|
||||
- button "vms-sharing-one-nvme-53ce295a" [ref=f6e138]
|
||||
- button "삭제" [ref=f6e139]
|
||||
- listitem [ref=f6e140]:
|
||||
- button "write-completion-boundaries-87bdee72" [ref=f6e141]
|
||||
- button "삭제" [ref=f6e142]
|
||||
- listitem [ref=f6e143]:
|
||||
- button "guest-block-io-to-virtqueue-59870a6f" [ref=f6e144]
|
||||
- button "삭제" [ref=f6e145]
|
||||
- listitem [ref=f6e146]:
|
||||
- button "packet-control-and-data-paths-72081090" [ref=f6e147]
|
||||
- button "삭제" [ref=f6e148]
|
||||
- listitem [ref=f6e149]:
|
||||
- button "virtio-balloon-inflate-deflate-30c54022" [ref=f6e150]
|
||||
- button "삭제" [ref=f6e151]
|
||||
- listitem [ref=f6e152]:
|
||||
- button "virtio-balloon-inflate-deflate-c63559c4" [ref=f6e153]
|
||||
- button "삭제" [ref=f6e154]
|
||||
- listitem [ref=f6e155]:
|
||||
- button "numa-vcpu-and-memory-placement-d085bdfc" [ref=f6e156]
|
||||
- button "삭제" [ref=f6e157]
|
||||
- listitem [ref=f6e158]:
|
||||
- button "page-fault-layers-in-a-vm-e8d37b87" [ref=f6e159]
|
||||
- button "삭제" [ref=f6e160]
|
||||
- listitem [ref=f6e161]:
|
||||
- button "guest-memory-address-translation-path-aa008f53" [ref=f6e162]
|
||||
- button "삭제" [ref=f6e163]
|
||||
- listitem [ref=f6e164]:
|
||||
- button "vm-exit-handling-cycle-f8448581" [ref=f6e165]
|
||||
- button "삭제" [ref=f6e166]
|
||||
- listitem [ref=f6e167]:
|
||||
- button "ap4-edge-trust-architecture-8125e9e8" [ref=f6e168]
|
||||
- button "삭제" [ref=f6e169]
|
||||
- listitem [ref=f6e170]:
|
||||
- button "batch-fetch-in-clause-cb44066a" [ref=f6e171]
|
||||
- button "삭제" [ref=f6e172]
|
||||
- listitem [ref=f6e173]:
|
||||
- button "ap4-edge-trust-architecture-1a916e10" [ref=f6e174]
|
||||
- button "삭제" [ref=f6e175]
|
||||
- listitem [ref=f6e176]:
|
||||
- button "ap3-bff-session-flow-1b005e15" [ref=f6e177]
|
||||
- button "삭제" [ref=f6e178]
|
||||
- listitem [ref=f6e179]:
|
||||
- button "ap3-bff-architecture-a27ea91c" [ref=f6e180]
|
||||
- button "삭제" [ref=f6e181]
|
||||
- listitem [ref=f6e182]:
|
||||
- button "ap2-mediator-handoff-flow-efe7039c" [ref=f6e183]
|
||||
- button "삭제" [ref=f6e184]
|
||||
- listitem [ref=f6e185]:
|
||||
- button "ap2-mediator-architecture-c95ed25f" [ref=f6e186]
|
||||
- button "삭제" [ref=f6e187]
|
||||
- listitem [ref=f6e188]:
|
||||
- button "projection-row-over-fetch-f2b1943b" [ref=f6e189]
|
||||
- button "삭제" [ref=f6e190]
|
||||
- listitem [ref=f6e191]:
|
||||
- button "cartesian-row-multiplication-dce2e166" [ref=f6e192]
|
||||
- button "삭제" [ref=f6e193]
|
||||
- listitem [ref=f6e194]:
|
||||
- button "eager-lazy-query-sequence-47c12bda" [ref=f6e195]
|
||||
- button "삭제" [ref=f6e196]
|
||||
- listitem [ref=f6e197]:
|
||||
- button "ap3-bff-session-flow-a8dfff6f" [ref=f6e198]
|
||||
- button "삭제" [ref=f6e199]
|
||||
- listitem [ref=f6e200]:
|
||||
- button "ap2-mediator-handoff-flow-8c2a6f8f" [ref=f6e201]
|
||||
- button "삭제" [ref=f6e202]
|
||||
- listitem [ref=f6e203]:
|
||||
- button "ap4-edge-forward-auth-flow-a6ec423a" [ref=f6e204]
|
||||
- button "삭제" [ref=f6e205]
|
||||
- listitem [ref=f6e206]:
|
||||
- button "ap3-csrf-boundary-971df81c" [ref=f6e207]
|
||||
- button "삭제" [ref=f6e208]
|
||||
- listitem [ref=f6e209]:
|
||||
- button "login-api-phase-split-3e354274" [ref=f6e210]
|
||||
- button "삭제" [ref=f6e211]
|
||||
- listitem [ref=f6e212]:
|
||||
- button "ap1-browser-bearer-flow-a7f8aa9e" [ref=f6e213]
|
||||
- button "삭제" [ref=f6e214]
|
||||
- listitem [ref=f6e215]:
|
||||
- button "ap1-direct-architecture-0adf4199" [ref=f6e216]
|
||||
- button "삭제" [ref=f6e217]
|
||||
- listitem [ref=f6e218]:
|
||||
- button "nplus1-query-fanout-644febe6" [ref=f6e219]
|
||||
- button "삭제" [ref=f6e220]
|
||||
- listitem [ref=f6e221]:
|
||||
- button "ap4-edge-trust-1cff2399" [ref=f6e222]
|
||||
- button "삭제" [ref=f6e223]
|
||||
- listitem [ref=f6e224]:
|
||||
- button "ap3-csrf-split-501dd1f7" [ref=f6e225]
|
||||
- button "삭제" [ref=f6e226]
|
||||
- listitem [ref=f6e227]:
|
||||
- button "ap3-bff-custody-82fa18bd" [ref=f6e228]
|
||||
- button "삭제" [ref=f6e229]
|
||||
- listitem [ref=f6e230]:
|
||||
- button "ap2-split-custody-779cb791" [ref=f6e231]
|
||||
- button "삭제" [ref=f6e232]
|
||||
- listitem [ref=f6e233]:
|
||||
- button "ap1-custody-v3-6e0376d2" [ref=f6e234]
|
||||
- button "삭제" [ref=f6e235]
|
||||
- listitem [ref=f6e236]:
|
||||
- button "ap1-custody-v2-e110bd98" [ref=f6e237]
|
||||
- button "삭제" [ref=f6e238]
|
||||
- listitem [ref=f6e239]:
|
||||
- button "ap1-credential-custody-f5e0c027" [ref=f6e240]
|
||||
- button "삭제" [ref=f6e241]
|
||||
- listitem [ref=f6e242]:
|
||||
- button "screenshot-from-2026-08-21-18-04-49-72f1f9c6" [ref=f6e243]
|
||||
- button "삭제" [ref=f6e244]
|
||||
- region [ref=f6e245]:
|
||||
- generic [ref=f6e246]:
|
||||
- paragraph [ref=f6e247]: LIVE
|
||||
- heading "즉시 미리보기" [level=2] [ref=f6e248]
|
||||
- alert [ref=f6e250]:
|
||||
- heading "초안을 미리 볼 수 없습니다" [level=2] [ref=f6e251]
|
||||
- list [ref=f6e252]:
|
||||
- listitem [ref=f6e253]: 1:1 TOPIC catalog entry is required
|
||||
- complementary [ref=f6e254]:
|
||||
- heading "작업 상태" [level=2] [ref=f6e255]
|
||||
- status "편집 상태" [ref=f6e256]: 저장됨
|
||||
- generic [ref=f6e257]:
|
||||
- generic [ref=f6e258]:
|
||||
- term [ref=f6e259]: 저장 버전
|
||||
- definition [ref=f6e260]: "1"
|
||||
- generic [ref=f6e261]:
|
||||
- term [ref=f6e262]: 종류
|
||||
- definition [ref=f6e263]: 환경 구성
|
||||
- paragraph [ref=f6e264]: 불완전한 초안도 저장할 수 있습니다. Ctrl+S 로도 저장합니다. 게시를 누르면 채워야 할 칸을 그 자리에 표시합니다.
|
||||
- generic [ref=f6e265]:
|
||||
- button "저장" [disabled] [ref=f6e266]
|
||||
- button "게시" [ref=f6e267]
|
||||
- paragraph [ref=f6e60]: 환경 구성 작업본을 만들었습니다.
|
||||
@@ -0,0 +1,144 @@
|
||||
- generic [ref=f7e100]:
|
||||
- link "본문으로 건너뛰기" [ref=f7e101] [cursor=pointer]:
|
||||
- /url: "#main-content"
|
||||
- banner [ref=f7e102]:
|
||||
- generic [ref=f7e103]:
|
||||
- link "TechLog Studio" [ref=f7e104] [cursor=pointer]:
|
||||
- /url: /studio
|
||||
- text: TechLog
|
||||
- generic [ref=f7e105]: Studio
|
||||
- navigation "Studio 주 탐색" [ref=f7e107]:
|
||||
- link "작업본" [ref=f7e108] [cursor=pointer]:
|
||||
- /url: /studio/documents
|
||||
- link "게시 기록" [ref=f7e109] [cursor=pointer]:
|
||||
- /url: /studio/publications
|
||||
- link "새 문서" [ref=f7e110] [cursor=pointer]:
|
||||
- /url: /studio/documents/new
|
||||
- link "주제·프로젝트" [ref=f7e111] [cursor=pointer]:
|
||||
- /url: /studio/taxonomy
|
||||
- link "Asset" [ref=f7e112] [cursor=pointer]:
|
||||
- /url: /studio/assets
|
||||
- link "릴리즈" [ref=f7e113] [cursor=pointer]:
|
||||
- /url: /studio/releases
|
||||
- link "공개 사이트 보기" [ref=f7e114] [cursor=pointer]:
|
||||
- /url: /
|
||||
- button "로그아웃" [ref=f7e115]
|
||||
- main [ref=f7e1]:
|
||||
- generic [ref=f7e2]:
|
||||
- generic [ref=f7e4]:
|
||||
- paragraph [ref=f7e5]: TAXONOMY
|
||||
- heading "주제와 프로젝트" [level=1] [ref=f7e6]
|
||||
- paragraph [ref=f7e7]: 문서를 게시하려면 주제가 필요합니다. 여기서 만들고 정리합니다.
|
||||
- tablist "주제와 프로젝트" [ref=f7e8]:
|
||||
- tab "주제 7" [selected] [ref=f7e116]
|
||||
- tab "프로젝트 4" [ref=f7e10]
|
||||
- tabpanel "주제 7" [ref=f7e117]:
|
||||
- generic [ref=f7e12]:
|
||||
- generic [ref=f7e13]: 새 주제
|
||||
- generic [ref=f7e14]:
|
||||
- textbox "새 주제" [ref=f7e15]:
|
||||
- /placeholder: 주제 이름
|
||||
- textbox "주제 slug" [ref=f7e16]:
|
||||
- /placeholder: slug (비우면 이름에서 생성)
|
||||
- button "주제 추가" [ref=f7e17]
|
||||
- paragraph [ref=f7e118]: 7개의 주제
|
||||
- generic [ref=f7e119]:
|
||||
- article [ref=f7e120]:
|
||||
- paragraph [ref=f7e121]: TOPIC
|
||||
- generic [ref=f7e122]:
|
||||
- heading [level=2] [ref=f7e123]:
|
||||
- link "네트워크 가상화" [ref=f7e124] [cursor=pointer]:
|
||||
- /url: /studio/topics/56089eec-a603-4eac-bbb6-6fb44392ea63
|
||||
- paragraph [ref=f7e125]: network-virtualization
|
||||
- generic [ref=f7e127]:
|
||||
- term [ref=f7e128]: 상태
|
||||
- definition [ref=f7e129]: 활성
|
||||
- generic [ref=f7e130]:
|
||||
- link "편집" [ref=f7e131] [cursor=pointer]:
|
||||
- /url: /studio/topics/56089eec-a603-4eac-bbb6-6fb44392ea63
|
||||
- button "삭제" [ref=f7e132]
|
||||
- article [ref=f7e133]:
|
||||
- paragraph [ref=f7e134]: TOPIC
|
||||
- generic [ref=f7e135]:
|
||||
- heading [level=2] [ref=f7e136]:
|
||||
- link "스토리지 가상화" [ref=f7e137] [cursor=pointer]:
|
||||
- /url: /studio/topics/23314be2-7350-44f5-8989-575c3b40898f
|
||||
- paragraph [ref=f7e138]: storage-virtualization
|
||||
- generic [ref=f7e140]:
|
||||
- term [ref=f7e141]: 상태
|
||||
- definition [ref=f7e142]: 활성
|
||||
- generic [ref=f7e143]:
|
||||
- link "편집" [ref=f7e144] [cursor=pointer]:
|
||||
- /url: /studio/topics/23314be2-7350-44f5-8989-575c3b40898f
|
||||
- button "삭제" [ref=f7e145]
|
||||
- article [ref=f7e146]:
|
||||
- paragraph [ref=f7e147]: TOPIC
|
||||
- generic [ref=f7e148]:
|
||||
- heading [level=2] [ref=f7e149]:
|
||||
- link "메모리 가상화" [ref=f7e150] [cursor=pointer]:
|
||||
- /url: /studio/topics/5675627f-3616-409c-990b-c537d3c68ed6
|
||||
- paragraph [ref=f7e151]: memory-virtualization
|
||||
- generic [ref=f7e153]:
|
||||
- term [ref=f7e154]: 상태
|
||||
- definition [ref=f7e155]: 활성
|
||||
- generic [ref=f7e156]:
|
||||
- link "편집" [ref=f7e157] [cursor=pointer]:
|
||||
- /url: /studio/topics/5675627f-3616-409c-990b-c537d3c68ed6
|
||||
- button "삭제" [ref=f7e158]
|
||||
- article [ref=f7e159]:
|
||||
- paragraph [ref=f7e160]: TOPIC
|
||||
- generic [ref=f7e161]:
|
||||
- heading [level=2] [ref=f7e162]:
|
||||
- link "실험대 환경 구성" [ref=f7e163] [cursor=pointer]:
|
||||
- /url: /studio/topics/886181b9-5d25-47bf-81a4-813084e3d9d7
|
||||
- paragraph [ref=f7e164]: lab-environment-build
|
||||
- generic [ref=f7e166]:
|
||||
- term [ref=f7e167]: 상태
|
||||
- definition [ref=f7e168]: 활성
|
||||
- generic [ref=f7e169]:
|
||||
- link "편집" [ref=f7e170] [cursor=pointer]:
|
||||
- /url: /studio/topics/886181b9-5d25-47bf-81a4-813084e3d9d7
|
||||
- button "삭제" [ref=f7e171]
|
||||
- article [ref=f7e172]:
|
||||
- paragraph [ref=f7e173]: TOPIC
|
||||
- generic [ref=f7e174]:
|
||||
- heading [level=2] [ref=f7e175]:
|
||||
- link "CPU 가상화" [ref=f7e176] [cursor=pointer]:
|
||||
- /url: /studio/topics/e42456e2-9efd-41ee-b676-df82e90f2f53
|
||||
- paragraph [ref=f7e177]: cpu-virtualization
|
||||
- generic [ref=f7e179]:
|
||||
- term [ref=f7e180]: 상태
|
||||
- definition [ref=f7e181]: 활성
|
||||
- generic [ref=f7e182]:
|
||||
- link "편집" [ref=f7e183] [cursor=pointer]:
|
||||
- /url: /studio/topics/e42456e2-9efd-41ee-b676-df82e90f2f53
|
||||
- button "삭제" [ref=f7e184]
|
||||
- article [ref=f7e185]:
|
||||
- paragraph [ref=f7e186]: TOPIC
|
||||
- generic [ref=f7e187]:
|
||||
- heading [level=2] [ref=f7e188]:
|
||||
- link "JPA 피드 조회 성능" [ref=f7e189] [cursor=pointer]:
|
||||
- /url: /studio/topics/72cf462e-efbc-48bc-b22f-64345f38d818
|
||||
- paragraph [ref=f7e190]: jpa-feed-query-performance
|
||||
- generic [ref=f7e192]:
|
||||
- term [ref=f7e193]: 상태
|
||||
- definition [ref=f7e194]: 활성
|
||||
- generic [ref=f7e195]:
|
||||
- link "편집" [ref=f7e196] [cursor=pointer]:
|
||||
- /url: /studio/topics/72cf462e-efbc-48bc-b22f-64345f38d818
|
||||
- button "삭제" [ref=f7e197]
|
||||
- article [ref=f7e198]:
|
||||
- paragraph [ref=f7e199]: TOPIC
|
||||
- generic [ref=f7e200]:
|
||||
- heading [level=2] [ref=f7e201]:
|
||||
- link "OAuth/OIDC 인증 경계" [ref=f7e202] [cursor=pointer]:
|
||||
- /url: /studio/topics/6ffdda4f-29e7-40f4-8ca0-36dca009a53a
|
||||
- paragraph [ref=f7e203]: oauth-oidc-auth-boundary
|
||||
- generic [ref=f7e205]:
|
||||
- term [ref=f7e206]: 상태
|
||||
- definition [ref=f7e207]: 활성
|
||||
- generic [ref=f7e208]:
|
||||
- link "편집" [ref=f7e209] [cursor=pointer]:
|
||||
- /url: /studio/topics/6ffdda4f-29e7-40f4-8ca0-36dca009a53a
|
||||
- button "삭제" [ref=f7e210]
|
||||
- paragraph [ref=f7e211]: 주제 실험대 환경 구성 을(를) 만들었습니다.
|
||||
@@ -15,6 +15,9 @@
|
||||
| Studio 에 넣고 저장한다 (Playwright MCP, 게시하지 않는다) | `publishing-tech-log-to-studio` |
|
||||
| 분석에서 리팩터링 작업 항목을 뽑는다 | `refactoring-from-analysis` |
|
||||
|
||||
**스킬을 돌릴 에이전트는 `.claude/agents/` 에 있다.** 단계마다 맡을 것이 정해져 있고 그때그때
|
||||
새로 만들지 않는다 — 배정은 「한 줄기로 실행할 때」의 표가 적고, 원장의 `runBy` 가 그 이름을 담는다.
|
||||
|
||||
구조가 갖춰졌는지는 `python3 scripts/verify-pipeline.py`가 검사한다.
|
||||
|
||||
## 실행 순서
|
||||
@@ -25,9 +28,9 @@
|
||||
→ 후보 처분 PROMOTE · MERGE_INTO · KEEP_IN_SSOT · NEEDS_EVIDENCE · NEEDS_DECISION
|
||||
→ 분해 계약 tech-log-tree.json — PROMOTE 이고 CONFIRMED 인 것만 올린다. 주제마다 독자 질문 한 줄
|
||||
→ 색인 생성 build-tech-log-tree.py · verify-tech-log-tree.py (error 0)
|
||||
→ 종류 선택 Case · Concept · Reference · Question · Decision
|
||||
→ 종류 선택 Case · Concept · Setup · Reference · Question · Decision
|
||||
→ 칸 채우기 종류마다 칸이 다르다
|
||||
→ 본문 작성 Case · Concept. 코드·표·다이어그램·이미지
|
||||
→ 본문 작성 Case · Concept · Setup. 코드·표·다이어그램·이미지
|
||||
→ 다이어그램 technical-visualizer 스킬. 손으로 SVG 를 그리지 않는다
|
||||
→ 파서 검사 check_body.mjs
|
||||
→ 문장 검사 check_prose.mjs (error 0) · style_profile.mjs
|
||||
@@ -35,10 +38,14 @@
|
||||
→ Studio 저장 → 게시 → 공개 화면 확인
|
||||
```
|
||||
|
||||
**코드·표·다이어그램·이미지는 본문에만 들어간다. 본문이 있는 종류는 Case와 Concept이다.**
|
||||
Reference·Question·Decision의 칸은 평문으로 렌더링된다. 그런 자료가 필요하면 Case나 Concept에
|
||||
**코드·표·다이어그램·이미지는 본문에만 들어간다. 본문이 있는 종류는 Case·Concept·Setup 셋이다.**
|
||||
Reference·Question·Decision의 칸은 평문으로 렌더링된다. 그런 자료가 필요하면 본문이 있는 종류에
|
||||
담고 `관계`로 가리킨다.
|
||||
|
||||
**Setup(환경 구성)만 끝난 일을 적지 않는다.** 나머지 다섯은 이미 일어난 일을 적고, Setup 은 읽는
|
||||
사람이 자기 기계에서 실행할 순서를 적는다. 그래서 명령이 본문에 들어가고 버전만 본문 밖의
|
||||
`pinnedVersions` 에 남는다. 검증일 칸이 없고, 프로젝트가 필수이며 주제는 비워도 된다.
|
||||
|
||||
## 한 줄기로 실행할 때
|
||||
|
||||
위 실행 순서를 사람이 손으로 잇지 않고 한 번에 돌리려면 `running-tech-log-pipeline` 을 쓴다.
|
||||
@@ -46,15 +53,33 @@ Reference·Question·Decision의 칸은 평문으로 렌더링된다. 그런 자
|
||||
앞 단계의 문맥이 새어 들어 스킬을 안 읽고도 그럴듯한 결과가 나오고, 그러면 스킬이 적용됐는지
|
||||
확인할 방법이 없어진다.
|
||||
|
||||
| # | 단계 | 스킬 |
|
||||
**그 에이전트를 그때그때 만들지 않는다.** 단계마다 맡을 에이전트가 `.claude/agents/` 에 있고,
|
||||
`Agent` 도구의 `subagent_type` 에 그 이름을 준다. 프롬프트만 새로 써서 일반 에이전트를 띄우면
|
||||
**어떤 규칙으로 일했는지가 어디에도 안 남는다** — 스킬을 열었다는 영수증은 남지만 연 사람이
|
||||
누구인지는 안 남는다.
|
||||
|
||||
| # | 단계 | 스킬 | 에이전트 |
|
||||
|---|---|---|---|
|
||||
| S1 | 코드베이스 → SSOT | `analyzing-codebase-for-tech-log` | `ssot-analyst` |
|
||||
| S2 | SSOT → 분해 계약 | `deriving-tech-log-root-tree` | `tree-deriver` |
|
||||
| S3 | 글감 → 기록 | `writing-tech-log-records` | `record-writer` |
|
||||
| S4 | 기록 → 그림 | `technical-visualizer` | `diagram-maker` |
|
||||
| S5 | AI 티 제거 | `rewriting-technical-prose-naturally` | `prose-rewriter` |
|
||||
| S6 | 일한 사람의 목소리 | `writing-as-the-person-who-did-it` | `voice-writer` |
|
||||
| S7 | Studio 저장 | `publishing-tech-log-to-studio` | `studio-validator` |
|
||||
|
||||
**이 배정의 정본은 `scripts/verify-pipeline-run.py` 의 `STAGES` 다.** 원장의 `runBy` 가 그
|
||||
이름을 담고, 검사기가 계약과 대조한 뒤 `.claude/agents/<이름>.md` 가 실재하는지까지 본다.
|
||||
|
||||
**단계가 아닌 에이전트가 넷 더 있다.** 기록 한 편을 놓고 역할을 가른 것이라 아무 단계에도
|
||||
붙지 않고, 부를지는 사람이 정한다.
|
||||
|
||||
| 에이전트 | 언제 | 안 하는 일 |
|
||||
|---|---|---|
|
||||
| S1 | 코드베이스 → SSOT | `analyzing-codebase-for-tech-log` |
|
||||
| S2 | SSOT → 분해 계약 | `deriving-tech-log-root-tree` |
|
||||
| S3 | 글감 → 기록 | `writing-tech-log-records` |
|
||||
| S4 | 기록 → 그림 | `technical-visualizer` |
|
||||
| S5 | AI 티 제거 | `rewriting-technical-prose-naturally` |
|
||||
| S6 | 일한 사람의 목소리 | `writing-as-the-person-who-did-it` |
|
||||
| S7 | Studio 저장 | `publishing-tech-log-to-studio` |
|
||||
| `source-auditor` | S3 앞. 원본의 주장을 `관측`·`추론`·`미검증`으로 가른 표를 만든다 | 기록을 안 쓴다 |
|
||||
| `fact-reviewer` | 쓴 뒤. 결과 문장을 원문과 한 글자씩 역대조한다 | 파일을 안 고친다 |
|
||||
| `reader-reviewer` | 쓴 뒤. 제목·요약·목차만 보고 30초 안에 읽히는지 본다 | 본문을 안 연다 |
|
||||
| `setup-runner` | Setup 을 쓴 뒤. 손으로 끝까지 칠 수 있는지 읽는다 | 실제로 치지는 않는다 |
|
||||
|
||||
**S4 의 입력은 둘이다.** 무엇을 그릴지는 방금 쓴 기록 본문이 정하고, 그림의 사실은 그 기록의
|
||||
`source` 앵커가 가리키는 SSOT 절이 댄다. 기록 `.md` 를 `techviz prepare` 에 넣지 않는다.
|
||||
@@ -65,8 +90,8 @@ Reference·Question·Decision의 칸은 평문으로 렌더링된다. 그런 자
|
||||
**S7 은 저장까지다.** 한 번이라도 게시한 문서는 게시를 취소해도 삭제가 409 로 거절된다.
|
||||
|
||||
한 런이 남기는 것은 산출물과 **런 원장** 둘이다. 원장은 단계마다 어떤 스킬을 실제로 열었고
|
||||
(`skillEcho` — 그 SKILL.md 의 문장을 원문 그대로 옮긴 것) 어떤 관문을 어떤 종료 코드로
|
||||
지났는지를 적는다.
|
||||
(`skillEcho` — 그 SKILL.md 의 문장을 원문 그대로 옮긴 것) **누가 열었고**(`runBy` — 그 단계를
|
||||
맡은 에이전트 이름) 어떤 관문을 어떤 종료 코드로 지났는지를 적는다.
|
||||
|
||||
```bash
|
||||
python3 scripts/verify-pipeline-run.py --init runs/<프로젝트>/<runId>/run.json \
|
||||
@@ -78,6 +103,49 @@ python3 scripts/verify-pipeline-run.py runs/<프로젝트>/<runId>/run.json
|
||||
스킬의 실제 문장인지, 관문이 돌았고 종료 코드가 0 이었는지, 적어 낸 산출물이 디스크에 있는지.
|
||||
건너뛴 단계도 사유를 적어야 한다. **S3·S5·S6 은 건너뛸 수 없다.**
|
||||
|
||||
**스킬을 고치면 그 전에 돈 런의 영수증이 현재 SKILL.md 에서 사라진다.** 그 영수증은 사실이다 —
|
||||
그때 그 문장이 거기 있었다. 원장을 고쳐 쓰는 것은 영수증 위조이고 틀린 문장을 스킬에 되살리는
|
||||
것은 검사기에 답하는 것이라, 둘 다 하지 않고 검사기가 git 으로 가른다.
|
||||
|
||||
| 영수증이 | 종료 | 문구 |
|
||||
|---|---|---|
|
||||
| 현재 SKILL.md 에 있다 | 통과 | (아무 말도 안 한다) |
|
||||
| 그 스킬의 과거 커밋에만 있다 | **warn** | `그 뒤에 스킬이 고쳐져 영수증을 대조할 수 없다` — 어느 커밋에 있었는지 적는다 |
|
||||
| 어느 판에도 없다 | **error** | `스킬 영수증이 그 스킬의 문장이 아니다` |
|
||||
| 과거를 못 봤다 (git 없음 · 이력 상한 200) | **warn** | `스킬의 과거 본문을 못 봐서 영수증을 대조하지 못했다` |
|
||||
|
||||
**관문도 같다.** 검사기에 관문을 더하면 그 전에 돈 런이 전부 「관문이 빠졌다」가 된다. 그 런은
|
||||
그때 요구되지 않은 것을 안 돌렸을 뿐이다. 원장에 없던 관문을 적어 넣는 것도, 요구를 빼는 것도
|
||||
하지 않고 **검사기가 git 으로 가른다** — 그 관문을 요구하기 시작한 커밋이 런이 끝난 날보다
|
||||
뒤면 warn 이고, 어느 커밋을 요구했는지 적는다. 런의 시각을 못 읽으면 봐주지 않고 error 다.
|
||||
|
||||
**`runBy` 도 같은 자리인데 가르는 기준이 다르다.** 여기서는 git 이 아니라 **원장이 스스로 밝힌
|
||||
판**(`schemaVersion`)으로 가른다. git 은 작업 트리를 못 봐서, 요구를 더한 커밋이 아직 안 들어갔으면
|
||||
`git log -S` 가 빈손으로 돌아오고 지난 런이 전부 error 로 뒤집힌다. 원장이 어느 계약으로 쓰였는지를
|
||||
읽으면 커밋 전후로 판정이 흔들리지 않는다.
|
||||
|
||||
| `runBy` 가 | 종료 | 문구 |
|
||||
|---|---|---|
|
||||
| 그 단계의 계약 에이전트다 | 통과 | (아무 말도 안 한다) |
|
||||
| `schemaVersion` 1 의 `"subagent"` | **warn** | `옛 판의 원장이라 누가 돌렸는지 적혀 있지 않다` |
|
||||
| `schemaVersion` 2 인데 다른 이름이다 | **error** | `단계를 맡은 에이전트가 계약과 다르다` |
|
||||
| 계약대로인데 그 `.md` 가 없다 | **error** | `그 에이전트의 정의가 없다` |
|
||||
|
||||
요약 줄이 「대조 못 한 영수증」과 **「누가 돌렸는지 모르는 단계」를 따로** 센다. 둘 다 warn 이지
|
||||
통과가 아니고, 고치는 방법이 달라서 칸을 나눴다 — 스킬을 열었다는 증거가 없는 것과, 증거는
|
||||
있는데 연 사람이 안 적힌 것은 다른 일이다.
|
||||
|
||||
```text
|
||||
PIPELINE RUN: PASS — 런 5 · error 0 · warn 40 · 대조 못 한 영수증 5 · 누가 돌렸는지 모르는 단계 35
|
||||
```
|
||||
|
||||
**옛 원장의 `runBy` 를 에이전트 이름으로 고쳐 쓰지 않는다.** 그때는 그 칸이 상수였고, 그렇게
|
||||
적은 것이 사실이다. 되찾는 길은 하나뿐이다 — **다음에 여는 런부터 이름이 적힌다.**
|
||||
|
||||
새 원장은 단계마다 `skillRevision` (그 시점 스킬의 커밋)을 적어서 이력을 안 훑고 그 커밋
|
||||
하나로 대조하게 한다. 작업 트리가 커밋과 다르면 `null` 이고, 칸이 없는 옛 원장은 이력 훑기로
|
||||
떨어진다.
|
||||
|
||||
원장은 `runs/<프로젝트>/<runId>/` 에 둔다. 프로젝트 폴더 안에 두지 않는다 — 거기는
|
||||
`final/` 과 `tech-log-studio/` 뿐이다.
|
||||
|
||||
@@ -151,7 +219,7 @@ docs/<프로젝트>/
|
||||
└── tech-log-studio/ Studio 에 올릴 글만
|
||||
├── tech-log-tree.json 분해 계약이자 색인. 이 프로젝트의 글감 전부다
|
||||
└── <주제 slug>/
|
||||
├── case/ concept/ reference/ question/ decision/
|
||||
├── case/ concept/ setup/ reference/ question/ decision/
|
||||
```
|
||||
|
||||
**분석하거나 반입하는 동안에만 있는 것이 따로 있다.**
|
||||
@@ -276,7 +344,7 @@ evidence:
|
||||
```
|
||||
|
||||
주제 폴더 이름은 Studio 주제의 slug 를 쓴다 — `jpa-feed-query-performance`,
|
||||
`oauth-oidc-auth-boundary`. Studio 가 주제별로 다섯 종류를 나눠 보여 주므로 폴더도 같은 모양이다.
|
||||
`oauth-oidc-auth-boundary`. Studio 가 주제별로 여섯 종류를 나눠 보여 주므로 폴더도 같은 모양이다.
|
||||
|
||||
Redis 20편은 한 글을 나눠 쓴 것이라 `docs/clean-architecture-backend-template/final/document.md`
|
||||
하나로 합쳐 두었다. SSOT 는 프로젝트마다 `final/document.md` 하나다.
|
||||
@@ -310,19 +378,19 @@ Redis 20편은 한 글을 나눠 쓴 것이라 `docs/clean-architecture-backend-
|
||||
|---|---|
|
||||
| `clean-architecture-backend-template` | `desktop-server-git/clean-architecture-backend-template` @ `21234e38` |
|
||||
| `n+1liner` | `github-project/ca-tmpl` @ `761384d` — 저장소 HEAD 는 다른 브랜치다 |
|
||||
| `ca-tmpl` | `github-project/ca-tmpl` — 같은 저장소의 아키텍처 경계 쪽 |
|
||||
| `keycloak` | `keycloak-pattern` — 패턴 넷이 `develop-keycloak-pattern1`~`4` 브랜치에 나뉘어 있어 `revisions` 로 tip 넷을 적는다 |
|
||||
| `keycloak-session-store` | `keycloak-pattern` @ `cdac9b8` — 같은 저장소의 실험 26건 |
|
||||
| `TechLog` | `desktop-server-git/tech-log-frontend` · `tech-log-backend` |
|
||||
| `virtualization` | 분석한 저장소가 없다 — 출발점이 반입한 문서 한 편이라 제1~4부는 `revision: null` 이고, 실험대인 제5~6부만 `keycloak-pattern` @ `9465582b` 로 고정한다 |
|
||||
|
||||
| 런 또는 파일 | 문서 |
|
||||
|---|---|
|
||||
| `ca-tmpl` | 실행 가능한 클린 아키텍처 — 선언이 아니라 빌드가 지키는 경계 |
|
||||
| `keycloak` | 브라우저 토큰에서 엣지 세션까지: Keycloak 인증 패턴 네 가지의 경계 설계 |
|
||||
| `keycloak-session-store` | 세션은 어디에 있는가 — Keycloak 다중 노드 실험 26건의 기록. `keycloak` 이 남긴 열린 질문 네 개에 측정으로 답한다 |
|
||||
| `n+1liner` | 하이라이트 피드 조회 성능 — N+1 진단과 조회 전략의 진화 |
|
||||
| `TechLog` | 계약이 먼저인 시스템에서 값이 사라지는 자리들 — TechLog를 만들며 만난 결함의 전수 기록 |
|
||||
| `clean-architecture-backend-template` | 62개 leaf를 23편으로 읽은 통합 분석 · 모듈 분석 65편을 제2부에 합쳐 46,437줄 · 기록 949건 (선별 재판정 대기) |
|
||||
| `virtualization` | CPU·메모리·네트워크·스토리지 가상화와 실험대 구축 — 반입한 개념 문서에 실험대 기록을 이어 붙였다 |
|
||||
|
||||
## 검사
|
||||
|
||||
@@ -342,6 +410,24 @@ python3 scripts/verify-pipeline.py # 위 셋을 모든 프로
|
||||
python3 -m unittest discover -s scripts/tests # 파서·검사기·생성기
|
||||
```
|
||||
|
||||
**기록 한 편에 런 원장 하나.** 원장의 `record` 칸이 그 런이 만든 기록을 가리킨다.
|
||||
`verify-pipeline.py` 의 `RUN COVERAGE` 줄이 기록과 원장을 짝지어 센다.
|
||||
|
||||
```text
|
||||
RUN COVERAGE: 기록 341 · 원장이 덮은 기록 5 · 원장 없는 기록 336
|
||||
```
|
||||
|
||||
**덮이지 않은 것은 error 가 아니다.** 파이프라인을 거치지 않고 손으로 쓴 기록이 있는 것
|
||||
자체는 잘못이 아니고, **지나간 일에 원장을 소급해 만드는 것은 영수증 위조다.** 다만 초록으로
|
||||
보이면 안 된다 — 원장이 전부 통과했다는 것과 기록이 전부 원장을 지났다는 것은 다른 말이고,
|
||||
이 줄이 없으면 앞의 것이 뒤의 것으로 읽힌다. 되찾는 길은 하나뿐이다: **다음에 쓰는 기록부터
|
||||
한 편당 하나씩 연다.**
|
||||
|
||||
```bash
|
||||
python3 scripts/verify-pipeline-run.py --init runs/<프로젝트>/<runId>/run.json \
|
||||
--project <프로젝트> --record <기록 경로>
|
||||
```
|
||||
|
||||
**검사기는 「볼 것이 없어서 통과」를 「문제 없음」이라고 쓰지 않는다.** 셋을 가른다.
|
||||
|
||||
| 상태 | 종료 코드 | 문구 |
|
||||
@@ -401,6 +487,19 @@ python3 scripts/check-figure-overlap.py <프로젝트> # 상자와 라벨이
|
||||
때문이다. 실측 예: 글자를 60자로 늘렸는데 배경은 131.9px 그대로였고 `check-figure-overlap`
|
||||
은 통과시켰다. 그런 SVG 는 눈으로 본다.
|
||||
|
||||
**그래서 검사기가 못 본 그림을 따로 센다.** 상자를 알아보는 근거는 techviz 렌더러가 붙인
|
||||
`class` 하나뿐이라, 손그림에서는 상자가 0개로 읽히고 겹칠 짝이 없어 겹침도 0 이 된다.
|
||||
고치기 전에는 그것이 `그림 182 · 겹친 그림 0` 으로 찍혔는데 **실제로 본 것은 18장**이었다.
|
||||
지금은 요약 줄이 갈라 적는다.
|
||||
|
||||
```text
|
||||
FIGURE OVERLAP: PASS — 그림 182 · 본 그림 18 · 겹친 그림 0 · 못 본 그림 164
|
||||
```
|
||||
|
||||
못 본 것은 결함이 아니다 — 손그림이 있는 것 자체는 잘못이 아니다. 다만 **초록으로 보이면
|
||||
안 된다.** 숫자를 되찾으려면 그 그림을 techviz 로 다시 만들어야 하고,
|
||||
`verify-project-layout.py` 의 「techviz 정본이 없는 그림」이 같은 모집단을 센다.
|
||||
|
||||
**그래도 마지막에는 눈으로 본다.** 글자가 상자 밖으로 조금 나가거나 화살표가 라벨을 지나는
|
||||
것은 좌표로 안 잡힌다.
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ Reference·Question·Decision의 칸은 평문으로 렌더링됩니다. 그런
|
||||
|
||||
`running-tech-log-pipeline` 이 일곱 단계(S1 SSOT → S2 분해 계약 → S3 기록 → S4 그림 →
|
||||
S5 AI 티 → S6 목소리 → S7 Studio 저장)를 정의하고 단계마다 서브에이전트를 띄웁니다.
|
||||
그 에이전트는 `.claude/agents/`에 있는 것을 쓰고 그때그때 만들지 않습니다 — 단계마다 맡을
|
||||
에이전트가 정해져 있고(`ssot-analyst`·`tree-deriver`·`record-writer`·`diagram-maker`·
|
||||
`prose-rewriter`·`voice-writer`·`studio-validator`), 런 원장의 `runBy`가 그 이름을 적습니다.
|
||||
한 런은 `runs/<프로젝트>/<runId>/run.json` 에 원장을 남기고, 그 원장이 절차를 지켰는지는
|
||||
따로 검사합니다.
|
||||
|
||||
@@ -63,14 +66,14 @@ python3 scripts/verify-pipeline-run.py runs/<프로젝트>/<runId>/run.json
|
||||
|
||||
```text
|
||||
docs/
|
||||
├── ca-tmpl/ 실행 가능한 클린 아키텍처
|
||||
├── clean-architecture-backend-template/
|
||||
│ 분석 진행 중 (모듈 7편)
|
||||
├── keycloak/ 인증 패턴 네 가지
|
||||
├── keycloak-session-store/ 세션 저장소와 장애 주입 실험 26건
|
||||
│ keycloak 의 열린 질문 네 개에 답한다
|
||||
├── n+1liner/ 피드 조회 성능
|
||||
└── TechLog/ 이 Studio를 만들며 만난 결함
|
||||
├── TechLog/ 이 Studio를 만들며 만난 결함
|
||||
└── virtualization/ CPU·메모리·네트워크·스토리지 가상화와 실험대 구축
|
||||
```
|
||||
|
||||
프로젝트 하나는 이렇게 생겼습니다.
|
||||
|
||||
@@ -33,18 +33,18 @@ source:
|
||||
|
||||
## 결론
|
||||
|
||||
간헐적으로 보인 것은 두 가지 결정적 어긋남이었고, 사용자는 둘 다 만났다.
|
||||
간헐적으로 보였을 뿐 실제로는 두 가지로 어긋났고, 사용자는 둘 다 만났다.
|
||||
|
||||
인증 : 남는 글자가 없어 빈 문자열 → 폼이 요청 전에 거절
|
||||
Redis 캐시 와 Redis 클러스터 : 둘 다 redis → 두 번째가 충돌
|
||||
|
||||
> 규칙은 간헐적이었던 적이 없다. **보이지 않았을 뿐이다** — slug 생성이 [a-z0-9] 만 남기고 나머지를 버려서, 한글 이름은 아무것도 기여하지 못했다.
|
||||
`5cffe30` 이 그것을 이렇게 적었다. 규칙은 간헐적이었던 적이 없다. 보이지 않았을 뿐이다 — slug 생성이 [a-z0-9] 만 남기고 나머지를 버려서, 한글 이름은 아무것도 기여하지 못했다.
|
||||
|
||||
한글을 버리지 않고 로마자로 옮긴다. 음절을 초성·중성·종성으로 산술 분해하므로 표가 필요 없고 결정적이다.
|
||||
|
||||
백엔드 아키텍처 → baekendeu-akitekcheo
|
||||
|
||||
국어의 로마자 표기법의 자모 대응만 적용하고 음운 변화 규칙은 일부러 뺐다. slug 는 읽는 것이지 발음하는 것이 아니고, 그 규칙을 넣으면 같은 이름이 문맥에 따라 다른 slug 가 된다.
|
||||
국어의 로마자 표기법의 자모 대응만 적용하고 음운 변화 규칙은 일부러 뺐다. 함수의 javadoc 이 그 이유를 적는다. slug 는 읽는 것이지 발음하는 것이 아니고, 그 규칙을 넣으면 같은 이름이 문맥에 따라 다른 slug 가 된다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ source:
|
||||
|
||||
편집기가 질문과 결정 목록을 못 읽으면 빈 배열로 삼키고 있었다. 서버는 404 를 주고 있었다 — 그 두 연산에 컨트롤러가 없었다.
|
||||
|
||||
작성자에게는 「아직 안 쓴 것」으로 읽힌다.
|
||||
작성자에게는 「아직 안 쓴 것」으로 읽힌다. 그래서 화면이 지킬 규칙을 하나 정했다.
|
||||
|
||||
> 거짓말을 하느니 못 읽었다고 말한다.
|
||||
거짓말을 하느니 못 읽었다고 말한다.
|
||||
|
||||
못 읽었을 때 못 읽었다고 적게 고쳤다. 같은 판단을 주제 탭에도 적용했다.
|
||||
|
||||
@@ -79,6 +79,8 @@ tech-log-backend : 911e8ba — 없던 두 컨트롤러를 구현
|
||||
|
||||
대신 저장할 때 그 대상이 실제로 있는지 확인한다. 확인하지 않으면 없는 id 가 그대로 저장되고, 공개 화면은 조용히 빈 focus 를 그린다 — 저장은 성공했는데 화면에는 아무것도 안 나오는, 이유를 알 수 없는 실패가 된다.
|
||||
|
||||
확인하는 것은 있는지까지다. 그 대상이 공개인지는 저장할 때 보지 않는다. 아직 게시하지 않은 프로젝트를 미리 지목해 두고 게시와 동시에 홈에 뜨게 하는 것이 정상적인 순서이고, 공개 여부는 공개 조회 쪽이 매번 다시 판단한다.
|
||||
|
||||
같은 화면의 두 자리가 반대로 처리돼 있었다. 저장 쪽은 없는 대상을 막고, 목록을 못 읽은 쪽은 없는 것으로 그렸다.
|
||||
|
||||
## 못 읽었다고 적는다
|
||||
|
||||
@@ -33,13 +33,13 @@ source:
|
||||
|
||||
## 결론
|
||||
|
||||
두 요청을 하나로 묶어 기다리면 한쪽의 실패가 전체를 실패로 만든다. 둘을 따로 읽도록 갈랐다.
|
||||
두 요청을 하나로 묶어 기다리면 한쪽만 실패해도 전체가 실패한다. 둘을 따로 읽도록 갈랐다.
|
||||
|
||||
더 미묘한 변종이 하나 더 있었다.
|
||||
더 미묘한 변종이 하나 더 있었고, `fd73bc8` 이 그것을 적어 두었다.
|
||||
|
||||
> Promise.all([gateway.foo()]) 은 foo 가 **거절하는 것만** 잡는다. 호출이 **동기적으로 던지면** 배열을 만드는 중에 터져 rejection handler 를 지나지 못하고, 그러면 홈 focus 한 칸 때문에 대시보드 전체가 빈 화면이 된다.
|
||||
Promise.all([gateway.foo()]) 은 foo 가 거절하는 것만 잡는다. 호출이 동기적으로 던지면 배열을 만드는 중에 터져 rejection handler 를 지나지 못하고, 그러면 홈 focus 한 칸 때문에 대시보드 전체가 빈 화면이 된다.
|
||||
|
||||
같은 판단을 주제 탭에도 적용했다.
|
||||
같은 판단을 나중에 주제 탭에도 적용했다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ source:
|
||||
|
||||
## 목적
|
||||
|
||||
매번 우는 검사가 읽히지 않게 되는 것을 막는다.
|
||||
검사 결과를 사람이 계속 읽게 하려고 정한 기준이다.
|
||||
|
||||
> 매번 늑대를 외치는 검사는 읽히지 않게 되고, 진짜 실패가 그 옆에 눈에 띄지 않은 채 앉아 있게 된다.
|
||||
매번 늑대를 외치는 검사는 읽히지 않게 되고, 진짜 실패가 그 옆에 눈에 띄지 않은 채 앉아 있게 된다.
|
||||
|
||||
## 규칙
|
||||
|
||||
|
||||
@@ -34,11 +34,11 @@ SPA 안에서 이동하면 화면이 열린다. 주소창에 그 주소를 직
|
||||
|
||||
## 결론
|
||||
|
||||
서빙 계약의 절반은 라우트 레지스트리에서 유도하고 있었고 나머지 절반은 손으로 유지하는 배열이었다.
|
||||
서빙 계약은 절반씩 다른 방식으로 만들어지고 있었다. 그것을 고친 `ab8c6c1` 이 원인을 이렇게 짚었다.
|
||||
|
||||
> 서빙 계약의 공개 절반은 라우트 레지스트리에서 패턴을 유도한다. **Studio 절반은 손으로 유지하는 배열이었고, 손으로 유지하는 배열이 실패하는 방식 그대로 실패했다** — ^/studio/assets$ 위의 주석이 바로 그 버그를 한 번 고친 기록이고, 라우트를 더하니 즉시 반복됐다.
|
||||
서빙 계약의 공개 절반은 라우트 레지스트리에서 패턴을 유도한다. Studio 절반은 손으로 유지하는 배열이었고, 손으로 유지하는 배열이 실패하는 방식 그대로 실패했다 — ^/studio/assets$ 위의 주석이 바로 그 버그를 한 번 고친 기록이고, 라우트를 더하니 즉시 반복됐다.
|
||||
|
||||
공개 절반도 유도라고 하기 어려웠다. 번들된 픽스처에 우연히 들어 있던 공개 경로를 전부 열거하고, 생성된 nginx 가 정확히 그것들을 location = 블록으로 게시했다. 빌드 이후에 게시된 기록은 SPA 에 묻기도 전에 엣지에서 404 였다. 경로 27개가 얼어 있었고 28번째는 무엇이든 닿을 수 없었다.
|
||||
공개 절반도 유도라고 하기 어려웠다. 서빙 계약이 번들된 픽스처에 우연히 들어 있던 공개 경로를 전부 열거하고, 생성된 nginx 가 정확히 그것들을 location = 블록으로 게시했다. 빌드 이후에 게시된 기록은 SPA 에 묻기도 전에 엣지에서 404 였다. 경로 27개가 얼어 있었고 28번째는 무엇이든 닿을 수 없었다.
|
||||
|
||||
지금은 라우트 계약에서 등록된 Public 라우트마다 정규식 하나를 만든다. 파라미터는 한 세그먼트만 잡고 슬래시는 잡지 않으므로 /cases/a/b 는 404 로 남는다.
|
||||
|
||||
|
||||
@@ -30,20 +30,16 @@ source:
|
||||
|
||||
홈 화면 하나에 종류 이름이 아홉 개 있었다.
|
||||
|
||||
text
|
||||
최근 기록 목록: CASE · CONCEPT · OPEN QUESTION · REFERENCE
|
||||
바로 아래 「종류별로 읽기」: 검증 기록 · 동작 원리 · 적용 기준 · 열린 질문
|
||||
|
||||
|
||||
두 목록이 같은 다섯 종류를 가리키는데 이름이 겹치지 않는다. 독자는 그 둘이 같은 것이라는 단서를 받지 못한다.
|
||||
|
||||
## 결론
|
||||
|
||||
> 표가 화면마다 복사되어 **여섯 벌**이었고 그래서 갈라졌다: 같은 QUESTION 이 공개 화면에서 "Open Question", 작업본 목록과 게시 기록에서 "Question", 편집기 상태 줄에서 "QUESTION" 이었다. **쓰는 사람은 같은 문서를 화면마다 다른 이름으로 만난다.**
|
||||
종류 이름을 한 곳에 모은 `ca1fc92` 가 원인을 짚었다. 표가 화면마다 복사되어 여섯 벌이었고 그래서 갈라졌다: 같은 QUESTION 이 공개 화면에서 "Open Question", 작업본 목록과 게시 기록에서 "Question", 편집기 상태 줄에서 "QUESTION" 이었다. 쓰는 사람은 같은 문서를 화면마다 다른 이름으로 만난다.
|
||||
|
||||
종류에서 이름으로 가는 표 하나로 모았다.
|
||||
|
||||
편집기 칸 이름도 공개 화면과 맞췄다.
|
||||
종류에서 이름으로 가는 표 하나로 모으고, 편집기 칸 이름도 공개 화면과 맞췄다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
@@ -69,7 +65,7 @@ tech-log-frontend : dc2fda7 · ca1fc92 · 82e992d
|
||||
|
||||
두 목록이 세로로 붙어 있다. 위는 계약의 enum 이름을, 아래는 사람이 붙인 이름을 쓴다. 독자는 둘이 같은 것이라는 단서를 어디서도 받지 못했다.
|
||||
|
||||
이름을 바꾸기 전에는 양쪽이 다 enum 이름이었으므로 적어도 같아 보였다. 이 화면은 이름을 바꾸면서 나빠졌다.
|
||||
이름을 바꾸기 전에는 양쪽이 다 enum 이름이었으므로 적어도 같아 보였다. 이름을 바꿔서 나빠졌다고 짚은 화면은 홈 하나다. 두 이름이 같이 뜨는 다른 화면을 전수로 세지는 않았다.
|
||||
|
||||
## 표가 여섯 벌이었다
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ source:
|
||||
|
||||
컬럼 이름 하나가 틀렸고, 그 쿼리가 한 번도 실행된 적이 없었다.
|
||||
|
||||
> 그 쿼리의 여섯 컬럼 중 다섯은 마이그레이션과 대조했다. 이 하나만 가정했고, 그것이 틀렸다.
|
||||
그 쿼리의 여섯 컬럼 중 다섯은 마이그레이션과 대조했다. 이 하나만 가정했고, 그것이 틀렸다.
|
||||
|
||||
표준 check 는 Testcontainers 를 띄우지 않는다. persistence SQL 은 한 번도 실행되지 않은 채 빌드가 통과하고, 컴파일도 단위 테스트도 컬럼 이름을 검증하지 못한다.
|
||||
|
||||
@@ -101,7 +101,7 @@ DB : 실제 PostgreSQL (Testcontainers)
|
||||
|
||||
삭제 경로 전용 통합 테스트 태스크를 만들고, 실패했던 그 쿼리를 포함해 여덟 시나리오를 실제 PostgreSQL 에서 돌린다.
|
||||
|
||||
전용 태스크로 뺀 이유는 컨테이너를 띄우는 데 시간이 들어 표준 검사에 넣으면 모든 빌드가 느려지기 때문이다. 대신 그 태스크를 돌리는 것은 사람이 기억해야 한다.
|
||||
표준 check 에 들어가지 않으므로 그 태스크를 돌리는 것은 사람이 기억해야 한다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ CI 게이트도 마찬가지였다. 라우트를 더하면서 기준값을 함
|
||||
첫 번째 : 화면 테스트를 별도 명령이 돌리는데 그것을 안 돌려 23건이 빨간 채로 여러 커밋을 지나갔다
|
||||
두 번째 : 주제 화면 셋을 더하면서 게이트 기준값을 빠뜨려 게이트가 빨간 채로 여러 커밋을 지나갔다
|
||||
|
||||
> **가드는 CI 에 묶여야 의미가 있습니다.** 사람이 기억해서 돌리는 가드는 절반만 존재합니다.
|
||||
가드는 CI 에 묶여야 의미가 있습니다. 사람이 기억해서 돌리는 가드는 절반만 존재합니다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
@@ -70,7 +70,7 @@ tech-log-frontend : fd73bc8 · fe6b56a
|
||||
|
||||
## 게이트 기준값을 빠뜨렸다
|
||||
|
||||
주제 화면 셋을 더할 때 CI 게이트가 요구하는 기준값 셋을 함께 올리지 않았다. 게이트는 라우트 집합과 증거 집합이 정확히 일치하기를 요구하므로 그 상태를 정확히 거절한다.
|
||||
주제 화면 셋을 더할 때 CI 게이트가 요구하는 기준값 셋을 또 빠뜨렸다 — 라우트를 더하면서 거기 딸린 손 목록을 빠뜨린 것이 처음이 아니었다. 게이트는 라우트 집합과 증거 집합이 정확히 일치하기를 요구하므로 그 상태를 정확히 거절한다.
|
||||
|
||||
게이트가 빨간 채로 여러 커밋을 지나갔고, 결정 링크 404 를 고치던 커밋에서야 함께 맞췄다.
|
||||
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
],
|
||||
"reference": [],
|
||||
"question": [],
|
||||
"decision": []
|
||||
"decision": [],
|
||||
"setup": []
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
{
|
||||
"version": "1.1",
|
||||
"id": "production-vs-optin",
|
||||
"title": "app-bootstrap의 main 클래스패스에는 어댑터 11개가 포함되고 참조 어댑터 3개는 의존 목록 밖에 있다",
|
||||
"question": "app-bootstrap의 main 프로젝트 의존에 포함된 어댑터와 의존 목록 밖의 opt-in 참조 어댑터는 어떻게 구분되는가?",
|
||||
"type": "concept",
|
||||
"direction": "LR",
|
||||
"audience": [
|
||||
"멀티모듈 Spring Boot 애플리케이션 설계자",
|
||||
"클린 아키텍처 구현 독자"
|
||||
],
|
||||
"summary": "main 프로젝트 의존은 어댑터 11개를 클래스패스에 올리고, grpc·graphql·websocket 세 참조 어댑터는 의존성을 선언하지 않는 방식으로 opt-in한다.",
|
||||
"alt": "왼쪽의 app-bootstrap main 의존 포함 어댑터 11개와 오른쪽의 main 의존 목록 밖 grpc·graphql·websocket 세 개를 비교한 그림. 클래스패스 구성 비교이며 활성 빈 수를 뜻하지 않는다.",
|
||||
"long_description": "왼쪽 비교 항목은 app-bootstrap의 main 프로젝트 의존에 포함되어 main 클래스패스에 들어오는 어댑터 11개를 나타낸다. 클래스패스 포함과 실제 빈 활성화는 별개이며 런타임 조건이 활성화를 추가로 결정한다. 오른쪽 비교 항목은 현재 main 의존 목록에 없는 grpc, graphql, websocket 세 참조 어댑터를 나타낸다. 이 셋은 클래스패스에 등록되면 기본 활성화되므로 의존성 선언을 하지 않는 것이 opt-in 수단이다. 두 수치는 main 의존 선언을 비교한 것이며 실행 시 활성 빈 전체를 측정한 값이 아니다.",
|
||||
"source_context": {
|
||||
"document": "/home/donghyeon/workspace/ai-tool/topic-arrange/executable-clean-architecture/claridoc-rewrite/document.md",
|
||||
"document_sha256": "81fb5cb8cd16eaae6916a0d0f2b3cddfabc39e58a87559466b52f922ca95a95b",
|
||||
"anchor": {
|
||||
"kind": "marker",
|
||||
"value": "production-vs-optin",
|
||||
"line": 502
|
||||
}
|
||||
},
|
||||
"composition": {
|
||||
"profile": "comparison",
|
||||
"diagram_only": true,
|
||||
"reference_ids": [
|
||||
"contract-comparison"
|
||||
],
|
||||
"rationale": "본문이 main 프로젝트 의존에 포함된 집합과 의존 목록 밖의 opt-in 집합을 명시적으로 비교하므로, 호출 관계나 시간 순서를 만들지 않고 같은 필드로 두 집합을 정렬하는 비교 문법이 독자의 질문에 가장 직접적으로 답한다."
|
||||
},
|
||||
"groups": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "main-classpath-adapters",
|
||||
"label": "main 의존 포함 · 11",
|
||||
"kind": "concept",
|
||||
"role": "option",
|
||||
"description": "app-bootstrap의 main 프로젝트 의존에 포함되어 main 클래스패스에 들어오는 어댑터 집합",
|
||||
"details": [
|
||||
"수량: 11개",
|
||||
"프로젝트 의존: 선언됨",
|
||||
"클래스패스: 포함",
|
||||
"어댑터: 포함 대상 11개",
|
||||
"활성화: 클래스패스와 별도",
|
||||
"측정 범위: main 의존 선언"
|
||||
],
|
||||
"emphasis": "primary",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 449,
|
||||
"end_line": 456
|
||||
},
|
||||
{
|
||||
"start_line": 476,
|
||||
"end_line": 480
|
||||
},
|
||||
{
|
||||
"start_line": 500,
|
||||
"end_line": 501
|
||||
},
|
||||
{
|
||||
"start_line": 517,
|
||||
"end_line": 517
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
},
|
||||
{
|
||||
"id": "omitted-optin-adapters",
|
||||
"label": "main 의존 목록 밖 · 3",
|
||||
"kind": "concept",
|
||||
"role": "option",
|
||||
"description": "저장소에는 있지만 app-bootstrap의 main 프로젝트 의존에는 선언되지 않은 참조 어댑터 집합",
|
||||
"details": [
|
||||
"수량: 3개",
|
||||
"프로젝트 의존: 선언하지 않음",
|
||||
"클래스패스: 제외",
|
||||
"어댑터: grpc · graphql · websocket",
|
||||
"활성화: 등록하면 기본 활성",
|
||||
"측정 범위: main 의존 선언"
|
||||
],
|
||||
"emphasis": "warning",
|
||||
"evidence": [
|
||||
{
|
||||
"start_line": 481,
|
||||
"end_line": 489
|
||||
},
|
||||
{
|
||||
"start_line": 500,
|
||||
"end_line": 501
|
||||
},
|
||||
{
|
||||
"start_line": 517,
|
||||
"end_line": 517
|
||||
}
|
||||
],
|
||||
"assumption": false
|
||||
}
|
||||
],
|
||||
"edges": [],
|
||||
"legend": [],
|
||||
"metadata": {
|
||||
"rationale": "14개 어댑터를 각각 카드로 반복하면 비교 필드가 흐려지고 밀도 예산을 넘으므로, 두 집합을 수량·의존 상태·클래스패스 상태·활성화 의미·측정 범위로 정렬했다."
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="610" viewBox="0 0 1280 610" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Layered architecture boundary problem</title>
|
||||
<desc id="desc">Four technical layers depend downward; the business layer is consequently tied to database technology.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1280" height="610" fill="#FFFFFF"/>
|
||||
<rect x="300" y="55" width="600" height="82" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="600" y="90" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">Presentation</text>
|
||||
<text x="600" y="115" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">Controller · View</text>
|
||||
<rect x="300" y="175" width="600" height="82" fill="#EAF3FF" stroke="#0B5CC4" stroke-width="2" rx="8" />
|
||||
<text x="600" y="210" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">Business Logic</text>
|
||||
<text x="600" y="235" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">Service · 도메인 규칙</text>
|
||||
<rect x="300" y="295" width="600" height="82" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="600" y="330" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">Data Access</text>
|
||||
<text x="600" y="355" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">Repository · DAO</text>
|
||||
<rect x="470" y="435" width="260" height="85" fill="#F6F7F9" />
|
||||
<ellipse cx="600" cy="435" rx="130" ry="10" fill="#F6F7F9" stroke="#59616B" stroke-width="1.6" />
|
||||
<path d="M 470 435 L 470 520" fill="none" stroke="#59616B" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 730 435 L 730 520" fill="none" stroke="#59616B" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 470 520 C 522 532, 678 532, 730 520" fill="none" stroke="#59616B" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="600" y="479.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="15" font-weight="700" fill="#25282D" text-anchor="middle">Database</text>
|
||||
<text x="600" y="500.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="450" fill="#667085" text-anchor="middle">기술 저장소</text>
|
||||
<path d="M 600 137 L 600 175" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="636.6" y="146" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="675" y="162" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">depends on</text>
|
||||
<path d="M 600 257 L 600 295" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="636.6" y="266" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="675" y="282" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">depends on</text>
|
||||
<path d="M 600 377 L 600 425" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="636.6" y="391" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="675" y="407" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">depends on</text>
|
||||
<rect x="975" y="182" width="250" height="150" fill="#FDEDEF" stroke="#D64550" stroke-width="1.6" rx="10" />
|
||||
<text x="1100" y="222" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="800" fill="#D64550" text-anchor="middle">문제</text>
|
||||
<text x="1100" y="255" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="15" font-weight="650" fill="#25282D" text-anchor="middle">
|
||||
<tspan x="1100" dy="0">도메인이 기술에 묶인다</tspan>
|
||||
<tspan x="1100" dy="21">경계가 컴파일러에</tspan>
|
||||
<tspan x="1100" dy="21">보이지 않는다</tspan>
|
||||
</text>
|
||||
<path d="M 900 216 L 975 216" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-red)" />
|
||||
<rect x="888.6" y="183" width="98.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="938" y="199" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="middle">DB·기술에 종속</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.6 KiB |
@@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="650" viewBox="0 0 1440 650" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Three architecture lenses</title>
|
||||
<desc id="desc">Layered, Hexagonal, and Clean are shown as three distinct views of the same inward dependency rule.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1440" height="650" fill="#FFFFFF"/>
|
||||
<rect x="40" y="45" width="420" height="455" fill="#FFFFFF" stroke="#8A919A" stroke-width="1.4" rx="10" />
|
||||
<text x="250" y="88" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="19" font-weight="750" fill="#25282D" text-anchor="middle">Layered</text>
|
||||
<text x="250" y="116" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="500" fill="#667085" text-anchor="middle">기술 책임을 층으로</text>
|
||||
<rect x="500" y="45" width="420" height="455" fill="#FFFFFF" stroke="#8A919A" stroke-width="1.4" rx="10" />
|
||||
<text x="710" y="88" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="19" font-weight="750" fill="#25282D" text-anchor="middle">Hexagonal</text>
|
||||
<text x="710" y="116" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="500" fill="#667085" text-anchor="middle">상호작용 경계를 포트로</text>
|
||||
<rect x="960" y="45" width="420" height="455" fill="#FFFFFF" stroke="#8A919A" stroke-width="1.4" rx="10" />
|
||||
<text x="1170" y="88" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="19" font-weight="750" fill="#25282D" text-anchor="middle">Clean</text>
|
||||
<text x="1170" y="116" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="500" fill="#667085" text-anchor="middle">정책 수준을 동심원으로</text>
|
||||
<rect x="105" y="160" width="290" height="60" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="250" y="195" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">Presentation</text>
|
||||
<path d="M 250 220 L 250 252" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="105" y="252" width="290" height="60" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="250" y="287" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">Application</text>
|
||||
<path d="M 250 312 L 250 344" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="105" y="344" width="290" height="60" fill="#EAF7F1" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="250" y="379" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">Domain</text>
|
||||
<polygon points="630,165 830,165 890,300 830,435 630,435 570,300" fill="#EAF3FF" stroke="#1677FF" stroke-width="2" />
|
||||
<text x="730" y="292" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="18" font-weight="750" fill="#0B5CC4" text-anchor="middle">Core</text>
|
||||
<circle cx="580" cy="300" r="8" fill="#FFFFFF" stroke="#1677FF" stroke-width="2" />
|
||||
<circle cx="880" cy="300" r="8" fill="#FFFFFF" stroke="#1677FF" stroke-width="2" />
|
||||
<path d="M 525 300 L 570 300" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<path d="M 890 300 L 935 300" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<text x="730" y="390" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#667085" text-anchor="middle">ports</text>
|
||||
<circle cx="1170" cy="300" r="150" fill="#F6F7F9" stroke="#858B94" stroke-width="1.6" />
|
||||
<circle cx="1170" cy="300" r="105" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.8" />
|
||||
<circle cx="1170" cy="300" r="55" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.8" />
|
||||
<text x="1170" y="305" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="750" fill="#168A5B" text-anchor="middle">Policy</text>
|
||||
<text x="1170" y="235" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#0B5CC4" text-anchor="middle">Use cases</text>
|
||||
<text x="1170" y="175" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#667085" text-anchor="middle">Frameworks</text>
|
||||
<path d="M 1310 300 L 1230 300" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="410" y="545" width="620" height="60" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.6" rx="30" />
|
||||
<text x="720" y="582" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="18" font-weight="750" fill="#0B5CC4" text-anchor="middle">한 규칙 · 의존은 안쪽으로만</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.8 KiB |
@@ -1,75 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1480" height="760" viewBox="0 0 1480 760" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Executable clean architecture module picture</title>
|
||||
<desc id="desc">Inbound and outbound adapters point to application-core, which depends independently on domain-core and shared-contract; app-bootstrap wires the application.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1480" height="760" fill="#FFFFFF"/>
|
||||
<polygon points="535,190 895,190 975,365 895,540 535,540 455,365" fill="#EAF3FF" stroke="#1677FF" stroke-width="2.2" />
|
||||
<text x="715" y="265" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="22" font-weight="750" fill="#0B5CC4" text-anchor="middle">application-core</text>
|
||||
<text x="715" y="296" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="500" fill="#667085" text-anchor="middle">use cases · ports</text>
|
||||
<rect x="555" y="355" width="235" height="82" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="672.5" y="390" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">domain-core</text>
|
||||
<text x="672.5" y="415" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">main 의존 0</text>
|
||||
<rect x="805" y="355" width="235" height="82" fill="#FFFFFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="922.5" y="390" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">shared-contract</text>
|
||||
<text x="922.5" y="415" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">main 의존 0</text>
|
||||
<text x="797" y="477" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="600" fill="#667085" text-anchor="middle">서로 직접 의존하지 않음</text>
|
||||
<rect x="35" y="215" width="330" height="330" fill="#FBFCFE" stroke="#AEB5BF" stroke-width="1.3" rx="10" />
|
||||
<rect x="44" y="204" width="120.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="51" y="220" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="start">Inbound 어댑터 ×4</text>
|
||||
<rect x="75" y="270" width="250" height="44" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="200" y="297" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">web</text>
|
||||
<rect x="75" y="332" width="250" height="44" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="200" y="359" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">batch</text>
|
||||
<rect x="75" y="394" width="250" height="44" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="200" y="421" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">messaging-in</text>
|
||||
<rect x="75" y="456" width="250" height="44" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="200" y="483" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">scheduler</text>
|
||||
<rect x="1115" y="100" width="330" height="500" fill="#FBFCFE" stroke="#AEB5BF" stroke-width="1.3" rx="10" />
|
||||
<rect x="1124" y="89" width="133.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1131" y="105" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="start">Outbound 어댑터 ×10</text>
|
||||
<rect x="1155" y="155" width="250" height="46" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="1280" y="183" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">persistence</text>
|
||||
<rect x="1155" y="221" width="250" height="46" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="1280" y="249" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">object storage</text>
|
||||
<rect x="1155" y="287" width="250" height="46" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="1280" y="315" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">notification</text>
|
||||
<rect x="1155" y="353" width="250" height="46" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="1280" y="381" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">cache</text>
|
||||
<rect x="1155" y="419" width="250" height="46" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="1280" y="447" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">messaging</text>
|
||||
<rect x="1155" y="485" width="250" height="46" fill="#FFFFFF" stroke="#69717B" stroke-width="1.5" rx="8" />
|
||||
<text x="1280" y="513" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">http client</text>
|
||||
<path d="M 365 365 L 455 365" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="371.6" y="340" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="410" y="356" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Depends on</text>
|
||||
<path d="M 1115 365 L 975 365" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="1006.6" y="340" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1045" y="356" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Depends on</text>
|
||||
<path d="M 715 330 L 672 355" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="599.6" y="318" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="638" y="334" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">Depends on</text>
|
||||
<path d="M 780 330 L 855 355" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-purple)" />
|
||||
<rect x="845.6" y="318" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="884" y="334" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">Depends on</text>
|
||||
<rect x="630" y="35" width="290" height="105" fill="#FFFFFF" stroke="#59616B" stroke-width="1.6" rx="3" />
|
||||
<path d="M 630 70 L 920 70" fill="none" stroke="#59616B" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 630 105 L 920 105" fill="none" stroke="#59616B" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<circle cx="648" cy="52.5" r="4" fill="#1677FF" />
|
||||
<circle cx="648" cy="87.5" r="4" fill="#1677FF" />
|
||||
<circle cx="648" cy="122.5" r="4" fill="#1677FF" />
|
||||
<text x="775" y="92.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="15" font-weight="700" fill="#25282D" text-anchor="middle">app-bootstrap</text>
|
||||
<text x="775" y="166" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="600" fill="#667085" text-anchor="middle">조립 루트</text>
|
||||
<path d="M 775 140 L 775 190" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="827.3" y="169" width="45.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="850" y="185" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">Wires</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 10 KiB |
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1380" height="700" viewBox="0 0 1380 700" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Boundary enforcement comparison</title>
|
||||
<desc id="desc">A two-by-two parallel comparison of four structures and their available boundary enforcement mechanisms; it is not a progression.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1380" height="700" fill="#FFFFFF"/>
|
||||
<rect x="520" y="28" width="239.3" height="30" fill="#F6F7F9" stroke="#AEB5BF" stroke-width="1.2" rx="15" />
|
||||
<text x="639.6" y="48" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#667085" text-anchor="middle">2×2 병렬 비교 · 진행 단계 아님</text>
|
||||
<rect x="50" y="95" width="620" height="245" fill="#FFFFFF" stroke="#757C85" stroke-width="1.4" rx="10" />
|
||||
<rect x="50" y="95" width="9" height="245" fill="#D64550" rx="4" />
|
||||
<text x="84" y="138" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="19" font-weight="750" fill="#25282D" text-anchor="start">단일모듈 Layered</text>
|
||||
<text x="84" y="165" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="550" fill="#667085" text-anchor="start">설명용 검출 예시</text>
|
||||
<text x="105" y="213" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="500" fill="#25282D" text-anchor="start">
|
||||
<tspan x="105" dy="0">• 도메인 → JPA 타입</tspan>
|
||||
<tspan x="105" dy="21.7">• 공유 클래스패스</tspan>
|
||||
</text>
|
||||
<rect x="400" y="265" width="116.8" height="30" fill="#F6F7F9" stroke="#D64550" stroke-width="1.2" rx="15" />
|
||||
<text x="458.4" y="285" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#D64550" text-anchor="middle">경계 장치 없음</text>
|
||||
<rect x="710" y="95" width="620" height="245" fill="#FFFFFF" stroke="#757C85" stroke-width="1.4" rx="10" />
|
||||
<rect x="710" y="95" width="9" height="245" fill="#B7791F" rx="4" />
|
||||
<text x="744" y="138" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="19" font-weight="750" fill="#25282D" text-anchor="start">단일모듈 Clean</text>
|
||||
<text x="744" y="165" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="550" fill="#667085" text-anchor="start">별도 규칙 필요</text>
|
||||
<text x="765" y="213" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="500" fill="#25282D" text-anchor="start">
|
||||
<tspan x="765" dy="0">• 패키지 경계</tspan>
|
||||
<tspan x="765" dy="21.7">• 위반 타입은 여전히 보임</tspan>
|
||||
</text>
|
||||
<rect x="1060" y="265" width="116.7" height="30" fill="#FFFFFF" stroke="#B7791F" stroke-width="1.2" rx="15" />
|
||||
<text x="1118.3" y="285" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#B7791F" text-anchor="middle">ArchUnit 필요</text>
|
||||
<rect x="50" y="385" width="620" height="245" fill="#FFFFFF" stroke="#757C85" stroke-width="1.4" rx="10" />
|
||||
<rect x="50" y="385" width="9" height="245" fill="#1677FF" rx="4" />
|
||||
<text x="84" y="428" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="19" font-weight="750" fill="#25282D" text-anchor="start">멀티모듈 Clean</text>
|
||||
<text x="84" y="455" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="550" fill="#667085" text-anchor="start">클래스패스 격리 가능</text>
|
||||
<text x="105" y="503" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="500" fill="#25282D" text-anchor="start">
|
||||
<tspan x="105" dy="0">• 모듈별 classpath</tspan>
|
||||
<tspan x="105" dy="21.7">• 금지 타입 자체가 없음</tspan>
|
||||
</text>
|
||||
<rect x="400" y="555" width="94.5" height="30" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.2" rx="15" />
|
||||
<text x="447.2" y="575" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#1677FF" text-anchor="middle">javac 차단</text>
|
||||
<rect x="710" y="385" width="620" height="245" fill="#FFFFFF" stroke="#757C85" stroke-width="1.4" rx="10" />
|
||||
<rect x="710" y="385" width="9" height="245" fill="#168A5B" rx="4" />
|
||||
<text x="744" y="428" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="19" font-weight="750" fill="#25282D" text-anchor="start">정책·테스트 설치 Clean</text>
|
||||
<text x="744" y="455" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="550" fill="#667085" text-anchor="start">명시 규칙 강제</text>
|
||||
<text x="765" y="503" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="500" fill="#25282D" text-anchor="start">
|
||||
<tspan x="765" dy="0">• Gradle 의존 정책</tspan>
|
||||
<tspan x="765" dy="21.7">• ArchUnit · test-the-test</tspan>
|
||||
</text>
|
||||
<rect x="1060" y="555" width="96.4" height="30" fill="#FFFFFF" stroke="#168A5B" stroke-width="1.2" rx="15" />
|
||||
<text x="1108.2" y="575" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#168A5B" text-anchor="middle">복수 게이트</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.9 KiB |
@@ -1,90 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1460" height="760" viewBox="0 0 1460 760" role="img" aria-labelledby="title desc">
|
||||
<title id="title">System boundary integrations and seams</title>
|
||||
<desc id="desc">Four implemented or configured external paths use solid arrows; three project-supplied extension seams use dashed arrows.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1460" height="760" fill="#FFFFFF"/>
|
||||
<rect x="35" y="45" width="1390" height="670" fill="#FBFCFE" stroke="#AEB5BF" stroke-width="1.3" rx="10" />
|
||||
<rect x="44" y="34" width="219.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="51" y="50" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="start">시스템 경계 · 구현 경로와 확장 seam</text>
|
||||
<rect x="90" y="82" width="313.6" height="30" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.2" rx="15" />
|
||||
<text x="246.8" y="102" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#0B5CC4" text-anchor="middle">실선 = 구현·설정 경로 존재 · 활성 런타임 아님</text>
|
||||
<rect x="780" y="82" width="236.4" height="30" fill="#FFFFFF" stroke="#7556D8" stroke-width="1.2" rx="15" />
|
||||
<text x="898.2" y="102" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#7556D8" text-anchor="middle">점선 = 프로젝트가 공급할 확장 seam</text>
|
||||
<rect x="95" y="170" width="250" height="72" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="220" y="211" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">persistence-jpa</text>
|
||||
<rect x="515" y="178" width="220" height="58" fill="#EAF3FF" />
|
||||
<ellipse cx="625" cy="178" rx="110" ry="10" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.6" />
|
||||
<path d="M 515 178 L 515 236" fill="none" stroke="#1677FF" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 735 178 L 735 236" fill="none" stroke="#1677FF" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 515 236 C 559 248, 691 248, 735 236" fill="none" stroke="#1677FF" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="625" y="209" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="15" font-weight="700" fill="#25282D" text-anchor="middle">PostgreSQL</text>
|
||||
<text x="625" y="230" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="450" fill="#667085" text-anchor="middle">드라이버 · dialect</text>
|
||||
<path d="M 345 206 L 515 206" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="395.5" y="181" width="69" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="430" y="197" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">구현·설정</text>
|
||||
<rect x="95" y="295" width="250" height="72" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="220" y="336" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">persistence-mongo</text>
|
||||
<rect x="515" y="303" width="220" height="58" fill="#EAF3FF" />
|
||||
<ellipse cx="625" cy="303" rx="110" ry="10" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.6" />
|
||||
<path d="M 515 303 L 515 361" fill="none" stroke="#1677FF" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 735 303 L 735 361" fill="none" stroke="#1677FF" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 515 361 C 559 373, 691 373, 735 361" fill="none" stroke="#1677FF" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="625" y="334" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="15" font-weight="700" fill="#25282D" text-anchor="middle">MongoDB</text>
|
||||
<text x="625" y="355" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="450" fill="#667085" text-anchor="middle">opt-in 스캐폴드</text>
|
||||
<path d="M 345 331 L 515 331" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="395.5" y="306" width="69" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="430" y="322" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">구현·설정</text>
|
||||
<rect x="95" y="420" width="250" height="72" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="220" y="461" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">objectstorage</text>
|
||||
<rect x="515" y="420" width="220" height="72" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="625" y="450" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">S3 / MinIO</text>
|
||||
<text x="625" y="475" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">선택형 백엔드</text>
|
||||
<path d="M 345 456 L 515 456" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="395.5" y="431" width="69" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="430" y="447" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">구현·설정</text>
|
||||
<rect x="95" y="545" width="250" height="72" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="220" y="586" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">fileserver</text>
|
||||
<polygon points="535,533 679,533 715,569 715,625 535,625" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.7" />
|
||||
<path d="M 679 533 L 679 569 L 715 569" fill="none" stroke="#1677FF" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="553" y="569" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="15" font-weight="700" fill="#25282D" text-anchor="start">파일시스템</text>
|
||||
<text x="553" y="598" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="500" fill="#667085" text-anchor="start">
|
||||
<tspan x="553" dy="0">구현 경로</tspan>
|
||||
</text>
|
||||
<path d="M 345 581 L 515 581" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="395.5" y="556" width="69" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="430" y="572" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">구현·설정</text>
|
||||
<rect x="820" y="220" width="235" height="75" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="937.5" y="262.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">notification</text>
|
||||
<circle cx="1180" cy="257" r="10" fill="#FFFFFF" stroke="#7556D8" stroke-width="2" />
|
||||
<rect x="1210" y="220" width="170" height="75" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1295" y="262.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">SlackClient seam</text>
|
||||
<path d="M 1055 257 L 1170 257" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-purple)" />
|
||||
<rect x="1093.0" y="232" width="39.1" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1112.5" y="248" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">seam</text>
|
||||
<rect x="820" y="370" width="235" height="75" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="937.5" y="412.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">cache-redis</text>
|
||||
<circle cx="1180" cy="407" r="10" fill="#FFFFFF" stroke="#7556D8" stroke-width="2" />
|
||||
<rect x="1210" y="370" width="170" height="75" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1295" y="412.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">RedisClient seam</text>
|
||||
<path d="M 1055 407 L 1170 407" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-purple)" />
|
||||
<rect x="1093.0" y="382" width="39.1" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1112.5" y="398" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">seam</text>
|
||||
<rect x="820" y="520" width="235" height="75" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="937.5" y="562.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">messaging</text>
|
||||
<circle cx="1180" cy="557" r="10" fill="#FFFFFF" stroke="#7556D8" stroke-width="2" />
|
||||
<rect x="1210" y="520" width="170" height="75" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1295" y="562.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">KafkaSender seam</text>
|
||||
<path d="M 1055 557 L 1170 557" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-purple)" />
|
||||
<rect x="1093.0" y="532" width="39.1" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1112.5" y="548" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">seam</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="530" viewBox="0 0 1440 530" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Package organization spectrum</title>
|
||||
<desc id="desc">A single axis places layer-first and feature-first at its ends and marks ca-tmpl as a hybrid supported by both feature and technical package evidence.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1440" height="530" fill="#FFFFFF"/>
|
||||
<path d="M 100 255 L 1340 255" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<circle cx="120" cy="255" r="9" fill="#FFFFFF" stroke="#667085" stroke-width="2" />
|
||||
<path d="M 120 180 L 120 330" fill="none" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="120" y="160" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="750" fill="#667085" text-anchor="middle">layer-first</text>
|
||||
<text x="120" y="358" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="550" fill="#667085" text-anchor="middle">기술 책임 중심</text>
|
||||
<circle cx="480" cy="255" r="9" fill="#FFFFFF" stroke="#25282D" stroke-width="2" />
|
||||
<path d="M 480 180 L 480 330" fill="none" stroke="#25282D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="480" y="160" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="750" fill="#25282D" text-anchor="middle">계층 소유 코어·어댑터</text>
|
||||
<text x="480" y="358" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="550" fill="#667085" text-anchor="middle">구조 경계</text>
|
||||
<circle cx="860" cy="255" r="13" fill="#1677FF" stroke="#1677FF" stroke-width="2" />
|
||||
<path d="M 860 180 L 860 330" fill="none" stroke="#1677FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="860" y="160" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="15" font-weight="750" fill="#1677FF" text-anchor="middle">ca-tmpl · hybrid</text>
|
||||
<text x="860" y="358" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="550" fill="#667085" text-anchor="middle">현재 저장소 배치</text>
|
||||
<circle cx="1320" cy="255" r="9" fill="#FFFFFF" stroke="#667085" stroke-width="2" />
|
||||
<path d="M 1320 180 L 1320 330" fill="none" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="1320" y="160" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="750" fill="#667085" text-anchor="middle">feature-first</text>
|
||||
<text x="1320" y="358" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="550" fill="#667085" text-anchor="middle">기능 응집 중심</text>
|
||||
<rect x="590" y="380" width="250" height="70" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="715" y="409" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">application.worklog</text>
|
||||
<text x="715" y="434" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">기능 패키지</text>
|
||||
<rect x="880" y="380" width="250" height="70" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1005" y="409" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">web.controller</text>
|
||||
<text x="1005" y="434" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">기술 패키지</text>
|
||||
<path d="M 715 380 L 820 280" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="666.0" y="299" width="108.0" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="720" y="315" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">supports hybrid</text>
|
||||
<path d="M 1005 380 L 900 280" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-purple)" />
|
||||
<rect x="956.0" y="299" width="108.0" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1010" y="315" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">supports hybrid</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.3 KiB |
@@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1420" height="560" viewBox="0 0 1420 560" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Spring Modulith evidence and decision boundary</title>
|
||||
<desc id="desc">A fixed Gradle-script search establishes zero Spring Modulith dependency declarations; adoption remains a separate conditional evaluation.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1420" height="560" fill="#FFFFFF"/>
|
||||
<rect x="45" y="65" width="800" height="420" fill="#FBFCFE" stroke="#1677FF" stroke-width="1.3" rx="10" />
|
||||
<rect x="54" y="54" width="142.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="61" y="70" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="start">확정 가능한 저장소 사실</text>
|
||||
<polygon points="95,155 344,155 380,191 380,345 95,345" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.7" />
|
||||
<path d="M 344 155 L 344 191 L 380 191" fill="none" stroke="#1677FF" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="113" y="191" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="15" font-weight="700" fill="#0B5CC4" text-anchor="start">검색 범위</text>
|
||||
<text x="113" y="220" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="500" fill="#667085" text-anchor="start">
|
||||
<tspan x="113" dy="0">고정된 Gradle</tspan>
|
||||
<tspan x="113" dy="17.4">빌드 스크립트 전체</tspan>
|
||||
</text>
|
||||
<circle cx="605" cy="250" r="92" fill="#EAF3FF" stroke="#1677FF" stroke-width="2" />
|
||||
<text x="605" y="240" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="32" font-weight="800" fill="#0B5CC4" text-anchor="middle">0건</text>
|
||||
<text x="605" y="275" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#0B5CC4" text-anchor="middle">Spring Modulith</text>
|
||||
<text x="605" y="300" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="600" fill="#667085" text-anchor="middle">의존 선언</text>
|
||||
<path d="M 380 250 L 513 250" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="414.4" y="225" width="64.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="446.5" y="241" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">전체 검색</text>
|
||||
<path d="M 915 75 L 915 485" fill="none" stroke="#AEB5BF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 6" />
|
||||
<rect x="880.6" y="88" width="68.8" height="21" fill="#FFFFFF" rx="2" />
|
||||
<text x="915" y="105" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#667085" text-anchor="middle">판단 경계</text>
|
||||
<rect x="985" y="115" width="390" height="320" fill="#FBFCFE" stroke="#7556D8" stroke-width="1.3" rx="10" stroke-dasharray="7 6" />
|
||||
<rect x="994" y="104" width="64.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1001" y="120" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="start">별도 평가</text>
|
||||
<rect x="1040" y="205" width="280" height="100" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1180" y="249" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">채택 여부</text>
|
||||
<text x="1180" y="273" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">
|
||||
<tspan x="1180" dy="0">별도 근거로</tspan>
|
||||
<tspan x="1180" dy="16.2">조건부 평가</tspan>
|
||||
</text>
|
||||
<path d="M 697 250 L 870 250 L 870 365 L 1040 365" fill="none" stroke="#7556D8" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" />
|
||||
<path d="M 901 351 L 929 379" fill="none" stroke="#7556D8" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 929 351 L 901 379" fill="none" stroke="#7556D8" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="1180" y="375" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#667085" text-anchor="middle">0건만으로 채택 결론을 내리지 않음</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.1 KiB |
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1500" height="780" viewBox="0 0 1500 780" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Complementary enforcement scopes</title>
|
||||
<desc id="desc">Five partially overlapping enforcement scopes surround boundary violations without implying a fixed order or speed ranking.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1500" height="780" fill="#FFFFFF"/>
|
||||
<rect x="545" y="25" width="276.5" height="30" fill="#F6F7F9" stroke="#AEB5BF" stroke-width="1.2" rx="15" />
|
||||
<text x="683.2" y="45" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#667085" text-anchor="middle">독립·보완 범위 · 고정 실행 순서 없음</text>
|
||||
<circle cx="750" cy="420" r="105" fill="#FDEDEF" stroke="#D64550" stroke-width="1.8" />
|
||||
<text x="750" y="410" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="20" font-weight="800" fill="#D64550" text-anchor="middle">경계 위반</text>
|
||||
<text x="750" y="440" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="600" fill="#667085" text-anchor="middle">종류별 검출 표면</text>
|
||||
<ellipse cx="470" cy="275" rx="255" ry="150" fill="none" stroke="#1677FF" stroke-width="1.8" />
|
||||
<rect x="443.1" y="131" width="53.9" height="23" fill="#FFFFFF" stroke="#1677FF" stroke-width="1" rx="2" />
|
||||
<text x="470" y="150" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="650" fill="#1677FF" text-anchor="middle">javac</text>
|
||||
<text x="470" y="178" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="600" fill="#667085" text-anchor="middle">클래스패스 범위</text>
|
||||
<ellipse cx="745" cy="225" rx="285" ry="135" fill="none" stroke="#1677FF" stroke-width="1.8" />
|
||||
<rect x="714.1" y="96" width="61.9" height="23" fill="#FFFFFF" stroke="#1677FF" stroke-width="1" rx="2" />
|
||||
<text x="745" y="115" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="650" fill="#1677FF" text-anchor="middle">Gradle</text>
|
||||
<text x="745" y="143" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="600" fill="#667085" text-anchor="middle">project dependency 범위</text>
|
||||
<ellipse cx="950" cy="415" rx="285" ry="145" fill="none" stroke="#1677FF" stroke-width="1.8" />
|
||||
<rect x="1011.1" y="281" width="77.8" height="23" fill="#FFFFFF" stroke="#1677FF" stroke-width="1" rx="2" />
|
||||
<text x="1050" y="300" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="650" fill="#1677FF" text-anchor="middle">ArchUnit</text>
|
||||
<text x="1050" y="328" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="600" fill="#667085" text-anchor="middle">구조 규칙 범위</text>
|
||||
<ellipse cx="730" cy="590" rx="300" ry="120" fill="none" stroke="#1677FF" stroke-width="1.8" />
|
||||
<rect x="671.1" y="671" width="117.7" height="23" fill="#FFFFFF" stroke="#1677FF" stroke-width="1" rx="2" />
|
||||
<text x="730" y="690" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="650" fill="#1677FF" text-anchor="middle">test-the-test</text>
|
||||
<text x="730" y="718" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="600" fill="#667085" text-anchor="middle">비공허성 범위</text>
|
||||
<ellipse cx="320" cy="485" rx="310" ry="145" fill="none" stroke="#7556D8" stroke-width="1.8" />
|
||||
<rect x="193.0" y="351" width="114.0" height="23" fill="#FFFFFF" stroke="#7556D8" stroke-width="1" rx="2" />
|
||||
<text x="250" y="370" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="650" fill="#7556D8" text-anchor="middle">리뷰 · 런타임</text>
|
||||
<text x="250" y="398" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="600" fill="#667085" text-anchor="middle">정적 규칙 밖 범위</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.6 KiB |
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="620" viewBox="0 0 1440 620" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Feed query ports and adapters</title>
|
||||
<desc id="desc">FeedController calls the concrete GetFeedUseCase; the application core owns FeedQueryPort, implemented by FeedQueryAdapter.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1440" height="620" fill="#FFFFFF"/>
|
||||
<polygon points="490,75 880,75 980,310 880,545 490,545 390,310" fill="#EAF3FF" stroke="#1677FF" stroke-width="2.3" />
|
||||
<text x="685" y="125" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#0B5CC4" text-anchor="middle" letter-spacing="0.8">application-core</text>
|
||||
<rect x="540" y="205" width="290" height="120" fill="#FFFFFF" stroke="#0B5CC4" stroke-width="1.8" rx="8" />
|
||||
<text x="685" y="259" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">GetFeedUseCase</text>
|
||||
<text x="685" y="283" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">
|
||||
<tspan x="685" dy="0">concrete service</tspan>
|
||||
<tspan x="685" dy="16.2">implements QueryUseCase<Q,R></tspan>
|
||||
</text>
|
||||
<circle cx="400" cy="310" r="10" fill="#FFFFFF" stroke="#1677FF" stroke-width="2" />
|
||||
<circle cx="970" cy="310" r="10" fill="#FFFFFF" stroke="#1677FF" stroke-width="2" />
|
||||
<text x="410" y="285" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="start">input boundary</text>
|
||||
<text x="960" y="285" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#0B5CC4" text-anchor="end">FeedQueryPort</text>
|
||||
<rect x="55" y="245" width="280" height="105" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="195" y="291.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">FeedController</text>
|
||||
<text x="195" y="316.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">driving adapter</text>
|
||||
<rect x="1060" y="245" width="300" height="105" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="1210" y="291.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">FeedQueryAdapter</text>
|
||||
<text x="1210" y="316.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">driven adapter · implements port</text>
|
||||
<path d="M 335 298 L 390 310" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="309.1" y="264" width="101.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="360" y="280" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Calls concrete</text>
|
||||
<path d="M 830 265 L 925 265 L 925 310 L 960 310" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="839.7" y="232" width="120.6" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="900" y="248" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Calls output port</text>
|
||||
<path d="M 1060 298 L 980 310" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-purple)" />
|
||||
<rect x="986.6" y="264" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1025" y="280" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">Implements</text>
|
||||
<rect x="570" y="385" width="318.5" height="30" fill="#FFFFFF" stroke="#A8BCE0" stroke-width="1.2" rx="15" />
|
||||
<text x="729.2" y="405" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#667085" text-anchor="middle">QueryUseCase<Q,R>는 별도 객체가 아닌 구현 계약</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.0 KiB |
@@ -1,144 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1640" height="1060" viewBox="0 0 1640 1060" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Idempotency execution branches</title>
|
||||
<desc id="desc">One deadline feeds two waiting entry points and four normal decisions; a separate claimed-execution lane shows RuntimeException cleanup outcomes.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1640" height="1060" fill="#FFFFFF"/>
|
||||
<rect x="25" y="35" width="1590" height="625" fill="#FBFCFE" stroke="#AEB5BF" stroke-width="1.3" rx="10" />
|
||||
<rect x="34" y="24" width="299.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="41" y="40" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="start">정상 결정 · 두 대기 진입점과 단일 200ms deadline</text>
|
||||
<rect x="55" y="95" width="260" height="72" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="185" y="136" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">execute(context, action, codec)</text>
|
||||
<rect x="360" y="115" width="178.0" height="30" fill="#FFF7E5" stroke="#B7791F" stroke-width="1.2" rx="15" />
|
||||
<text x="449.0" y="135" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#B7791F" text-anchor="middle">deadline = 시작 + 200ms</text>
|
||||
<rect x="625" y="95" width="210" height="72" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="730" y="136" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">store.find(scope, now)</text>
|
||||
<path d="M 315 131 L 360 131" fill="none" stroke="#B7791F" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-amber)" />
|
||||
<rect x="302.2" y="106" width="70.5" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="337.5" y="122" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#B7791F" text-anchor="middle">초기화 1회</text>
|
||||
<path d="M 560 131 L 625 131" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="566.7" y="106" width="51.6" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="592.5" y="122" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">lookup</text>
|
||||
<polygon points="930,85 1015,131 930,177 845,131" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.6" />
|
||||
<text x="930" y="124.4" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">
|
||||
<tspan x="930" dy="0">record</tspan>
|
||||
<tspan x="930" dy="15">존재?</tspan>
|
||||
</text>
|
||||
<path d="M 835 131 L 845 131" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<polygon points="1170,160 1260,210 1170,260 1080,210" fill="#FFFFFF" stroke="#7556D8" stroke-width="1.6" />
|
||||
<text x="1170" y="203.4" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">
|
||||
<tspan x="1170" dy="0">fingerprint</tspan>
|
||||
<tspan x="1170" dy="15">일치?</tspan>
|
||||
</text>
|
||||
<polygon points="930,270 1020,320 930,370 840,320" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.6" />
|
||||
<text x="930" y="313.4" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">
|
||||
<tspan x="930" dy="0">tryBegin</tspan>
|
||||
<tspan x="930" dy="15">성공?</tspan>
|
||||
</text>
|
||||
<path d="M 1015 145 L 1100 190" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-purple)" />
|
||||
<rect x="1028.6" y="142.5" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1057.5" y="158.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">Present</text>
|
||||
<path d="M 930 177 L 930 270" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="904.2" y="198.5" width="51.6" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="930" y="214.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Absent</text>
|
||||
<rect x="1375" y="120" width="205" height="70" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1477.5" y="149" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">fingerprint-mismatch</text>
|
||||
<text x="1477.5" y="174" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">422</text>
|
||||
<path d="M 1260 190 L 1375 155" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-red)" />
|
||||
<rect x="1285.4" y="147.5" width="64.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1317.5" y="163.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="middle">Mismatch</text>
|
||||
<polygon points="1170,304 1255,350 1170,396 1085,350" fill="#FFFFFF" stroke="#7556D8" stroke-width="1.6" />
|
||||
<text x="1170" y="343.4" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">
|
||||
<tspan x="1170" dy="0">record</tspan>
|
||||
<tspan x="1170" dy="15">status</tspan>
|
||||
</text>
|
||||
<path d="M 1170 260 L 1170 304" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-purple)" />
|
||||
<rect x="1147.3" y="257" width="45.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1170" y="273" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">Match</text>
|
||||
<rect x="1375" y="300" width="205" height="70" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="1477.5" y="329" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">replay-hit</text>
|
||||
<text x="1477.5" y="354" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">action 0회</text>
|
||||
<path d="M 1255 335 L 1375 335" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="1279.8" y="310" width="70.4" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1315" y="326" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">COMPLETED</text>
|
||||
<polygon points="1170,452 1257.5,500 1170,548 1082.5,500" fill="#FFF7E5" stroke="#B7791F" stroke-width="1.6" />
|
||||
<text x="1170" y="493.4" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#B7791F" text-anchor="middle">
|
||||
<tspan x="1170" dy="0">now <</tspan>
|
||||
<tspan x="1170" dy="15">deadline?</tspan>
|
||||
</text>
|
||||
<path d="M 1170 396 L 1170 452" fill="none" stroke="#B7791F" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-amber)" />
|
||||
<rect x="1134.8" y="399" width="70.4" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1170" y="415" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#B7791F" text-anchor="middle">IN_FLIGHT</text>
|
||||
<path d="M 1020 320 L 1082 480" fill="none" stroke="#B7791F" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-amber)" />
|
||||
<rect x="1012.6" y="375" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1051" y="391" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#B7791F" text-anchor="middle">Claim lost</text>
|
||||
<rect x="1375" y="465" width="205" height="70" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1477.5" y="494" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">in-flight</text>
|
||||
<text x="1477.5" y="519" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">409</text>
|
||||
<path d="M 1258 500 L 1375 500" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-red)" />
|
||||
<rect x="1259.3" y="475" width="114.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1316.5" y="491" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="middle">Deadline reached</text>
|
||||
<rect x="1010" y="565" width="210" height="56" fill="#FFF7E5" stroke="#B7791F" stroke-width="1.5" rx="8" />
|
||||
<text x="1115" y="598" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">20ms 대기 후 재조회</text>
|
||||
<path d="M 1170 548 L 1115 565" fill="none" stroke="#B7791F" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-amber)" />
|
||||
<rect x="1088.5" y="531.5" width="108.0" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1142.5" y="547.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#B7791F" text-anchor="middle">Before deadline</text>
|
||||
<path d="M 1010 593 L 740 593 L 740 167" fill="none" stroke="#B7791F" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-ink)" />
|
||||
<rect x="805.4" y="567" width="89.2" height="20" fill="#FBFCFE" rx="2" />
|
||||
<text x="850" y="583" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#B7791F" text-anchor="middle">Retry lookup</text>
|
||||
<rect x="25" y="715" width="1590" height="300" fill="#FBFCFE" stroke="#1677FF" stroke-width="1.3" rx="10" />
|
||||
<rect x="34" y="704" width="305.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="41" y="720" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="start">클레임 후 실행 · RuntimeException과 discard 결과</text>
|
||||
<rect x="70" y="795" width="220" height="76" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="180" y="838" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">action.get()</text>
|
||||
<rect x="330" y="795" width="220" height="76" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="440" y="838" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">codec.serialize(result)</text>
|
||||
<rect x="630" y="795" width="220" height="76" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="740" y="838" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">store.complete(...)</text>
|
||||
<rect x="925" y="795" width="220" height="76" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="1035" y="827" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">new · action 1회</text>
|
||||
<text x="1035" y="852" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">응답 저장</text>
|
||||
<path d="M 290 833 L 330 833" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="281.1" y="808" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="310" y="824" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">Success</text>
|
||||
<path d="M 550 833 L 630 833" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="561.1" y="808" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="590" y="824" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">Success</text>
|
||||
<path d="M 850 833 L 925 833" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="858.6" y="808" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="887.5" y="824" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">Success</text>
|
||||
<path d="M 930 370 L 930 700 L 180 700 L 180 795" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="504.8" y="674" width="70.4" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="540" y="690" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Claim won</text>
|
||||
<rect x="1210" y="760" width="180" height="70" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1300" y="800" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">RuntimeException</text>
|
||||
<rect x="1210" y="900" width="180" height="70" fill="#FFFFFF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1300" y="940" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">store.discard(scope)</text>
|
||||
<path d="M 180 871 L 180 935 L 1190 935 L 1190 795 L 1210 795" fill="none" stroke="#D64550" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" />
|
||||
<path d="M 440 871 L 440 935 L 1190 935 L 1190 795 L 1210 795" fill="none" stroke="#D64550" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" />
|
||||
<path d="M 740 871 L 740 935 L 1190 935 L 1190 795 L 1210 795" fill="none" stroke="#D64550" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" />
|
||||
<path d="M 1300 830 L 1300 900" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-red)" />
|
||||
<rect x="1271.1" y="840" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1300" y="856" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="middle">cleanup</text>
|
||||
<rect x="1430" y="835" width="170" height="65" fill="#FFFFFF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1515" y="861.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">원래 예외 재전파</text>
|
||||
<text x="1515" y="886.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">discard 성공</text>
|
||||
<rect x="1430" y="930" width="170" height="65" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1515" y="956.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="700" fill="#25282D" text-anchor="middle">discard 예외 대체 가능</text>
|
||||
<text x="1515" y="981.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">정리 불확실</text>
|
||||
<path d="M 1390 930 L 1430 867" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-red)" />
|
||||
<rect x="1381.1" y="873.5" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1410" y="889.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="middle">returns</text>
|
||||
<path d="M 1390 950 L 1430 962" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-red)" />
|
||||
<rect x="1384.2" y="931" width="51.6" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1410" y="947" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="middle">throws</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 19 KiB |
@@ -1,108 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="978" height="673" viewBox="0 0 978 673" role="img" aria-labelledby="diagram-title diagram-description">
|
||||
<title id="diagram-title">전송 타입은 inbound adapter에서 Command·Query로 수렴한다</title>
|
||||
<desc id="diagram-description">왼쪽에서 오른쪽으로 읽는다. web은 HTTP DTO, grpc는 protobuf message, graphql은 GraphQL request, websocket은 WebSocket message를 각 어댑터 경계에서 처리한다. 네 어댑터는 전송 기술 타입을 application-core로 넘기지 않고 Command 또는 Query로 변환한다. 변환된 입력만 Application use case를 호출한다.</desc>
|
||||
<metadata>{"techviz":{"spec_version":"1.1","id":"inbound-transport-boundary","profile":"component-flow"},"source_context":{"document":"/home/donghyeon/workspace/ai-tool/topic-arrange/executable-clean-architecture/claridoc-rewrite/document.md","document_sha256":"04fbab095d33d301746c34f7cca305730919bad3c341bcf63b8ad3ee3b396d31","anchor":{"kind":"marker","value":"inbound-transport-boundary","line":336}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
</marker>
|
||||
<style>
|
||||
:root { color-scheme: light; }
|
||||
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
|
||||
.canvas { fill: #ffffff; }
|
||||
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
|
||||
.group-label-bg { fill: #ffffff; }
|
||||
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
|
||||
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
|
||||
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
|
||||
.edge.style-dotted { stroke-dasharray: 2 5; }
|
||||
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
|
||||
.edge.emphasis-muted { stroke: #9ca3af; }
|
||||
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
|
||||
.edge-label-bg { fill: #ffffff; }
|
||||
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
|
||||
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
|
||||
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
|
||||
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
|
||||
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
|
||||
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
|
||||
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
|
||||
.node-shape.assumption { stroke-dasharray: 4 4; }
|
||||
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
|
||||
.controller-led { fill: #4b5563; }
|
||||
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
|
||||
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
|
||||
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
|
||||
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
|
||||
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
|
||||
.node-detail { font-size: 11px; fill: #374151; }
|
||||
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
|
||||
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
|
||||
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
|
||||
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
|
||||
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
|
||||
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
|
||||
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
|
||||
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
|
||||
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
|
||||
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="canvas" width="978" height="673" />
|
||||
<polyline class="edge kind-request style-solid emphasis-primary" points="599.0,344.0 679.0,344.0 679.0,344.0 759.0,344.0" data-evidence="322-328" />
|
||||
<rect class="edge-label-bg" x="643.2" y="302.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="679.0" y="317.0">유스케이스 호출</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="253.0,424.0 349.5,424.0 349.5,351.3 446.0,351.3" data-evidence="322-327" />
|
||||
<rect class="edge-label-bg" x="341.1" y="373.7" width="64.9" height="22" rx="3" />
|
||||
<text class="edge-label" x="373.5" y="388.7">GraphQL</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="253.0,264.0 349.5,264.0 349.5,336.7 446.0,336.7" data-evidence="322-327" />
|
||||
<rect class="edge-label-bg" x="337.7" y="286.3" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="373.5" y="301.3">Protobuf</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="286.0,104.0 366.0,104.0 366.0,322.0 446.0,322.0" data-evidence="322-327" />
|
||||
<rect class="edge-label-bg" x="354.2" y="199.0" width="71.6" height="22" rx="3" />
|
||||
<text class="edge-label" x="390.0" y="214.0">HTTP DTO</text>
|
||||
<polyline class="edge kind-data style-solid emphasis-normal" points="254.5,584.0 350.2,584.0 350.2,366.0 446.0,366.0" data-evidence="322-328" />
|
||||
<rect class="edge-label-bg" x="335.1" y="461.0" width="78.3" height="22" rx="3" />
|
||||
<text class="edge-label" x="374.2" y="476.0">WebSocket</text>
|
||||
<g id="node-web">
|
||||
<rect class="node-shape kind-adapter emphasis-normal role-source" data-evidence="315-317" x="70.0" y="60.0" width="216.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="178.0" y="87.0">web</text>
|
||||
<line class="node-detail-divider" x1="84.0" y1="108.0" x2="272.0" y2="108.0" />
|
||||
<text class="node-detail" x="86.0" y="125.0">HTTP · JSON DTO</text>
|
||||
<text class="node-detail" x="86.0" y="141.0">validation · auth · errors</text>
|
||||
</g>
|
||||
<g id="node-grpc">
|
||||
<rect class="node-shape kind-adapter emphasis-normal role-source" data-evidence="315-318" x="103.0" y="220.0" width="150.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="178.0" y="247.0">grpc</text>
|
||||
<line class="node-detail-divider" x1="117.0" y1="268.0" x2="239.0" y2="268.0" />
|
||||
<text class="node-detail" x="119.0" y="285.0">Protobuf message</text>
|
||||
<text class="node-detail" x="119.0" y="301.0">server lifecycle</text>
|
||||
</g>
|
||||
<g id="node-graphql">
|
||||
<rect class="node-shape kind-adapter emphasis-normal role-source" data-evidence="315-319" x="103.0" y="380.0" width="150.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="178.0" y="407.0">graphql</text>
|
||||
<line class="node-detail-divider" x1="117.0" y1="428.0" x2="239.0" y2="428.0" />
|
||||
<text class="node-detail" x="119.0" y="445.0">GraphQL request</text>
|
||||
<text class="node-detail" x="119.0" y="461.0">query · mutation</text>
|
||||
</g>
|
||||
<g id="node-websocket">
|
||||
<rect class="node-shape kind-adapter emphasis-normal role-source" data-evidence="315-320" x="101.5" y="540.0" width="153.0" height="88.0" rx="7" />
|
||||
<text class="node-label" x="178.0" y="567.0">websocket</text>
|
||||
<line class="node-detail-divider" x1="115.5" y1="588.0" x2="240.5" y2="588.0" />
|
||||
<text class="node-detail" x="117.5" y="605.0">WebSocket message</text>
|
||||
<text class="node-detail" x="117.5" y="621.0">STOMP · realtime</text>
|
||||
</g>
|
||||
<g id="node-command-query">
|
||||
<rect class="node-shape kind-interface emphasis-primary role-service" data-evidence="322-328" x="446.0" y="308.5" width="153.0" height="71.0" rx="22" />
|
||||
<text class="node-label" x="522.5" y="335.5">Command / Query</text>
|
||||
<line class="node-detail-divider" x1="460.0" y1="356.5" x2="585.0" y2="356.5" />
|
||||
<text class="node-detail" x="462.0" y="373.5">application input</text>
|
||||
</g>
|
||||
<g id="node-use-case">
|
||||
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="322-328" x="759.0" y="308.5" width="174.0" height="71.0" rx="7" />
|
||||
<text class="node-label" x="846.0" y="335.5">Application use case</text>
|
||||
<line class="node-detail-divider" x1="773.0" y1="356.5" x2="919.0" y2="356.5" />
|
||||
<text class="node-detail" x="775.0" y="373.5">transport type 없음</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 8.5 KiB |
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1480" height="720" viewBox="0 0 1480 720" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Lock timeout routing gap</title>
|
||||
<desc id="desc">The adapter contract declares a lock timeout and a classifier maps its code to HTTP 409, but zero production callers and zero dedicated web handlers leave that route disconnected.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1480" height="720" fill="#FFFFFF"/>
|
||||
<rect x="40" y="45" width="930" height="300" fill="#FBFCFE" stroke="#AEB5BF" stroke-width="1.3" rx="10" />
|
||||
<rect x="49" y="34" width="131.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="56" y="50" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="start">계약과 분류 · 존재함</text>
|
||||
<rect x="80" y="145" width="250" height="90" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="205" y="184" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">DistributedLockPort</text>
|
||||
<text x="205" y="209" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">adapter 계약</text>
|
||||
<rect x="385" y="145" width="260" height="90" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="515" y="195" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">LockAcquisitionTimeoutException</text>
|
||||
<rect x="700" y="145" width="230" height="90" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="815" y="195" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">CONCURRENCY_LOCK_TIMEOUT</text>
|
||||
<path d="M 330 190 L 385 190" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="300.3" y="165" width="114.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="357.5" y="181" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">Adapter contract</text>
|
||||
<path d="M 645 190 L 700 190" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="640.4" y="165" width="64.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="672.5" y="181" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Declares</text>
|
||||
<rect x="1015" y="45" width="420" height="300" fill="#FBFCFE" stroke="#AEB5BF" stroke-width="1.3" rx="10" />
|
||||
<rect x="1024" y="34" width="67.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1031" y="50" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="start">HTTP 분류</text>
|
||||
<rect x="1060" y="145" width="330" height="90" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="1225" y="184" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">LOCK_TIMEOUT → HTTP 409</text>
|
||||
<text x="1225" y="209" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">분류 계약</text>
|
||||
<path d="M 930 190 L 1060 190" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="956.6" y="165" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="995" y="181" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Classifies</text>
|
||||
<rect x="110" y="430" width="320" height="100" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="270" y="474" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">프로덕션 애플리케이션/유스케이스</text>
|
||||
<text x="270" y="499" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">호출자 0</text>
|
||||
<rect x="560" y="430" width="270" height="100" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="695" y="474" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">전용 웹 핸들러</text>
|
||||
<text x="695" y="499" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">0</text>
|
||||
<rect x="1040" y="430" width="340" height="100" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1210" y="474" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">현재 HTTP 409</text>
|
||||
<text x="1210" y="499" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">보장 없음</text>
|
||||
<path d="M 430 480 L 500 480 L 500 275 L 515 275 L 515 235" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" />
|
||||
<path d="M 482 337 L 518 373" fill="none" stroke="#D64550" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 518 337 L 482 373" fill="none" stroke="#D64550" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<rect x="450.2" y="308" width="99.7" height="21" fill="#FFFFFF" rx="2" />
|
||||
<text x="500" y="325" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#D64550" text-anchor="middle">현재 연결 없음</text>
|
||||
<path d="M 830 480 L 940 480 L 940 275 L 1225 275 L 1225 235" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" />
|
||||
<path d="M 922 387 L 958 423" fill="none" stroke="#D64550" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 958 387 L 922 423" fill="none" stroke="#D64550" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<rect x="898.8" y="358" width="82.4" height="21" fill="#FFFFFF" rx="2" />
|
||||
<text x="940" y="375" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#D64550" text-anchor="middle">Not routed</text>
|
||||
<rect x="560" y="600" width="360" height="70" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="740" y="629" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">GlobalExceptionHandler</text>
|
||||
<text x="740" y="654" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">Exception fallback</text>
|
||||
<path d="M 920 635 L 1040 500" fill="none" stroke="#667085" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-muted)" />
|
||||
<rect x="949.7" y="574" width="120.6" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1010" y="590" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="middle">Fallback response</text>
|
||||
<path d="M 972 462 L 1008 498" fill="none" stroke="#D64550" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 1008 462 L 972 498" fill="none" stroke="#D64550" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.6 KiB |
@@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="760" viewBox="0 0 1440 760" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Logical ownership rings</title>
|
||||
<desc id="desc">Project modules depend inward while adapter-owned framework surfaces and bootstrap-owned composition surfaces remain distinct.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1440" height="760" fill="#FFFFFF"/>
|
||||
<rect x="70" y="55" width="1300" height="640" fill="#FBFCFE" stroke="#7E8792" stroke-width="1.5" rx="18" />
|
||||
<rect x="98" y="45" width="123.7" height="21" fill="#FFFFFF" rx="2" />
|
||||
<text x="105" y="62" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#667085" text-anchor="start">프로젝트 소유 표면</text>
|
||||
<rect x="265" y="145" width="910" height="475" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.8" rx="18" />
|
||||
<rect x="293" y="135" width="99.7" height="21" fill="#FFFFFF" rx="2" />
|
||||
<text x="300" y="152" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#0B5CC4" text-anchor="start">안쪽 모듈 경계</text>
|
||||
<rect x="460" y="250" width="520" height="270" fill="#EAF3FF" stroke="#0B5CC4" stroke-width="2" rx="18" />
|
||||
<rect x="565" y="300" width="310" height="82" fill="#FFFFFF" stroke="#0B5CC4" stroke-width="1.5" rx="8" />
|
||||
<text x="720" y="335" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">application-core</text>
|
||||
<text x="720" y="360" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">Spring DI · SLF4J</text>
|
||||
<rect x="565" y="420" width="310" height="68" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="720" y="448" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">domain-core</text>
|
||||
<text x="720" y="473" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">main 외부 의존 0</text>
|
||||
<rect x="95" y="210" width="280" height="105" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="235" y="256.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">web adapter</text>
|
||||
<text x="235" y="280.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">
|
||||
<tspan x="235" dy="0">MVC · Security</tspan>
|
||||
<tspan x="235" dy="16.2">Validation</tspan>
|
||||
</text>
|
||||
<rect x="95" y="410" width="280" height="105" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="235" y="456.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">persistence adapter</text>
|
||||
<text x="235" y="480.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">
|
||||
<tspan x="235" dy="0">JPA · PostgreSQL</tspan>
|
||||
<tspan x="235" dy="16.2">DB 구체 의존</tspan>
|
||||
</text>
|
||||
<rect x="1065" y="280" width="250" height="150" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="1190" y="349" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">app-bootstrap</text>
|
||||
<text x="1190" y="373" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">
|
||||
<tspan x="1190" dy="0">Boot · Flyway · 관측</tspan>
|
||||
<tspan x="1190" dy="16.2">Security 조립</tspan>
|
||||
</text>
|
||||
<path d="M 375 262 L 480 262 L 480 325 L 565 325" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="406.6" y="234" width="126.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="470" y="250" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Project dependency</text>
|
||||
<path d="M 375 462 L 500 462 L 500 360 L 565 360" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="414.6" y="434" width="126.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="478" y="450" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Project dependency</text>
|
||||
<path d="M 1065 355 L 950 355 L 950 340 L 875 340" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="906.6" y="314" width="126.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="970" y="330" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Project dependency</text>
|
||||
<path d="M 720 382 L 720 420" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="656.6" y="376" width="126.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="720" y="392" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">Project dependency</text>
|
||||
<rect x="590" y="555" width="127.7" height="30" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.2" rx="15" />
|
||||
<text x="653.9" y="575" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#168A5B" text-anchor="middle">DOMAIN_IS_PURE</text>
|
||||
<path d="M 720 555 L 720 488" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" marker-end="url(#arrow-green)" />
|
||||
<rect x="751.0" y="521" width="108.0" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="805" y="537" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">Enforces purity</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.9 KiB |
@@ -1,121 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="960" viewBox="0 0 1600 960" role="img" aria-labelledby="title desc">
|
||||
<title id="title">MDC request and asynchronous lifecycle</title>
|
||||
<desc id="desc">The primary request lifecycle, configured task decorator propagation, cleanup failure window, and unsupported executor path are separated.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1600" height="960" fill="#FFFFFF"/>
|
||||
<rect x="35" y="40" width="1530" height="320" fill="#FBFCFE" stroke="#AEB5BF" stroke-width="1.3" rx="10" />
|
||||
<rect x="44" y="29" width="149.1" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="51" y="45" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="start">요청 스레드 · 정리 전제</text>
|
||||
<rect x="70" y="145" width="185" height="78" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="162.5" y="178" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">요청 헤더</text>
|
||||
<text x="162.5" y="203" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">traceparent</text>
|
||||
<rect x="300" y="145" width="185" height="78" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="392.5" y="189" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">살균·채택·생성</text>
|
||||
<rect x="540" y="145" width="185" height="78" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="632.5" y="178" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">요청 MDC</text>
|
||||
<text x="632.5" y="203" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">5키 put</text>
|
||||
<rect x="780" y="145" width="185" height="78" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="872.5" y="189" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">요청 처리</text>
|
||||
<rect x="1020" y="145" width="185" height="78" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="1112.5" y="178" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">사용자 가명화</text>
|
||||
<text x="1112.5" y="203" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">MDC에는 가명만</text>
|
||||
<rect x="1260" y="145" width="185" height="78" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="1352.5" y="189" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">http_request log</text>
|
||||
<rect x="1460" y="145" width="120" height="78" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="1520" y="178" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">5키 remove</text>
|
||||
<text x="1520" y="203" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">앞 단계 완료 시</text>
|
||||
<path d="M 255 184 L 300 184" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<path d="M 485 184 L 540 184" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<path d="M 725 184 L 780 184" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<path d="M 965 184 L 1020 184" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<path d="M 1205 184 L 1260 184" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<path d="M 1445 184 L 1460 184" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="179.8" y="154" width="70.4" height="20" fill="#FBFCFE" rx="2" />
|
||||
<text x="215" y="170" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="middle">Normalize</text>
|
||||
<rect x="488.6" y="154" width="32.8" height="20" fill="#FBFCFE" rx="2" />
|
||||
<text x="505" y="170" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="middle">Put</text>
|
||||
<rect x="901.1" y="154" width="57.9" height="20" fill="#FBFCFE" rx="2" />
|
||||
<text x="930" y="170" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="middle">finally</text>
|
||||
<rect x="545" y="265" width="250" height="58" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="670" y="299" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">응답 헤더 · Envelope meta</text>
|
||||
<rect x="830" y="265" width="250" height="58" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="955" y="288" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">OutboundCorrelation</text>
|
||||
<text x="955" y="313" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">같은 스레드에서 read</text>
|
||||
<rect x="1110" y="279" width="130.1" height="30" fill="#FFF7E5" stroke="#B7791F" stroke-width="1.2" rx="15" />
|
||||
<text x="1175.0" y="299" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#B7791F" text-anchor="middle">MDC 비면 UNKNOWN</text>
|
||||
<path d="M 632 223 L 670 265" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="596.1" y="238" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="625" y="254" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Project</text>
|
||||
<path d="M 725 223 L 930 265" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="805.5" y="232" width="39.1" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="825" y="248" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Read</text>
|
||||
<path d="M 1080 294 L 1110 294" fill="none" stroke="#B7791F" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-amber)" />
|
||||
<rect x="1062.9" y="269" width="64.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1095" y="285" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#B7791F" text-anchor="middle">Fallback</text>
|
||||
<rect x="1010" y="385" width="515" height="92" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="10" />
|
||||
<text x="1035" y="420" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="800" fill="#D64550" text-anchor="start">정리 실패 창</text>
|
||||
<text x="1035" y="450" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="650" fill="#25282D" text-anchor="start">가명화 또는 log 실패 시 5키 제거 보장 없음</text>
|
||||
<path d="M 1110 223 L 1110 385" fill="none" stroke="#D64550" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" />
|
||||
<path d="M 1340 223 L 1340 385" fill="none" stroke="#D64550" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" />
|
||||
<rect x="35" y="530" width="1110" height="370" fill="#FBFCFE" stroke="#1677FF" stroke-width="1.3" rx="10" />
|
||||
<rect x="44" y="519" width="282.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="51" y="535" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="start">구성된 비동기 경계 · applicationTaskExecutor</text>
|
||||
<rect x="55" y="625" width="230" height="75" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="170" y="656.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">applicationTaskExecutor</text>
|
||||
<text x="170" y="681.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">configured</text>
|
||||
<rect x="315" y="625" width="210" height="75" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="420" y="667.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">AsyncContextTaskDecorator</text>
|
||||
<rect x="580" y="575" width="210" height="75" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="685" y="606.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">caller MDC 캡처</text>
|
||||
<text x="685" y="631.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">제출 시</text>
|
||||
<rect x="580" y="705" width="210" height="75" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="685" y="736.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">worker 이전 MDC</text>
|
||||
<text x="685" y="761.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">보관</text>
|
||||
<rect x="850" y="625" width="220" height="75" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="960" y="656.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">task 동안 caller MDC</text>
|
||||
<text x="960" y="681.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">설치</text>
|
||||
<path d="M 285 662 L 315 662" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="246.0" y="634" width="108.0" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="300" y="650" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">Configured with</text>
|
||||
<path d="M 525 650 L 580 612" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="523.6" y="606" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="552.5" y="622" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Capture</text>
|
||||
<path d="M 525 675 L 580 742" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="533.0" y="683.5" width="39.1" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="552.5" y="699.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Save</text>
|
||||
<path d="M 790 612 L 850 650" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="791.1" y="606" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="820" y="622" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Install</text>
|
||||
<path d="M 790 742 L 850 688" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" marker-end="url(#arrow-blue)" />
|
||||
<rect x="787.9" y="690" width="64.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="820" y="706" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Preserve</text>
|
||||
<rect x="850" y="790" width="220" height="70" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="960" y="819" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">이전 worker MDC 복원</text>
|
||||
<text x="960" y="844" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">finally</text>
|
||||
<path d="M 960 700 L 960 790" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" marker-end="url(#arrow-green)" />
|
||||
<rect x="931.1" y="720" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="960" y="736" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">Restore</text>
|
||||
<path d="M 632 223 L 632 500 L 170 500 L 170 625" fill="none" stroke="#1677FF" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" />
|
||||
<rect x="286.4" y="477" width="227.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="400" y="493" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="middle">Submit through configured executor</text>
|
||||
<rect x="1190" y="530" width="375" height="370" fill="#FBFCFE" stroke="#D64550" stroke-width="1.3" rx="10" stroke-dasharray="7 6" />
|
||||
<rect x="1199" y="519" width="120.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1206" y="535" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="start">구성 밖 비동기 경계</text>
|
||||
<rect x="1230" y="640" width="295" height="80" fill="#FFFFFF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1377.5" y="685" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">원시 스레드 · 다른 executor</text>
|
||||
<rect x="1230" y="790" width="295" height="70" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1377.5" y="830" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">MDC 자동 복사 없음</text>
|
||||
<path d="M 1377 720 L 1377 790" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-red)" />
|
||||
<rect x="1313.6" y="730" width="126.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1377" y="746" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="middle">Does not auto-copy</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 18 KiB |
@@ -1,84 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1480" height="780" viewBox="0 0 1480 780" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Measured module policy excerpt</title>
|
||||
<desc id="desc">Five centered source rows point to allowed targets on each side, exposing asymmetric access to domain-core, shared-contract, and support.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1480" height="780" fill="#FFFFFF"/>
|
||||
<rect x="480" y="25" width="296.9" height="30" fill="#F6F7F9" stroke="#AEB5BF" stroke-width="1.2" rx="15" />
|
||||
<text x="628.4" y="45" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#667085" text-anchor="middle">정책 비대칭 선택 발췌 · 전체 그래프 아님</text>
|
||||
<rect x="55" y="120" width="270" height="68" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="190" y="159" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">domain-core</text>
|
||||
<rect x="475" y="112" width="530" height="84" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="740" y="148" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">support · 공유 기반</text>
|
||||
<text x="740" y="173" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">source</text>
|
||||
<rect x="1155" y="120" width="270" height="68" fill="#FFFFFF" stroke="#68717B" stroke-width="1.5" rx="8" />
|
||||
<text x="1290" y="159" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">application-core</text>
|
||||
<path d="M 475 154 L 325 154" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="371.1" y="129" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="400" y="145" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<path d="M 1005 154 L 1155 154" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="1051.1" y="129" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1080" y="145" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<rect x="55" y="245" width="270" height="68" fill="#FFFFFF" stroke="#68717B" stroke-width="1.5" rx="8" />
|
||||
<text x="190" y="284" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">application-core</text>
|
||||
<rect x="475" y="237" width="530" height="84" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="740" y="273" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">messaging · cache · notification · httpclient</text>
|
||||
<text x="740" y="298" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">source</text>
|
||||
<rect x="1155" y="245" width="270" height="68" fill="#FFFFFF" stroke="#68717B" stroke-width="1.5" rx="8" />
|
||||
<text x="1290" y="284" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">support</text>
|
||||
<path d="M 475 279 L 325 279" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="371.1" y="254" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="400" y="270" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<path d="M 1005 279 L 1155 279" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="1051.1" y="254" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1080" y="270" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<rect x="55" y="370" width="270" height="68" fill="#FFFFFF" stroke="#68717B" stroke-width="1.5" rx="8" />
|
||||
<text x="190" y="409" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">application-core</text>
|
||||
<rect x="475" y="362" width="530" height="84" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="740" y="398" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">objectstorage · fileserver · persistence-mongo</text>
|
||||
<text x="740" y="423" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">source</text>
|
||||
<rect x="1155" y="370" width="270" height="68" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1290" y="409" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">shared-contract</text>
|
||||
<path d="M 475 404 L 325 404" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="371.1" y="379" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="400" y="395" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<path d="M 1005 404 L 1155 404" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="1051.1" y="379" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1080" y="395" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<rect x="55" y="495" width="270" height="68" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="190" y="534" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">domain-core</text>
|
||||
<rect x="475" y="487" width="530" height="84" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="740" y="523" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">identifier · support 없음</text>
|
||||
<text x="740" y="548" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">source</text>
|
||||
<rect x="1155" y="495" width="270" height="68" fill="#FFFFFF" stroke="#68717B" stroke-width="1.5" rx="8" />
|
||||
<text x="1290" y="534" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">application-core</text>
|
||||
<path d="M 475 529 L 325 529" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="371.1" y="504" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="400" y="520" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<path d="M 1005 529 L 1155 529" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="1051.1" y="504" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1080" y="520" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<rect x="55" y="620" width="270" height="68" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="190" y="659" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">domain-core</text>
|
||||
<rect x="475" y="612" width="530" height="84" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="740" y="648" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">application-core</text>
|
||||
<text x="740" y="673" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">source</text>
|
||||
<rect x="1155" y="620" width="270" height="68" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1290" y="659" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="14" font-weight="700" fill="#25282D" text-anchor="middle">shared-contract</text>
|
||||
<path d="M 475 654 L 325 654" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="371.1" y="629" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="400" y="645" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<path d="M 1005 654 L 1155 654" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="1051.1" y="629" width="57.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1080" y="645" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Allowed</text>
|
||||
<text x="740" y="755" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="550" fill="#667085" text-anchor="middle">각 행의 가운데 source → 양쪽 allowed target · 간선 교차 없음</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,52 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1420" height="670" viewBox="0 0 1420 670" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Multi-module versus single-module enforcement</title>
|
||||
<desc id="desc">Two parallel panels contrast isolated compile classpaths with one shared classpath and an ArchUnit-only boundary.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1420" height="670" fill="#FFFFFF"/>
|
||||
<rect x="40" y="55" width="650" height="535" fill="#FBFCFE" stroke="#1677FF" stroke-width="1.3" rx="10" />
|
||||
<rect x="49" y="44" width="164.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="56" y="60" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="start">멀티모듈 · 분리 클래스패스</text>
|
||||
<rect x="95" y="135" width="220" height="85" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="205" y="171.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">domain-core</text>
|
||||
<text x="205" y="196.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">Spring/JPA 타입 없음</text>
|
||||
<rect x="410" y="135" width="220" height="85" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="520" y="171.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">adapter</text>
|
||||
<text x="520" y="196.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">Spring/JPA 소유</text>
|
||||
<rect x="80" y="285" width="565" height="80" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="362" y="318" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#D64550" text-anchor="middle">금지 import</text>
|
||||
<text x="362" y="346" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#0B5CC4" text-anchor="middle">타입이 classpath에 없어 javac 실패</text>
|
||||
<path d="M 315 178 L 410 178" fill="none" stroke="#D64550" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 5" />
|
||||
<path d="M 346 162 L 378 194" fill="none" stroke="#D64550" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M 378 162 L 346 194" fill="none" stroke="#D64550" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<rect x="220" y="430" width="96.4" height="30" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.2" rx="15" />
|
||||
<text x="268.2" y="450" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#168A5B" text-anchor="middle">독립 컴파일</text>
|
||||
<text x="362" y="505" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="650" fill="#667085" text-anchor="middle">§26 테스트 독립성의 뿌리</text>
|
||||
<rect x="730" y="55" width="650" height="535" fill="#FBFCFE" stroke="#7556D8" stroke-width="1.3" rx="10" />
|
||||
<rect x="739" y="44" width="164.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="746" y="60" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="start">단일모듈 · 공유 클래스패스</text>
|
||||
<rect x="780" y="125" width="550" height="170" fill="#FFFFFF" stroke="#737B85" stroke-width="1.5" rx="10" />
|
||||
<rect x="820" y="175" width="200" height="70" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="920" y="215" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">domain package</text>
|
||||
<rect x="1090" y="175" width="200" height="70" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="1190" y="215" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">adapter package</text>
|
||||
<text x="1055" y="150" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#7556D8" text-anchor="middle">하나의 compile classpath</text>
|
||||
<path d="M 1020 210 L 1090 210" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-purple)" />
|
||||
<rect x="982.1" y="185" width="145.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1055" y="201" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">Spring import도 컴파일</text>
|
||||
<rect x="865" y="360" width="380" height="90" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1055" y="399" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">ArchUnit</text>
|
||||
<text x="1055" y="424" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">실행 전까지 위반 코드가 존재</text>
|
||||
<text x="1055" y="520" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="650" fill="#667085" text-anchor="middle">방어선이 테스트 실행 시점으로 늦어짐</text>
|
||||
<rect x="500" y="615" width="311.9" height="30" fill="#F6F7F9" stroke="#AEB5BF" stroke-width="1.2" rx="15" />
|
||||
<text x="655.9" y="635" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#667085" text-anchor="middle">두 패널은 진행 단계가 아니라 강제력의 병렬 비교</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.5 KiB |
@@ -1,60 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1450" height="730" viewBox="0 0 1450 730" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Outbox state machine</title>
|
||||
<desc id="desc">Pending is claimed into in-flight, which can publish, fail for retry, become dead, or be reclaimed after visibility timeout.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1450" height="730" fill="#FFFFFF"/>
|
||||
<circle cx="55" cy="345" r="10" fill="#25282D" />
|
||||
<rect x="125" y="300" width="190" height="90" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="220" y="339" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">PENDING</text>
|
||||
<text x="220" y="364" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">append 결과</text>
|
||||
<rect x="500" y="290" width="220" height="110" fill="#FFF7E5" stroke="#B7791F" stroke-width="1.5" rx="8" />
|
||||
<text x="610" y="339" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">IN_FLIGHT</text>
|
||||
<text x="610" y="364" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">선점됨</text>
|
||||
<rect x="970" y="95" width="210" height="90" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="1075" y="134" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">PUBLISHED</text>
|
||||
<text x="1075" y="159" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">종착 상태</text>
|
||||
<rect x="970" y="300" width="210" height="90" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1075" y="339" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">FAILED</text>
|
||||
<text x="1075" y="364" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">재시도 가능</text>
|
||||
<rect x="970" y="515" width="210" height="90" fill="#F6F7F9" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="1075" y="554" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">DEAD</text>
|
||||
<text x="1075" y="579" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">종착 · FIFO 차단</text>
|
||||
<rect x="1290" y="95" width="120" height="90" fill="#FFFFFF" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="1350" y="134" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">삭제</text>
|
||||
<text x="1350" y="159" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">보존기간 후</text>
|
||||
<path d="M 65 345 L 125 345" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="69.2" y="320" width="51.6" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="95" y="336" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">append</text>
|
||||
<path d="M 315 345 L 500 345" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="369.1" y="320" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="407.5" y="336" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">claimBatch</text>
|
||||
<path d="M 720 320 L 850 320 L 850 140 L 970 140" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="806.9" y="109" width="86.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="850" y="125" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">publish 성공</text>
|
||||
<path d="M 720 345 L 970 345" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-red)" />
|
||||
<rect x="765.1" y="320" width="159.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="845" y="336" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="middle">실패 · attemptCount < 3</text>
|
||||
<path d="M 720 370 L 850 370 L 850 560 L 970 560" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="753.6" y="529" width="192.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="850" y="545" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">attemptCount ≥ 3 · markDead</text>
|
||||
<path d="M 1180 140 L 1290 140" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="1206" y="115" width="58" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1235" y="131" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">보존기간</text>
|
||||
<path d="M 970 375 L 820 450 L 610 450 L 610 400" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-red)" />
|
||||
<rect x="685.4" y="425" width="209.1" height="21" fill="#FFFFFF" rx="2" />
|
||||
<text x="790" y="442" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#D64550" text-anchor="middle">next_attempt_at 경과 후 재선점</text>
|
||||
<path d="M 560 290 C 430 170, 760 140, 680 290" fill="none" stroke="#B7791F" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-ink)" />
|
||||
<rect x="523.3" y="158" width="173.4" height="21" fill="#FFFFFF" rx="2" />
|
||||
<text x="610" y="175" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#B7791F" text-anchor="middle">가시성 제한 시간 후 재선점</text>
|
||||
<text x="1075" y="650" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#667085" text-anchor="middle">DEAD에는 자동 후속 전이 없음</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 8.6 KiB |
@@ -1,75 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1500" height="810" viewBox="0 0 1500 810" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Outbox write and relay paths</title>
|
||||
<desc id="desc">The atomic write path and the post-commit relay path are separated; a configured five-second poll connects the pending row to claimBatch.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1500" height="810" fill="#FFFFFF"/>
|
||||
<rect x="40" y="45" width="1420" height="260" fill="#FBFCFE" stroke="#1677FF" stroke-width="1.3" rx="10" />
|
||||
<rect x="49" y="34" width="188.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="56" y="50" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#0B5CC4" text-anchor="start">원자적 쓰기 경로 · 트랜잭션 안</text>
|
||||
<rect x="95" y="125" width="230" height="78" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="210" y="169" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">비즈니스 유스케이스</text>
|
||||
<rect x="475" y="105" width="340" height="118" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="645" y="158" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">tx.inWrite</text>
|
||||
<text x="645" y="183" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">도메인 쓰기 + append</text>
|
||||
<polygon points="1050,95 1284,95 1320,131 1320,235 1050,235" fill="#FFFFFF" stroke="#1677FF" stroke-width="1.7" />
|
||||
<path d="M 1284 95 L 1284 131 L 1320 131" fill="none" stroke="#1677FF" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<text x="1068" y="131" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="15" font-weight="700" fill="#0B5CC4" text-anchor="start">outbox_event</text>
|
||||
<text x="1068" y="160" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="500" fill="#667085" text-anchor="start">
|
||||
<tspan x="1068" dy="0">status: PENDING</tspan>
|
||||
<tspan x="1068" dy="17.4">같은 write transaction</tspan>
|
||||
</text>
|
||||
<path d="M 325 164 L 475 164" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="380.5" y="139" width="39.1" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="400" y="155" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Flow</text>
|
||||
<path d="M 815 164 L 1050 164" fill="none" stroke="#1677FF" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-blue)" />
|
||||
<rect x="891.0" y="139" width="83.0" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="932.5" y="155" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#1677FF" text-anchor="middle">Creates row</text>
|
||||
<rect x="40" y="400" width="1420" height="355" fill="#FBFCFE" stroke="#7556D8" stroke-width="1.3" rx="10" />
|
||||
<rect x="49" y="389" width="277.8" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="56" y="405" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="start">커밋 이후 릴레이 경로 · publish는 트랜잭션 밖</text>
|
||||
<rect x="80" y="500" width="210" height="82" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="185" y="535" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">OutboxRelayScheduler</text>
|
||||
<text x="185" y="560" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">fixedDelay=PT5S</text>
|
||||
<rect x="345" y="500" width="210" height="82" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="450" y="546" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">handle()</text>
|
||||
<rect x="565" y="500" width="210" height="82" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="670" y="535" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">claimBatch</text>
|
||||
<text x="670" y="560" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">SKIP LOCKED + FIFO</text>
|
||||
<rect x="825" y="500" width="210" height="82" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="930" y="535" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">occurredAt 재정렬</text>
|
||||
<text x="930" y="560" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">오름차순</text>
|
||||
<rect x="1085" y="500" width="210" height="82" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1190" y="535" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">publish</text>
|
||||
<text x="1190" y="560" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">트랜잭션 밖</text>
|
||||
<path d="M 290 541 L 345 541" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-purple)" />
|
||||
<path d="M 555 541 L 565 541" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-purple)" />
|
||||
<path d="M 775 541 L 825 541" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-purple)" />
|
||||
<path d="M 1035 541 L 1085 541" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-purple)" />
|
||||
<path d="M 1185 582 L 1185 660 L 1010 660" fill="none" stroke="#168A5B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-green)" />
|
||||
<rect x="790" y="625" width="220" height="70" fill="#EAF7F1" stroke="#168A5B" stroke-width="1.5" rx="8" />
|
||||
<text x="900" y="654" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="13" font-weight="700" fill="#25282D" text-anchor="middle">markPublished</text>
|
||||
<text x="900" y="679" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">PUBLISHED</text>
|
||||
<rect x="1061.1" y="634" width="57.9" height="20" fill="#FBFCFE" rx="2" />
|
||||
<text x="1090" y="650" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#168A5B" text-anchor="middle">Success</text>
|
||||
<path d="M 1260 582 L 1260 640" fill="none" stroke="#D64550" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-red)" />
|
||||
<rect x="1220" y="640" width="210" height="70" fill="#FDEDEF" stroke="#D64550" stroke-width="1.5" rx="8" />
|
||||
<text x="1325" y="669" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">markFailed / markDead</text>
|
||||
<text x="1325" y="694" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">backoff 또는 종착</text>
|
||||
<rect x="1291.1" y="604" width="57.9" height="20" fill="#FBFCFE" rx="2" />
|
||||
<text x="1320" y="620" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#D64550" text-anchor="middle">Failure</text>
|
||||
<path d="M 1185 235 L 1185 355 L 670 355 L 670 500" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-purple)" />
|
||||
<rect x="834.9" y="328" width="190.3" height="21" fill="#FFFFFF" rx="2" />
|
||||
<text x="930" y="345" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="650" fill="#7556D8" text-anchor="middle">설정된 fixedDelay=PT5S 폴링</text>
|
||||
<rect x="430" y="700" width="311.6" height="30" fill="#FFF7E5" stroke="#B7791F" stroke-width="1.2" rx="15" />
|
||||
<text x="585.8" y="720" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#B7791F" text-anchor="middle">IN_FLIGHT stuck → 가시성 제한 시간 후 재선점</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="app.diagrams.net" modified="2026-07-23T00:00:00.000Z" agent="techviz-harness" version="24.7.17" type="device">
|
||||
<diagram id="production-vs-optin" name="app-bootstrap의 main 클래스패스에는 어댑터 11개가 포함되고 참조 어댑터 3개는 의존 목록 밖에 있다">
|
||||
<mxGraphModel dx="680" dy="300" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="n_main-classpath-adapters" value="main 의존 포함 · 11<br/>수량: 11개<br/>프로젝트 의존: 선언됨<br/>클래스패스: 포함<br/>어댑터: 포함 대상 11개<br/>활성화: 클래스패스와 별도<br/>측정 범위: main 의존 선언" tooltip="app-bootstrap의 main 프로젝트 의존에 포함되어 main 클래스패스에 들어오는 어댑터 집합 | Evidence: L449-L456, L476-L480, L500-L501, L517-L517" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#2563eb;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxGeometry x="45.0" y="45.0" width="251.0" height="156.0" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="n_omitted-optin-adapters" value="main 의존 목록 밖 · 3<br/>수량: 3개<br/>프로젝트 의존: 선언하지 않음<br/>클래스패스: 제외<br/>어댑터: grpc · graphql · websocket<br/>활성화: 등록하면 기본 활성<br/>측정 범위: main 의존 선언" tooltip="저장소에는 있지만 app-bootstrap의 main 프로젝트 의존에는 선언되지 않은 참조 어댑터 집합 | Evidence: L481-L489, L500-L501, L517-L517" style="whiteSpace=wrap;html=1;rounded=1;strokeWidth=2;fontSize=14;fontStyle=1;fillColor=#ffffff;strokeColor=#2d4357;verticalAlign=middle;strokeColor=#d97706;fillColor=#fffdf5;" vertex="1" parent="1">
|
||||
<mxGeometry x="348.0" y="45.0" width="251.0" height="156.0" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="660" viewBox="0 0 1400 660" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Runtime call versus source dependency</title>
|
||||
<desc id="desc">Two lanes separate runtime dispatch from source dependencies and contract ownership.</desc>
|
||||
<metadata>{"generator":"_work/regenerate-technical-assets.py","canvas_policy":"diagram-only","decorative_effects":false}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
<marker id="arrow-muted" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#667085"/></marker>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#1677FF"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#7556D8"/></marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#168A5B"/></marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#D64550"/></marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#B7791F"/></marker>
|
||||
</defs>
|
||||
<rect width="1400" height="660" fill="#FFFFFF"/>
|
||||
<rect x="35" y="45" width="1330" height="245" fill="#FBFCFE" stroke="#C5CBD3" stroke-width="1.3" rx="10" />
|
||||
<rect x="44" y="34" width="233.9" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="51" y="50" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="start">실행 시점 관계 · 실선 = 호출·디스패치</text>
|
||||
<rect x="100" y="125" width="245" height="90" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="222.5" y="164" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">FeedController</text>
|
||||
<text x="222.5" y="189" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">driving adapter</text>
|
||||
<rect x="555" y="105" width="285" height="130" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="697.5" y="164" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">GetFeedUseCase</text>
|
||||
<text x="697.5" y="189" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">concrete service</text>
|
||||
<rect x="1060" y="125" width="250" height="90" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="1185" y="164" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">SpringTransactionPort</text>
|
||||
<text x="1185" y="189" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">runtime implementation</text>
|
||||
<path d="M 345 170 L 555 170" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="405.4" y="145" width="89.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="450" y="161" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">Runtime call</text>
|
||||
<path d="M 840 170 L 1060 170" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)" />
|
||||
<rect x="892.8" y="145" width="114.3" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="950" y="161" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">Runtime dispatch</text>
|
||||
<rect x="35" y="350" width="1330" height="255" fill="#FBFCFE" stroke="#C5CBD3" stroke-width="1.3" rx="10" />
|
||||
<rect x="44" y="339" width="284.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="51" y="355" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="start">계약 소유·소스 의존 · 점선 = 타입·계약을 향함</text>
|
||||
<rect x="95" y="440" width="260" height="85" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="225" y="461" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="600" fill="#667085" text-anchor="middle" letter-spacing="0.8"><<interface>></text>
|
||||
<text x="225" y="482.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">QueryUseCase<Q,R></text>
|
||||
<text x="225" y="507.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">application-core contract</text>
|
||||
<rect x="565" y="420" width="270" height="125" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8" />
|
||||
<text x="700" y="476.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">GetFeedUseCase</text>
|
||||
<text x="700" y="501.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">implements · calls</text>
|
||||
<rect x="1060" y="440" width="240" height="85" fill="#F1EDFF" stroke="#7556D8" stroke-width="1.5" rx="8" />
|
||||
<text x="1180" y="461" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="600" fill="#667085" text-anchor="middle" letter-spacing="0.8"><<interface>></text>
|
||||
<text x="1180" y="482.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">TransactionPort</text>
|
||||
<text x="1180" y="507.5" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="450" fill="#667085" text-anchor="middle">application-core contract</text>
|
||||
<path d="M 565 465 L 355 465" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-purple)" />
|
||||
<rect x="421.6" y="440" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="460" y="456" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">Implements</text>
|
||||
<path d="M 835 465 L 1060 465" fill="none" stroke="#7556D8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-purple)" />
|
||||
<rect x="902.9" y="440" width="89.2" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="947.5" y="456" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#7556D8" text-anchor="middle">Runtime call</text>
|
||||
<rect x="1080" y="370" width="200" height="50" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8" />
|
||||
<text x="1180" y="400" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="12" font-weight="700" fill="#25282D" text-anchor="middle">SpringTransactionPort</text>
|
||||
<path d="M 1180 420 L 1180 440" fill="none" stroke="#667085" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="7 5" marker-end="url(#arrow-muted)" />
|
||||
<rect x="1221.7" y="422" width="76.7" height="20" fill="#FFFFFF" rx="2" />
|
||||
<text x="1260" y="438" font-family='"Noto Sans CJK KR", "Apple SD Gothic Neo", sans-serif' font-size="11" font-weight="650" fill="#667085" text-anchor="middle">Implements</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 8.6 KiB |
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="330" viewBox="0 0 1400 330" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Runtime call</title>
|
||||
<desc id="desc">FeedController calls GetFeedUseCase, which dispatches to SpringTransactionPort at runtime.</desc>
|
||||
<metadata>{"source":"runtime-call-source-dependency.svg","panel":"upper","canvas_policy":"diagram-only"}</metadata>
|
||||
<defs>
|
||||
<marker id="arrow-ink" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 4 L 0 8 z" fill="#25282D"/></marker>
|
||||
</defs>
|
||||
<rect width="1400" height="330" fill="#FFFFFF"/>
|
||||
<rect x="35" y="45" width="1330" height="245" fill="#FBFCFE" stroke="#C5CBD3" stroke-width="1.3" rx="10"/>
|
||||
<rect x="44" y="34" width="233.9" height="20" fill="#FFFFFF" rx="2"/>
|
||||
<text x="51" y="50" font-family="'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif" font-size="11" font-weight="650" fill="#667085" text-anchor="start">실행 시점 관계 · 실선 = 호출·디스패치</text>
|
||||
<rect x="100" y="125" width="245" height="90" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8"/>
|
||||
<text x="222.5" y="164" font-family="'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif" font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">FeedController</text>
|
||||
<text x="222.5" y="189" font-family="'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif" font-size="12" font-weight="450" fill="#667085" text-anchor="middle">driving adapter</text>
|
||||
<rect x="555" y="105" width="285" height="130" fill="#EAF3FF" stroke="#1677FF" stroke-width="1.5" rx="8"/>
|
||||
<text x="697.5" y="164" font-family="'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif" font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">GetFeedUseCase</text>
|
||||
<text x="697.5" y="189" font-family="'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif" font-size="12" font-weight="450" fill="#667085" text-anchor="middle">concrete service</text>
|
||||
<rect x="1060" y="125" width="250" height="90" fill="#FFFFFF" stroke="#59616B" stroke-width="1.5" rx="8"/>
|
||||
<text x="1185" y="164" font-family="'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif" font-size="16" font-weight="700" fill="#25282D" text-anchor="middle">SpringTransactionPort</text>
|
||||
<text x="1185" y="189" font-family="'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif" font-size="12" font-weight="450" fill="#667085" text-anchor="middle">runtime implementation</text>
|
||||
<path d="M 345 170 L 555 170" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)"/>
|
||||
<rect x="405.4" y="145" width="89.2" height="20" fill="#FFFFFF" rx="2"/>
|
||||
<text x="450" y="161" font-family="'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif" font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">Runtime call</text>
|
||||
<path d="M 840 170 L 1060 170" fill="none" stroke="#25282D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow-ink)"/>
|
||||
<rect x="892.8" y="145" width="114.3" height="20" fill="#FFFFFF" rx="2"/>
|
||||
<text x="950" y="161" font-family="'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif" font-size="11" font-weight="650" fill="#25282D" text-anchor="middle">Runtime dispatch</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |