GraphQL을 통하여 Rest API를 감싸보자.

Graph QL을 가지고 Rest API를 감싸보자.

graphQL

GraphQL 영상출처

제목에서와 같이 GraphQL을 가지고 Rest API를 감싸는 방법에 대하여 알아보자.
이 방법을 통하여 GraphQL의 2가지 장점인 Over-fetching, Under-fetching을 경험해 볼 수 있다.

Query

제일먼저 schema를 구현한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 원하는 구조를 생성한다.
type Movie {
id: Int!
title: String!
rating: Float!
summary: String!
language: String!
medium_cover_image: String!
url: String!

}

type Query {
# 왼쪽은 이름, 오른쪽은 return할 값.
# movies: [Movie]!
movies(limit: Int, rating: Float): [Movie]!,
getMovie(movie_id: Int!): Movie,
getSuggest(movie_id: Int!): [Movie]!
}

schema를 구현하였다면 다음으로 db.js의 함수부분을 구현한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import axios from "axios";
const BASE_URL = "https://yts.am/api/v2/";
const LIST_MOVIES_URL = `${BASE_URL}list_movies.json`;
const MOVIE_DETAILS_URL = `${BASE_URL}movie_details.json`;
const MOVIE_SUGGESTIONS_URL = `${BASE_URL}movie_suggestions.json`;

// Movie List를 가져오는 함수
export const getMovies = async (limit, rating) => {
const {
data: {
data: {
movies
}
}
} = await axios.get(LIST_MOVIES_URL, {
params: {
limit: limit,
minimum_rating: rating
}
});
return movies
}

// 원하는 Movie를 가져오는 함수
export const getMovieById = async (id) => {
const {
data: {
data: {
movie
}
}
} = await axios.get(MOVIE_DETAILS_URL, {
params: {
movie_id: id
}
})
return movie
}

// 해당영화의 추천영화를 가져오는 함수
export const getSuggestions = async (id) => {
const {
data: {
data : { movies }
}
} = await axios.get(MOVIE_SUGGESTIONS_URL, {
params: {
movie_id: id
}
})
return movies
}


schema와 함수를 만든 후 resolver부분에서 db에서 구현한 함수를가지고 구현한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { getMovies, getMovieById, getSuggestions } from "./db";

// resolvers는 함수로 구현한다.
const resolvers = {
Query: {
movies: (_, { limit, rating }) => getMovies(limit, rating),
getMovie: (_, { movie_id }) => getMovieById(movie_id),
getSuggest: (_, { movie_id }) => getSuggestions(movie_id)
}
};

export default resolvers;

이제 아래의 예시를 보면 getMovie, getSuggest 2개를 한번에 사용해서 원하는 값들만 가져올수있다.
REST API였다면 2번의 호출할 것을 1번에 끝내는 것이다.
이것을 통하여 불필요한 정보까지 포함해서 가져오는 Over-fetching과 하나를 완성하려고 많은 소스를 요청하는 Under-fetching을 해소할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
// input
query {
getMovie(movie_id: 666) {
title,
id,
url
},
getSuggest(movie_id: 666) {
title,
id,
url
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// output
{
"data": {
"getMovie": {
"title": "Chaplin",
"id": 666,
"url": "https://yts.am/movie/chaplin-1992"
},
"getSuggest": [
{
"title": "Mary Shelley",
"id": 7922,
"url": "https://yts.am/movie/mary-shelley-2017"
},
{
"title": "Dracula",
"id": 975,
"url": "https://yts.am/movie/dracula-1979"
},
{
"title": "Don't Say a Word",
"id": 965,
"url": "https://yts.am/movie/dont-say-a-word-2001"
},
{
"title": "An Adventure in Space and Time",
"id": 237,
"url": "https://yts.am/movie/an-adventure-in-space-and-time-2013"
}
]
}
}

Mutation

윗 부분은 Query에 관한 부분이였다면 아래는 데이터를 변경하는 Mutation부분을 구현해보도록 하겠습니다.

우선 schema를 먼저 작성하도록 하겠습니다.
이때 기존에서 추가로 Mutation부분을 구현해야한다.

1
2
3
4
5
6
7
8
type Mutation {
addMovie(limit: Int!, title: String!,
rating: Float!,
summary: String!,
language: String!,
medium_cover_image: String!,
url: String!): [Movie]
}

schema 작성후의 기존의 getMovies로 불러오는 배열에 원하는 값을 추가해보도록 하겠습니다.
여기서 getMovies의 limit을 지정한만큼 받은 후 맨뒤에 값을 추가해보도록 하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// db.js
export const addMovie = async (limit, title, rating, summary, language, medium_cover_image, url) => {
// 원하는 값만큼 movie list를 가져온다.
let movies = await getMovies(limit)

const newMovie = {
id: 1111111111,
title,
rating,
summary,
language,
medium_cover_image,
url
}

// movies = {...movies, ...newMovie}
movies[limit] = newMovie
return movies
}

그다음 reserver.js에도 기존에서 Mutation부분을 추가해줘야한다.

1
2
3
4
5
6
7
8
9
10
11
const resolvers = {
Query: {
movies: (_, { limit, rating }) => getMovies(limit, rating),
getMovie: (_, { movie_id }) => getMovieById(movie_id),
getSuggest: (_, { movie_id }) => getSuggestions(movie_id)
},
Mutation: {
addMovie: (_, { limit, title, rating, summary, language, medium_cover_image, url}) =>
addMovie(limit, title, rating, summary, language, medium_cover_image, url)
}
};

이렇게하면 input과 output값이 아래와 같이 나온다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// input
mutation {
addMovie(
limit: 2,
title: "김세준의 Blog",
rating: 10.0,
summary: "good",
language: "한국어",
medium_cover_image: "https://kimsejune.github.io/",
url: "https://kimsejune.github.io/") {
id,
title,
rating,
summary,
language,
medium_cover_image,
url
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//output
{
"data": {
"addMovie": [
{
"id": 9860,
"title": "Made in Hong Kong",
"rating": 7.6,
"summary": "Autumn Moon (Sam Lee), a low-rent triad living in Hong Kong, struggles to find meaning in his hopelessly violent existence.",
"language": "English",
"medium_cover_image": "https://yts.am/assets/images/movies/made_in_hong_kong_1997/medium-cover.jpg",
"url": "https://yts.am/movie/made-in-hong-kong-1997"
},
{
"id": 9859,
"title": "From the Life of the Marionettes",
"rating": 7.5,
"summary": "Made during Bergman's tax-related exile in Germany, the film continues the story of Katarina and Peter EGermann, the feuding, childless, professional couple who appear in one episode of \"Scenes From A Marriage.\" After Peter perpetrates a horrendous crime in its first scene, the rest of the film consists of a non-linear examination of his motivations, incorporating a police psychological investigation, scenes from the EGermanns' married life, and dream sequences.",
"language": "English",
"medium_cover_image": "https://yts.am/assets/images/movies/from_the_life_of_the_marionettes_1980/medium-cover.jpg",
"url": "https://yts.am/movie/from-the-life-of-the-marionettes-1980"
},
{
"id": 1111111111,
"title": "김세준의 Blog",
"rating": 10,
"summary": "good",
"language": "한국어",
"medium_cover_image": "https://kimsejune.github.io/",
"url": "https://kimsejune.github.io/"
}
]
}
}

Share