목차
Rails Global ID로 전역 객체 식별하기
Global ID는 Rails의 모든 객체를 식별할 수 있는 URI(Uniform Resource Identifier)입니다.
💡 이 글은 작성된지 1년 이상 지났습니다. 정보글의 경우 최신 내용이 아닐 수 있음에 유의해주세요.
Global ID는 Rails의 모든 객체를 식별할 수 있는 URI(Uniform Resource Identifier)입니다. 생성된 문자열은 다음과 같은 형식을 갖습니다.
gid://MyApplication/ModelName/123
Global ID는 rails에 이미 내장되어 있습니다. 우선 다음과 같이 initializer를 설정합니다.
require "global_id"
GlobalID.app ||= (ENV["APP_NAME"] || "MyApplication")
ActiveRecord 모델에서는 다음과 같이 설정하고,
# 모델에 선언
class Member < ApplicationRecord
include GlobalID::Identification
end
객체의 Global ID 값을 확인하거나, 반대로 Global ID로 해당하는 객체를 찾을 때는 다음과 같이 합니다.
### 객체의 Global ID 값 확인
gid = Member.find(39).to_global_id
gid.to_s
# => "gid://MyApplication/Member/39"
### Global ID로 객체 불러오기
gid = "gid://MyApplication/Member/39"
GlobalID::Locator.locate(gid)
# => #<Member:... @id=39>
만약 Global ID를 외부에 노출해야한다면 모델의 이름이나 id 값이 평문으로 보이는 것이 꺼림칙할 수 있습니다. 예를 들어 JWT 형식의 API 토큰에 회원 정보를 싣고 싶을 때, 해당 회원의 id 숫자가 페이로드에 그대로 들어있다면 보안상 좋지 않겠죠.
이러한 상황에서는 Signed Global ID를 사용할 수 있습니다.
우선 initializer에 다음 한 줄을 추가합니다.
SignedGlobalID.verifier ||= GlobalID::Verifier.new(
Rails.application.key_generator.generate_key("signed_global_id")
)
이 단계에서 생성한 키 값이 Global ID를 암호화/복호화하는 데에 사용됩니다. 암호화된 Global ID는 다음과 같이 사용할 수 있습니다.
sgid = Member.find(39).to_s
# => "XXXXXXXXXXXXXX"
GlobalID::Locator.locate_signed(sgid)
# => #<Member:... @id=39>
또한 암호화된 Signed Global ID의 유효 기간을 설정할 수도 있습니다. 만료 기간이 필수적인 API 토큰에 사용하거나, 한정된 시간동안 리소스를 외부에 공유하는 상황 등에 유용합니다.
expiring_sgid = Member.find(39).to_sgid(expires_in: 1.hour)
Global ID는 GraphQL의 전역 객체 식별(Global Object Identification)의 수요와 궁합이 잘 맞습니다. GraphQL, 특히 Relay에서는 데이터 캐시나 refetch를 위해 모든 노드의 전역적인 식별자를 필요로 하는데, 이 때에 사용하기 적당한 것이지요.
우선 각 타입에 Relay Node 인터페이스를 지정합니다.
class Types::MemberType < GraphQL::Schema::Object
implements GraphQL::Types::Relay::Node
end
GraphQL 스키마 설정에 다음 정보들을 추가해줍니다.
class ApplicationSchema < GraphQL::Schema
# ...
def self.id_from_object(object, type_definition, query_ctx)
object.to_sgid
end
def self.object_from_id(object_id, query_ctx)
GlobalID::Locator.locate(object_id)
end
end
마지막으로, Global ID를 통해 획득한 객체가 GraphQL상의 어떤 타입에 맵핑되는지에 대한 정보가 필요합니다. 이는 타입 리졸버를 통해 구현합니다.
class ApplicationSchema < GraphQL::Schema
def self.resolve_type(abs_type, object, query_ctx)
case object
when Member then Types::MemberType
else raise GraphQL::Schema::InvalidTypeError
end
end
# ...
end
관련된 글
Rails와 GitHub Actions에 커버리지 레포트를 달아보자
이 블로그의 CMS이기도 한 Shiori를 대폭 리팩토링하면서 테스트가 얼마나 잘 작성되어있는지 궁금해졌습니다.
Ruby on WebAssembly: 살짝 맛보기
Ruby 3.2에 추가된 WebAssembly 지원을 간단하게 테스트해봅시다.
Ruby의 and와 &&는 다르다
Ruby로 프로그램을 짜다보면 반드시 밟게되는 지뢰가 있습니다. 바로 or 과 ||, 혹은 and 와 && 연산자의 우선순위가 달라서 발생하는 일입니다.
본 사이트의 저작물은 별도의 언급이 없는 한 크리에이티브 커먼즈 저작자표시-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.
© 2011 - 2024 Hoerin Doh, All rights reserved.
작성한 댓글은 giscus를 통해 GitHub Discussion에 저장됩니다.