Files
document-haness/docs/TechLog/tech-log-studio/addresses-frozen-at-publish-time/case/case-a-slug-rule-that-threw-korean-away.md
T
DongHyeonkaandClaude Opus 5 ab59130196 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>
2026-09-17 11:02:02 +09:00

5.8 KiB

kind, slug, title, topic, topicName, project, status, lastVerifiedOn, sourceRevision, source
kind slug title topic topicName project status lastVerifiedOn sourceRevision source
CASE a-slug-rule-that-threw-korean-away slug 생성이 한글을 버려 주제 만들기가 간헐적으로 실패했다 addresses-frozen-at-publish-time 주소가 만들어지고 굳어지는 곳 TechLog 게시 전 2026-09-04 tech-log@2026-09-02
final/document.md#§13.6

slug 생성이 한글을 버려 주제 만들기가 간헐적으로 실패했다

주제 만들기가 간헐적으로 실패했다 — 「그 slug 를 가진 주제가 이미 있습니다」. 다른 이름으로 다시 하면 됐다. 규칙은 간헐적이었던 적이 없었고 보이지 않았을 뿐이다. slug 생성이 영문 소문자와 숫자만 남기고 나머지를 버려서, 한글 이름은 아무것도 기여하지 못했다.

관계

  • 계약은 앵커라고 적었고 만드는 쪽은 경로를 만들었다 주소를 만드는 다른 곳에서 난 사건이다.
  • 한 화면에 종류 이름이 아홉 개 떠 있었다 — 표가 여섯 벌이었다 이름을 다루는 다른 사건이다.
  • 서버가 준 주소는 라우트 표에 맞춰 보고, 맞는 라우트가 없으면 링크로 그리지 않는다 slug 가 주소의 재료라는 점에서 이어진다.

문제

주제를 만들면 가끔 「그 slug 를 가진 주제가 이미 있습니다」가 나왔다. 다른 이름으로 다시 하면 됐다.

같은 이름으로 두 번 만든 적은 없었다.

결론

간헐적으로 보였을 뿐 실제로는 두 가지로 어긋났고, 사용자는 둘 다 만났다.

인증 : 남는 글자가 없어 빈 문자열 → 폼이 요청 전에 거절 Redis 캐시 와 Redis 클러스터 : 둘 다 redis → 두 번째가 충돌

5cffe30 이 그것을 이렇게 적었다. 규칙은 간헐적이었던 적이 없다. 보이지 않았을 뿐이다 — slug 생성이 [a-z0-9] 만 남기고 나머지를 버려서, 한글 이름은 아무것도 기여하지 못했다.

한글을 버리지 않고 로마자로 옮긴다. 음절을 초성·중성·종성으로 산술 분해하므로 표가 필요 없고 결정적이다.

백엔드 아키텍처 → baekendeu-akitekcheo

국어의 로마자 표기법의 자모 대응만 적용하고 음운 변화 규칙은 일부러 뺐다. 함수의 javadoc 이 그 이유를 적는다. slug 는 읽는 것이지 발음하는 것이 아니고, 그 규칙을 넣으면 같은 이름이 문맥에 따라 다른 slug 가 된다.

검증 환경

tech-log-frontend : 5cffe30 · 7093d84 변환 : 국어의 로마자 표기법의 자모 대응 · 음운 변화 규칙 제외 확인 방식 : 한글 이름 여럿으로 주제를 만들어 생성된 slug 를 확인

재현 조건

  1. 주제 이름을 인증 으로 적고 저장한다 — 옛 규칙에서는 빈 slug 가 되어 폼이 거절한다
  2. Redis 캐시 와 Redis 클러스터 를 차례로 만든다 — 옛 규칙에서는 두 번째가 충돌한다
  3. 새 규칙에서 같은 이름들의 slug 를 확인한다

본문

간헐적으로 보인 이유

slug 생성이 영문 소문자와 숫자가 아닌 것을 전부 하이픈으로 바꿨다. 한글은 전부 거기 걸리므로 이름에 섞인 영문과 숫자만 남는다.

이름 옛 규칙이 만든 slug 무엇이 일어났나
인증 (빈 문자열) 폼이 요청 전에 거절
Redis 캐시 redis 만들어짐
Redis 클러스터 redis 두 번째가 충돌

두 결과가 서로 달라 보인다. 하나는 폼 오류이고 하나는 중복 오류이며, 어느 쪽도 「한글이 버려졌다」로 읽히지 않는다. 작성자에게는 「가끔 안 되다가 이름을 바꾸면 되는」 현상이었다.

규칙은 간헐적이었던 적이 없다. 보이지 않았을 뿐이다 — slug 생성이 [a-z0-9] 만 남기고 나머지를 버려서, 한글 이름은 아무것도 기여하지 못했다.

산술 분해로 로마자를 만든다

한글 음절은 초성·중성·종성이 정해진 순서로 조합된 코드다. 음절 코드에서 시작 코드를 빼고 중성 수와 종성 수로 나누면 세 값이 그대로 나온다.

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]}`;
}

변환표가 필요 없고 같은 입력에 늘 같은 결과가 나온다. 한글이 아닌 문자는 그대로 돌려주고 뒤의 필터가 처리한다.

백엔드 아키텍처 → baekendeu-akitekcheo

음운 변화 규칙을 뺀 이유

국어의 로마자 표기법에는 자모 대응 외에 음운 변화 규칙이 있다. 그것을 넣지 않았고, 함수의 javadoc 이 이유를 적는다.

표기법은 국어의 로마자 표기법의 자모 대응만 쓴다 — 음운 변동(자음동화 같은 것)은 반영하지 않는다. slug 는 읽히기 위한 것이지 발음을 옮기기 위한 것이 아니고, 변동 규칙을 넣으면 같은 이름이 문맥에 따라 다른 slug 가 될 수 있다.

문서 slug 와 같은 모양으로 맞췄다

결과가 문서 slug 와 같은 정규식을 만족하게 했다. 한 저장소가 두 가지 slug 규칙을 갖지 않도록 한 것이고, 그래서 주제 slug 와 문서 slug 를 같은 검사로 볼 수 있다.

확인하지 못한 것

음운 변화 규칙을 빼서 같은 발음의 다른 이름이 다른 slug 가 된다. 그 충돌이 실제로 얼마나 자주 나는지는 재지 않았다.