-
GraphQL 알아보기개발/Front-end 2021. 8. 2. 23:30
Why?
TIL 레파지토리의 문서들을 얻기 위해 Github Content API를 이용했다. 루트 폴더들, 각 폴더의 문서들, 문서의 내용 등을 REST API를 사용하여 얻었는 데 아무래도 엔드 포인트가 많다 보니 코드가 복잡해졌다. 또 내가 원하는 데이터만 받고 싶은 데 응답 구조가 정해져있다보니 응답 데이터를 직접 가공하는 수 밖에 없었다. 휴 불편하네~ 😥 불편하다. 생각하던 중 Github이 GraphQL API를 제공한다는 것을 발견했다. 예전에 잠깐 공부했을 때는 REST API보다 왜 편한지 이해를 못했는 데 역시 사람은 몸소 겪어봐야 아나보다. 직접 사용해보니 너무 너무 편했다. 그래서 오늘의 TIL은 GraphQL에 대해 정리해보려한다.
✨ GraphQL 이란?
GraphQL(gql)는 API를 위한 쿼리 언어로 API 서버에서 원하는 데이터를 효율적으로 가져올 수 있다. GraphQL을 사용하는 앱은 느린 모바일 네트워크 연결에서도 빠르게 수행할 수 있다고 한다.
GraphQL API 서버는 gql로 작성된 쿼리를 입력으로 받고 쿼리를 처리한 결과를 클라이언트에게 돌려준다. HTTP API 처럼 gql도 특정 데이터베이스나 플랫폼에 종속적이지않다.
네트워크 방식에도 종속적이지 않다. 일반적으로 gql의 인터페이스간 송수신은 네트워크 레이어 L7의 HTTP POST 메서드와 웹 소켓 프로토콜을 활용한다. 필요에 따라서는 L4의 TCP/UDP나 L2의 이더넷 프레임을 활용할 수 있다.
REST API와 차이점?
- REST API가 여러 URL에서 데이터를 얻는 것과 달리 GraphQL은 모든 데이터를 하나의 엔드 포인트를 통해 가져올 수 있다.
- REST API는 각 엔드포인트마다 데이터베이스 SQL 쿼리가 달라지고 gql API는 gql 스키마 타입마다 데이터베이스 SQL 쿼리가 달라진다.
- REST API는 리소스를 URL로 표현하고 GraphQL은 Query로 표현한다고 할 수 있다.
GraphQL 구조
📌 Query
HTTP Method의
GET
과 비슷Fields
{ players { name } } { "data": { "players": [ { "name": "Pogba" }, { "name": "Lukaku" }, { "name": "Rashford" }, { "name": "Marshal" } ] } }
- 객체에서 원하는 필드만 리턴 받을 수 있다.
- 쿼리는 한번의 요청으로 객체나 필드를 순회하여 관련된 데이터들도 가져올 수 있다. 이런 작업을 REST API에서 하려면 API 호출을 여러 번 해야한다.
Arguments
{ player(id: "Pogba") { name kit { shirtSize, bootSize } } } { "data": { "players": { "name": "Pogba", "kit": [ { "shirtSize": "large", "shoeSize": "medium" } ] } } }
- arguments를 쿼리의 필드 및 중첩된 객체들에 전달하여 원하는 데이터만 얻을 수 있다
- REST API에서 ?name=Pogba 또는 /Pogba (/:name 형식일 때) 와 같은 목적으로 사용한다.
Aliases
{ player(id: "Pogba") { name kit { shirtSize, bootSize } } player(id: "Lukaku") { name kit { shirtSize, bootSize } } } { player1: player(id: "Pogba") { name kit { shirtSize, bootSize } } player2: player(id: "Lukaku") { name kit { shirtSize, bootSize } } } { "data": { "player1": { "name": "Pogba", "kit": [ { "shirtSize": "large", "shoeSize": "medium" } ] } "player2": { "name": "Lukaku", "kit": [ { "shirtSize": "extralarge", "shoeSize": "large" } ] } } }
- 필드 이름을 겹쳐서 사용할 수는 없기에 왼쪽처럼 할 수 없다. 오른쪽 처럼 별칭을 써서 사용하면 된다.
Operation name
query PlayerDetails { player(id: "Pogba") { name kit { shirtSize, bootSize } } }
- Operation Type: query, mutation과 같은 키워드
- Operation Name: 이 작업이 무슨 일을 하는 지(메소드 명처럼..?)
Variables
query PlayerDetails($id: String) { player(id: $id) { name kit { shirtSize, bootSize } } }
- arguments를 동적으로 받고 싶을 때 사용한다.
- Operation Name 옆에 변수를 $변수이름: 타입 형태로 정의한다. 만약
String!
형태로 정의했다면 id는 반드시 String이어야 한다. - 실제 argument로 사용하려면 필드명: $변수이름 형태로 사용하면 된다.
- 전달된 변수가 없으면 디폴트로 사용할 값을 정의할 수도 있다.
$id: String = "Pogba"
형태로 쓰면 된다.
Fragments
{ player(id: "Pogba") { name kit { shirtSize, bootSize } } player(id: "Lukaku") { name kit { shirtSize, bootSize } } } { player1: player(id: "Pogba") { ...playerKit } player2: player(id: "Lukaku") { ...playerKit } } fragment playerKit on player { name, kit { shirtSize, shoeSize } }
- 왼쪽을 보면 name과 kit이 반복된다. 이처럼 동일한 구조의 필드를 가져올 때 fragment 를 이용하면 오른쪽처럼 간편하게 쓸 수 있다.
Inline Fragments
query HeroForEpisode($ep: Episode!) { hero(episode: $ep) { name ... on Droid { primaryFunction } ... on Human { height } } } { "data": { "hero": { "name": "R2-D2", "primaryFunction": "Astromech" } } }
- 위 쿼리에서 hero 필드는 $ep 인수에 따라 Human 또는 Droid 타입인 Character 인터페이스를 리턴한다.
- 위 쿼리의 name 필드는 Character 인터페이스에 있는 값이다.
- Fragment의 타입 질의 기능을 이용하면 Human 타입일 때, Droid 타입일 때 가져올 데이터를 정의할 수 있다.
- 어떤 필드가 Interface나 union 타입으로 리턴된다면 inline fragment로 원하는 데이터만 받을 수 있다.
}id: ID! name: String! friends: [Character] appearsIn: [Episode]!
}id: ID! name: String! friends: [Character] appearsIn: [Episode]! starships: [Starship] totalCredits: Int
}id: ID! name: String! friends: [Character] appearsIn: [Episode]! primaryFunction: String
- type Droid implements Character {
- type Human implements Character {
- interface Character {
Directives
query PlayerDetails ($playerShirtDirective: Boolean!){ player(id: "Pogba") { name, kit { shirtSize @skip(if: $playerShirtDirective) bootSize } } }
- 특정 필드를 포함할 지, 스킵할 지를 정할 수 있다.
- @skip : true면 스킵
- @include : true면 포함
📌 Mutation
HTTP Method의
POST
PATCH
DELETE
와 비슷mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { createReview(episode: $ep, review: $review) { stars commentary } } { "ep": "JEDI", "review": { "stars": 5, "commentary": "This is a great movie!" } } { "data": { "createReview": { "stars": 5, "commentary": "This is a great movie!" } } }
참고
'개발 > Front-end' 카테고리의 다른 글
DOM API란? (0) 2021.08.02 SSR vs CSR vs SPA (0) 2021.08.02 CORS란? (0) 2021.08.02 부트스트랩의 sr-only 클래스가 하는 일 (0) 2021.08.02 가깝고도 먼 HTML (0) 2021.08.02