GraphQL(gql)은 Structed Query Language(sql)와 마찬가지로 쿼리 언어이다. 하지만 gql과 sql의 언어적 구조 차이는 매우 크다.
sql은 데이터베이스 시스템에 저장된 데이터를 효율적으로 가져오는 것이 목적이고, gql은 웹 클라이언트가 데이터를 서버로부터 효율적으로 가져오는 것이 목적이다.
sql의 문장은 주로 백엔드에서 작성하고 호출하는 반면, gql의 문장은 주로 클라이언트 시스템에서 작성하고 호출한다.
서버사이드 gql 어플리케이션은 gql로 작성된 쿼리를 입력으로 받아 쿼리를 처리한 결과를 다시 클라이언트로 돌려준다.
클라이언트 개발을 하면서 서버와 통신을 해야 할 때 보통 서버가 구현해놓은 API를 호출해 데이터를 보내거나 받아온다. 보통 앱에서 하나의 View를 그리기 위해서는 보통 여러 번 API를 호출해야하고, 호출을 통해 받아온 데이터를 조합해야한다.
앱의 페이지가 복잡해질수록 많은 호출을 해야하고 데이터 조합을 위해 순차적인 처리가 들어가는 경우가 많아지기 때문에 데이터를 조합하는 것은 매우 복잡해진다.
=> 이를 해결하기 위해서는 앱 단에서 직접 뷰에서 보여질 쿼리를 만들어 모든 데이터를 한 번에 가져와야 하는데 기존의 REST API에서는 사용자는 서버에서 정의한 데이터 구조만을 가져와 불가하다.
GraphQL은 클라이언트에서 자기에 필요한 데이터만을 쿼리할 수 있도록 하여 위의 문제를 해결할 수 있다. 이를 위해 GraphQL은 클라이언트에서 사용할 Query Language를 정의한다. 클라이언트가 자신에게 필요한 데이터에 대한 Query를 선언해 GraphQL에 넘기면 GraphQL은 Query를 해석해서 서버에서 필요한 데이터를 가져온 후 클라이언트에 해당 쿼리에 대한 데이터를 반환한다.
즉, GraphQL은 쿼리를 선언하면 해당 값에 맞는 데이터를 가져온다. 이는 선언적으로 쿼리를 처리함으로써 더욱 직관적으로 어떤 데이터가 오는지를 알 수 있게 된다.
(밑에 두 개는 아직 안 써봐서 모르겠다. 빨리 플젝을 해보자)~~
위처럼, gql API를 사용하면 여러번 네트워크 호출을 할 필요없이 한 번의 네트워크 호출로 처리를 할 수 있다.
먼저 REST 방식이다. 각각의 리소스는 Endpoint로 정의되고, Endpoint에 특정 HTTP Method로서 요청하여 데이터를 검색할 수 있다.
/musicals/1
{
"title": "Elisabeth",
"company": {
"name": "EMK",
"number": "02-123-1234"
}
}
위의 예시를 GraphQL로 옮겨보자! 일단 Musical과 Company라는 타입을 정의해야한다.
type Musical {
id: ID
title: String
company: Company
}
type Company {
id: ID
name: String
number: String
musicals: [Musical]
}
여기서 gql과의 큰 차이점이 드러난다. gql에서는 리소스의 유형과 리소스를 가져오는 방식이 완전하게 분리되어 있다. 우리는 지금 Musical과 Company의 형태만 정의했지, 클라이언트에서 데이터를 어떻게 요청할 수 있는지에 대한 아무런 정보가 없다.
Musical과 Company에 접근할 수 있도록, 우리는 Query라는 타입이 필요하다.
type Query {
musical(id: ID!): Musical
company(id: ID!): Company
}
이제 REST와 비슷한 방식으로 요청을 할 수 있다
/graphql?query={ musical(id: "1") {title, company {name}}}
{
"title": "Elisabeth",
"company": {
"name": "EMK"
}
}
REST와 다르게 각 Resource에 대한 Endpoint가 따로 존재하지 않고, 하나의 엔드포인트만 존재한다. 또한 해당 엔드포인트로 요청 시, 원하는 리소스와 해당 리소스에서 원하는 필드를 특정하는 gql query를 함께 보낸다.
GET /musicals/:id
GET /companies/:id
GET /musicals/:id/comments
POST /musicals/:id/comments
REST API에서 제일 먼저 생각해야 하는 것은 어떤 엔드포인트로 요청을 보내야하는지이다. 각 엔드포인트는 해당 리소스를 가리키며, 요청의 HTTP method에 따라 어떤 작업을 진행하는지 달라진다.
gql에서는 url을 Resource를 특정 짓는 것에 사용하지 않는다. GraphQL Schema가 resource를 특정 짓는다. 또한 HTTP Method로 어떤 작업을 진행하게 되는지 구분하지 않고, Query와 Mutation이라는 타입을 사용해 구분한다.
API를 요청하면 요청으로부터 Resource, HTTP Method에 해당하는 사전에 정의된 로직을 실행하고 결과를 되돌려준다.
app.get("/musicals/:id", function (req, res) {
const { id } = req.params;
// working with DB or call different API, ...
const result = {
musical: "Elisabeth",
company: {
name: "EMK",
number: "02-123-1234",
},
};
res.send(result);
});
musical과 company 필드를 갖는 json 데이터를 응답하는 /musicals 엔드포인트가 있다. /musicals 엔드포인트는 GET 방식 요청에서만 반응한다. 클라이언트에서 이 서버의 GET /musicals/1
로 요청하면 다음 응답을 받을 수 있다.
{
"title": "Elisabeth",
"company": {
"name": "EMK",
"number": "02-123-1234"
}
}
const resolvers = {
Query: {
musical: (parent, args) => {
// working with DB or call different API, ...
const result = {
title: "Elisabeth",
};
return result;
},
company: (parent, args) => ({ name: "Elisabeth", number: "02-123-1234" }),
},
};
REST와는 다르게 Query 타입의 musical와 같은 특정 필드에 해당하는 함수를 제공한다. GraphQL에서는 이런 함수를 resolver라고 한다.
클라이언트에서 다음의 쿼리를 통해 요청하여 응답을 받을 수 있다.
query {
musical(id: "1") {
title
company {
name
number
}
}
}
서버로 요청이 들어오면, 서버는 요청에서 GraphQL Query를 찾는다. query에 존재하는 field들의 resolver를 호출하고, 각 필드마다 호출된 resolver의 반환값을 모아 query의 형태와 일치하는 json 데이터를 응답한다.
{
"title": "Elisabeth",
"company": {
"name": "EMK",
"number": "02-123-1234"
}
}
참고
[GraphQL]1. GraphQL이란 무엇인가?
GraphQL 개념잡기
GraphQL과 REST의 차이점
GraphQL이란?