작업 범위: .technical-label의 자간·대문자 규칙을 한글용 기본과 라틴 전용 수식자로 분리
기준일: 2026-08-18 UTC
manual-impact: none (공개 매뉴얼 문안 변경 없음, 표기만 조정)
배경
서체를 자가호스팅으로 적재한 직후(8908275) .technical-label의 자간이 드러났다.
letter-spacing: 0.04em + text-transform: uppercase는 라틴 라벨(On this page,
Working Guide / v1, 01 / Concept)에는 맞지만 한글 라벨(통과 조건, 읽는 것,
남기는 것, 권장 문서 · 사용자가 씁니다)에는 과해 성기게 보였다. 한글은 자간을
벌리지 않는 것이 기본이다.
RED
구현 전에 tests/browser/technical-label.spec.ts를 먼저 쓰고 실행했다.
1) [chromium] › tests/browser/technical-label.spec.ts:11:5 › 한글 라벨은 자간을 벌리지 않고, 라틴 라벨은 계속 벌린다
Error: expect(received).toBe(expected) // Object.is equality
Expected: 0
Received: 0.4736
31 | // 한글: 벌어지지 않는다. 대문자 변환도 한글에는 뜻이 없으므로 걸지 않는다.
> 32 | expect(spacingPx(koreanStyle.letterSpacing)).toBe(0);
| ^
2) [chromium] › tests/browser/technical-label.spec.ts:43:5 › 한글이 섞인 라벨은 라틴 수식자를 받지 않는다
Error: expect(received).toBe(expected) // Object.is equality
Expected: 0
Received: 0.4736
51 | const mixedSpacing = await mixed.evaluate((el) => getComputedStyle(el).letterSpacing);
> 52 | expect(spacingPx(mixedSpacing)).toBe(0);
| ^
2 failed
첫 시도에서는 한글 라벨이 SM 갈래 패널에 있어 toBeVisible()이 hidden으로 실패했다
(규칙이 아니라 탭 상태 때문). 탭 링크 .track-tab--sm을 눌러 패널을 편 뒤 위의
자간 값 불일치라는 진짜 RED를 확인했다.
GREEN
| 명령 | 결과 |
|---|---|
npx playwright test |
PASS — 119 passed (기존 117 + 신규 2) |
npm run verify:app |
PASS — verify:docs, test(unit/integration), typecheck, lint, build, verify:bundle 전부 통과 |
구현 결과
app/globals.css:.technical-label기본 규칙에서letter-spacing과text-transform을 제거해 한글용으로 만들고,.technical-label--latin수식자에 기존 표기(0.04em+uppercase)를 옮겼다. 색·서체·크기·굵기는 그대로다.- 라틴 전용 라벨 30곳에
technical-label--latin을 붙였다:app/page.tsx(9),app/guide/page.tsx(7),app/roadmap/page.tsx(5),app/5240lab/page.tsx(4),app/docs/page.tsx(1),app/not-found.tsx(1),components/MaturityLevels.tsx,components/MaturityScore.tsx,components/EvidenceExplorer.tsx. - 기본 규칙에 남긴 라벨 17곳은 전부 한글이 주인이다:
통과 조건,읽는 것,남기는 것,목차,{n}편,{n}단계,권장 문서 · {authorship},남기는 문서 · {authorship},HOW / {steps}(steps가 한글),문서 / {sourcePath}(앞머리가 한글). - 판별 기준은 "내용이 순수 라틴·숫자·기호인가"이며,
AI가 채웁니다처럼 라틴이 섞였을 뿐 한글이 주인인 것은 기본 규칙 쪽이다. 이 기준을 CSS 주석으로 남겼다.
테스트가 진짜인지
- 한글·라틴 두 값을 같은 테스트에서 읽어
koreanPx < latinPx로 비교한다. 규칙을 통째로 지우면 두 값이 같아져 실패한다. .technical-label--latin수식자를 지우면 라틴 쪽> 0.3단언과uppercase단언이 실패한다. 기본 규칙에 자간을 되돌리면 한글 쪽toBe(0)이 실패한다.- 선택자가 아니라 화면에 보이는 문구(
통과 조건,On this page,권장 문서)로 요소를 잡아, 클래스 이름만 바꾸는 리팩터링에는 견디고 표기가 되돌아가면 깨진다.
브리프와 어긋난 지점
없다. 브리프의 참조 내용(app/globals.css의 두 선언, 라벨 목록)은 현재 소스와
일치했다. 라틴 판별은 브리프 지시대로 소스를 직접 읽어 수행했다.
남은 우려
- 이 보고서는
content/docs-manifest.mjs에 등록하지 않았다. 공개 여부는 사람이 정한다.
독립 검토 지적 반영 (2026-08-18)
Critical 0건, 명세 준수 충족. Important 1건과 Minor 1건을 닫았다.
I-1 회귀 테스트가 판정 자체를 보호하지 못했다
지적: 기존 두 테스트는 통과 조건·On this page·권장 문서 세 요소만
검사한다. 규칙(CSS)이 통째로 사라지는 것은 막지만, 이 커밋의 본체인 47곳의 갈래
판정 — 한글 라벨에 --latin을 붙이거나 라틴 라벨에서 떼는 오분류 — 은 전혀 잡지
못했다.
RED: app/guide/page.tsx:86의 한글 라벨 읽는 것에 일부러
technical-label--latin을 붙이고 기존 테스트를 돌렸다. 통과했다 — 그것이
결함이다.
Running 2 tests using 2 workers
✓ 1 [chromium] › tests/browser/technical-label.spec.ts:43:5 › 한글이 섞인 라벨은 라틴 수식자를 받지 않는다 (1.4s)
✓ 2 [chromium] › tests/browser/technical-label.spec.ts:11:5 › 한글 라벨은 자간을 벌리지 않고, 라틴 라벨은 계속 벌린다 (1.4s)
2 passed (3.6s)
수정: tests/browser/technical-label.spec.ts에 라벨을 열거하지 않고 규칙
자체를 못 박는 전수 불변식 테스트를 더했다. /·/guide·/docs·/5240lab·
/roadmap 다섯 경로의 .technical-label을 $$eval로 전부 읽어(오늘 143개),
한글이 든 라벨은 letter-spacing: 0 + text-transform: none, 한글이 없는 라틴
라벨은 > 0.3px + uppercase를 단언한다. 갈래 패널이 접혀 있어도 요소는 DOM에
있으므로 탭 클릭 없이 SM 라벨까지 덮는다. 같은 주입을 유지한 채 다시 돌리니
이번엔 실패했다.
✘ 3 [chromium] › tests/browser/technical-label.spec.ts:63:5 › 모든 라벨이 갈래 규칙을 지킨다 — 한글은 자간 0, 라틴은 자간을 벌린다 (2.2s)
Error: /guide :: "읽는 것" — 한글 라벨인데 자간이 벌어져 있다
Expected: 0
Received: 0.4736
반대 방향도 확인했다. 진짜 라틴 라벨(app/5240lab/page.tsx:46
Reading key)에서 --latin을 떼는 반대 오분류를 주입하니 같은 테스트가 잡았다.
Error: /5240lab :: "Reading key" — 라틴 라벨인데 자간이 벌어지지 않았다
Expected: > 0.3
Received: 0
멀쩡한 입력을 막지 않는지도 확인했다. 다섯 경로의 라벨 143개를 전수로 덤프해
본 결과 텍스트가 비었거나 한글도 라틴 글자도 없는(숫자·기호 ·만인) 라벨은 한
곳도 없다. 숫자·중점이 섞인 라벨(01 · 브레인스토밍, 4편 · 모든 단계에 적용,
HOW / 작업 분해 · 격리 구현)은 모두 한글을 포함해 기본 규칙 쪽으로 정확히
판정된다.
다만 "한글 없음 → 라틴"이라는 이분법은 그런 라벨이 생기면 자동으로 라틴 취급해
판정을 사람 대신 정해버린다. 단언을 느슨하게 푸는 대신 정확히 좁혔다 —
분기를 셋으로 나눠 한글도 라틴 글자도 없는 라벨은 명시적으로 실패시키고, 갈래를
사람이 정해 테스트를 함께 고치도록 메시지를 남겼다. 오늘 해당 라벨이 0개이므로
멀쩡한 입력을 막지 않는다. 이 분기가 실제로 발화하는지는 라벨 텍스트를 · 01 ·로
잠깐 바꿔 확인한 뒤 원복했다.
기존 두 테스트는 남겼다. 전수 불변식이 자간·대문자 판정은 포함하지만, 기존
테스트는 그 세 라벨이 여전히 존재하고 탭을 편 뒤 실제로 visible인지까지 함께
본다 — 전수 검사가 덮지 않는 축이다.
M-1 "남은 우려"가 실제보다 과했다
{layer.id}에 한글이 들어가면 판정이 어긋난다는 우려를 적었으나 소스를 확인하니
과했다. lib/evidence-schema.ts의 HARNESS_LAYER_IDS가 as const 튜플이고
HarnessLayerId는 그 유니온이며, content/layers.ts의 HarnessLayer.id가 그
타입이다. 임의 데이터가 흘러드는 자리가 아니라 타입으로 닫혀 있고, 런타임에도
parseLayer가 집합 밖 값을 ContentValidationError로 거부한다. 한글 id가
들어오려면 튜플 자체를 고쳐야 하고, 그렇게 고쳐도 위의 전수 불변식이 /5240lab
경로에서 잡는다. 해당 항목을 "남은 우려"에서 지웠다.
검증
| 명령 | 결과 |
|---|---|
npx playwright test |
PASS — 120 passed (기준선 119 + 신규 1) |
npm run verify:app |
PASS |
주입은 전부 원복했고 git status로 tests/browser/technical-label.spec.ts 외에
변경이 없음을 확인했다. next-env.d.ts는 이 커밋 이전부터 dirty였으므로 손대지
않고 스테이징에서도 제외했다.