Interfaces
인터페이스란 객체타입에 의해 구현될 수 있는 필드의 집합이다.
인터페이스는 필드를 가지고 있지만 실제로 인스턴스화 되지는 않는다. 대신 객체가 인터페이스를 구현하는데 이때 해당 객체는 인터페이스의 멤버가 된다. 또한 필드는 인터페이스 타입을 리턴할 수도 있다. 이 때 리턴된 객체는 해당 그 인터페이스의 멤버일 수 있다.
예를 들어, Customer
(인터페이스) 가 Individual
(객체) 또는 a Company
(객체) 둘 중 하나라고 보자.
interface Customer {
name: String!
outstandingBalance: Int!
}
type Company implements Customer {
employees: [Individual!]!
name: String!
outstandingBalance: Int!
}
type Individual implements Customer {
company: Company
name: String!
outstandingBalance: Int!
}
Customer 인터페이스는 두가지 필드를 필요로 한다. name: String!
과 outstandingBalance: Int!
이다. Company
와 Individual
둘 다 해당 필드들을 구현하였으므로 이 둘은 Customer를 구현할 수 있다. Customer의 구현은 각각의 정의에 implements Customer를 추가함으로써 이행된다.
쿼리를 할 때, 인터페이스에 있는 필드들을 가져올 수 있다.
{
customers(first: 5) {
name
outstandingBalance
}
}
객체가 Company
이던 Individual
던 상관 없다. 여전히 name
과 outstandingBalance
필드를 가져올 수 있다. 만약 특정한 객체를 가져오고 싶다면 inline fragment를 이용하여 쿼리를 하면 된다. 예를 들어:
{
customers(first: 5) {
name
... on Individual {
company { name }
}
}
}
이 뜻은 customer 이 Individual 일때는 회사의 이름도 함께 가져오라는 것을 뜻한다.
인터페이스는 객체의 일부분이 호환성있게 사용될 때에, 그리고 그들이 많은 필드를 공통으로 가지고 있을 때 아주 좋은 선택이 될 수 있다. 만약 공통으로 가지고 있는 필드가 없다면 Union 을 쓰는게 더 적합하다.
Defining Interface Types
인터페이스는 GraphQL::Schema::Interface
를 포함하는 Ruby 모듈이다. 먼저 베이스 모듈을 만들자.
module Types::BaseInterface
include GraphQL::Schema::Interface
end
그리고 베이스 인터페이스를 각 인터페이스에 포함한다.
module Types::RetailItem
include Types::BaseInterface
description "Something that can be bought"
field :price, Types::Price, "How much this item costs", null: false
def price
# 옵션: 여기에 price 구현
end
definition_methods do
# 옵션: 만약 이 메서드가 정의되어 있다면 이것은 `Schema.resolve_type` 를 오버라이드 한 것
def resolve_type(object, context)
# ...
end
end
end
인터페이스 클래스는 절대로 인스턴스화 되지 않는다. 런타임때에 오직 .resolve_type 메서드만 불린다.(만약 이게 정의되어 있다면)
Implementing Variant Types
class Types::Car < Types::BaseObject
implements Types::RetailObject
# ... 추가적인 필드
end
class Types::Purse < Types::BaseObject
implements Types::RetailObject
# ... 추가적인 필드
end
Implementing Fields
인터페이스는 signature과 함께 필드 구현을 제공할 수도 있다.(Interfaces may provide field implementations along with the signatures.) 예를 들어:
field :price, Types::Price, "How much this item costs", null: false
# 해당 필드가 `::Price` object 를 리턴하도록 구현
def price
::Price.from_cents(@object.price_in_cents)
end
이 메서드는 해당 인터페이스를 구현하는 객체에 의해 호출될 것이다. 객체 클래스에서 이 price 메서드 부분을 오버라이드 하는 것은 가능하다.
더 자세하 내용은 Fields guide서 보자.
Definition Methods
인터페이스 모듈에 definition_methods do ... end
를 추가하여 헬퍼 메서드를 추가할 수 있다.
- 해당 메서드는 인터페이스 자체에서도 클래스 메서드로 사용할 수 있다.
- 이 클래스 메서드는 해당 인터페이스를 include 하는 인터페이스에서도 사용될 수 있다.
이러한 방법으로 클래스 메서드들은 해당 인터페이스를 include 하는 다른 인터페이스들에 상속될 수 있다.( definition_methods는 ActiveSupport::Concern의 class_methods와 같다. 그러나 이 것과의 충돌을 피하기 위해 다른 이름을 가지고 있는 것이다.)
베이스 인터페이스에 헬퍼 정의에 추가할 수 있고 이후 구체적인 인터페이스에서 이를 이용할 수 있다.
# 우선, 헬퍼 메서드 `BaseInterface`의 정의 메서드에 추가한다.
module Types::BaseInterface
include GraphQL::Schema::Interface
definition_methods do
# 이 메서드를 price 필드를 추가하고 디폴트 구현을 위해 사용해라
def price_field
field(:price, ::Types::Price, null: false)
define_method(:price) do
::Price.from_cents(@object.price_in_cents)
end
end
end
end
# 그리고 이후에 호출
module Types::ForSale
include Types::BaseInterface
# 이것은 definition methods 로부터 `price_field` 를 호출한다
price_field
end
Resolve Type
필드가 리턴하는 것의 타입이 인터페이스 일 경우 GraphQL은 리턴 값으로 어떠한 구체적인 객체 타입을 사용해야 되는지 알아내야 한다. 위의 예시에서 보면 각customer 은 반드시 Individual
또는 Company
로 분류된다. 이건 다음와 같이 할 수 있다.
- top-level
Schema.resolve_type
메서드 제공하거나 또는 definition_methods
안에서 interface-level.resolve_type
메서드 제공
이 메서드는 객체가 반드시 명확해야 할 때마다 호출된다.
module Types::RetailItem
include Types::BaseInterface
definition_methods do
# `object` 에 대해 어떠한 객체타입을 사용해야 되는지 결정한다
def resolve_type(object, context)
if object.is_a?(::Car) || object.is_a?(::Truck)
Types::Car
elsif object.is_a?(::Purse)
Types::Purse
else
raise "Unexpected RetailItem: #{object.inspect}"
end
end
end
end
Orphan Types
만약 인터페이스를 구현하는 객체 타입을 추가하였을 때,그런데 그 객체 타입이 당신의 스키마에 적당히 나타나지 않을 때, 해당 객체를 인터페이스의 orphan_types에 추가할 수 있다.
module Types::RetailItem
include Types::BaseInterface
# ...
orphan_types Types::Comment
end
대안으로, 스키마의 orphan_types 에 그 객체타입을 추가할 수 있다.
class MySchema < GraphQL::Schema
orphan_types Types::Comment
end
This is required because a schema finds it types by traversing its fields, starting with query
, mutation
and subscription
.
만약 객체가 인터페이스를 통해서만 연결되어 있고 필드의 리턴타입이 아니라면, orphan_types을 통해서 명시적으로 스키마에 연결되어 있어야 한다.
type Query {
node(id: ID!): Node
}
interface Node {
id: ID!
}
type Comment implements Node {
id: ID!
}
Comment
는 반드시 orphan_types을 통해서 추가되어야 하는데 이것은 필드의 리턴타입으로는 쓰이지 않기 때문이다.(오직 Node
와 ID
만이 리턴타입으로 쓰임) - (?)
* 해당 글은 번역기 돌리다가 크롬 번역기 말도 안되는 해석에 지친 본인이 나중에 참고할 의도로 대충대충 발로 해석한 것이니 참고용으로만 사용하시길 바랍니다.
* 출처:http://graphql-ruby.org/type_definitions/interfaces.html
'배움의 즐거움 > 프로그래밍' 카테고리의 다른 글
(26) Graphql-ruby - 리스트 (0) | 2019.01.01 |
---|---|
(25) Graphql-ruby - 유니언 (0) | 2019.01.01 |
(23) Graphql-ruby - input 객체 (0) | 2019.01.01 |
(22) Graphql-ruby - enums (0) | 2019.01.01 |
(21) Graphql-ruby - 스칼라 (0) | 2019.01.01 |