docs(TechLog): 남은 주제를 다시 쓰고 SSOT 를 저장소 실물로 더 보강한다
주제 11~13 을 다시 쓰고, Case 가 얇은 것들을 저장소에서 실물을 확인해 채웠다.
§13.4 ManagementClientSafeMessages — 삭제 관련 코드 여섯의 고정 문구와
원문 메시지를 내보내지 않는 이유(javadoc)
§16.1 다섯 참조가 전부 DOCUMENT_IN_USE 하나로 나가고, SSOT 가 인용한 영어 문장은
DeleteDocumentDraftUseCase 안에 남는 진단 메시지라 밖으로 나가지 않는다
§13.6 romanizeSyllable 실물과 음운 변동을 뺀 이유, 문서 slug 와 같은 정규식을 쓰는 이유
§15.4 check:types 가 도는 tsconfig 여섯 — app·node·test·recipes·web-worker·service-worker
SSOT 62,643 → 67,526 자. 인용한 코드는 전부 저장소에서 찾아 대조했다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b1653dbba8
commit
6917ce2420
@@ -1082,7 +1082,27 @@ QUESTION → 열린 질문
|
||||
|
||||
게이트웨이가 서버의 클라이언트 안전 메시지를 실어 나르고 화면이 그것을 보이게 했습니다.
|
||||
|
||||
**이 문제는 아직 완전히 안 끝났습니다** — §14.2 를 보세요.
|
||||
서버는 오류 코드마다 고정 문구를 갖고 있습니다(`ManagementClientSafeMessages`). 예외의 원문
|
||||
메시지를 그대로 내보내지 않는 이유가 클래스 javadoc 에 적혀 있습니다:
|
||||
|
||||
> code 별 고정 문구. 예외의 원문 메시지는 진단용이라 그대로 내보내지 않는다 — 저장소 제약
|
||||
> 이름이나 SQL 조각이 새어 나갈 수 있고, 그건 클라이언트가 분기할 값도 아니다.
|
||||
|
||||
삭제와 관련된 코드만 여섯입니다. 화면이 추측하던 세 가지는 이 중 셋에 해당합니다:
|
||||
|
||||
```java
|
||||
case VERSION_CONFLICT -> "다른 곳에서 먼저 수정되었습니다. 새로 불러온 뒤 다시 시도해 주세요";
|
||||
case DOCUMENT_PUBLISHED -> "공개된 기록은 삭제할 수 없습니다. 먼저 공개를 취소해 주세요";
|
||||
case DOCUMENT_IN_USE -> "이 기록을 참조하는 곳이 있어 삭제할 수 없습니다";
|
||||
case QUESTION_IN_USE -> "이 질문을 참조하는 곳이 있어 삭제할 수 없습니다";
|
||||
case DECISION_IN_USE -> "이 결정을 참조하는 곳이 있어 삭제할 수 없습니다";
|
||||
case TOPIC_IN_USE -> "이 주제를 쓰는 기록이 있어 삭제할 수 없습니다";
|
||||
```
|
||||
|
||||
코드는 카테고리와 재시도 가능 여부도 함께 답합니다 — `DOCUMENT_IN_USE(Category.CONFLICT, 409,
|
||||
false)`. 그래서 화면이 셋을 나열하지 않아도 어느 것인지 코드로 갈립니다.
|
||||
|
||||
**이 문제는 아직 완전히 안 끝났습니다** — §16.1 을 보세요.
|
||||
|
||||
### 13.5 편집기 칸 이름을 공개 화면과 맞췄다 (`82e992d`)
|
||||
|
||||
@@ -1108,9 +1128,30 @@ QUESTION → 열린 질문
|
||||
- `Redis 캐시` 와 `Redis 클러스터` → **둘 다 `redis`** → 두 번째가 충돌
|
||||
|
||||
**한글을 버리지 않고 로마자로 옮깁니다.** 음절을 초성·중성·종성으로 산술 분해하므로 표가
|
||||
필요 없고 결정적입니다: `백엔드 아키텍처` → `baekendeu-akitekcheo`. 국어의 로마자 표기법의
|
||||
**자모 대응만** 적용하고 음운 변화 규칙은 일부러 뺐습니다 — slug 는 읽는 것이지 발음하는 것이
|
||||
아니고, 그 규칙을 넣으면 같은 이름이 문맥에 따라 다른 slug 가 됩니다.
|
||||
필요 없고 결정적입니다: `백엔드 아키텍처` → `baekendeu-akitekcheo`.
|
||||
|
||||
```ts
|
||||
function romanizeSyllable(codePoint: number): string {
|
||||
if (codePoint < SYLLABLE_BASE || codePoint > SYLLABLE_LAST) {
|
||||
return String.fromCodePoint(codePoint);
|
||||
}
|
||||
const offset = codePoint - SYLLABLE_BASE;
|
||||
const initial = Math.floor(offset / (MEDIAL_COUNT * FINAL_COUNT));
|
||||
const medial = Math.floor((offset % (MEDIAL_COUNT * FINAL_COUNT)) / FINAL_COUNT);
|
||||
const final = offset % FINAL_COUNT;
|
||||
return `${INITIALS[initial]}${MEDIALS[medial]}${FINALS[final]}`;
|
||||
}
|
||||
```
|
||||
|
||||
국어의 로마자 표기법의 **자모 대응만** 적용하고 음운 변화 규칙은 일부러 뺐습니다. 함수의
|
||||
javadoc 이 그 이유와 결과 모양까지 적어 두었습니다:
|
||||
|
||||
> 표기법은 국어의 로마자 표기법의 자모 대응만 쓴다 — 음운 변동(자음동화 같은 것)은 반영하지
|
||||
> 않는다. slug 는 읽히기 위한 것이지 발음을 옮기기 위한 것이 아니고, 변동 규칙을 넣으면 같은
|
||||
> 이름이 문맥에 따라 다른 slug 가 될 수 있다.
|
||||
>
|
||||
> 결과는 문서 slug 와 같은 모양이다 (`^[a-z0-9]+(?:-[a-z0-9]+)*$`) — 한 저장소가 두 가지 slug
|
||||
> 규칙을 갖지 않도록.
|
||||
|
||||
---
|
||||
|
||||
@@ -1267,6 +1308,9 @@ jpa-feed-query-performance 축 이름 「조회 전략」 축 3개
|
||||
|
||||
### 15.4 배포 전 검증 (사람이 돌려야 하는 것)
|
||||
|
||||
`check:types` 는 tsconfig 여섯 개를 차례로 돌립니다 — app · node · test · recipes ·
|
||||
web-worker · service-worker. 루트 tsconfig 를 직접 부르는 명령은 그중 어느 것도 지나지 않습니다.
|
||||
|
||||
```bash
|
||||
# 프론트 — 다섯 개를 다 돌린다. npx tsc --noEmit 은 아무것도 검사하지 않는다
|
||||
npm run check:types
|
||||
@@ -1299,6 +1343,11 @@ python3 scripts/check-openapi.py && python3 scripts/check-consistency.py \
|
||||
another record still links to this one; unlink it first
|
||||
```
|
||||
|
||||
막는 것은 다섯 참조 중 하나인데, 다섯이 전부 같은 오류 코드(`DOCUMENT_IN_USE`)로 나갑니다.
|
||||
클라이언트에 나가는 문구는 코드마다 하나로 고정돼 있으므로(§13.4), 참조 종류를 문구로 가르려면
|
||||
코드를 먼저 갈라야 합니다. 위 영어 문장은 서버 안에 남는 진단 메시지이고 밖으로 나가지
|
||||
않습니다(`DeleteDocumentDraftUseCase`).
|
||||
|
||||
실제로 막는 것은 이 중 하나입니다:
|
||||
|
||||
```sql
|
||||
|
||||
+24
-5
@@ -64,7 +64,7 @@ tech-log-frontend : 5cffe30 · 7093d84
|
||||
|
||||
## 간헐적으로 보인 이유
|
||||
|
||||
slug 생성이 영문 소문자와 숫자만 남기고 나머지를 버렸다. 한글 이름은 통째로 사라지므로, 결과가 이름에 영문이 얼마나 섞였는지에 따라 갈린다.
|
||||
slug 생성이 영문 소문자와 숫자가 아닌 것을 전부 하이픈으로 바꿨다. 한글은 전부 거기 걸리므로 이름에 섞인 영문과 숫자만 남는다.
|
||||
|
||||
| 이름 | 옛 규칙이 만든 slug | 무엇이 일어났나 |
|
||||
|---|---|---|
|
||||
@@ -72,13 +72,28 @@ slug 생성이 영문 소문자와 숫자만 남기고 나머지를 버렸다.
|
||||
| `Redis 캐시` | `redis` | 만들어짐 |
|
||||
| `Redis 클러스터` | `redis` | 두 번째가 충돌 |
|
||||
|
||||
사용자는 둘 다 만났다. 어느 쪽도 「한글이 버려졌다」로 보이지 않고, 하나는 폼 오류로 하나는 중복 오류로 나타난다.
|
||||
두 결과가 서로 달라 보인다. 하나는 폼 오류이고 하나는 중복 오류이며, 어느 쪽도 「한글이 버려졌다」로 읽히지 않는다. 작성자에게는 「가끔 안 되다가 이름을 바꾸면 되는」 현상이었다.
|
||||
|
||||
> 규칙은 간헐적이었던 적이 없다. **보이지 않았을 뿐이다** — slug 생성이 `[a-z0-9]` 만 남기고 나머지를 버려서, 한글 이름은 아무것도 기여하지 못했다.
|
||||
|
||||
## 산술 분해로 로마자를 만든다
|
||||
|
||||
한글 음절은 초성·중성·종성이 정해진 순서로 조합된 코드다. 음절 코드에서 세 값을 산술로 분해할 수 있으므로 변환표가 필요 없고, 같은 입력에 늘 같은 결과가 나온다.
|
||||
한글 음절은 초성·중성·종성이 정해진 순서로 조합된 코드다. 음절 코드에서 시작 코드를 빼고 중성 수와 종성 수로 나누면 세 값이 그대로 나온다.
|
||||
|
||||
```ts
|
||||
function romanizeSyllable(codePoint: number): string {
|
||||
if (codePoint < SYLLABLE_BASE || codePoint > SYLLABLE_LAST) {
|
||||
return String.fromCodePoint(codePoint);
|
||||
}
|
||||
const offset = codePoint - SYLLABLE_BASE;
|
||||
const initial = Math.floor(offset / (MEDIAL_COUNT * FINAL_COUNT));
|
||||
const medial = Math.floor((offset % (MEDIAL_COUNT * FINAL_COUNT)) / FINAL_COUNT);
|
||||
const final = offset % FINAL_COUNT;
|
||||
return `${INITIALS[initial]}${MEDIALS[medial]}${FINALS[final]}`;
|
||||
}
|
||||
```
|
||||
|
||||
변환표가 필요 없고 같은 입력에 늘 같은 결과가 나온다. 한글이 아닌 문자는 그대로 돌려주고 뒤의 필터가 처리한다.
|
||||
|
||||
```text
|
||||
백엔드 아키텍처 → baekendeu-akitekcheo
|
||||
@@ -86,9 +101,13 @@ slug 생성이 영문 소문자와 숫자만 남기고 나머지를 버렸다.
|
||||
|
||||
## 음운 변화 규칙을 뺀 이유
|
||||
|
||||
국어의 로마자 표기법에는 자모 대응 외에 음운 변화 규칙이 있다. 그것을 넣지 않았다.
|
||||
국어의 로마자 표기법에는 자모 대응 외에 음운 변화 규칙이 있다. 그것을 넣지 않았고, 함수의 javadoc 이 이유를 적는다.
|
||||
|
||||
slug 는 읽는 것이지 발음하는 것이 아니다. 음운 변화를 적용하면 같은 글자가 앞뒤에 무엇이 오느냐에 따라 다르게 옮겨지고, 그러면 이름의 일부만 바뀌어도 앞쪽 slug 가 달라진다. 결정적이지 않은 slug 는 주소로 쓸 수 없다.
|
||||
> 표기법은 국어의 로마자 표기법의 자모 대응만 쓴다 — 음운 변동(자음동화 같은 것)은 반영하지 않는다. slug 는 읽히기 위한 것이지 발음을 옮기기 위한 것이 아니고, 변동 규칙을 넣으면 같은 이름이 문맥에 따라 다른 slug 가 될 수 있다.
|
||||
|
||||
## 문서 slug 와 같은 모양으로 맞췄다
|
||||
|
||||
결과가 문서 slug 와 같은 정규식을 만족하게 했다. 한 저장소가 두 가지 slug 규칙을 갖지 않도록 한 것이고, 그래서 주제 slug 와 문서 slug 를 같은 검사로 볼 수 있다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+35
-7
@@ -7,9 +7,18 @@ topicName: 주제 안의 축
|
||||
project: TechLog
|
||||
status: 게시 전
|
||||
lastVerifiedOn: 2026-09-04
|
||||
assets:
|
||||
- key: home-tabs-keycloak
|
||||
file: ../../../final/evidence/browser/home-tabs-keycloak.png
|
||||
- key: home-topic-tabs-2
|
||||
file: ../../../final/evidence/browser/home-topic-tabs-2.png
|
||||
- key: home-tabs-grouped
|
||||
file: ../../../final/evidence/browser/home-tabs-grouped.png
|
||||
evidence:
|
||||
- ../../../final/evidence/browser/home-tabs-grouped.png
|
||||
- ../../../final/evidence/browser/home-tabs-keycloak.png
|
||||
- ../../../final/evidence/browser/home-topic-tabs.png
|
||||
- ../../../final/evidence/browser/home-topic-tabs-2.png
|
||||
- ../../../final/evidence/browser/home-tabs-grouped.png
|
||||
- ../../../final/evidence/browser/tab-metrics.txt
|
||||
sourceRevision: tech-log@2026-09-02
|
||||
source:
|
||||
@@ -62,23 +71,42 @@ tech-log-frontend : 604ded5 → de4cb8b → 3bb724b · 2b2f443
|
||||
|
||||
## 세 단계
|
||||
|
||||
1단계는 주제 하나만 펼치고 아래에 「다른 주제 N개 보기」 한 줄을 뒀다. 그 줄은 목록을 다 읽고 나서야 만나는 곳이라 대개 지나쳤다 — JPA 주제는 홈에 있으면서도 없는 것과 같았다.
|
||||
1단계는 주제 하나만 펼치고 아래에 「다른 주제 N개 보기」 한 줄을 뒀다. 홈이 「무엇을 만들었나」로 시작하고 있었는데, 30초 안에 알아야 할 것은 무엇을 견줬는가였다.
|
||||
|
||||
2단계에서 제목 자리를 주제 이름 탭이 대신하게 했다. 30px 에 굵기 650 으로 세웠다.
|
||||
그 줄은 목록을 다 읽고 나서야 만나는 곳이라 대개 지나쳤다. JPA 주제는 홈에 있으면서도 없는 것과 같았다.
|
||||
|
||||
3단계에서 탭을 칩 크기로 낮추고 개수 상한을 없앴다. 상한은 주제마다 상세를 미리 받느라 둔 것인데, 그러면 상한 밖의 주제가 다시 밀려난다.
|
||||
2단계에서 제목 자리를 주제 이름 탭이 대신하게 했다. 30px 에 굵기 650 으로 세웠고, 고른 탭에 파란 밑줄을 뒀다.
|
||||
|
||||
3단계에서 탭을 칩 크기로 낮추고 개수 상한을 없앴다. 주제가 열 개, 스무 개가 되면 이름만으로 화면이 덮이기 때문이다.
|
||||
|
||||
:::evidence key="home-tabs-keycloak" alt="탭을 구역 제목 급으로 세운 2단계 화면" caption=" " zoom="true"
|
||||
:::
|
||||
|
||||
## 상한이 왜 있었나
|
||||
|
||||
상한은 주제마다 상세를 미리 받느라 둔 것이었다. 상세를 다 받으려면 요청이 주제 수만큼 늘어나므로 그 수를 제한해야 했다.
|
||||
|
||||
그러면 상한 밖의 주제가 다시 밀려난다. 1단계에서 「다른 주제」 줄에 밀려나던 것과 같은 결과가 된다.
|
||||
|
||||
## 요청 구조를 바꿨다
|
||||
|
||||
탭 줄은 목록 호출 하나가 주는 전부다. 상세는 고른 탭만 그때 받아 캐시한다.
|
||||
|
||||
주제가 몇 개가 되든 홈이 처음 보내는 요청은 목록 하나와 주제 하나로 고정된다.
|
||||
그래서 주제가 몇 개가 되든 홈이 처음 보내는 요청은 목록 하나와 주제 하나로 고정된다. 상한을 없앨 수 있었던 이유가 이것이고, 상한을 먼저 없애고 요청을 그대로 뒀다면 주제가 늘수록 홈이 느려졌을 것이다.
|
||||
|
||||
## 시각 언어를 두 번 고쳤다
|
||||
|
||||
고른 탭의 파란 밑줄을 없앴다. 주제가 스무 개면 밑줄 설 곳 스무 개가 나란히 늘어선다.
|
||||
고른 탭의 파란 밑줄을 없앴다. 주제가 스무 개면 밑줄 설 곳 스무 개가 함께 늘어선다.
|
||||
|
||||
칩으로 낮추니 목록 위에 글자만 떠 있는 것처럼 보였다. 고른 탭에 알약 형태를 주고, 묶음의 윗선을 목록이 아니라 패널이 갖게 해서 탭 줄이 그 선에 바로 얹히게 했다.
|
||||
칩으로 낮추니 목록 위에 글자만 떠 있는 것처럼 보였다. 고른 것이 색으로만 달라 누를 수 있는 것으로 읽히지 않았고, 탭 줄과 목록 사이가 선 없이 30px 비어 두 덩어리로 갈렸다.
|
||||
|
||||
:::evidence key="home-topic-tabs-2" alt="칩으로 낮춘 직후의 비교 구역 확대" caption=" " zoom="true"
|
||||
:::
|
||||
|
||||
고른 탭에 알약 형태를 주고, 묶음의 윗선을 목록이 아니라 패널이 갖게 해서 탭 줄이 그 선에 바로 얹히게 했다.
|
||||
|
||||
:::evidence key="home-tabs-grouped" alt="고른 탭에 형태를 주고 탭 줄을 패널 윗선에 얹은 최종 화면" caption=" " zoom="true"
|
||||
:::
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+12
-6
@@ -50,23 +50,29 @@ topic (주제)
|
||||
|
||||
## 축 이름은 주제가 정한다
|
||||
|
||||
내부 이름은 축으로 고정하고, 화면에 보이는 이름은 주제가 자기 칸에 적는다. 인증 경계 주제는 「구조」, 조회 성능 주제는 「조회 전략」이다.
|
||||
내부 이름은 축으로 고정하고, 화면에 보이는 이름은 주제가 자기 칸에 적는다.
|
||||
|
||||
주제마다 비교하는 것이 다르기 때문이다. 인증 경계 주제는 credential 을 어디에 두느냐로 갈리므로 「구조」이고, 조회 성능 주제는 같은 데이터를 어떻게 읽느냐로 갈리므로 「조회 전략」이다. 이름을 한 값으로 고정하면 둘 중 하나에서 어긋난다.
|
||||
|
||||
## 기록은 여러 축에 걸린다
|
||||
|
||||
한 기록이 여러 축에 걸릴 수 있다. PKCE 는 SPA 와 BFF 양쪽에 관계된다.
|
||||
|
||||
아무 축에도 걸리지 않은 기록은 그 주제의 공통 기록으로 읽는다. 「공통」이라는 축을 따로 만들지 않는다.
|
||||
아무 축에도 걸리지 않은 기록은 그 주제의 공통 기록으로 읽는다. 「공통」이라는 축을 따로 만들지 않는 이유는, 만들면 그 축이 비교 화면에 한 줄로 서서 다른 축들과 견주는 것처럼 보이기 때문이다.
|
||||
|
||||
## 외래키가 없다
|
||||
|
||||
`record_variant` 는 외래키를 갖지 않는다. 기록이 종류마다 다른 테이블에 살기 때문이다 — 문서·열린 질문·프로젝트 결정이 각각 다른 테이블이다. 종류와 아이디의 쌍으로만 가리킨다.
|
||||
기록이 어느 축에 걸리는지를 담는 표는 외래키를 갖지 않는다. 기록이 종류마다 다른 테이블에 살기 때문이다 — 문서·열린 질문·프로젝트 결정이 각각 다른 테이블이고, 하나의 외래키로 셋을 함께 가리킬 수 없다.
|
||||
|
||||
검증 상태와 게시 기록이 이미 같은 방식으로 기록을 가리키고 있었다.
|
||||
그래서 종류와 아이디의 쌍으로만 가리킨다. 이 방식은 이 저장소에서 처음 쓰는 것이 아니고, 검증 상태와 게시 기록이 이미 같은 방식으로 기록을 가리키고 있었다.
|
||||
|
||||
대가는 데이터베이스가 참조 무결성을 지켜 주지 않는다는 것이다. 기록을 지울 때 그 쌍을 함께 지우는 것은 코드가 한다.
|
||||
|
||||
## 사람이 쓰는 칸
|
||||
|
||||
주제의 논지, 축의 요약과 결론은 기록을 합쳐 자동으로 나오는 글이 아니다. 특히 결론은 비교표가 읽는 칸이라 기록의 요약 첫 줄을 잘라 쓰면 안 된다.
|
||||
주제의 논지, 축의 요약과 결론은 기록을 합쳐 자동으로 나오는 글이 아니다.
|
||||
|
||||
특히 결론은 비교표가 읽는 칸이라 기록의 요약 첫 줄을 잘라 쓰면 안 된다. 요약은 그 축이 무엇인지 말하고 결론은 그 축에서 무엇을 알게 됐는지 말하므로, 둘을 같은 문장으로 채우면 비교표가 아무것도 비교하지 않는다.
|
||||
|
||||
## 같은 구조가 다르게 보일 때
|
||||
|
||||
@@ -86,6 +92,6 @@ jpa-feed-query-performance 축 이름 「조회 전략」 축 3개
|
||||
fetch-join-paging ← CASE 1
|
||||
```
|
||||
|
||||
축마다 기록이 하나씩이고 축 제목을 그 기록 제목과 비슷하게 적으면 「문서가 그대로 나온다」로 보인다.
|
||||
축마다 기록이 하나씩이고 축 제목을 그 기록 제목과 비슷하게 적으면 「문서가 그대로 나온다」로 보인다. 구조 차이가 아니라 내용 양의 차이다.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
+9
-7
@@ -32,24 +32,26 @@ source:
|
||||
|
||||
## 결정문
|
||||
|
||||
한 질문에 여러 구조를 만들어 비교하는 경우, 주제를 그 수만큼 쪼개지 않고 주제 안에 축을 하나 둔다. 축의 이름은 주제가 정하고, 기록은 여러 축에 걸릴 수 있으며, 아무 축에도 걸리지 않은 기록은 그 주제의 공통 기록으로 읽는다.
|
||||
한 질문에 여러 구조를 만들어 비교하는 경우, 주제를 그 수만큼 쪼개지 않고 주제 안에 축을 하나 둔다.
|
||||
|
||||
축의 이름은 주제가 정하고, 기록은 여러 축에 걸릴 수 있으며, 아무 축에도 걸리지 않은 기록은 그 주제의 공통 기록으로 읽는다.
|
||||
|
||||
## 판단 이유
|
||||
|
||||
주제를 넷으로 쪼개면 PKCE·CSRF·Authorization Code 처럼 네 구조가 함께 쓰는 기록을 어디에 둘지 애매해진다. 어느 한 주제에 넣으면 나머지 셋에서 그 기록에 닿을 수 없고, 넷에 복사하면 같은 글이 넷이 된다.
|
||||
주제를 넷으로 쪼개면 네 구조가 함께 쓰는 기록을 어디에 둘지 애매해진다. PKCE·CSRF·Authorization Code 가 그런 기록이다. 어느 한 주제에 넣으면 나머지 셋에서 그 기록에 닿을 수 없고, 넷에 복사하면 같은 글이 넷이 된다.
|
||||
|
||||
네 주제가 나란히 서면 그것이 같은 질문의 네 답이라는 것을 화면이 말하지 못해 비교도 어려워진다.
|
||||
비교도 어려워진다. 네 주제가 나란히 서면 그것이 같은 질문의 네 답이라는 것을 화면이 말하지 못하고, 독자는 목록에서 넷을 각각 열어 봐야 한다.
|
||||
|
||||
축을 주제 안에 두면 공통 기록은 축을 고르지 않고 두면 되고, 여러 구조에 걸치는 기록은 여러 축에 건다. 화면은 축을 나란히 세워 비교로 그린다.
|
||||
|
||||
축 이름을 주제가 정하게 한 것은 주제마다 비교 축이 다르기 때문이다. 인증 경계 주제의 축은 구조이고 조회 성능 주제의 축은 조회 전략이다.
|
||||
축 이름을 주제가 정하게 한 것은 주제마다 비교하는 것이 다르기 때문이다. 인증 경계 주제는 credential 을 어디에 두느냐로 갈리고 조회 성능 주제는 같은 데이터를 어떻게 읽느냐로 갈린다.
|
||||
|
||||
## 영향
|
||||
|
||||
주제의 논지와 축의 이름·요약·결론은 기록에서 자동으로 나오지 않는다. 사람이 쓰는 칸이라 기록을 스무 개 붙여도 그 문장은 누가 고치기 전까지 바뀌지 않는다.
|
||||
축의 이름·요약·결론이 기록에서 자동으로 나오지 않는다. 주제의 논지와 축의 요약·결론은 사람이 쓰는 칸이고, 기록을 스무 개 붙여도 그 문장은 누가 고치기 전까지 그대로다.
|
||||
|
||||
홈의 비교 구역에서 줄은 문서가 아니라 축이다. 줄을 늘리려면 Studio 에서 축을 추가해야 한다.
|
||||
|
||||
기록이 어느 축에 걸리는지를 담는 표에 외래키를 걸 수 없다. 기록이 종류마다 다른 테이블에 살기 때문이다.
|
||||
기록이 어느 축에 걸리는지를 담는 표에 외래키를 걸 수 없다. 기록이 종류마다 다른 테이블에 살기 때문이고, 그래서 기록을 지울 때 그 쌍을 함께 지우는 것은 코드가 한다.
|
||||
|
||||
축에 걸린 기록이 하나뿐이면 축 제목과 그 기록 제목이 비슷해져 「문서가 그대로 나온다」로 보인다. 구조 차이가 아니라 내용 양의 차이다.
|
||||
축이 붙은 기록 수가 적으면 「문서가 그대로 나온다」로 보인다. 축마다 기록이 하나씩이고 축 제목을 그 기록 제목과 비슷하게 적으면 그렇게 읽힌다. 구조 차이가 아니라 내용 양의 차이다.
|
||||
|
||||
+35
-6
@@ -57,22 +57,51 @@ tech-log-backend : 857e6a9 — 삭제 거절 사유를 클라이언트 안전
|
||||
|
||||
## 화면이 세 가지를 추측했다
|
||||
|
||||
화면 문구는 「게시됐거나, 참조하는 곳이 있거나, 누가 먼저 고쳤을 수 있습니다」였다. 세 가지 원인을 나열하고 어느 것인지는 말하지 않는다.
|
||||
작업본 삭제가 실패하면 화면이 「게시됐거나, 참조하는 곳이 있거나, 누가 먼저 고쳤을 수 있습니다」를 적었다. 세 가지 원인을 나열하고 어느 것인지는 말하지 않는다.
|
||||
|
||||
서버는 하나를 답하고 있었다. 게이트웨이가 그 답을 버리고 화면이 자기 목록을 그렸다.
|
||||
|
||||
## 서버는 코드로 답한다
|
||||
|
||||
서버는 오류 코드마다 고정 문구를 갖고 있다. 삭제와 관련된 것만 여섯이다.
|
||||
|
||||
```java
|
||||
case VERSION_CONFLICT -> "다른 곳에서 먼저 수정되었습니다. 새로 불러온 뒤 다시 시도해 주세요";
|
||||
case DOCUMENT_PUBLISHED -> "공개된 기록은 삭제할 수 없습니다. 먼저 공개를 취소해 주세요";
|
||||
case DOCUMENT_IN_USE -> "이 기록을 참조하는 곳이 있어 삭제할 수 없습니다";
|
||||
case QUESTION_IN_USE -> "이 질문을 참조하는 곳이 있어 삭제할 수 없습니다";
|
||||
case DECISION_IN_USE -> "이 결정을 참조하는 곳이 있어 삭제할 수 없습니다";
|
||||
case TOPIC_IN_USE -> "이 주제를 쓰는 기록이 있어 삭제할 수 없습니다";
|
||||
```
|
||||
|
||||
화면이 추측하던 셋이 이 중 셋에 그대로 있다. 코드는 카테고리와 재시도 가능 여부도 함께 답한다.
|
||||
|
||||
## 왜 원문 메시지를 안 내보내나
|
||||
|
||||
고정 문구를 두는 이유가 그 클래스의 javadoc 에 있다.
|
||||
|
||||
> code 별 고정 문구. 예외의 원문 메시지는 진단용이라 그대로 내보내지 않는다 — 저장소 제약 이름이나 SQL 조각이 새어 나갈 수 있고, 그건 클라이언트가 분기할 값도 아니다.
|
||||
|
||||
그래서 화면이 실어 나를 수 있는 것은 이 고정 문구다. 서버 안의 진단 메시지는 서버 로그에만 남는다.
|
||||
|
||||
## 무엇이 구분되지 않았나
|
||||
|
||||
버전 충돌은 「누가 먼저 고쳤다」이고 참조 존재는 「사용 중」이다. 문구가 셋을 한꺼번에 적으므로 작성자는 둘을 구분할 수 없다.
|
||||
세 원인은 해야 할 일이 다르다.
|
||||
|
||||
버전 충돌이면 다시 받아서 지우면 되고, 참조가 있으면 그 참조를 먼저 풀어야 한다.
|
||||
| 무엇이 막았나 | 작성자가 할 일 |
|
||||
|---|---|
|
||||
| 버전 충돌 | 다시 받아서 지운다 |
|
||||
| 참조가 있음 | 그 참조를 먼저 푼다 |
|
||||
| 이미 게시됨 | 게시를 취소한다 |
|
||||
|
||||
## 서버의 답을 실어 나른다
|
||||
문구가 셋을 함께 적으므로 작성자는 어느 것인지 모른 채 세 가지를 다 시도하게 된다. 어떤 삭제는 되고 어떤 삭제는 안 되는 것을 지켜보면서도 둘을 구분할 방법이 없었다.
|
||||
|
||||
게이트웨이가 서버의 클라이언트 안전 메시지를 그대로 싣고 화면이 그것을 보인다.
|
||||
## 고친 것
|
||||
|
||||
게이트웨이가 서버의 클라이언트 안전 메시지를 그대로 싣고 화면이 그것을 보인다. 서버가 답하지 않은 것을 화면이 만들지 않는다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
서버가 답하는 그 한 문장이 다섯 이유를 다 같은 말로 덮고 있다. 참조 검사가 다섯 테이블을 하나로 묶어 검사하기 때문이고, 그 문제는 아직 열려 있다.
|
||||
코드 하나에 문구 하나라는 구조는 참조가 어느 표에서 왔는지까지는 말하지 못한다. 다섯 참조가 전부 같은 코드로 나가므로, 그 안에서 어느 것이 막았는지는 아직 열려 있다.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
+12
-4
@@ -67,9 +67,9 @@ tech-log-frontend : dc2fda7 · ca1fc92 · 82e992d
|
||||
바로 아래 「종류별로 읽기」: 검증 기록 · 동작 원리 · 적용 기준 · 열린 질문
|
||||
```
|
||||
|
||||
두 목록이 세로로 붙어 있다. 위는 계약의 enum 이름을, 아래는 사람이 붙인 이름을 쓴다.
|
||||
두 목록이 세로로 붙어 있다. 위는 계약의 enum 이름을, 아래는 사람이 붙인 이름을 쓴다. 독자는 둘이 같은 것이라는 단서를 어디서도 받지 못했다.
|
||||
|
||||
이 화면은 이름을 바꾸기 전보다 나빠졌다. 바꾸기 전에는 양쪽이 다 enum 이름이라 적어도 같아 보였다.
|
||||
이름을 바꾸기 전에는 양쪽이 다 enum 이름이었으므로 적어도 같아 보였다. 이 화면은 이름을 바꾸면서 나빠졌다.
|
||||
|
||||
## 표가 여섯 벌이었다
|
||||
|
||||
@@ -77,9 +77,17 @@ tech-log-frontend : dc2fda7 · ca1fc92 · 82e992d
|
||||
|
||||
> 표가 화면마다 복사되어 **여섯 벌**이었고 그래서 갈라졌다: 같은 QUESTION 이 공개 화면에서 "Open Question", 작업본 목록과 게시 기록에서 "Question", 편집기 상태 줄에서 "QUESTION" 이었다. **쓰는 사람은 같은 문서를 화면마다 다른 이름으로 만난다.**
|
||||
|
||||
여섯 벌이 동시에 틀린 것이 아니다. 이름을 바꿀 때 그중 일부만 고쳤고, 어느 것을 고쳤는지가 화면마다 달랐다.
|
||||
|
||||
## 표 하나로 모았다
|
||||
|
||||
종류에서 표시 이름으로 가는 표를 하나 만들고 여섯 곳이 그것을 쓰게 했다. 종류가 늘면 그 표에서 빠진 값을 컴파일러가 잡는다.
|
||||
종류에서 표시 이름으로 가는 표를 하나 만들고 여섯 곳이 그것을 쓰게 했다. 그 표의 javadoc 이 왜 한 곳에 있는지를 적는다.
|
||||
|
||||
> 한 곳에 두면 다음에 종류가 늘어날 때도 한 번만 고친다. 종류를 더하면 이 표가 비어 있는 것을 타입이 잡는다 — `Record<RecordKind, string>` 이므로 빠진 종류가 있으면 컴파일되지 않는다.
|
||||
|
||||
같은 파일에 표가 하나 더 있다. 다섯 종류를 셋으로 접어 지식의 상태로 만드는 표다.
|
||||
|
||||
> 독자가 실제로 구분해야 하는 것은 지식의 상태다. 다섯 종류를 셋으로 접는다 — 확인한 것, 정리한 것, 아직 모르는 것. 목록에서 미해결만 눈에 띄게 하는 것이 이 표의 쓰임이다. 미해결이 이 기록의 가장 정직한 신호인데 다섯 종류가 같은 회색 11px 로 나오면 그것이 가장 안 보인다.
|
||||
|
||||
## 편집기 칸 이름도 맞췄다
|
||||
|
||||
@@ -96,6 +104,6 @@ tech-log-frontend : dc2fda7 · ca1fc92 · 82e992d
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
이름을 바꾸기 전보다 나빠진 화면이 홈 하나였다는 것은 화면으로 확인했다. 다른 화면에서 두 이름이 같이 뜨는 곳을 전수로 세지는 않았다.
|
||||
이름을 바꾸기 전보다 나빠진 화면이 홈이라는 것은 화면으로 확인했다. 다른 화면에서 두 이름이 같이 뜨는 곳을 전수로 세지는 않았다.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
+9
-3
@@ -62,7 +62,7 @@ tech-log-frontend : a6413d0 → af5a6bb
|
||||
|
||||
Case, Reference, Open Question 은 그 글을 어떤 형식에 담았는지를 말한다. 그 글이 독자에게 무엇을 주는지는 말하지 않는다.
|
||||
|
||||
독자는 그 말을 먼저 배워야 목록을 읽을 수 있었다. 뜻풀이는 홈 바닥 2,000px 아래에 있었다.
|
||||
독자는 그 말을 먼저 배워야 목록을 읽을 수 있었고, 뜻풀이는 홈 바닥 2,000px 아래에 있었다. 목록을 훑는 사람이 거기까지 내려가지 않는다.
|
||||
|
||||
## 1차 — 하는 일을 말하게 했다
|
||||
|
||||
@@ -72,9 +72,11 @@ Question → 아직 모르는 것 Concept → 어떻게 동작하나
|
||||
Decision → 이렇게 하기로
|
||||
```
|
||||
|
||||
이름만 읽어도 그 글이 무엇을 주는지 알 수 있다. 뜻풀이를 찾아 내려갈 이유가 없어진다.
|
||||
|
||||
## 2차 — 문어체로 다시 세웠다
|
||||
|
||||
1차 안이 기술 기록의 톤에 비해 가벼웠다. 역할은 그대로 말하되 문어체로 바꿨다.
|
||||
**그런데 이게 기술 기록의 톤에 비해 가벼웠다.** 역할은 그대로 말하되 문어체로 바꿨다.
|
||||
|
||||
```text
|
||||
CASE → 검증 기록 CONCEPT → 동작 원리
|
||||
@@ -82,9 +84,13 @@ REFERENCE → 적용 기준 DECISION → 설계 결정
|
||||
QUESTION → 열린 질문
|
||||
```
|
||||
|
||||
두 안이 말하는 것은 같다. 「직접 해보니」와 「검증 기록」은 둘 다 그 글이 재현한 결과라고 말한다. 갈린 것은 이 사이트의 다른 글들이 쓰는 어조와 맞느냐다.
|
||||
|
||||
## 계약의 kind 는 그대로 뒀다
|
||||
|
||||
바꾼 것은 화면에 보이는 이름이다. 계약의 `RecordKind` 는 다섯 값 그대로이고 주소도 바뀌지 않았다.
|
||||
바꾼 것은 화면에 보이는 이름이다. 계약의 `RecordKind` 는 다섯 값 그대로이고 주소도 그대로다.
|
||||
|
||||
표시 이름과 계약 값을 갈라 두었기 때문에 두 번 바꾸면서 계약을 한 번도 건드리지 않았다. 계약을 바꿨다면 반입한 두 저장소가 함께 움직여야 했고, 이미 게시된 주소도 함께 흔들렸을 것이다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+15
-14
@@ -29,19 +29,25 @@ source:
|
||||
|
||||
작성자의 목소리로 쓰인 글을 고쳐 쓰다 두 번 거절당하는 것을 막는다.
|
||||
|
||||
톤을 지적할 때 사용자가 가리키는 것은 문장의 품질이 아니라 그 말을 누가 쓰는가다. 「더 나은 문장」을 제안할 때와 그 사람의 말투로 쓸 때 필요한 것이 다르고, 후자는 제안하는 쪽이 잘하지 못한다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. 톤을 지적받으면 고쳐 쓰기 전에 어떤 말을 쓸지 묻는다
|
||||
|
||||
「더 나은 문장」을 제안하는 것과 그 사람의 말투로 쓰는 것은 다른 일이고, 후자는 제안하는 쪽이 잘하지 못한다.
|
||||
고쳐 쓴 안을 내면 그것도 같은 이유로 거절될 수 있다. 실제로 첫 번째 안이 거절당했고, 결국 사용자가 직접 쓴 텍스트를 그대로 실었다.
|
||||
|
||||
### 2. 무엇이 AI 스러운지 구체적으로 받아 적는다
|
||||
|
||||
무엇을 하는지 말하지 않는 동사로 끝나는 것과 번역투가 실제로 지적된 두 가지였다.
|
||||
지적이 「AI 스럽다」로 끝나면 무엇을 고칠지 알 수 없다. 이 저장소에서 받은 지적은 두 갈래였다 — 무엇을 하는지 말하지 않는 동사로 끝나는 것(「섞는다」·「함께 기록한다」·「흩어지지 않게」)과 번역투(「결론이 서는 조건」·「프로젝트를 답니다」·「접근을 나눠 견주고」).
|
||||
|
||||
### 3. 작성자가 이미 쓰는 말투를 따른다
|
||||
|
||||
Case 소제목에 쓰는 말이 「~한 것」 명사형이면 새 제목도 그 형태로 맞춘다. 의문형 꼬리와 이 기록에서 쓰지 않는 낱말은 쓰지 않는다.
|
||||
새 문구를 만들 때 기존 글에 없던 어미나 낱말을 들이지 않는다. 이 저장소의 Case 소제목은 「이 구조에서 감수한 것」처럼 「~한 것」 명사형이라, 의문형 꼬리를 단 「무엇을 견줬나」는 그 소제목들과 나란히 서지 못했다.
|
||||
|
||||
### 4. 고친 결과를 전후로 함께 적는다
|
||||
|
||||
무엇이 무엇으로 바뀌었는지 남겨 두면 다음에 같은 문구를 고칠 때 방향이 정해진다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
@@ -52,17 +58,12 @@ Case 소제목에 쓰는 말이 「~한 것」 명사형이면 새 제목도 그
|
||||
## 예외
|
||||
|
||||
- 오류 문구처럼 서버가 답한 사실을 그대로 실어야 하는 곳에서는 말투를 묻지 않는다. 무엇을 실을지를 먼저 정한다.
|
||||
|
||||
- 계약이나 코드가 정한 이름은 이 규칙에서 뺀다. 표시 이름만 바꾸고 계약 값은 건드리지 않는다.
|
||||
- 계약이나 코드가 정한 이름은 이 규칙에서 뺀다. 표시 이름만 바꾸고 계약 값은 그대로 둔다.
|
||||
|
||||
## 예시
|
||||
|
||||
- 「섞는다」·「함께 기록한다」·「흩어지지 않게」가 무엇을 하는지 말하지 않는 동사로 끝난다고 지적받았다.
|
||||
|
||||
- 「결론이 서는 조건」·「프로젝트를 답니다」·「접근을 나눠 견주고」가 번역투로 지적받았다.
|
||||
|
||||
- 「무엇을 견줬나」는 의문형 꼬리에 이 기록에서 쓰지 않는 낱말이었다. 작성자가 Case 소제목에 쓰는 말은 「이 구조에서 감수한 것」처럼 「~한 것」 명사형이라 그쪽에 맞췄다.
|
||||
|
||||
- 「이 프로젝트가 밝힌 것」은 「프로젝트를 통해 확인한 결과」로, 「운영 가능한 설계로 연결합니다」는 「실제 운영에 적용할 수 있는 형태로 정리합니다」로 바꿨다.
|
||||
|
||||
- 고쳐 쓴 첫 번째 안이 거절당한 뒤 사용자가 직접 쓴 텍스트를 실었다.
|
||||
- 「섞는다」·「함께 기록한다」·「흩어지지 않게」가 무엇을 하는지 말하지 않는 동사로 끝난다고 지적받았다
|
||||
- 「결론이 서는 조건」·「프로젝트를 답니다」·「접근을 나눠 견주고」가 번역투로 지적받았다
|
||||
- 「무엇을 견줬나」는 의문형 꼬리에 이 기록에서 쓰지 않는 낱말이었다. 작성자가 Case 소제목에 쓰는 말은 「~한 것」 명사형이라 그쪽에 맞췄다
|
||||
- 「이 프로젝트가 밝힌 것」을 「프로젝트를 통해 확인한 결과」로 바꿨다
|
||||
- 「운영 가능한 설계로 연결합니다」를 「실제 운영에 적용할 수 있는 형태로 정리합니다」로 바꿨다
|
||||
|
||||
@@ -976,7 +976,8 @@
|
||||
"evidenceFiles": [
|
||||
"../../../final/evidence/raw/db/decision-path-after-v15.txt",
|
||||
"../../../final/evidence/raw/api/decision-anchor-fixed.txt",
|
||||
"../../../final/evidence/raw/audit/dead-link-sweep.txt"
|
||||
"../../../final/evidence/raw/audit/dead-link-sweep.txt",
|
||||
"../../../final/evidence/raw/audit/link-audit.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1682,11 +1683,21 @@
|
||||
"file": "an-axis-inside-a-topic/case/case-the-comparison-band-changed-three-times.md",
|
||||
"status": "게시 전",
|
||||
"studioId": "",
|
||||
"assets": [],
|
||||
"assetFiles": [],
|
||||
"assets": [
|
||||
"home-tabs-keycloak",
|
||||
"home-topic-tabs-2",
|
||||
"home-tabs-grouped"
|
||||
],
|
||||
"assetFiles": [
|
||||
"home-tabs-keycloak.png",
|
||||
"home-topic-tabs-2.png",
|
||||
"home-tabs-grouped.png"
|
||||
],
|
||||
"evidenceFiles": [
|
||||
"../../../final/evidence/browser/home-tabs-grouped.png",
|
||||
"../../../final/evidence/browser/home-tabs-keycloak.png",
|
||||
"../../../final/evidence/browser/home-topic-tabs.png",
|
||||
"../../../final/evidence/browser/home-topic-tabs-2.png",
|
||||
"../../../final/evidence/browser/home-tabs-grouped.png",
|
||||
"../../../final/evidence/browser/tab-metrics.txt"
|
||||
]
|
||||
}
|
||||
@@ -3018,5 +3029,5 @@
|
||||
"unlisted": 0,
|
||||
"candidates": 91
|
||||
},
|
||||
"ssotSha256": "4f2c6b31f152a7d786e76d8efda5fe27c056ff685a031ba8b772c9595e7d03ad"
|
||||
"ssotSha256": "6fbb5ffd943df5a6ce53624997f9ebc5b3c80de9c0adf17f62af951f8e55488d"
|
||||
}
|
||||
|
||||
+18
-3
@@ -64,17 +64,32 @@ tsconfig : 루트가 project references 만 나열
|
||||
|
||||
이 저장소의 루트 tsconfig 는 컴파일 대상을 갖지 않는다. `"files": []` 에 project references 만 나열하고, 각 프로젝트는 자기 tsconfig 를 갖는다.
|
||||
|
||||
`npx tsc --noEmit` 은 references 를 따라가지 않는다. 루트가 지목한 파일 집합만 보고, 그 집합이 비어 있으므로 아무것도 검사하지 않은 채 0 으로 끝난다. references 를 따라가려면 그 모드로 부르는 별도 명령이 필요하다.
|
||||
`npx tsc --noEmit` 은 references 를 따라가지 않는다. 루트가 지목한 파일 집합만 보고, 그 집합이 비어 있으므로 아무것도 검사하지 않은 채 0 으로 끝난다.
|
||||
|
||||
## 실제 검사는 여섯 번 돈다
|
||||
|
||||
`check:types` 는 tsconfig 여섯 개를 차례로 돌린다.
|
||||
|
||||
| tsconfig | 무엇을 검사하나 |
|
||||
|---|---|
|
||||
| app | 애플리케이션 소스 |
|
||||
| node | 빌드·스크립트 |
|
||||
| test | 테스트 |
|
||||
| recipes | 선택 레시피 |
|
||||
| web-worker | 웹 워커 |
|
||||
| service-worker | 서비스 워커 |
|
||||
|
||||
여섯을 따로 두는 이유는 각각 다른 런타임 타입 정의를 쓰기 때문이다. 워커는 DOM 을 갖지 않고 node 는 브라우저 전역을 갖지 않는다.
|
||||
|
||||
## 통과가 무엇을 뜻했나
|
||||
|
||||
이 명령이 성공했을 때 확인된 것은 「루트 tsconfig 가 유효하다」까지다. 코드가 컴파일되는지는 묻지 않았다.
|
||||
짧은 명령이 성공했을 때 확인된 것은 「루트 tsconfig 가 유효하다」까지다. 코드가 컴파일되는지는 묻지 않았다.
|
||||
|
||||
운영에서 릴리즈 목록 화면이 비었고 콘솔에 `ReferenceError` 가 났다. 링크 컴포넌트 import 가 빠졌고, 이동 함수는 정의된 적이 없었다. 둘 다 컴파일이 잡는 종류의 오류인데 컴파일이 돌지 않았다.
|
||||
|
||||
## 올바른 명령으로 돌렸을 때
|
||||
|
||||
여섯 프로젝트를 각각 돌리는 명령으로 바꾸자 릴리즈 목록의 두 오류에 더해 네 가지가 함께 나왔다.
|
||||
여섯을 다 돌리자 릴리즈 목록의 두 오류에 더해 네 가지가 함께 나왔다.
|
||||
|
||||
| 무엇이 나왔나 | 어떤 종류인가 |
|
||||
|---|---|
|
||||
|
||||
+12
-5
@@ -64,22 +64,29 @@ tech-log-frontend : fd73bc8 · fe6b56a
|
||||
|
||||
> 화면 테스트는 `test:unit` 이 아니라 `test:tech-log` 가 돌린다. 그것을 돌리지 않아 위 두 결함과, 의도한 변경에 고정돼 있던 단언들이 **23건 빨간 채로 여러 커밋을 지나갔다.**
|
||||
|
||||
23건 중에는 실제 결함을 잡은 것도 있고 의도한 변경에 고정된 단언도 있었다. 둘을 구분하려면 그 명령을 돌려야 하는데, 돌리지 않으니 둘 다 그대로 남았다.
|
||||
23건이 한 종류가 아니었다는 것이 문제를 키웠다. 일부는 실제 결함을 잡은 것이고 일부는 의도한 변경에 단언이 고정돼 있던 것이다. 둘을 구분하려면 그 명령을 돌려 하나씩 봐야 하는데, 돌리지 않으니 둘 다 그대로 남았다.
|
||||
|
||||
## 게이트 기준값을 빠뜨렸다
|
||||
|
||||
주제 화면 셋을 더하면서 CI 게이트가 요구하는 기준값 셋을 올리지 않았다. 게이트는 정확히 그것을 거절한다.
|
||||
주제 화면 셋을 더할 때 CI 게이트가 요구하는 기준값 셋을 함께 올리지 않았다. 게이트는 라우트 집합과 증거 집합이 정확히 일치하기를 요구하므로 그 상태를 정확히 거절한다.
|
||||
|
||||
게이트가 빨간 채로 여러 커밋을 지나갔고, 결정 링크 404 를 고치던 커밋에서야 맞췄다.
|
||||
게이트가 빨간 채로 여러 커밋을 지나갔고, 결정 링크 404 를 고치던 커밋에서야 함께 맞췄다.
|
||||
|
||||
## 가드가 아니라 실행이 빠졌다
|
||||
|
||||
두 번 다 가드는 정확했다. 화면 테스트는 실제 결함을 잡았고 게이트는 기준값이 어긋난 것을 정확히 거절했다.
|
||||
두 번 다 가드는 정확했다.
|
||||
|
||||
| | 가드가 있었나 | 가드가 잡았나 | 무엇이 빠졌나 |
|
||||
|---|---|---|---|
|
||||
| 화면 테스트 23건 | o | o | 그 명령을 돌리지 않음 |
|
||||
| CI 게이트 기준값 | o | o | 결과를 보지 않음 |
|
||||
|
||||
가드가 없어서 샌 것이 아니라 결과를 아무도 읽지 않아서 샜다. 그래서 가드를 하나 더 넣는 것으로는 같은 일이 다시 난다.
|
||||
|
||||
> **가드는 CI 에 묶여야 의미가 있습니다.** 사람이 기억해서 돌리는 가드는 절반만 존재합니다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
다섯 명령을 CI 에 묶는 작업은 하지 않았다. 메모리와 배포 전 검증 목록으로만 남겨 뒀다.
|
||||
다섯 명령을 CI 에 묶는 작업은 하지 않았다. 지금 남은 것은 메모리와 배포 전 검증 목록이고, 그 목록을 읽는 것도 사람이다.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
+13
-13
@@ -31,17 +31,21 @@ source:
|
||||
|
||||
## 목적
|
||||
|
||||
무엇도 잡지 못하는 가드가 초록불로 남는 것을 막는다. 이 상태는 가드가 있는 것과 화면에서 구분되지 않는다.
|
||||
무엇도 잡지 못하는 가드가 초록불로 남는 것을 막는다.
|
||||
|
||||
가드를 넣었다는 것과 그 가드가 무엇을 잡는다는 것은 다르다. 둘은 화면에서 구분되지 않는다 — 검사가 통과했을 때 그 결함이 원래 없었는지 검사가 그것을 안 보는지 알 방법이 없다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. 결함을 되돌려 그 가드가 실제로 멈추는 것을 확인한 뒤 커밋한다
|
||||
|
||||
계약에서 값을 빼고 대조 검사가 빨개지는지 본다. 매핑을 떼어 보고 그 연산 하나를 짚는지 본다.
|
||||
계약에서 값을 빼고 대조 검사가 빨개지는지 본다. 매핑을 떼어 보고 그 연산을 짚는지 본다. 굵기 선언을 빼고 제목 급 검사가 멈추는지 본다.
|
||||
|
||||
되돌리는 것이 어려우면 그 가드가 무엇을 전제하는지 다시 본다. 되돌릴 수 없는 상태를 잡는 가드는 그 상태가 어떻게 생기는지를 아무도 모른다는 뜻이다.
|
||||
|
||||
### 2. 가드가 짚는 대상이 하나인지 본다
|
||||
|
||||
전부를 짚으면 어디가 문제인지 알 수 없고, 결과가 곧 읽히지 않는다.
|
||||
전부를 짚으면 어디가 문제인지 알 수 없고, 결과가 곧 읽히지 않는다. 매핑 하나를 떼었을 때 그 연산 하나만 나와야 한다.
|
||||
|
||||
### 3. 되돌릴 수 없는 것은 현재 상태를 대신 증거로 남긴다
|
||||
|
||||
@@ -49,7 +53,7 @@ source:
|
||||
|
||||
### 4. 가드를 CI 에 묶는다
|
||||
|
||||
사람이 기억해서 돌리는 가드는 절반만 존재한다.
|
||||
사람이 기억해서 돌리는 가드는 절반만 존재한다. 가드가 정확해도 결과를 아무도 읽지 않으면 빨간 채로 여러 커밋을 지나간다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
@@ -59,16 +63,12 @@ source:
|
||||
|
||||
## 예외
|
||||
|
||||
- 결함을 되돌릴 수 없는 것 — 이미 마이그레이션으로 고친 데이터, 지난 배포에서만 나던 상태 — 은 현재 상태가 고쳐져 있음을 증거로 남긴다.
|
||||
|
||||
- 결함을 되돌릴 수 없는 것 — 이미 마이그레이션으로 고친 데이터, 지난 배포에서만 나던 상태 — 은 현재 상태가 고쳐져 있음을 대신 증거로 남긴다.
|
||||
- 기존 가드를 옮기거나 이름만 바꾸는 변경은 되돌려 확인하지 않아도 된다. 다만 옮긴 뒤에 한 번은 돌린다.
|
||||
|
||||
## 예시
|
||||
|
||||
- 가드 셋을 각각 결함으로 되돌려 실제로 빨개지는 것을 확인한 기록을 남겼다.
|
||||
|
||||
- 계약에서 CONCEPT 을 빼자 백엔드의 계약 대조 테스트가 빨개졌다.
|
||||
|
||||
- 매핑을 떼어 보고 계약 대조 테스트가 그 연산 하나를 정확히 짚는 것을 확인했다.
|
||||
|
||||
- 굵기 선언을 빼 보고 제목 급 검사가 실제로 멈추는 것을 확인했다.
|
||||
- 가드 셋을 각각 결함으로 되돌려 실제로 빨개지는 것을 확인한 기록을 남겼다
|
||||
- 계약에서 CONCEPT 을 빼자 백엔드의 계약 대조 테스트가 빨개졌다
|
||||
- 매핑을 떼어 보고 계약 대조 테스트가 그 연산 하나를 정확히 짚는 것을 확인했다
|
||||
- 굵기 선언을 빼 보고 제목 급 검사가 실제로 멈추는 것을 확인했다
|
||||
|
||||
+16
-17
@@ -31,26 +31,32 @@ CI 에 묶이지 않은 검증이 남아 있으면 그것을 돌리는 것은
|
||||
|
||||
## 목적
|
||||
|
||||
배포 전에 돌려야 하는 것을 사람이 기억에 의존해 고르는 것을 막는다. 두 번 빠뜨려 결함이 배포까지 갔다.
|
||||
배포 전에 돌려야 하는 것을 사람이 기억에 의존해 고르는 것을 막는다.
|
||||
|
||||
이 저장소에서 두 번 빠뜨려 결함이 배포까지 갔다. 명령이 여럿이고 그중 일부만 도는 것이 가능한 구조에서는 「돌렸다」가 무엇을 돌렸다는 뜻인지 정해 두어야 한다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. 프론트는 다섯 개를 다 돌린다
|
||||
|
||||
타입 검사 · lint · 단위 테스트 · 컴포넌트 테스트 · 화면 테스트다. 화면 테스트는 단위 테스트 명령이 돌리지 않는다.
|
||||
타입 검사·lint·단위 테스트·컴포넌트 테스트·화면 테스트다. 화면 테스트는 단위 테스트 명령이 돌리지 않으므로, 넷을 돌리고 「전부 통과」라고 읽으면 화면 테스트가 빠진다.
|
||||
|
||||
### 2. 타입 검사는 프로젝트를 순회하는 명령으로 돌린다
|
||||
|
||||
루트 tsconfig 를 직접 부르는 명령은 한 파일도 검사하지 않고 성공한다.
|
||||
루트 tsconfig 를 직접 부르는 명령은 한 파일도 검사하지 않고 성공한다. 루트가 `"files": []` 에 project references 만 나열하기 때문이다.
|
||||
|
||||
### 3. 백엔드는 커밋한 뒤에 빌드한다
|
||||
|
||||
빌드 산출물 이름에 커밋 해시가 들어간다. 작업 트리가 더러우면 해시가 달라져 stale 산출물 검사가 멈춘다.
|
||||
빌드 산출물 이름에 커밋 해시가 들어간다. 작업 트리가 더러우면 해시가 달라져 stale 산출물 검사가 멈춘다. 이 순서를 몰라 두 번 헤맸다.
|
||||
|
||||
### 4. 테스트를 npm 이나 npx 로 감싸 돌리지 않는다
|
||||
|
||||
`npm_config_*` 환경 변수가 설정되어 CI 워크플로 생성 테스트가 실패한다. 그 변수를 지우고 실행기를 직접 부른다.
|
||||
|
||||
### 5. 환경 때문에 실패하는 것은 실패로 세지 않되 목록에 적는다
|
||||
|
||||
하위 프로세스를 띄우는 세 케이스는 이 환경에서 실패하고 같은 리비전의 다른 실행에서도 똑같이 재현된다. 코드 변경과 무관하다는 것을 적어 두지 않으면 다음 사람이 그것을 고치려 든다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
- CI 에 묶이지 않은 검증이 남아 있는 저장소에서 배포 직전에 하는 일
|
||||
@@ -59,19 +65,12 @@ CI 에 묶이지 않은 검증이 남아 있으면 그것을 돌리는 것은
|
||||
|
||||
## 예외
|
||||
|
||||
- CI 가 그 명령을 돌리면 이 목록에서 뺀다.
|
||||
|
||||
- 환경 때문에 실패하는 것은 실패로 세지 않는다. 하위 프로세스를 띄우는 세 케이스는 이 환경에서 실패하고 같은 리비전의 다른 실행에서도 똑같이 재현되므로 코드 변경과 무관하다.
|
||||
- CI 가 그 명령을 돌리면 이 목록에서 뺀다. 사람이 기억해서 돌리는 가드는 절반만 존재한다.
|
||||
- 테스트 실행기가 메모리를 더 필요로 하는 조합이면 힙을 올린다. 증상이 테스트 실패가 아니라 실행기를 완료할 수 없다는 메시지로 나와 원인을 가린다.
|
||||
|
||||
## 예시
|
||||
|
||||
- 프론트 다섯 명령 :
|
||||
`npm run check:types`
|
||||
`npm run lint`
|
||||
단위 · 컴포넌트 · 화면 테스트
|
||||
|
||||
- 백엔드 : 커밋한 뒤 stale 산출물을 지우고 빌드한다. 이 순서를 몰라 두 번 헤맸다.
|
||||
|
||||
- 설계 패키지 : 계약 자체의 유효성 · 세 계약 사이의 정합 · 프론트와 백엔드가 아는 종류와 오류 코드가 같은지, 셋을 돌린다.
|
||||
|
||||
- 테스트 JVM 힙이 기본값이면 컨텍스트 캐시와 아키텍처 검사와 컨테이너가 겹치면서 메모리가 모자란다. 증상이 테스트 실패가 아니라 실행기를 완료할 수 없다는 메시지라 원인을 가린다.
|
||||
- 화면 테스트를 돌리지 않아 23건이 빨간 채로 여러 커밋을 지나갔다
|
||||
- 루트 tsconfig 를 직접 부르는 명령이 통과해서 운영의 ReferenceError 를 못 봤다
|
||||
- 커밋 전에 빌드해 stale 산출물 검사가 멈춘 것을 두 번 겪었다
|
||||
- 설계 패키지는 계약 자체의 유효성·세 계약 사이의 정합·양쪽이 아는 종류와 오류 코드가 같은지 셋을 돌린다
|
||||
|
||||
Reference in New Issue
Block a user