Notion 스타일 블록 에디터를 만들다 보니 각 블록에 고유 ID가 필요했습니다. 처음에는 UUID를 쓸까 했는데, 로컬 투두 앱에 36자짜리 ID는 좀 과하지 않나 싶었거든요. 그러다 JavaScript toString(base) 진법 변환을 알게 됐고, 7글자짜리 깔끔한 ID를 만들 수 있었습니다.
JavaScript toString(base)가 뭔지, 진법이 뭐고 왜 36을 쓰는지 정리해봤습니다.
고유 ID, 어떻게 만들지?
블록 에디터에서 각 블록에 고유 ID가 필요합니다. React의 key에도 쓰고, 블록을 찾거나 삭제할 때도 ID로 구분하니까요.
처음에는 crypto.randomUUID()를 쓸까 했는데, 결과가 이렇게 생겼습니다:
"550e8400-e29b-41d4-a716-446655440000"
이걸 보고 “로컬에서 돌아가는 투두 앱에 이 길이가 필요한가?” 싶었습니다. 블록이 많아봐야 수십 개인데 36자짜리 ID라니. 좀 더 짧고 가벼운 방법이 없나 찾아보다가 JavaScript toString(base) 진법 변환 패턴을 알게 됐습니다.
JavaScript toString(base) — 진법 변환이 뭔가요
Number.toString()에 숫자를 넣으면 해당 진법으로 변환해줍니다. 이게 생각보다 신기합니다:
Math.random() = 0.7293846... (랜덤 소수)
.toString(10) → "0.7293846..." 10진법 (기본값, 숫자 0~9만 사용)
.toString(16) → "0.bab3f8c..." 16진법 (0~9 + a~f)
.toString(36) → "0.q3m7kp2..." 36진법 (0~9 + a~z)
JavaScript toString(base)에서 base가 바로 진법입니다. 10을 넣으면 우리가 평소 쓰는 10진법, 16을 넣으면 16진법(hex), 36을 넣으면 36진법으로 변환됩니다.
진법이 커질수록 같은 숫자를 더 짧은 문자열로 표현할 수 있습니다. 이걸 ID 생성에 활용하는 겁니다.
왜 하필 36진법인가?
여기서 “왜 16이나 20이 아니라 36이지?” 하는 의문이 들 수 있는데요. 36은 JavaScript toString(base)에서 쓸 수 있는 최대 진법이기 때문입니다. 숫자 10개(09) + 알파벳 26개(az) = 36. 37 이상을 넣으면 RangeError가 나면서 에러가 터집니다.
같은 7글자로 만들 수 있는 ID 수를 비교하면 차이가 확 느껴집니다:
| 진법 | 사용 문자 | 7글자 경우의 수 |
|---|---|---|
| 10진법 | 0-9 |
1천만 개 |
| 16진법 | 0-9, a-f |
약 2.7억 개 |
| 36진법 | 0-9, a-z |
약 784억 개 |
짧으면서도 충돌 확률이 가장 낮은 조합입니다. 글자 수는 같은데 경우의 수가 이렇게 차이 나는 게 진법의 힘이라고 할 수 있겠습니다.
실제 사용 패턴
JavaScript toString(base) 진법 변환을 실제로 ID 생성에 쓸 때는 이런 패턴입니다:
Math.random().toString(36).substring(2, 9)
// ^^ ^^^^
// 36진법 "0." 제거하고 7글자만 추출
//
// 결과: "q3m7kp2" 같은 짧은 랜덤 ID
Math.random()이 0.q3m7kp2... 같은 문자열을 만들어주고, substring(2, 9)로 앞의 "0."을 잘라내고 7글자만 가져오는 겁니다.
블록 에디터에서는 이런 식으로 썼습니다:
const newBlock = {
id: Math.random().toString(36).substring(2, 9), // "q3m7kp2"
type: 'text',
content: '',
checked: false,
};
매번 새 블록을 만들 때마다 호출하면 되고, 코드도 한 줄이라 가볍습니다.
언제 이걸 쓰고 언제 UUID를 쓸까
JavaScript toString(base) 진법 변환으로 만든 ID는 편하지만 만능은 아닙니다. 상황에 맞게 골라야 합니다.
로컬에서 돌아가는 블록 에디터나 투두 앱 수준이면 toString(36)으로 충분합니다. 빠르고, 짧고, 읽기도 편하니까요.
다만 서버 간 동기화가 필요하거나 ID 충돌이 절대 나면 안 되는 상황이라면 crypto.randomUUID()를 쓰는 게 안전합니다. 길이는 길지만 충돌 확률이 사실상 0에 가깝거든요.
정리하면, 가벼운 프로젝트에서 짧은 ID가 필요할 때 JavaScript toString(base) 진법 변환은 아주 실용적인 선택입니다.