Queries 와 Mutations
이번 페이지에서는 어떻게 GraphQL 서버로 쿼리를 보내는지 알아보자.
Queries and Mutations
Fields
간단히 말해서 GraphQL은 객체에 특정 필드를 요청하는 것이다. 이것을 실행하였을 때 얻을 수 있는 간단한 쿼리와 결과를 보면서 시작해보자
쿼리와 결과가 완전히 같은 모양인 것을 볼 수 있다. 너는 항상 너가 예상한 것을 돌려받을 것이고 이것은 GraphQL에서 중요한 점이다. 그리고 서버는 클라이언트가 요구하는게 무엇인지 정확히 알고 있다.
name 필드는 String 타입을 반환하고, 이 경우에는 R2-D2라는 스타워즈의 영웅이다.
한 가지 더 - 위의 쿼리는 상호적이다. 무슨말이냐 하면 너는 너가 원하는대로 바꿀 수 있고 새로운 결과를 볼 수 있다. appearsIn 이라는 필드를 hero 객체에 추가하고 새로운 결과를 보자.
이전의 예제에서는 String 타입으로 반환되는 hero의 이름만 요청하였는데 fields는 객체를 참고할 수 있다. 이러한 경우에는 해당 객체의 필드에 sub-selection을 만들어야 한다.
GraphQL 쿼리는 객체들과 그들의 fields를 가로질러갈 수 있는데, 이는 기존의 REST 구조에서는 여러 요청을 필요로 하는 것과 달리, 클라이언트가 관계된 많은 데이터들을 하나의 요청으로 가져올 수 있게끔 한다.
위 예시에서 friends 필드는 배열을 반환한다. GraphQL 쿼리는 단수 아이템이든 복수 아이템(lists)이든 같은 모양이지만 우리는 스키마에 나타난 것을 통해 둘 중 어떤 것인지 예측할 수 있다.
Arguments
GraphQL이 객체와 그들의 fields를 아우르며 가져올 수 있다고만 해도 GraphQL은 데이터를 가져오는데 이미 굉장히 유용하다. 그러나 fields에 arguments를 보내는 기능을 추가한다면 더 흥미로워지는걸 볼 수 있다.
REST 같은 시스템에서는 쿼리 파라미터와 URL 세그먼트를 이용하여 오직 하나의 set of arguments 를 보낼 수 있다. 그러나 GraphQL에서는 모든 필드와 nested 객체들은 각자 자신의 set of arguments 들을 가질 수 있고, 이는 GraphQL 을 멀티 API 요청을 가능하게끔 한다.
You can even pass arguments into scalar fields, to implement data transformations once on the server, instead of on every client separately. (무슨말인지 모르겠음)
arguments 는 또한 다른 타입이 될 수 있다. 위의 예시에서 보면, 우리는 제한된 옵션중 하나를 표현하기 위하여(길이의 단위) Enumeration type 을 썼다. GraphQL 에는 다양한 디폴트 타입들이 있긴 하나, 포멧에 serialized만 할 수 있다면 GraphQL 서버는 자신만의 커스텀 타입을 선언할 수 있다.
Aliases
너가 좀 예리하다면 이걸 알아차렸을 것이다.
결과 객체 필드(empireHero/jediHero)가 쿼리의 필드의 이름과 매치하지만 arguments는 같지 않기 때문에 같은 필드에 다른 arguments로 쿼리를 보낼 수 없다는 것을 말이다.
이게 aliases가 필요한 이유이다. 이 것을 이용하여 너는 결과값 필드의 이름을 너가 원하는대로 변경할 수 있다.
위의 예시에서처럼, 두 개의 hero 필드는 충돌할 수 있는데, 우리가 다른 별명으로 이름을 정했기 때문에 한 개의 요청을 통해 두 개의 결과를 모두 얻을 수 있다.
eimpireHero/jediHero -> 이 부분이 aliases
Fragments
우리의 앱에 두 명의 히어로와 그들의 친구들을 나란히 보여주는 페이지가 있다고 해보자. 적어도 한번은 필드를 반복해야되기 때문에 쿼리가 복잡해질 것이다.
그게 GraphQL 에서 재사용가능한 유닛인 fragments 가 있는 이유다. fragments 으로 필드 셋을 구성할 수 있으며, 이를 너가 필요한 쿼리에 포함할 수 있다. 아래는 fragments를 이용하여 앞에서 말한 상황을 해결하는 예시이다.
만약에 필드를 반복해야한다면 쿼리가 얼마나 반복적이 될 지 예상할 수 있다. fragments 의 개념은 복잡한 앱 데이터 요청을 작은 단위로 나누는데 종종 사용되며, 특히 다른 fragments 을 가진 많은 UI 구성 요소들을 하나의 데이터 가져오기로 합칠 때 사용할 수 있다.
Using variables inside fragments - fragments에서 변수 사용하기
fragments는 쿼리와 뮤테이션에서 선언된 변수에 접근하는 것도 가능하다. 변수는 여기서 보기.
Operation name
지금까지는 query 키워드와 쿼리 이름을 생략하여 표현하였지만 실제 앱에서는 애매하지 않게 하는 것이 좋다.
아래 예시에서는 opration type으로 query를 사용하였고, operation name 으로 HeroNameAndFriends 을 사용하였다.
opertaion type 은 query, mutation, subscription 중 하나이며 너가 어떤 작업을 할 것인지를 보여준다. operation type 은 쿼리 축약법으로 보여주지 않는다면 반드시 필요하며, 이를 생략할 경우에는 해당 작업에 이름과 변수를 부여할 수 없다.
operation name 은 너가 하려는 작업을 뜻하는 명시적인 이름이다. operation name 은 오직 다중작업을 할 때에만 필요로 하지만, 디버깅이나 서버측에서 로깅할때 굉장히 도움이 되므로 이 이름을 사용하는 것이 좋다. 네트워크 로그나 GraphQL 서버에서 문제가 생겼을 때, 이 이름을 사용해서 쿼리를 구분하는 것이 내용을 해석하는 방법보다 쉽기 때문이다.
이 이름은 너가 가장 좋아하는 프로그래밍 언어에서 함수 이름과 같다고 생각하면 된다. 예를 들어 자바스크립에서는 익명함수로 쉽게 작업할 수 있지만, 함수에 이름을 주었을 때에는 이를 추적하거나 디버깅하거나 로그하기 쉽다. GraphQL 쿼리와 뮤테이션, fragments 이름도 마찬가지로 다른 요청들을 확인할 때 서버 측에서 유용한 디버깅 툴이 될 수 있다.
Variables
우리는 지금까지 쿼리 문자열에 arguments 들을 적었다. 그러나 대부분의 어플리케이션에서는 arguments 필드는 동적일 것이다. 예를 들어, 너가 관심있는 스타워즈 에피소드를 선택할 수 있는 드롭다운이 있거나, 검색할 수 있는 필드 또는 필터가 있을 수 있다.
이러한 동적인 arguments 를 직접 쿼리 문자열에 보내는 것은 좋은 생각이 아니다. 왜냐하면 클라이언트 측의 코드가 동적으로 쿼리 문자열을 조작해야 하며, GraphQL 의 포멧이 맞게 시리얼라이즈 해야하기 때문이다. 대신에 GraphQL에서는 동적 값들을 쿼리에서 빼내어 개별적인 집합으로 보내는 것이 가능하다. 이러한 값들을 variables 이라고 부른다.
우리가 variables 로 작업할 때는 세가지가 필요하다.
- 쿼리에 있는 동적인 값을
$variableName
로 대체한다. $variableName
을 쿼리가 받는 변수 중 하나로 선언한다.variableName: value
를 분리된, 변수의 집합(일반적으로 JSON)으로 보낸다.
다 같이 보면 이러하다.
이제 클라이언트 코드에서는 우리는 완전히 새로운 쿼리를 만드는 대신에 단순히 다른 값들을 보낼 수 있다. 또한 쿼리에서 어떤 arguments 가 동적이 될 것인지 표시해 주는 것은 좋은 관행이다. 절대로 쿼리를 만들 때 사용자가 제공한 값들을 사용하여 string interpolation을 하는 것을 피해야 한다.
Variable definitions
위에 쿼리에서 보면 변수 정의는 ($episode: Episode) 이 부분이다. 이 것은 타입 언어의 함수에서 argument를 정의하는 것과 같은 방식이다. 모든 변수들을 $ 뒤에 나열하고 그 뒤에 이것의 타입을 보여준다. 여기서는 Episode 가 타입이다.
선언된 모든 변수는 스칼라, enums 혹은 input object types 중 반드시 하나여야 한다. 만약 복잡한 객체를 필드에 넣고 싶다면 서버측의 어떤 input type 매치되는지 알아야 한다. chema page 에서 input obejct types 에 대하여 더 배워보자.
변수 정의는 선택적 또는 필수적이다. 위와 같은 경우에서는 Episode 타입 앞에 !가 없기 때문에 선택적이다. 그러나 변수를 보내는 필드가 non-null arguments 를 원하다면, 그 변수는 반드시 필요하다.
이러한 변수 정의의 문법에 대해서 더 알고 싶다면 GraphQL schema 언어에 대해서 배우는 것이 도움이 된다. shcema 언어는 Schema page 에 더 자세히 설명되어 있다.
Default variables
디폴트 값 또한 타입 뒤에 선언함으로서 지정할 수 있다.
query HeroNameAndFriends($episode: Episode = JEDI) { hero(episode: $episode) { name friends { name } } }
모든 변수에 대해서 디폴드값이 주어져 있다면, 쿼리를 변수 없이도 요청할 수 있다. 만약 어떤 변수가 변수 집합을 통해 보내진다면 이 디폴드 값을 오버라이드 할 것이다.
Directives
어떻게 변수를 이용하여 동적인 쿼리에 string interpolation 을 피할 수 있는지 이야기해 보았다. arguments에 변수를 보내는 것은 이러한 문제의 많은 부분을 해결하지만, 변수를 이용하여 쿼리의 구조와 형태를 동적으로 바꿀 수 있는 방법이 필요할 것이다. 예를 들어, 한가지 이상의 필드를 포함하는, 요약되고 상세한 뷰를 가진 UI 컴포넌트가 있다고 해보자.
이러한 컴포넌트를 위한 쿼리를 만들어보자.
withFriends 에 true 값을 보내도록 변경해보고 결과값이 어떻게 바뀌는지 보자.
우리는 GraphQL 의 새로운 기능인 directive 를 썼다. direct 는 필드나 fragment 에 포함될 수 있으며 서버가 원하는데로 쿼리의 수행에 영향을 미친다. core GraphQL 스펙은 두 개의 directive 를 포함하는데, 이는 모든 spec-compliant GraphQL 서버 구현에서 반드시 지원되어야 한다.
- @include(if: Boolean) argument 가 true 일 때에만 이 필드를 포함한다.
- @skip(if: Boolean) argument 가 true 면 이 필드를 스킵한다.
Directives는 쿼리에 필드를 더하거나 제거할 때 문자열 조작을 필요로 하는 상황에서 벗어나는데 도움이 된다. 서버 구현에서 완전히 새로운 directive를 정의함으로써 실험적인 부분을 추가할 수 있다.
Mutations
대부분의 GraphQL 에 관한 논의는 데이터 가져오기에 집중해 있지만, 서버측의 데이터를 수정하는 것 또한 데이터 플랫폼에 있어서 중요하다.
REST 에서는 일부 요청은 서버 측에 부작용을 일으킬 수 있는데, 관습적으로 데이터를 수정할 때 GET 을 사용하지 않도록 한다.
GraphQL도 비슷하다 - 기술적으로는 어떠한 쿼리도 데이터 쓰기에 영향을 줄 수 있도록 구현될 수 있다. 하지만, 데이터를 쓰는 것에 대한 요청은 mutation 을 통해서 보내지는 것이 관습이다.
쿼리와 같이, 뮤테이션 필드가 객체타입을 반환하면 당신은 nested 필드를 요청할 수 있다. 이 것은 업데이트 이후에 해당 객체의 새로운 상태를 가져오는데 유용하다. mutation 의 간단한 예시를 봐보자.
createReview 필드가 새롭게 생성된 리뷰의 stars 와 commentary 필드를 리턴하는 것에 주목해보자. 이것은 기존에 존재하는 데이터를 수정하는데 유용한데, 예를 들어 필드를 증가시킬 때 우리는 값을 변형하고 필드의 새로운 값을 가져오는 쿼리를 하나의 요청으로 할 수 있기 때문이다.
이 예시에서, 알아챘는지 모르겠지만, 우리가 보낸 review 변수는 스칼라 타입이 아니다. 이것은 input object type 으로, argument 로써 보내질 수 있는 특별한 종류의 오브젝트 타입이다.
Multiple fields in mutations - 변형의 다양한 필드
mutation 은 query 와 같이 여러개의 필드를 포함할 수 있다. 둘 사이에는 이름을 제외하고 특징적인 차이점이 하나 있다.
query 필드는 평행적(parallel)으로 실행되지만, mutation 필드는 하나 하나씩 순서로 진행된다.
이는 하나의 요청에 있는 두 개의 incrementCredits mutation 에서, 반드시 두 번째 mutation 이 시작하기 전에 첫 번째 mutation 이 끝나는데, 이는 이 두 조건이 경쟁하지 않게끔 한다.
Inline Fragments
다른 타입 시스템들과 같이 GraphQl 스키마는 인터페이스와 유니언 타입(공용체)을 정의하는 기능을 포함하고 있다. 이에 관해서는 스키마 가이드에서 배운다.
만약에 인터페이스나 공용체를 리턴하는 필드를 요청한다면, 데이터에 접근하기 위해서 inline fragments 를 이용해야 될 것이다. 가장 쉬운 방법은 아래 예시와 같다.
이 쿼리를 보면, hero 필드는 Character 타입을 리턴하는데, 이는 에피소드 argument에 따라 Human이거나 Droid 둘 중 하나이다. 우리는 Character 인터페이스에 존재하는 필드들만 요청할 수 있는데 예를 들어 name 이다.
실제 타입에 맞는 필드를 요청하기 위하여, 우리는 type 조건문과 함께 inline fragment 를 사용할 수 있다. 첫 번째 fragment 는 on Droid 라고 명시되어 있기 때문에 primaryFunction 필드는 오직 히어로에서 리턴되는 Character가 Droid 타입일 때만 실행된다. 비슷하게, height 필드는 Human 타입일때만 실행된다.
이름이 지정된 fragments 도 항상 타입이 붙기 때문에 비슷하게 사용될 수 있다.
Meta fields
만약 GraphQL 에서 어떤 타입을 돌려받을지 알 수 없는 상황이라면, 클라이언트 측에서 해당 데이터를 어떻게 다룰 것인지 결정해야 한다. GraphQL 은 객체 타입의 이름을 얻기 위해 __typename 이라는 메타필드를 언제라도 요청할 수 있게끔 되어 있다.
In the above query, search
returns a union type that can be one of three options. It would be impossible to tell apart the different types from the client without the __typename
field.
위의 쿼리에서 서치는 공용체 타입을 리턴하는데, 이는 세 가지 옵션 중 하나일 수 있다. __typename 필드가 없다면 클라이언트 측에서 해당 필드가 어떤 타입인지 알 수가 없다.
GraphQl 은 몇 개의 메타필드를 제공하는데, Introspection system 에서 확인할 수 있다.
* 해당 글은 번역기 돌리다가 크롬 번역기 말도 안되는 해석에 지친 본인이 나중에 참고할 의도로 대충대충 발로 해석한 것이니 참고용으로만 사용하시길 바랍니다.
* 출처: https://graphql.github.io/learn/queries/
'배움의 즐거움 > 프로그래밍' 카테고리의 다른 글
(6) GraphQL - 인트로스펙션 (0) | 2018.12.31 |
---|---|
(5) GraphQL - 실행 (0) | 2018.12.31 |
(4) GraphQL - 유효성 검사 (0) | 2018.12.31 |
(3) GraphQL - 스키마와 타입 (0) | 2018.12.31 |
(1) GraphQL - 소개 (0) | 2018.12.31 |