이전 기사에서 Snowflake IDs에 대해 이야기했습니다. 그들은 매우 좋지만, "Worker IDs"를 관리해야 하므로 약간의 구성이 필요합니다.
더 간단한 것을 원하면서도 동일한 이점(보안 URL과 빠른 데이터베이스 정렬)을 얻고 싶다면 ULIDs (Universally Unique Lexicographically Sortable Identifiers)를 살펴보아야 합니다.
왜 UUID를 사용하지 않을까요?
대부분의 개발자는 자동 증가하는 정수에서 UUID로 전환하는 이유는 그들이 자신의 사업 규모를 숨기고 싶기 때문입니다. 주문 ID가 order/550e8400-e29b...라면, 당신이 1명의 고객이든 100만 명의 고객이든 아무도 모릅니다.
하지만 UUID는 큰 문제가 있습니다: 그들은 완전히 무작위입니다.
백만 개의 무작위 UUID를 데이터베이스에 삽입하면 "B-Tree" 인덱스가 분할되고 느려집니다. 당신의 데이터베이스는 새로운 데이터를 넣을 위치를 하드 디스크 전체를 뛰어다니며 찾아야 합니다.
ULIDs는 이를 해결합니다.
ULID는 128비트(UUID와 같습니다)이지만 ID의 첫 부분은 시간戳입니다는 이를 의미합니다 ULIDs는 시간 순서대로입니다. UUID와 같이 고유하지만 정수와 같이 정렬됩니다.
Rails 8 앱에서 ULIDs를 구현하는 방법은 다음 4단계로 간단합니다.
단계 1: Gem 설치
우리는 문자열을 생성하기 위해 ulid gem을 사용할 것입니다. 이를 Gemfile에 추가하세요.
gem "ulid"
터미널에서 bundle install를 실행하세요.
단계 2: ULID 문제
우리는 모델에 ULIDs를 추가하는 것을 매우 쉽게 만들고 싶습니다. 이를 위해 Concern을 사용하는 것이 가장 좋습니다. 이 코드는 새로운 레코드를 생성할 때마다 ULID가 생성되고 ID에 할당되도록 보장합니다.
app/models/concerns/has_ulid.rb에서 새로운 파일을 만드세요:
# app/models/concerns/has_ulid.rb
module HasUlid
extend ActiveSupport::Concern
included do
# Before we save to the DB, generate the ULID
before_create :set_ulid
end
private
def set_ulid
# ULID.generate creates a string like: 01ARZ3NDEKTSV4RRFFQ69G5FAV
self.id ||= ULID.generate
end
end
단계 3: 이전 작업
새로운 모델을 만들 때는 Rails에 id이(가) string이(가)라는 것을 알려야 하며, 기본 자동 증가 논리를 비활성화해야 합니다.
rails g model Product name:string
이민 파일을 엽니다하고 이렇게 수정하세요:
# db/migrate/XXXXXXXXXXXXXX_create_products.rb
class CreateProducts < ActiveRecord::Migration[8.0]
def change
# id: false stops the automatic integer ID
create_table :products, id: false do |t|
# We use string for ULID primary key
t.string :id, primary_key: true
t.string :name
t.timestamps
end
end
end
단계 4: 모델을 업데이트합니다
이제 단계 2에서 쓴 우려 사항만 포함하세요.
# app/models/product.rb
class Product < ApplicationRecord
include HasUlid
end
STEP 5: 작동하는 모습을 보세요
Rails 콘솔을 엽니다bin/rails c) 몇 가지 제품을 만드세요:
Product.create(name: "Laptop")
Product.create(name: "Monitor")
Product.create(name: "Keyboard")
# Check the IDs
Product.pluck(:id)
# => ["01HQV...", "01HQV...", "01HQV..."]
자세히 보면 모든 ID가 동일한 문자로 시작합니다. 같은 분에 생성되었기 때문입니다. 정렬할 수 있으므로 여전히 Product.order(:id)를 실행할 수 있으며, 그들은 올바른 시간 순서로 정렬됩니다!
왜 Rails에서 ULIDs를 선호하는가
- 더 나은 성능: ID가 정렬할 수 있기 때문에 PostgreSQL (또는 SQLite)는 인덱스 끝에 이를 추가할 수 있습니다. 이는 "쓰기 집중형" 앱에 비해 랜덤 UUID보다 훨씬 빠릅니다.
- 읽기 쉬움: ULIDs는 혼동을 유발하는 문자("I", "L", "O" 등)를 제외한 특별한 알파벳(Crockford's Base32)을 사용합니다. 이로 인해 사용자가 입력해야 할 경우 더 쉽게 읽을 수 있습니다.
- 설정 없음: Snowflake ID와는 달리 서버 ID나 워커 노드를 구성할 필요가 없습니다. 그저 gem을 설치하고 사용하면 됩니다.
거의 그것이 전부입니다. 애플리케이션을 훨씬 더 전문적이고 확장 가능하게 만드는 작은 아키텍처 변경입니다.












