GraphQL Study01
GraphQL
이란?
API
를 만들 때 사용할 수 있는 쿼리 언어타입 시스템을 사용하여 쿼리를 실행하는 서버사이드 런타임
런타임(
runtime
) : 컴퓨터 프로그램이 실행되고 있는 동안의 동작- 쿼리를 실행하고 이에 대한에 데이터를 받을 수 있는 런타임
GraphQL
은 선언형(declarative
) 데이터 패칭(fetching
) 언어- 특정 언어, 프레임워크와 무관
특징
- 위계적
- 필드 내에 필드가 중첩 가능
- 쿼리와 그에 대한 반환 데이터는 형태가 서로 같다.
- 제품 중심적
- 클라이언트가 요구하는 데이터와 클라이언트가 지원하는 언어 및 런타임에 맞춰 동작
- 엄격한 타입 제한
GraphQL
서버는GraphQL
타입 시스템을 사용- 스키마 데이터 포인트마다 특정 타입을 명시, 이를 기초로 유효성 검사
- 클라이언트 맞춤 쿼리
GraphQL
서버는 클라이언트 쪽에서 받아서 사용 할 수 있는 데이터를 제공
- 인트로스펙티브(
introspective
)- 스키마 확인 가능
기존 REST
의 문제
오버패칭
- 클라이언트 측에서 필요하지 않은 데이터까지 수신하는 경우가 다수
ex
. 현재 페이체크 측 관리자 페이지에서 회사 이름만을 받아오고 싶은데, 회사 전체 정보를 수신하고 있음언더패칭
- 필요한 데이터를 요청하기 위해 서브데이터를 추가적으로 요청해야 하는 경우, 서버에 2번의 요청 필요
유연성 부족
- 하나의 기능을 만들 때마다 엔드포인트가 늘어나는 구조
쿼리어
query
select
mutation
insert
,update
,delete
subscription
- 데이터에 변화가 생기면 알림을 주는 형태
query
1 | query { |
query
키워드는 생략 가능위의 코드는 아래와 같다.
1
2
3
4
5
6 > {
> hero {
> name
> }
> }
>
위의 요청을 해석하면, hero
란 이름으로 정의된 타입에서 name
만 가지고 와달라, 가 되며 서버에서 제대로 정의가 되어 있다면 아래가 응답이 된다.
1 | { |
응답은 항상 data
라는 필드 안에 존재한다.
여기서 눈여겨 보아야 할 것은 쿼리와 결과가 정확히 동일하다는 점
GraphQL
쿼리는 연관된 객체와 필드를 탐색할 수 있기에, 기존 REST
의 언더패칭의 문제 해결 가능
요청
1
2
3
4
5
6
7
8{
hero {
name
friends {
name
}
}
}응답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
조건 query
인자를 주는 것으로 해당 조건에 맞는 것만을 조회도 가능
요청
1
2
3
4
5
6{
human(id: "1000") {
name
height
}
}응답
1
2
3
4
5
6
7
8{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
}
}
REST
의 경우 단일 인자만 전달 가능한 반면, GraphQL
은 객체 전체를 전달도 가능
여러 번의 요청을 한 번에
요청
요청해야 하는 건은 다음과 같다.
1
2
3
4
5
6query lifts {
allLifts {
name
status
}
}1
2
3
4
5
6query trails {
allTrails {
name
difficulty
}
}그러면 이렇게 하나의 레이블로 묶어 요청도 가능하다
1
2
3
4
5
6
7
8
9
10query allList {
allLifts {
name
status
}
allTrails {
name
difficulty
}
}
응답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{
"data": {
"allLifts": [
{
"name": "Astra Express",
"status": "OPEN"
},
],
"allTrails": [
{
"name": "Blue Bird",
"difficulty": "intermediate"
},
]
}
}
레이블
일명 작업 타입. 디버깅이나 서버 측에서 로깅하는데 사용하며 문제 발생 시 로그에 찍힌 레이블을 확인해 어디가 문제인지 확인 가능
별칭
DB와 매한가지로, 일명 as
기능을 쿼리 단에서 제공
요청
1
2
3
4
5
6
7
8{
empireHero: hero(episode: "EMPIRE") {
name
}
jediHero: hero(episode: "JEDI") {
name
}
}응답
1
2
3
4
5
6
7
8
9
10{
"data": {
"empireHero": {
"name": "Luke Skywalker"
},
"jediHero": {
"name": "R2-D2"
}
}
}
프래그먼트
재사용 가능한 단위
어떨 때 쓰나요?
1 | query { |
현재 name
, status
, capacity
, night
, elevationGain
필드가 중복
이런 경우, 서버에서 어떤 타입에 정의된 것인지 on
뒤에 기재해준 뒤 fragment
를 먼저 선언하고
1 | fragment liftInfo on Lift { |
실제 조회 단에서 해당 정의를 사용
1 | { |
실제로 서버로 요청되는 것은 위에 중복이 많은 코드와 동일
변수
동적 값을 정적으로 타이핑했던 쿼리를 지우고, 이를 별도로 전달하기
요청
1
2
3
4
5
6
7query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}요청 파라미터
1
2
3{
"episode": "JEDI"
}
응답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
변수의 정의 ($episode: Episode)
$episode
의 변수 타입은Episode
임을 선언모든 변수는 서버에서 일치하는 입력 타입이어야 한다
아마 위의 예제에서 서버는
Episode
타입이 정의되어 있고,string
을 문자열로 요구할 것임을 유추 가능자세한 변수 선언은 따로 정리 예정
만약 필수 인자라면
($episode : Episode!)
기본 값을 정의하고자 한다면
($episode: Episode = "JEDI")
mutation
요청
1
2
3
4
5
6mutation ($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}Episode
,ReviewInput
타입을 받는mutation
- 그 중
createReview
타입인 것을 찾아서 episode
와review
를 넣고- 해당 데이터(막 만든 데이터)의
stars
,commentary
를 반환
요청 파라미터
1
2
3
4
5
6
7{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
응답
1
2
3
4
5
6
7
8{
"data": {
"createReview": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
}
query
필드와 mutation
필드의 차이
query
필드는 병렬로 실행mutation
필드는 하나씩 차례로 실행- 하나의 요청에서 두 개의 뮤테이션을 보낸다고 가정하면 처음 보낸 요청은 두 번째 요청 전에 완료되는 것이 보장된다
subscription
1 | subscription { |
위의 쿼리가 실행되면, listen
상태가 되며 다른 요청에 의해 mutation
이 되면 name
, capacity
, status
를 반환
like
를 누르면 알림이 오는 시스템 등에 이용