기반 가이드 7단계로 실험대를 철거하고 다시 세운 뒤 virtualization setup 9편과 keycloak-session-store 26편을 순서대로 밟았다. 24편은 끝까지, 11편은 되는 데까지 밟았고 밟은 범위를 편마다 적었다. 명령이 못 도는 것을 고쳤다. - kubectl 을 `kc-lab-1` 에서 치라고 적었는데 그 기계에 kubeconfig 가 없다. 라벨 639개와 각 편의 「어디서 치는가」를 `[lab host]` 로 옮겼다 - `-o custom-columns=…[0]…` 이 zsh 에서 글로브로 읽혀 안 돈다. 28곳에 따옴표 - busybox `sed` 가 끝 개행을 안 붙여 A-3 의 측정이 언제나 0 이었다 - `--token-file ~/node-token` 뒤에 그 파일을 지우면 k3s agent 가 재부팅을 못 견딘다. `/etc/rancher/node-token` 으로 옮기는 처방을 재서 넣었다 - 게스트에 없는 도구를 전제로 한 명령 넷 — `conntrack`·`dig`·`strings`·`nginx -v` - `echo` 와 JWT 헤더가 `"이름" : [ 값 ]` 으로 찍는데 문서는 공백 없이 옮겨 적어 그 실측으로 만든 grep·sed 가 한 줄도 못 잡는다 - B-0 이 `directAccessGrantsEnabled` 와 계정 완성을 빠뜨려 B-3 이 못 돈다 - D-4·D-4a 가 `test-server` 와 `certbot-renew.*` 를 가리키는데 실제로는 `kc-lab-edge` 의 `certbot.service` 다 - `virsh setmaxmem --config` 를 `dominfo` 로 판정하면 틀린다. `--inactive` 로 - `LIBVIRT_DEFAULT_URI` 를 rc 에만 넣으면 `ssh host '명령'` 에서 안 먹는다 결과가 조건부인 것을 갈랐다. - readiness 는 즉시 안 뒤집힌다. A-1·A-2 의 60초 창을 적었다 - 03 의 층 ②③ `301` 은 04 이후의 값이고 그 단계에서는 `404` 다 - A-0 의 로그 필터를 요청 직후에 치면 정반대 결론이 나온다 - A-5 의 한 방향 차단은 잠깐 `1` 이었다 `2` 로 돌아온다 증거는 두 프로젝트의 `evidence/raw/` 에 99벌을 README 와 함께 남겼다. 비밀은 길이만 적었고 화면에 찍힌 토큰은 가렸다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
189 lines
10 KiB
Markdown
189 lines
10 KiB
Markdown
---
|
|
name: publishing-tech-log-to-studio
|
|
description: Use when a finished Tech Log record .md must be put into Tech Log Studio through the browser with Playwright MCP — creating or opening the working copy, uploading assets, filling the per-kind fields, and saving. Save only; this skill never publishes.
|
|
metadata:
|
|
version: "1.0.1"
|
|
language: "ko-KR"
|
|
---
|
|
|
|
# Studio 반입 — 저장까지만
|
|
|
|
## 이 스킬의 경계
|
|
|
|
기록 `.md` 하나를 Studio 편집 화면에 넣고 **저장**한다. 거기서 끝난다.
|
|
|
|
**게시하지 않는다.** 게시는 공개 사이트에 올리는 일이고, 되돌리려면 `unpublish` 를 해야 하며
|
|
그 사이에 누구나 본다. 더 중요한 것은 **한 번이라도 게시한 문서는 게시를 취소해도 지워지지
|
|
않는다는 것이다** — 삭제가 409 로 거절되고 「공개된 기록은 삭제할 수 없습니다」가 뜬다. 그래서
|
|
시험 삼아 게시하지 않는다. 게시는 사람이 미리보기를 읽고 판단한다.
|
|
|
|
편집 화면 오른쪽 `aside` 에 버튼이 `저장`·`게시` 둘뿐이다. **`게시` 를 누르면 저장·검증·
|
|
미리보기·게시가 한 번에 돈다.** 이 스킬은 `저장` 만 누른다.
|
|
|
|
## 들어가기 전 조건
|
|
|
|
| 조건 | 확인 |
|
|
|---|---|
|
|
| 기록 `.md` 가 파서를 통과했다 | `studio-body.py` 로 바꾼 파일에 `check_body.mjs`, error 0 |
|
|
| 문장 검사를 지났다 | `check_prose.mjs` error 0 |
|
|
| 인용이 SSOT 에 실재한다 | `check_evidence.mjs <프로젝트> --repo` |
|
|
| 브라우저가 이미 로그인돼 있다 | 아래 「인증」 |
|
|
|
|
**검사를 안 지난 초안을 넣지 않는다.** 저장은 빈 칸도 받아 주기 때문에(Studio 는 한 번에 다
|
|
쓰지 않아도 저장되게 만들어져 있다) 넣는 것 자체는 성공한다. 그래서 파서·문장 검사를 여기서
|
|
대신 잡아 주지 않는다.
|
|
|
|
## 인증
|
|
|
|
Studio 는 조회에도 권한을 요구한다. 읽는 것이 게시 전 초안이기 때문이다.
|
|
|
|
**이 저장소에 자격증명을 두지 않는다.** 이미 로그인된 브라우저 세션을 쓴다. 편집 화면을 열었을
|
|
때 상태 `aside` 가 25초 안에 안 뜨면 **인증이 안 된 것으로 보고 멈춘다.** 로그인 화면을
|
|
자동으로 통과하려 들지 않는다 — 사용자에게 로그인해 달라고 말하고 기다린다.
|
|
|
|
```
|
|
aside[class*="studio-document-status"] ← 이것이 안 보이면 인증 실패
|
|
```
|
|
|
|
## 주소
|
|
|
|
| 무엇 | 주소 |
|
|
|---|---|
|
|
| Studio | `https://hyeonworks.com/studio` |
|
|
| 편집 화면 | `https://hyeonworks.com/studio/documents/<id>/edit` |
|
|
| 새 문서 | Studio 에서 `새 문서` → 종류 선택 → `작업본 만들기` |
|
|
|
|
기록 frontmatter 의 `id` 와 `studio:` 가 이미 차 있으면 **새로 만들지 않는다.** 그 주소로 바로
|
|
간다. 비어 있으면 새로 만들고, 받은 uuid 와 편집 주소를 기록 frontmatter 에 적는다.
|
|
|
|
## 절차
|
|
|
|
### 1. 기록을 읽고 무엇을 넣을지 정한다
|
|
|
|
`kind` 로 칸 목록이 정해진다. 어떤 `##` 제목이 Studio 의 어느 칸인지는
|
|
[references/studio-form-map.md](references/studio-form-map.md), 그 칸이 서버에서 어떤 이름과
|
|
자료형인지는 [references/studio-api.md](references/studio-api.md).
|
|
|
|
**frontmatter 는 메타데이터고, 본문의 `##` 가 Studio 의 칸이며, 제목 아래 첫 문단이 `요약`
|
|
이다.** 계약에 없는 `##` 는 화면에 자리가 없어 통째로 사라진다.
|
|
|
|
**본문은 이제 화면으로 넣지 않는다.** 편집 화면의 본문이 블록 편집기로 바뀌어 블록 하나가
|
|
`<textarea>` 하나다 — 기록 한 편이 블록 171개인 것을 실제로 봤다. 그런데 저장이 서버로 보내는
|
|
것은 여전히 `bodyMarkdown` 이라는 마크다운 문자열 하나다. 그래서 **옮기는 일은 화면이 아니라
|
|
`PUT /api/v1/studio/documents/{id}` 로 한다** — 로그인된 그 페이지 안에서 `fetch` 로 부르므로
|
|
세션과 CSRF 가 그대로 실린다. 화면은 읽고 대조하는 데 쓴다.
|
|
|
|
### 2. 그림이 있으면 Asset 을 먼저 올린다
|
|
|
|
frontmatter `assets:` 의 `file:` 이 올릴 파일이고 `key:` 는 저장소 쪽 이름이다. 올리면 서버가
|
|
`<이름>-<해시8>` 형태의 키를 준다.
|
|
|
|
**본문을 넣기 전에 올린다.** 순서를 뒤집으면 미리보기가 본문 전체를 막는다 —
|
|
`1:1 supported local evidence key not found: <asset-key>`. 다른 경로로 올렸으면 편집 화면을
|
|
한 번 새로 고쳐야 목록을 다시 받는다. 본문 문법 문제로 오해하기 쉽다.
|
|
|
|
본문은 서버가 준 키로 바꿔서 넣는다.
|
|
|
|
```bash
|
|
python3 scripts/studio-body.py <기록.md> --key <저장소 key>=<서버가 준 key> --body-only
|
|
```
|
|
|
|
저장소의 `.md` 는 마크다운 이미지로 두고 고치지 않는다. `:::evidence` 는 Studio 렌더러의
|
|
구문이라 저장소에 쓰면 편집기에서 그림이 안 보인다.
|
|
|
|
### 3. 칸을 채운다
|
|
|
|
되풀이되는 칸(`영향`·`선택지`·`사실`처럼 여러 줄인 것)은 **줄 수를 먼저 맞추고** 값을 넣는다.
|
|
줄이 모자란 채로 채우면 뒤엣것이 조용히 버려진다. 셀렉터와 배치 실행 방법은
|
|
[references/playwright-recipes.md](references/playwright-recipes.md).
|
|
|
|
**본문이 없는 세 종류(Reference·Question·Decision)의 칸은 마크다운 블록 파서를 안 거친다.**
|
|
그래도 전부 글자로 나오지는 않는다 — 렌더러(`tech-log-frontend` 의
|
|
`presentation/shared/public-render/prose-text.tsx`)가 **백틱 쌍은 인라인 `<code>` 로** 살리고,
|
|
빈 줄은 문단으로, 한 줄 바꿈은 `<br>` 로 남긴다. **글자 그대로 나오는 것은 별표·파이프·`#`·
|
|
코드펜스·인용 표지 `>` 다.** 백틱을 빼지 않는다 — 빼면 식별자가 민무늬로 나온다.
|
|
|
|
**환경 구성에도 되풀이 칸이 하나 있다.** `고정한 버전` 은 줄마다 `이름`·`버전` 입력 둘이고
|
|
「버전 추가」 버튼으로 늘린다. 여기도 줄 수를 먼저 맞추고 값을 넣는다.
|
|
|
|
### 4. 저장한다
|
|
|
|
API 로 넣었으면 `PUT` 이 200 을 내는 것이 저장이다. **`expectedVersion` 은 저장 직전에 `GET`
|
|
으로 읽은 `document.version` 이다** — 지어내지 않는다. 보내지 않은 칸은 비워지므로 그 종류의
|
|
칸을 전부 담는다.
|
|
|
|
화면으로 고쳤으면 버튼을 누른다.
|
|
|
|
```
|
|
aside[class*="studio-document-status"] 안의 `저장` 버튼
|
|
→ 같은 aside 의 글자가 `저장됨` 으로 바뀔 때까지 기다린다 (최대 30초)
|
|
```
|
|
|
|
`저장됨` 을 못 보면 실패다. 그 `aside` 의 글자를 그대로 읽어서 보고한다. **버튼을 다시 누르지
|
|
않는다** — 검증 오류로 막힌 것을 연타로 뚫으려다 `게시` 를 누르게 된다.
|
|
|
|
버전은 같은 `aside` 의 첫 `dd` 에 있다. 저장 전후로 읽어 두면 실제로 올라갔는지 보인다.
|
|
|
|
### 5. 미리보기로 읽는다
|
|
|
|
`즉시 미리보기` 탭은 저장한 값이 아니라 **화면에 입력한 값**을 렌더링한다. 그래서 게시 없이도
|
|
공개 화면과 같은 블록 렌더러로 본문을 볼 수 있다.
|
|
|
|
읽을 항목은 `../writing-tech-log-records/references/studio-draft-review.md` 의 목록을 쓴다.
|
|
고칠 것이 있으면 `편집` 탭으로 돌아가 고치고 다시 저장한다.
|
|
|
|
### 6. 기록에 되적는다
|
|
|
|
새로 만들었으면 frontmatter 의 `id` 와 `studio:` 를 채우고 색인을 다시 만든다.
|
|
|
|
```bash
|
|
python3 scripts/build-tech-log-tree.py <프로젝트>
|
|
python3 scripts/verify-tech-log-tree.py <프로젝트>
|
|
```
|
|
|
|
`build-tech-log-tree.py` 는 frontmatter 의 `id` 가 있으면 `publication` 을 `게시됨` 으로
|
|
적는다. **이 칸은 「Studio 에 있다」는 뜻이지 「공개돼 있다」가 아니다.** 공개 여부는 기록의
|
|
`status` 와 `public:` 이 말한다.
|
|
|
|
## 시험용 초안을 남기지 않는다
|
|
|
|
확인하려고 만든 작업본은 지운다. **여섯 종류 전부 삭제 경로가 있다.** 앞의 다섯은
|
|
`tech-log-backend` @ `a000f87` 의 `ManagementDocumentController` 에서 셌고, 환경 구성은
|
|
`tech-log-frontend` @ `9e5642c` 의 `management-api.openapi.yaml:606-628`(`deleteSetupDraft`)
|
|
에서 읽었다. 경로 앞머리가 줄마다 다른 것은 두 문서가 각자 적는 대로 옮겼기 때문이다.
|
|
|
|
| 종류 | 경로 | 본문 |
|
|
|---|---|---|
|
|
| Case | `DELETE /api/v1/studio/cases/{id}` (`:72`) | `{"expectedVersion": <저장 버전>}` |
|
|
| Reference | `DELETE /api/v1/studio/references/{id}` (`:81`) | `{"expectedVersion": <저장 버전>}` |
|
|
| Concept | `DELETE /api/v1/studio/concepts/{id}` (`:95`) | `{"expectedVersion": <저장 버전>}` |
|
|
| Setup | `DELETE /api/v1/studio/setups/{id}` | `{"expectedVersion": <저장 버전>}` |
|
|
| Question | `DELETE /api/v1/studio/questions/{id}` (`:114`) | `{"expectedVersion": <저장 버전>}` |
|
|
| Decision | `DELETE /api/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 은 계약에 삭제 경로가 없다」고 적혀 있었고 그것은 틀렸다.**
|
|
확인 없이 적힌 문장이 옮겨 다녔다 — 이 배치에서 그 문장을 코드 주석과 보고서로 다시 옮긴
|
|
일이 있었다. **서버를 서술할 때는 확인한 리비전을 함께 적는다.** 리비전이 없으면 서버가
|
|
바뀌어도 아무도 모른다.
|
|
|
|
**지워지지 않는 것은 따로 있다.** 게시한 적이 있으면 `DOCUMENT_PUBLISHED` 로 409 가 나고
|
|
(「공개된 기록은 삭제할 수 없습니다」), 관계로 참조된 문서는 `DOCUMENT_IN_USE` 로 막힌다
|
|
(「이 기록을 참조하는 곳이 있어 삭제할 수 없습니다」). 참조하는 쪽의 관계를 먼저 끊는다.
|
|
|
|
## 실패했을 때
|
|
|
|
| 증상 | 원인 |
|
|
|---|---|
|
|
| `aside` 가 안 뜬다 | 인증. 로그인 화면을 자동으로 넘기지 않는다 |
|
|
| 미리보기가 본문 전체를 막는다 | Asset 을 올리기 전에 본문을 넣었다. 화면을 새로 고친다 |
|
|
| 칸을 못 찾는다 (`label` 매치 0) | 그 종류에 없는 칸이다. `studio-form-map.md` 를 다시 본다 |
|
|
| 값이 잘렸다 | 되풀이 칸의 줄 수를 안 맞추고 채웠다 |
|
|
| `저장됨` 이 안 뜬다 | 검증이 막은 것이다. `aside` 글자를 읽어 보고한다 |
|