Files
document-haness/docs/TechLog/final/evidence/raw/guards/kind-tables-now.txt
T

73 lines
5.8 KiB
Plaintext

「손으로 나열한 목록」이 표로 바뀌었나 — 현재 코드
출처: tech-log-frontend @ 2b2f443 / tech-log-backend @ 8cd8ee3
조회: 2026-09-04
§3 의 주장: 종류를 나열하는 자리를 Record<Kind,_> 나 sealed switch 식으로 바꿔
새 종류가 늘면 컴파일러가 빈 자리를 잡게 했다.
======================================================================
### 프론트 — Record<Kind, _> 로 바뀐 자리
src/features/tech-log/presentation/public/components/explore-filter-form.tsx:12: 목록을 손으로 적지 않고 `Record<RecordKind, …>` 에서 뽑는다. 종류가 늘면 이 표가 비어 있는
src/features/tech-log/presentation/public/components/explore-filter-form.tsx:16:const EXPLORE_KIND_ORDER: Record<RecordKind, number> = {
src/features/tech-log/presentation/shared/document-kind-labels.ts:18: * 타입이 잡는다 — `Record<RecordKind, string>` 이므로 빠진 종류가 있으면 컴파일되지 않는다.
src/features/tech-log/presentation/shared/document-kind-labels.ts:20:export const DOCUMENT_KIND_LABELS: Record<RecordKind, string> = {
src/features/tech-log/presentation/shared/document-kind-labels.ts:35:export const DOCUMENT_KIND_STATES: Record<RecordKind, "CONFIRMED" | "SETTLED" | "OPEN"> = {
src/features/tech-log/presentation/shared/document-kind-labels.ts:53:export const EXPLORE_KIND_PATHS: Record<RecordKind, string> = {
src/features/tech-log/presentation/studio/components/document-list.tsx:15: 작업본 목록이 거를 수 있는 종류. 손으로 나열하지 않고 `Record<RecordKind, …>` 에서 뽑는다 —
src/features/tech-log/presentation/studio/components/document-list.tsx:18:const STUDIO_KIND_ORDER: Record<RecordKind, number> = {
src/features/tech-log/adapters/mock/validate-working-copy.ts:58: const BRANCH_FIELDS: Record<RecordKind, string[]> = {
src/features/tech-log/adapters/http/http-management-gateway.ts:153: 아래 표가 `Record<DeletableDocumentKind, …>` 인 것이 실제로 종류를 강제하는 지점이다.
src/features/tech-log/adapters/http/http-management-gateway.ts:155: const OPERATIONS: Record<DeletableDocumentKind, string> = {
src/features/tech-log/adapters/http/http-public-content-gateway.ts:186: const OPERATIONS: Record<RecordKind, string> = {
### 프론트 — 남아 있는 종류 삼항 사슬이 있나 (있으면 여기 나온다)
src/features/tech-log/presentation/studio/components/publication-event-preview-screen.tsx:157: renderModel.kind === "CASE" ? renderModel.bodyBlocks : [],
src/features/tech-log/adapters/mock/validate-working-copy.ts:71: const stringFields = ["title", "slug", "summary", ...(kind === "CASE" ? ["problem", "conclusion", "environment", "reproduction", "bodyMarkdown"] : []), ...(kind === "CONCEPT" ? ["bodyMarkdown", "basisVersion"] : []), ...(kind === "REFERENCE" ? ["purpose"] : []), ...(kind === "QUESTION" ? ["nextValidation"] : []), ...(kind === "PROJECT_DECISION" ? ["statement", "rationale"] : [])];
### 백엔드 — sealed switch 를 식으로 쓴 자리 (컴파일러가 빠진 가지를 요구한다)
../tech-log-backend/src/application-core/src/main/java/dev/caskeleton/application/techlog/studio/model/PublicPaths.java:25: return switch (kind) {
../tech-log-backend/src/application-core/src/main/java/dev/caskeleton/application/techlog/studio/model/PublicPaths.java-26- case CASE -> "/cases/" + slug;
../tech-log-backend/src/application-core/src/main/java/dev/caskeleton/application/techlog/studio/model/PublicPaths.java-27- case REFERENCE -> "/references/" + slug;
### 백엔드 — PublicSql.pathOf 에 CONCEPT 이 들어갔나 (13번째 사례)
static String pathOf(String resourceType, String slug, String projectSlug) {
return switch (resourceType) {
case "CASE" -> "/cases/" + slug;
case "REFERENCE" -> "/references/" + slug;
case "QUESTION" -> "/questions/" + slug;
case "CONCEPT" -> "/concepts/" + slug;
case "PROJECT" -> "/projects/" + slug;
case "PROJECT_DECISION" ->
projectSlug == null ? null : "/projects/" + projectSlug + "/decisions#" + slug;
case "RELEASE" -> "/releases/" + slug;
default -> null;
};
}
======================================================================
정직한 평가 — 이 증거가 드러내는 남은 구멍 둘
[1] PublicSql.pathOf 는 여전히 컴파일러가 강제하지 못한다
switch 의 대상이 sealed enum(RecordKind)이 아니라 String(resourceType)이다.
그래서 `default -> null` 이 남아 있고, 새 종류를 더할 때 이 자리를 빠뜨리면
컴파일은 통과하고 경로가 null 로 나간다 — §3 이 경고하는 바로 그 모양이다.
같은 파일의 PublicPaths.forKind 는 RecordKind 로 switch 하므로 강제된다.
둘의 차이는 pathOf 가 공개 투영의 resource_type 을 다루기 때문이다. 그 칸은
RecordKind 에 없는 값(PROJECT, RELEASE)도 담는다 — 그래서 String 이다.
지금은 PublicPathsTest 가 RecordKind 전수를 돌며 막고 있지만, pathOf 만 쓰는
경로(홈 focus 의 recentDecision)는 그 테스트가 닿지 않는다.
[2] validate-working-copy.ts 의 stringFields 는 아직 삼항 사슬이다
다만 모양이 다르다 — 배타적 사슬이 아니라 「종류마다 칸을 더한다」는 가산형이다.
종류를 빠뜨리면 "잘못된 분기로 떨어진다"가 아니라 "그 종류의 추가 칸을 검사하지
않는다"가 된다. 덜 위험하지만 조용하기는 마찬가지다.
같은 파일의 BRANCH_FIELDS 는 Record<RecordKind, string[]> 로 바뀌어 있다(58줄).
그쪽이 실제로 종류를 강제하는 자리다.
즉 §3 의 「표로 바꿨다」는 대부분 사실이지만 전부는 아니다. 위 둘은 남아 있다.