README.ko.md
English | 日本語 | Русский | 简体中文 | Bahasa Indonesia | 한국어 | العربية
JavaScript를 위한 가볍고 안전하며 URL 친화적인 고유 String ID 생성기
“대단한 레벨의 무의미한 완벽주의, 경의를 표하지 않을 수 없습니다”
crypto.randomUUID() 보다 50% 더 빠릅니다.import { nanoid } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"
Nano ID는 <b><a href="https://evilmartians.com/">Evil Martians</a></b>가 만들었습니다. 저희는 <b>개발자 도구, AI, 사이버 보안 스타트업</b>을 위한 미국의 디자인 및 엔지니어링 컨설팅 회사입니다.
Nano ID 는 (랜덤 기반의) UUID v4와 비교할 수 있습니다. 아이디로 생성되는 랜덤 비트의 수가 유사하여 (Nano ID는 126개, UUID는 122개), 동일한 아이디가 생성될 수 있는 충돌 확률도 비슷합니다:
10억 분의 1의 확률로 충돌이 발생하기 위해서는 103조 번의 v4 ID가 생성되어야 합니다.
Nano ID와 UUID v4에는 두 가지의 주요한 차이점이 있습니다:
crypto.randomUUID 및 uuid/v4 보다 빠릅니다.$ node ./test/benchmark.js
nope-id 20,386,830 ops/sec
nanoid 20,434,827 ops/sec
customAlphabet 20,544,476 ops/sec
crypto.randomUUID 12,865,759 ops/sec
uuid v4 7,930,104 ops/sec
@napi-rs/uuid 5,573,171 ops/sec
uid/secure 6,308,267 ops/sec
@lukeed/uuid 5,278,597 ops/sec
nanoid for browser 311,497 ops/sec
secure-random-string 301,667 ops/sec
uid-safe.sync 297,815 ops/sec
Non-secure:
uid 20,286,757 ops/sec
nanoid/non-secure 2,397,594 ops/sec
rndm 2,445,462 ops/sec
랜덤 생성기에 대한 설명은 다음 기사를 참고하세요: Secure random values (in Node.js)
예측불가성. 안전하지 않은 Math.random()를 사용하는 대신, Nano ID는
Node.js에서 제공하는 crypto 모듈과 브라우저의 Web Crypto API를 사용합니다.
이 모듈들은 예측이 불가능한 하드웨어 레벨의 랜덤 생성기입니다.
균일성. random % alphabet 는 랜덤 아이디 생성기를 만들때 흔히들 하는 실수입니다.
이 경우 분포(수학)가 일정하지 않게 되며, 일부 기호들은 다른 기호들보다 사용될 확률이 낮아집니다.
이로 인해 브루트포스를 사용한 시도의 횟수를 줄일 수 있는 결과를 가져옵니다.
Nano ID는 이보다 더 좋은 알고리즘을 사용하며, 균일성 테스트를 수행하였습니다.
문서화: Nano ID의 모든것이 코드에 문서화되어있습니다. 소스 코드에서 코멘트를 확인하세요.
취약점: 보안 취약점에 대한 리포트는 아래 링크를 사용해주세요. Tidelift security contact. Tidelift에서 취약점에 대한 수정 및 알림등을 도와줄 수 있습니다.
npm install nanoid
JSR 은 npm을 대체할 수 있는 오픈 거버넌스이며 npm과 달리 현재 활발히 개발중에 있습니다.
npx jsr add @sitnik/nanoid
Node.js, Deno, Bun 등에서 사용할 수 있습니다.
// 모든 `nanoid` import를 `@sitnik/nanoid`로 변경합니다
import { nanoid } from '@sitnik/nanoid'
Deno 사용시에는 deno add jsr:@sitnik/nanoid 명령어로 설치하고
jsr:@sitnik/nanoid로 import 합니다.
빠른 적용을 위해 CDN에서 Nano ID를 가져올 수 있습니다. 하지만 로딩 성능이 늦어지므로 프로덕션에서는 사용하는 것을 추천하지 않습니다.
import { nanoid } from 'https://cdn.jsdelivr.net/npm/nanoid/nanoid.js'
기본적으로 Nano ID는 URL에 친화적인 기호들을 사용하여 21자리의 랜덤한 문자열을 생성합니다. (이들은 UUID v4와 유사한 중복생성 확률을 가집니다)
import { nanoid } from 'nanoid'
model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"
생성되는 랜덤 아이디의 크기를 줄이고 싶으면 파라미터로 해당 길이 값을 넣어주면 됩니다. (이렇게 하면 중복 생성이 발생할 확률도 올라갑니다)
nanoid(10) //=> "IRFa-VaY2b"
아래의 아이디 중복확률 계산기로 여러분의 아이디가 얼마나 안전한지 확인해보세요.
사용자 커스텀 문자열 이나 사용자가 만든 랜덤 생성기 를 사용할 수도 있습니다.
customAlphabet 함수를 사용하면 지정된 문자열 내에서 지정된 개수로 이루어진
랜덤 아이디를 생성할 수 있는 nanoid 함수를 만들 수 있습니다
import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid() //=> "4f90d13a42"
import { customAlphabet } from 'nanoid/non-secure'
const nanoid = customAlphabet('1234567890abcdef', 10)
user.id = nanoid()
아이디 충돌 확률 계산기를 사용하여 여러분이 지정한 문자와 사이즈에 대한 중복 생성 확률을 확인해보세요.
nanoid-dictionary 에서 어떤 문자열을 사용할 수 있는지에 대한 옵션을 확인할 수 있습니다.
커스텀 문자열은 256개 이하의 개수를 가져야 합니다. 그 외의 경우 내부의 생성 알고리즘의 보안성을 보장할 수 없습니다.
기본 사이즈를 지정한 후에, 새로 만들어진 함수를 부를 때 생성할 문자열의 개수를 다시 지정하는 것도 가능합니다
import { customAlphabet } from 'nanoid'
const nanoid = customAlphabet('1234567890abcdef', 10)
model.id = nanoid(5) //=> "f01a2"
customRandom 함수를 사용하면 nanoid에서 사용할 문자열과 알고리즘을 지정할 수 있습니다.
아래 예시는 시드 값을 기반으로 하는 랜덤 생성기를 사용하였습니다.
import { customRandom } from 'nanoid'
const rng = seedrandom(seed)
const nanoid = customRandom('abcdef', 10, size => {
return new Uint8Array(size).map(() => 256 * rng())
})
nanoid() //=> "fbaefaadeb"
콜백 함수 random 는 반드시 배열의 크기를 받을 수 있어야 하며, 랜덤 숫자로 이루어진 배열을 리턴해야 합니다.
URL 친화적인 기호들을 사용하여 customRandom 을 실행하려면,
urlAlphabet을 사용해 기본 문자셋을 가져올 수 있습니다.
const { customRandom, urlAlphabet } = require('nanoid')
const nanoid = customRandom(urlAlphabet, 10, random)
참고로, Nano ID 버전이 변경됨에 따라 랜덤 생성기의 호출 시퀀스가 변경될 수 있습니다. 시드기반 생성기를 사용하는 경우 동일한 결과가 나오는 것을 보장할 수 없습니다.
Nano ID는 보안과 낮은 충돌 확률을 위해 하드웨어 랜덤 바이트 생성기를 사용합니다. 만약 보안이 중요하지 않다면 하드웨어 랜덤 생성기를 사용하지 않도록 설정할 수 있습니다.
import { nanoid } from 'nanoid/non-secure'
const id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqLJ"
단, non-secure 버전은 secure 버전보다 느리다는 점에 유의하세요. 꼭 필요한 경우에만 사용하세요.
React에서 key props에 Nano ID를 사용하는 것은 좋지 않습니다.
이 값은 렌더링 될 때마다 동일한 값을 가지고 있어야 합니다.
function Todos({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={nanoid()}>
{' '}
/* 절대 하지 마세요 */
{todo.text}
</li>
))}
</ul>
)
}
랜덤값보다 리스트 내에서 가져올 수 있는 값을 사용하여 안정된 값을 키 값으로 사용하는 것이 좋습니다.
const todoItems = todos.map(todo => <li key={todo.id}>{todo.text}</li>)
key 값으로 사용할 적절한 값이 없는 경우에도 nanoid()를 사용하기 보다는 인덱스 값을 사용하세요:
const todoItems = todos.map((text, index) => (
<li key={index}>
{' '}
/* 추천되는 방식은 아니지만 nanoid()를 쓰는것 보단 낫습니다 */
{text}
</li>
))
랜덤한 아이디를 생성하여 HTML 요소간의 연결 (label과 input 등)에 사용하는 경우에는
React 18에서 추가된 useId 를 추천합니다.
React Native에는 내장된 랜덤 생성기가 존재하지 않습니다.
아래의 폴리필을 사용하면 순수 React Native와 Expo(39버전 이상) 환경에서
Nano ID를 사용할 수 있습니다.
react-native-get-random-values 문서를 확인하고 설치합니다import 'react-native-get-random-values'
import { nanoid } from 'nanoid'
PouchDB와 CouchDB에서는 ID 값을 _로 시작할 수 없습니다.
Nano ID는 기본적으로 _를 맨 앞에 붙일 수 있기 때문에,
이 문제를 해결하기 위해 접두어 (prefix)가 필요합니다.
아래와 같이 접두어를 붙여서 사용합니다.
db.put({
_id: 'id' + nanoid(),
…
})
터미널에서 npx nanoid 명령어를 입력하면 고유 ID를 생성할 수 있습니다.
Node.js가 시스템에 설치되어있어야 하며, Nano ID는 미리 설치되어있지 않아도 됩니다.
$ npx nanoid
npx: installed 1 in 0.63s
LZfXLFzPPR4NNrgjlWDxn
생성할 아이디의 길이를 변경하려면 --size (or -s) 옵션을 붙여줍니다:
$ npx nanoid --size 10
L3til0JS4z
사용자 커스텀 문자열을 사용하려면 --alphabet (or -a) 옵션을 사용합니다
(이 때 --size도 같이 사용해야 합니다):
$ npx nanoid --alphabet abc --size 15
bccbcabaabaccab
Nano ID는 생성된 문자열을 TypeScript의 순수 string이 아닌 타입에도 할당할 수 있습니다. 예를 들어 아래와 같이 가능합니다:
declare const userIdBrand: unique symbol
type UserId = string & { [userIdBrand]: true }
// 명시적으로 타입 파라미터를 사용:
mockUser(nanoid<UserId>())
interface User {
id: UserId
name: string
}
const user: User = {
// 자동으로 UserId 타입으로 캐스팅됨:
id: nanoid(),
name: 'Alice'
}
Nano ID는 다양한 언어로 포팅되었습니다. 클라이언트/서버에 관계없이 아래의 다양한 포팅된 버전들을 사용할 수 있습니다.
이 외의 환경에서는 CLI를 사용하여 터미널 환경에서 랜덤 아이디를 생성할 수 있습니다.
customAlphabet에서 사용할 수 있는 일반적인 문자열들을 nanoid-dictionary로 찾아볼 수 있습니다.nanoid-good를 사용할 수 있습니다.