#1 GraphQL - Queries and Mutations (번역) 정보
#1 GraphQL - Queries and Mutations (번역)본문
http://graphql.org/learn/
Introduction to GraphQL
GraphQL의 작동 방식 및 사용방법을 이 기사 시리즈에서 배웁니다. GraphQL 서비스를 작성하는 방법에 대한 문서를 찾으십니까? GraphQL을 다양한 언어로 구현하는 데 도움이 되는 라이브러리가 있습니다.
GraphQL은 API를 위한 쿼리 언어이며 데이터를 정의한 유형 시스템을 사용하여 쿼리를 실행하는 서버 측 런타임입니다. GraphQL은 특정 데이터베이스 또는 스토리지 엔진에 묶여 있지 않으며 대신 기존 코드 및 데이터에 의해 지원됩니다.
GraphQL 서비스는 해당 유형에 유형 및 필드를 정의한 다음 각 유형별로 각 필드에 대한 기능을 제공함으로써 생성됩니다. 예를 들어 로그인 한 사용자가 누구인지(나 me) 그 사용자의 이름을 알 수 있는 GraphQL 서비스는 다음과 같습니다.
type Query { me: User } type User { id: ID name: String }
각 유형의 각 필드에 대한 기능과 함께 :
function Query_me(request) { return request.auth.user; } function User_name(user) { return user.getName(); }
GraphQL 서비스가 실행되면 (일반적으로 웹 서비스의 URL에서) GraphQL 쿼리를 전송하여 유효성을 검사하고 실행할 수 있습니다. 수신된 쿼리는 먼저 정의 된 유형과 필드 만 참조하는지 확인한 다음 제공된 기능을 실행하여 결과를 생성합니다.
예를 들면 다음과 같습니다.
{ me { name } }
JSON 결과를 생성 할 수 있습니다.
{ "me": { "name": "Luke Skywalker" } }
GraphQL에 대한 자세한 내용: 쿼리 언어, 시스템 유형, GraphQL 서비스의 작동 방식 및 GraphQL을 사용하여 이 섹션에서 작성된 기사의 일반적인 문제를 해결하는 모범 사례에 대해 자세히 알아보십시오.
Queries and Mutations
이 페이지에서는 GraphQL 서버를 쿼리하는 방법에 대해 자세히 학습합니다.
Fields
GraphQL은 객체에 대한 특정 필드를 요구하는 것이 가장 간단합니다. 아주 간단한 쿼리와 실행했을 때의 결과를 살펴 보겠습니다.
{
hero {
name
}
}
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
쿼리가 결과와 정확히 동일한 모양을 가지고 있음을 즉시 알 수 있습니다. 이것은 GraphQL에서 필수적입니다. 왜냐하면 항상 기대했던 것을 되 찾을 수 있고, 서버는 클라이언트가 요구하는 필드를 정확히 알고 있기 때문입니다.
필드 이름은 문자열 유형을 반환합니다.이 경우 스타 워즈의 주 영웅 이름 인 "R2-D2"를 반환합니다.
아, 한 가지 더 - 위 쿼리는 대화식입니다. 즉, 원하는대로 변경하고 새로운 결과를 볼 수 있습니다. 쿼리의 hero 개체에 appearIn 필드를 추가하고 새로운 결과를 확인하십시오.
앞의 예제에서 우리는 String을 반환한 영웅의 이름을 물었습니다. 그러나 필드는 Objects를 참조 할 수도 있습니다. 이 경우 해당 객체에 대한 필드의 하위 선택을 할 수 있습니다. GraphQL 쿼리는 관련 오브젝트와 필드를 트래버스 할 수 있으므로 고전 REST 아키텍처에서 필요로하는 것처럼 라운드 트립을 여러 번 수행하는 대신 클라이언트가 한 번의 요청으로 많은 관련 데이터를 페치할 수 있습니다.
{
hero {
name
# Queries can have comments!
friends {
name
}
}
}
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
이 예에서 friends 필드는 항목의 배열을 반환합니다. GraphQL 쿼리는 단일 항목이나 항목 목록 모두에 대해 동일하게 보이지만 스키마에 표시된 항목을 기반으로 예상되는 항목을 알 수 있습니다.
Arguments
우리가 할 수 있는 유일한 일은 객체와 필드를 가로 지르는(traverse) 것이었다면 GraphQL은 이미 데이터를 가져 오는 데 매우 유용한 언어가 되었습니다. 그러나 필드에 인수를 전달하는 기능을 추가하면 훨씬 재미있는 일이 발생합니다.
{
human(id: "1000") {
name
height
}
}
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
}
}
REST와 같은 시스템에서는 요청에 쿼리 매개 변수와 URL 세그먼트라는 단일 인수 집합 만 전달할 수 있습니다. 그러나 GraphQL에서는 모든 필드와 중첩 된 객체가 고유 한 인수 세트를 얻을 수 있으므로 GraphQL은 여러 API 가져 오기를 대체 할 수 있습니다. 스칼라 필드에 인수를 전달하여 모든 클라이언트에서 개별적으로 수행하는 대신 서버에서 데이터 변환을 한 번 구현할 수도 있습니다.
{
human(id: "1000") {
name
height(unit: FOOT)
}
}
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 5.6430448
}
}
}
인수는 여러 유형이 될 수 있습니다. 위의 예제에서 우리는 유한 옵션 세트 중 하나를 나타내는 열거 유형(Enumeration type)을 사용했습니다 (이 경우 길이 단위 인 METER 또는 FOOT). GraphQL은 기본형의 유형과 함께 제공되지만, GraphQL 서버는 전송 형식으로 직렬화 할 수있는 한 자체 사용자 정의 유형을 선언 할 수도 있습니다.
Read more about the GraphQL type system here.
Aliases
눈치가 있다면 결과 개체 필드가 쿼리의 필드 이름과 일치하지만 인수는 포함하지 않으므로 다른 인수를 사용하여 같은 필드를 직접 쿼리 할 수는 없습니다. 그래서 별칭이 필요합니다. 필드의 결과를 원하는 이름으로 바꿀 수 있습니다.
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
{
"data": {
"empireHero": {
"name": "Luke Skywalker"
},
"jediHero": {
"name": "R2-D2"
}
}
}
위의 예에서 두 개의 영웅 필드는 서로 충돌했지만 서로 다른 이름으로 별칭을 지정할 수 있으므로 한 번에 두 가지 결과를 모두 얻을 수 있습니다.
Fragments
앱에서 상대적으로 복잡한 페이지가 있다고 가정 해 봅시다. 친구와 함께 두 영웅을 나란히 볼 수 있습니다. 그러한 쿼리는 복잡해질 수 있습니다. 필드를 적어도 두 번 반복해야하기 때문에 비교의 각면마다 하나씩 반복해야하기 때문입니다.
이것이 GraphQL에 파편(fragments)이라는 재사용 가능한 단위가 포함 된 이유입니다. 프래그먼트를 사용하면 필드 세트를 구성한 다음 필요한 곳에 쿼리에 포함시킬 수 있습니다. 다음은 프래그먼트를 사용하여 위의 상황을 해결할 수있는 방법의 예입니다.
{
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
{
"data": {
"leftComparison": {
"name": "Luke Skywalker",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
},
{
"name": "C-3PO"
},
{
"name": "R2-D2"
}
]
},
"rightComparison": {
"name": "R2-D2",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
필드가 반복 될 경우 위 쿼리가 꽤 반복적일 수 있음을 알 수 있습니다. 프래그먼트의 개념은 복잡한 응용 프로그램 데이터 요구 사항을 작은 조각으로 분할하는 데 자주 사용됩니다. 특히 프래그먼트가 다른 많은 UI 구성 요소를 하나의 초기 데이터 가져 오기로 결합해야 하는 경우에 특히 그렇습니다.
Variables
지금까지 우리는 모든 인수를 쿼리 문자열에 작성했습니다. 그러나 대부분의 응용 프로그램에서 필드에 대한 인수는 동적입니다. 예를 들어 어떤 Star Wars에 관심이 있는지 또는 검색 필드 또는 필터 집합을 선택할 수있는 드롭 다운이 있을 수 있습니다.
클라이언트 측 코드는 쿼리 문자열을 런타임에 동적으로 조작하고 GraphQL 관련 형식으로 직렬화해야하기 때문에 이러한 동적 인수를 쿼리 문자열에 직접 전달하는 것은 좋지 않습니다. 대신 GraphQL은 동적 값을 쿼리에서 제외시키고 이를 별도의 사전으로 전달하는 최고 수준의 방법을 제공합니다. 이 값을 변수라고합니다.
변수 작업을 시작할 때는 다음 세 가지 작업을 수행해야합니다.
- 쿼리의 정적 값을 $variableName으로 바꿉니다.
- $variableName을 쿼리에서 허용하는 변수 중 하나로 선언하십시오.
- variableName 전달 : 별도의 전송 특정 (일반적으로 JSON) 변수 사전의 값
다음은 모두 같은 모습입니다.
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
Variables
{
"episode": "JEDI"
}
results
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
이제 클라이언트 코드에서 완전히 새로운 쿼리를 작성하지 않고 단순히 다른 변수를 전달할 수 있습니다. 이것은 일반적으로 쿼리의 어떤 인자가 동적 일 것으로 예상되는지를 나타내는 좋은 방법이기도합니다. 사용자가 입력한 값으로 쿼리를 생성하기 위해 문자열 보간(interpolation)을해서는 안됩니다.
Variable definitions
변수 정의는 위 쿼리에서 ($episode: Episode)와 비슷한 부분입니다. 형식화 된 언어의 함수에 대한 인수 정의와 마찬가지로 작동합니다. $가 앞에 붙은 모든 변수와 그 유형 (이 경우 Episode)을 나열합니다.
선언 된 모든 변수는 스칼라, enum 또는 입력 오브젝트 유형이어야합니다. 따라서 복잡한 객체를 필드에 전달하려면 서버에서 일치하는 입력 유형을 알아야합니다. 스키마 페이지에서 입력 개체 유형에 대해 자세히 알아보십시오.
변수 정의는 선택적이거나 필수일 수 있습니다. 위의 경우에는 에피소드 유형 옆에 !가 없으므로 선택 사항입니다. 그러나 변수를 전달하는 필드에 널이 아닌 인수가 필요한 경우에는 변수도 필요합니다.
이러한 변수 정의 구문에 대한 자세한 내용을 보려면 GraphQL 스키마 언어를 익히는 것이 좋습니다. 스키마 언어는 스키마 페이지에서 자세히 설명합니다.
Operation name
위 예제에서 보았던 한 가지는 쿼리가 작업 이름(operation name)을 얻었습니다. 지금까지는 쿼리 키워드와 쿼리 이름을 모두 생략 한 간단한 구문을 사용했지만 프로덕션 응용 프로그램에서는 코드를 덜 모호하게 만드는 데 유용합니다.
이것을 좋아하는 프로그래밍 언어의 함수 이름처럼 생각하십시오. 예를 들어, JavaScript에서는 익명 함수를 사용하여 쉽게 작업 할 수 있지만 함수 이름을 지정하면 추적하여 코드를 디버깅하고 호출 할 때 로그하는 것이 더 쉽습니다. 같은 방식으로, GraphQL 질의와 뮤테이션 이름은 프래그먼트 이름과 함께 다른 GraphQL 요청을 식별하기 위해 서버 측에서 유용한 디버깅 도구가 될 수 있습니다.
Directives
변수를 사용하여 동적 문자열을 생성하는 수동 문자열 보간(interpolation) 작업을 피할 수 있는 방법에 대해 위에서 설명했습니다. 인수에 변수를 전달하면 이러한 문제를 상당히 해결할 수 있지만 변수를 사용하여 쿼리의 구조와 모양을 동적으로 변경하는 방법이 필요할 수도 있습니다. 예를 들어 요약보기와 상세보기가있는 UI 구성 요소를 상상해보십시오. 여기에는 다른 보기보다 많은 필드가 포함되어 있습니다.
그런 구성 요소에 대한 쿼리를 작성해 보겠습니다.
query Hero($episode: Episode, $withFriends: Boolean!) {
hero(episode: $episode) {
name
friends @include(if: $withFriends) {
name
}
}
}
variables
{
"episode": "JEDI",
"withFriends": false
}
results
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
위의 변수를 편집하여 withFriends에 true를 전달하고 결과가 어떻게 변경되는지보십시오.
우리는 디렉티브(directive)라는 GraphQL의 새로운 기능을 사용해야 했습니다. 지시문(directive)은 필드 또는 프래그먼트 포함에 첨부 될 수 있으며 서버가 원하는 방식으로 쿼리 실행에 영향을 줄 수 있습니다. 핵심 GraphQL 사양에는 정확히 2개의 지시문이 포함되어 있습니다.이 지시문은 사양 호환 GraphQL 서버 구현에서 지원해야합니다.
- @include(if: Boolean) 인수가 true 인 경우에만이 필드를 결과에 포함하십시오.
- @skip(if: Boolean) 인수가 true이면이 필드를 건너 뜁니다.
지시문은 쿼리의 필드를 추가 및 제거하기 위해 문자열 조작을해야하는 상황에서 빠져 나오는 데 유용 할 수 있습니다. 서버 구현은 완전히 새로운 지시문을 정의하여 시험적인 기능을 추가할 수도 있습니다.
Mutations
GraphQL에 대한 대부분의 논의는 데이터 가져 오기에 초점을 맞춥니다. 그러나 완전한 데이터 플랫폼은 서버측 데이터도 수정해야합니다.
REST에서는 모든 요청이 서버에 부작용을 일으킬 수 있지만 관습에 따라 데이터 수정에 GET 요청을 사용하지 않는 것이 좋습니다. GraphQL은 기술적으로 모든 쿼리를 구현하여 데이터를 기록 할 수 있습니다. 그러나 쓰기를 발생시키는 모든 작업을 명시적으로 변형을 통해 보내야 한다는 규칙을 설정하는 것이 유용합니다.
쿼리와 마찬가지로 변형 필드가 객체 유형을 반환하면 중첩 필드를 요청할 수 있습니다. 이는 업데이트 후에 객체의 새로운 상태를 가져 오는 데 유용할 수 있습니다. 간단한 예제 뮤테이션을 살펴 보겠습니다.
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
variables
{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
results
{
"data": {
"createReview": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
}
createReview 필드가 새로 작성된 리뷰의 스타 및 코멘터리 필드를 리턴하는 방법에 유의하십시오. 이는 하나의 요청으로 필드의 새 값을 변경하고 쿼리 할 수 있기 때문에 기존 데이터를 변경하는 경우 (예: 필드를 증가시킬 때) 특히 유용합니다.
이 예제에서 전달한 검토 변수는 스칼라가 아닙니다. 이는 입력 객체 유형으로, 인수로 전달 될 수있는 특별한 종류의 객체 유형입니다. 스키마 페이지의 입력 유형에 대해 자세히 알아보십시오.
Multiple fields in mutations
뮤테이션에는 쿼리와 마찬가지로 여러 필드가 포함될 수 있습니다. 이름 이외에 쿼리와 뮤테이션 사이에는 중요한 차이점이 있습니다.
쿼리 필드가 병렬로 실행되는 동안 뮤테이션 필드는 차례대로 실행됩니다.
즉, 하나의 요청에서 두 개의 incrementCredits 변이를 보내면 두 번째가 시작되기 전에 첫 번째가 완료되어 자신과 경쟁 조건을 갖지 않도록해야 합니다.
Inline Fragments
많은 다른 유형의 시스템과 마찬가지로 GraphQL 스키마에는 인터페이스와 공용체 유형을 정의하는 기능이 포함되어 있습니다. 스키마 가이드에서 이들에 대해 자세히 알아보십시오.
인터페이스 또는 공용체 형식을 반환하는 필드를 쿼리하는 경우 인라인 프래그먼트를 사용하여 기본 구체 형식(underlying concrete type)의 데이터에 액세스해야합니다. 예를 통해 보는 것이 가장 쉽습니다.
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}
variables
{
"ep": "JEDI"
}
results
{
"data": {
"hero": {
"name": "R2-D2",
"primaryFunction": "Astromech"
}
}
}
이 쿼리에서 영웅 필드는 에피소드 인수에 따라 Human 또는 Droid일 수 있는 Character 유형을 반환합니다. 직접 선택에서는 이름과 같이 Character 인터페이스에 있는 필드 만 요청할 수 있습니다.
구체적인 유형의 필드를 요청하려면 유형 조건이 있는 인라인 프래그먼트를 사용해야 합니다. 첫 번째 조각은 Droid에서 ...로 표시되기 때문에 primaryFunction 필드는 영웅에서 반환 된 캐릭터가 Droid 유형 인 경우에만 실행됩니다. 인간 유형의 높이 필드에 대해서도 마찬가지입니다.
명명된 프래그먼트(named fragment)는 항상 동일한 유형으로 사용될 수 있으므로 명명된 프래그먼트도 같은 방식으로 사용할 수 있습니다.
Meta fields
GraphQL 서비스에서 어떤 유형을 얻을지 모르는 상황이 발생하면 클라이언트에서 해당 데이터를 처리하는 방법을 결정할 방법이 필요합니다. GraphQL을 사용하면 쿼리의 모든 지점에서 메타 필드인 __typename을 요청하여 해당 지점에서 개체 유형의 이름을 가져올 수 있습니다.
{
search(text: "an") {
__typename
... on Human {
name
}
... on Droid {
name
}
... on Starship {
name
}
}
}
{
"data": {
"search": [
{
"__typename": "Human",
"name": "Han Solo"
},
{
"__typename": "Human",
"name": "Leia Organa"
},
{
"__typename": "Starship",
"name": "TIE Advanced x1"
}
]
}
}
위의 쿼리에서 검색은 세 가지 옵션 중 하나 일 수있는 공용체 형식을 반환합니다. __typename 필드가 없으면 클라이언트와 다른 유형을 구분할 수 없습니다.
GraphQL 서비스는 몇 가지 메타 필드를 제공하며 나머지는 인트로 스펙션 시스템을 노출하는 데 사용됩니다.
0
댓글 0개