PostgreSQL 18 없이 Ruby on Rails에서 UUIDv7 사용하기

Using UUIDv7 with Rails without PostgreSQL 18

작성자
발행일
2025년 11월 09일

핵심 요약

  • 1 PostgreSQL 18의 네이티브 UUIDv7 지원 없이 Ruby on Rails 애플리케이션에서 UUIDv7을 활용하는 방법을 제시합니다.
  • 2 사용자 정의 PostgreSQL 함수를 생성하고 이를 기존 및 신규 테이블의 기본 키 생성에 적용하여 UUIDv7을 구현하는 과정을 상세히 설명합니다.
  • 3 UUIDv7은 시간 순서대로 정렬되어 B-tree 인덱스 성능을 개선하고 기본 키를 통한 데이터 정렬 정확도를 높이는 이점을 제공합니다.

도입

PostgreSQL 18에서 네이티브 UUIDv7 지원이 추가되어 시간 순서대로 정렬되는 UUID를 통해 B-tree 인덱스 파편화를 줄이고 기본 키를 이용한 데이터 정렬 정확도를 높일 수 있게 되었습니다. 현재 Ruby on Rails는 기본 키로 UUIDv4만을 지원하지만, 데이터베이스를 PostgreSQL 18로 업그레이드하거나 Rails의 패치를 기다릴 필요 없이 즉시 UUIDv7의 이점을 활용할 수 있는 방안이 제시됩니다. 본 문서는 애플리케이션이 이미 기본 키로 UUID를 사용하고 있다는 전제하에, 이러한 기능을 Ruby on Rails 환경에 통합하는 방법을 다룹니다.

PostgreSQL 18 없이 Rails에서 UUIDv7을 사용하기 위해 다음 단계를 수행합니다.

1. 사용자 정의 UUIDv7 함수 생성

PostgreSQL 18의 네이티브 uuidv7() 함수가 없어도, ActiveRecord::Migration을 통해 generate_uuidv7이라는 사용자 정의 함수를 생성하여 UUIDv7 값을 생성할 수 있습니다. 이 함수는 PostgreSQL 18의 정밀도(밀리초 + 서브 밀리초 타임스탬프 + 랜덤)를 모방하며, PostgreSQL용 pg_uuidv7 확장 기능에서 아이디어를 얻었습니다. 함수명은 향후 PostgreSQL 18로 업그레이드 시 충돌을 피하기 위해 다르게 지정됩니다.

ruby class CreateCustomUuidv7Function < ActiveRecord::Migration[8.1] def up execute <<-SQL CREATE FUNCTION generate_uuidv7(timestamptz DEFAULT clock_timestamp()) RETURNS uuid AS $$ SELECT ENCODE( SUBSTRING(int8send(FLOOR(t_ms)::int8) FROM 3) || int2send((7<<12)::int2 | ((t_ms-floor(t_ms))*4096)::int2) || SUBSTRING(uuid_send(gen_random_uuid()) FROM 9 FOR 8) , 'hex')::uuid FROM (SELECT extract(epoch FROM $1)*1000 AS t_ms) s $$ LANGUAGE sql volatile parallel safe; SQL end def down execute "DROP FUNCTION IF EXISTS generate_uuidv7(timestamptz);" end end

2. 기존 테이블의 기본 키 업데이트

기존에 UUID를 기본 키로 사용하는 모든 테이블에 대해, 해당 기본 키의 기본 생성 함수를 gen_random_uuid() (UUIDv4)에서 새로 생성한 generate_uuidv7() (UUIDv7)으로 변경하는 마이그레이션을 수행합니다. 이는 ActiveRecord::Base.connection.tables를 순회하며 ALTER TABLE ... ALTER COLUMN ... SET DEFAULT SQL 명령을 통해 이루어집니다.

ruby class ConvertIdColumnsToUseCustomUuidv7Function < ActiveRecord::Migration[8.1] def up ActiveRecord::Base.connection.tables.each do |table| pk = ActiveRecord::Base.connection.primary_key(table) next unless pk column = ActiveRecord::Base.connection.columns(table).find { |col| col.name == pk } next unless column.sql_type_metadata.type == :uuid execute <<-SQL.squish ALTER TABLE #{table} ALTER COLUMN #{pk} SET DEFAULT generate_uuidv7(); SQL end end def down ActiveRecord::Base.connection.tables.each do |table| pk = ActiveRecord::Base.connection.primary_key(table) next unless pk column = ActiveRecord::Base.connection.columns(table).find { |col| col.name == pk } next unless column.sql_type_metadata.type == :uuid execute <<-SQL.squish ALTER TABLE #{table} ALTER COLUMN #{pk} SET DEFAULT gen_random_uuid(); SQL end end end

3. 신규 테이블에 UUIDv7 적용

향후 생성될 테이블에는 create_table 헬퍼에 id: :uuid, default: -> { "generate_uuidv7()" } 옵션을 추가하여 기본 키가 자동으로 UUIDv7로 생성되도록 설정할 수 있습니다.

ruby class CreatePosts < ActiveRecord::Migration[8.1] def change create_table :posts, id: :uuid, default: -> { "generate_uuidv7()" } do |t| t.string :title t.string :body t.timestamps end end end

4. PostgreSQL 18 업그레이드 시 전환

데이터베이스가 PostgreSQL 18 이상으로 업그레이드되면, 기존 테이블의 기본 키 생성 함수를 네이티브 uuidv7()로 전환하고 더 이상 필요 없는 사용자 정의 generate_uuidv7() 함수를 제거할 수 있습니다.

ruby class ConvertIdColumnsToUseNativeUuidv7Function < ActiveRecord::Migration[8.1] def change ActiveRecord::Base.connection.tables.each do |table| pk = ActiveRecord::Base.connection.primary_key(table) next unless pk column = ActiveRecord::Base.connection.columns(table).find { |col| col.name == pk } next unless column.sql_type_metadata.type == :uuid execute <<-SQL.squish ALTER TABLE #{table} ALTER COLUMN #{pk} SET DEFAULT uuidv7(); SQL end execute "DROP FUNCTION IF EXISTS generate_uuidv7(timestamptz);" end end 이러한 구현을 위해서는 config.active_record.schema_format = :sql 설정을 통해 structure.sql 파일을 생성하거나, 사용자 정의 함수 관리를 지원하는 Gem을 사용해야 합니다.

결론

본 문서는 PostgreSQL 18로의 업그레이드나 Ruby on Rails의 공식 패치를 기다리지 않고도, 현재 Rails 애플리케이션에서 UUIDv7의 이점을 활용할 수 있는 실용적인 해결책을 제시합니다. 사용자 정의 PostgreSQL 함수를 통해 시간 순서가 보장되는 UUID를 생성하고, 이를 기존 및 신규 테이블의 기본 키에 적용함으로써 B-tree 인덱스 효율성을 높이고 데이터 정렬 능력을 향상시킬 수 있습니다. 또한, 향후 PostgreSQL 18 이상 버전으로 업그레이드 시 네이티브 `uuidv7()` 함수로 원활하게 전환할 수 있는 경로를 제공하여, 애플리케이션 성능 최적화와 미래 지향적인 데이터베이스 전략을 동시에 달성할 수 있습니다.

댓글 0

로그인이 필요합니다

댓글을 작성하거나 대화에 참여하려면 로그인이 필요합니다.

로그인 하러 가기

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!