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을 사용해야 합니다.