⚠ New Class-Based API ⚠
여기서는 v1.8.0 새로운 API 에 대해서 알아볼 것이다.
Class-based API
GraphQL 1.8
+ 에서는 스키마를 빌드할 때 Ruby class를 사용할 수 있다. class 스타일과 define 스타일 정의 두 가지를 섞어서 스키마에 사용할 수 있다.
해당 버전의 새로운 특성들의 전반적인 사항들:
API에 대해서 배울 내용:
- Schema class
- Common type configurations
- Object classes
- Interface classes
- Union classes
- Enum classes
- Input Object classes
- Scalar classes
- Customizing definitions
- Custom introspection
Rationale & Goals
이 새로운 API는 새로 시작하는 사람들과 스키마 커스터마이징에 좋은 경험을 주도록 개선하도록 노력하였다. 이를 위해 친근한 루비 구조(class & method) 과 함께 GraphQL-ruby DSL로 대체되었다.
추가적으로, 이 새로운 API는 현재 스키마 정의와 상호 보완적이므로 차차 사용될 수 있을 것이다.
Compatibility & Migration overview
사용자의 스키마 부분은 하나씩 대체될 수 있으므로 정의도 점점 바꿔나갈 수 있다.
Classes
일반적으로 .define { ... }
블락은 class로 변경될 것이다.
GraphQL::{X}Type 를 사용하는 대신에, GraphQL::Schema::{X} 예를 들면:
GraphQL::ObjectType.define { ... } =>
GraphQL::Schema::Object
자신의 앱에 base 클래스를 만드는 것을 추천하며, 그리고 나서 그 base 클래스를 각각의 타입에 상속하도록 한다. (rails 의 ApplicationController 같이, Customizing Definitions 더보기)
스키마 정의 클래스의 구체적인 클래스는 아래에 볼 수 있다.
Type Instances
이전의 GraphQL::{X}Type
는 여전히 사용되기는 한다. 각각의 새로운 GraphQL::Schema::{X}
클래스는 일부 메서드를 구현한다.
.to_graphql
:GraphQL::{X}Type
의 새로운 인스턴스를 생성.graphql_definition
:GraphQL::{X}Type
의 캐쉬된 인스턴스를 리턴
만약 이 새로운 정의 스타일에 어긋나는 현재 자신의 코드가 있다면 내재된 타입 객체를 얻기 위해서는 graphql_definition 를 호출하면 된다.
아래 설명된 것처럼, to_graphql 는 타입 시스템을 커스터마이징 하기 위해서 덮어쓸 수 있다.
List Types and Non-Null Types
이전에, 리스트 타입은 types[T] 로 표현되었고, non-null 타입은 types[T] 로 표현되었다. 이제는:
리스트 타입은 Ruby 배열 [T]
로 표현된다. 예를 들어, field :owners, [Types::UserType]
디폴트로는, 리스트의 값은 non-null 이다. 그러므로
[Types::UserType]
은[User!]
이 된다.
만약 리스트의 값이 null이 될 수 있다면 배열뒤에 null: true 를 추가하여 [Types::UserType, null: true] 가 [User] 이 된다.
field: project, Types::ProjectType, null: true
Non-null 타입은 키워드 argument인 null:
또는 required:
로 표현된다.
field 에서는 null: 키워드를 받는데, null 이 될 수 있는 것은 null: true 로 표현되고, null 이 될 수 없는(!와 같은 뜻) 은 null: false로 표현할 수 있다.
argument 에서는 required: 키워드를 받는데, argument 가 non-null 이라면 required: true 로 표현되고, null 이 될 수 있다면 required: false로 표현된다.
레거시 스타일 클래스에서는 리스트와 non-null 타입을 만들기 위해서 Ruby 메서드를 사용했을 것이다.
#to_non_null_type
는 타입을 non-null로 변경한다 (예를 들어,T.to_non_null_type
은!T
와 동일하다)#to_list_type
는 타입을 리스트로 변환한다. (예를 들어,T.to_list_type
는types[T]
와 동일하다)
The !
메서드는 내장된 연산자와의 모호성을 피하기 위해서 제거됨 (field “someField” type must return GraphQL::BaseType, not FalseClass (false) 에러발생함)
호환을 위해, !
를 class-based 타입 정의에 backport 하고 싶을 것이다. 이를 위해서는 두 가지 방법이 있다:
A refinement, activated in file scope or class/module scope:
# 해당 스콥에서`!` 를사용할수 있게끔 함
using GraphQL::DeprecatedDSL
A monkeypatch, activated in global scope:
# 전체에서 `!` 를 사용 가능하게 함
GraphQL::DeprecatedDSL.activate
Connection fields & types
connection(...)
메서드는 존재하지 않는다. 대신에 connection field 가 타입 이름에서 참조된다. 만약에 타입 이름이 Connection 으로 끝난다면 해당 필드는 Connection 필드로서 다뤄진다. 이는 connection: true
또는 connection: false 키워드를 전달함으로써 덮어쓸 수 있다.
# 타입 명이 "connection"으로 끝나므로 이것은 connection 으로 다뤄진다.
field :projects, Types::ProjectType.connection_type
Resolve function compatibility
만약 클래스를 사용하여 타입을 정의한다면, GraphQL-Ruby resolve 함수를 사용할 수 있다.
# Proc literal 이나 #call-able 사용
field :something, ... resolve: ->(obj, args, ctx) { ... }
# 미리 정의된 필드 사용
field :do_something, field: Mutations::DoSomething.field
# GraphQL::Function 사용
field :something, function: Functions::Something.new
resolve 함수로 구현할 때는(obj, args, ctx)
파라미터는 로 예전과 같다.
Upgrader
1.8
는 .define
-based 신택스를 class
-based 신택스로 바꾸기 위한 auto-upgrader 를 포함하고 있다. This upgrader is a pipeline of sequential transform operations.(?) 이것은 기본 파이프라인과 함께 제공되지만 내장된 파이프라인을 커스텀 된 것으로 교체함으로써 커스터마이징 하는 것도 가능하다.
이 upgrader 는 parser 에 의존성을 가지고 있는데, 프로젝트에 직접 추가해야 된다.(예를 들면 Gemfile 에 추가하는 것 처럼)
위의 두 가지 문법이 호환되므로 당신의 프로젝트에서 한번에 파일 한 개씩 변환될 것이다. 이렇게 함으로써, 몇개의 파일을 변환해본 뒤 테스트를 실행하여 문제점들을 확인하고 계속해서 진행할 수 있다.
이 변환은 완벽하지 않지만 대부분의 케이스들을 커버할 것이다. 더 질문이 있거나 버그에 대한 키포트는 여기서 하면 된다.
Using the Default Upgrade Task
The upgrader ships with rake tasks, included as a railtie (source). The railtie will be automatically installed by your Rails app, and it provides the following tasks:
upgrader 는 railtie (source) 라는 rake 태스크와 함께 온다. 이 railtie 는 자동적으로 너의 Rails 앱에 의해 설치되고 다음과 같은 태스크를 제공한다.
graphql:upgrade:schema[path/to/schema.rb]
: 스키마 파일 업그레이드graphql:upgrade:member[path/to/some/type.rb]
: 타입 정의 업그레이드(객체, 인터페이스, 공용체 등등)graphql:upgrade[app/graphql/**/*]
:_(type|interface|enum|union).rb
와 같은 접미사가 있는 파일에 대해 업그레이드 실행graphql:upgrade:create_base_objects[path/to/graphql/]
: 베이스 clase 들을 추가함
Writing a Custom Upgrade Task
커스텀 태스크가 다음과 같은 이유로 필요할 수도 있다.
- transformation pipeline 을 커스터마이징 하고 싶을 때
- Rails 를 사용하지 않기 때문에 railtie 가 동작하지 않을 때
커스텀 태스크를 작성하기 위해서 rake task 나 루비 스크립트를 작성하면 되는데, 이 때 업그레이드의 API를 직접 사용하면 된다.
아래를 보면 타입 정의를 업그레이드 하기 위한 기본 변형 파이프 라인의 코드를 볼 수 있다.
# 기존의 소스코드를 문자열로 읽음
original_source = File.read("path/to/type.rb")
# 기존 변형으로 업그레이더 초기화
upgrader = GraphQL::Upgrader::Member.new(original_source)
# 변형을 실행하고 변형된 소스 코드를 가져옴
transformed_source = upgrader.upgrade
# 새로운 코드로 해당 파일을 업데이트
File.write("path/to/type.rb", transformed_source)
커스텀 코드에서는 GraphQL::Upgrader::Member.new
에 키워드를 보내면 된다.
type_transforms:
소스코드 전체에 적용, 처음에 적용field_transforms:
각각의 필드/커넥션/argument 정의에 적용clean_up_transforms:
타입 또는 필드 변환 후 소스코드 전체에 적용
이 변형은 순서대로 진행된다. .unshift() 를 추가한다면 파이프라인의 맨 끝부분이 아니라 시작 부분에 변형을 추가할 수 있다.
For example, in script/graphql-upgrade
:
#!/usr/bin/env ruby
# @example Upgrade app/graphql/types/user_type.rb:
# script/graphql-upgrade app/graphql/types/user_type.rb
#기존의 define-to-class 변형을 커스텀 변형으로 대체
type_transforms = GraphQL::Upgrader::Member::DEFAULT_TYPE_TRANSFORMS.map { |t|
if t == GraphQL::Upgrader::TypeDefineToClassTransform
GraphQL::Upgrader::TypeDefineToClassTransform.new(base_class_pattern: "Platform::\\2s::Base")
else
t
end
}
# 이 변형자를 리스트의 시작부분에 추가
type_transforms.unshift(GraphQL::Upgrader::ConfigurationToKwargTransform.new(kwarg: "visibility"))
# 업그레이더 실행
original_text = File.read(ARGV[0])
upgrader = GraphQL::Upgrader::Member.new(original_text, type_transforms: type_transforms)
transformed_text = upgrader.upgrade
File.write(filename, transformed_text)
Writing a custom transformer
변형 파이프라인에서 객체는 다음과 같다.
.new.apply(input_text)
에 응답하고 변형된 코드를 리턴.apply(input_text)
에 응답하고 변형된 코드를 리턴
라이브러리는 GraphQL::Upgrader::Transform
base class 와 함께 편리한 몇개의 메서드를 제공한다. 아래 제시된 내장 변형자들을 커스터마이징 하는것도 가능하다.
예를 들어 아래는 타입 정의를 model_type(model) do ... end
팩토리 메서드에서 class-based 신택스로 새롭게 작성한 것이다.
# `model_type` factory를 위핸 커스텀 변형 생성:
class ModelTypeToClassTransform < GraphQL::Upgrader::Transform
def initialize
# 내부에 타입 클래스가 있는 factory 메서드 호출
@find_pattern = /^( +)([a-zA-Z_0-9:]*) = model_type\(-> ?\{ ?:{0,2}([a-zA-Z_0-9:]*) ?\} ?\) do/
# 클래스 정의로 변경
@replace_pattern = "\\1class \\2 < Platform::Objects::Base\n\\1 model_name \"\\3\""
end
def apply(input_text)
input_text.sub(@find_pattern, @replace_pattern)
end
end
# 해당 클래스를 파이프라인 시작에 추가
type_transforms.unshift(ModelTypeToClassTransform)
Roadmap
이 기능을 구현하기 위한 계획은 다음과 같다.
- 진행중:
- ☐ 새로운 API 에 대해 피드백 받기 (사용성 & 목적)
- - graphql 1.8:
- ☑ 싱글톤 대신 클래스에 기초한 스키마 정의 API 빌드
- ☑ GitHub의 GraphQL 스키마의 일부를 새 API에 마이그레이션
☑ 고급 클래스 기반 features 빌드:
- ☑ 커스텀
Context
클래스 - ☑ 커스텀 introspection 타입
- ☐
Custom directives현재는 투가가치 없음 - ☐
Custom필요없음Schema#execute
method
- ☑ 커스텀
- ☑ GitHub’s GraphQL schema 를 모두 새 API 에 마이그레이션
- graphql 1.9:
☐ 새 API를 반영하도록 모든 GraphQL-Ruby docs 업데이트
- graphql 1.10:
- ☐
.define
: 격리
- graphql 2.0:
- ☐
.define
: 제거
Common Type Configurations
몇몇의 설정은 아래와 같이 모든 타입에 사용 가능하다.
graphql_name
는 타입 name 을 오버라이드 함overrides the type name. (디폴트 값은 네임스페이스를 제외한 루비 상수 이름, 예를들어 Types::TodoList 일 경우에 디폴트 네임은 TodoList)description
은 GraphQL introspection을 위한 설명을 제공
예를 들면:
class Types::TodoList < GraphQL::Schema::Object # 또는 Scalar, Enum, Union, 뭐든지 간에
graphql_name "List" # 디폴트 값"TodoList" 을 오버라이드
description "해야 할 일들의 리스트"
end
* 해당 글은 번역기 돌리다가 크롬 번역기 말도 안되는 해석에 지친 본인이 나중에 참고할 의도로 대충대충 발로 해석한 것이니 참고용으로만 사용하시길 바랍니다.
* 출처: http://graphql-ruby.org/schema/class_based_api.htm
'배움의 즐거움 > 프로그래밍' 카테고리의 다른 글
(13) Graphql-ruby - 루트 타입 (0) | 2018.12.31 |
---|---|
(12) Graphql-ruby - 제한하여 보여주기 (0) | 2018.12.31 |
(10) Graphql-ruby - 스키마 테스팅 (0) | 2018.12.31 |
(9) Graphql-ruby - Generators (0) | 2018.12.31 |
(8) Graphql-ruby - Introspection (0) | 2018.12.31 |