
📌GraphQL
- 페이스북에서 만든 쿼리 언어(Query Language)
- 그와 동시에 쿼리에 대한 데이터를 받을 수 있는 런터임이기도 하다.
✔️GraphQL의 특징
- API서버에서 엄격하게 정의된 endpoint 들에 요청하는 대신, 한번의 요청으로 정확히 가져오고 싶은 데이터를 가져올 수 있게 도와주는 쿼리를 보낼수 있다.
- Frontend와 Backend의 협업 방식에 많은 변화를 가져올 수 있습니다. Backend에서의 많은 로직을 Frontend로 분산할 수 있다.
- GraphQL spec을 이행하기만 하면 어떤 특정 언어에 종속되지 않고 사용할 수 있다. (go, JAVA, JS, Python, Ruby 등 모든 곳에 사용할 수 있다.)
✔️GraphQL 의 목적 (SQL 과의 차이)
- 페이스북에 의해서 REST API 문제를 해결하기 위해 만들어졌다.
(REST API 설명부분에 자세히 다룰 예정)
- Structed Query Language(이하 sql)은 데이터베이스 시스템에 저장된 데이터를 효율적으로 가져오는 것이 목적이고, GraphQL(이하 gql)은 웹 클라이언트가 데이터를 서버로 부터 효율적으로 가져오는 것이 목적이다.
- sql의 문장(statement)은 주로 백앤드 시스템에서 작성하고 호출 하는 반면, gql의 문장은 주로 클라이언트 시스템에서 작성하고 호출 한다.
✔️GraphQL은 보통 어디에 쓰이나요?
- 같은 API를 쓰더라도 필요로하는 정보가 사용자마다 다를 수 있고, 사용하는 기기들마다 다를 수 있고, 또는 그 외의 추가적인 상황에 따라 다를 수 있는데, 그런 서비스들에서는 효율적이다.
✔️GraphQL Operation Type
Query
- 데이터 조회
Mutation
- 데이터 수정
subscription
- 실시간 애플리케이션 구현을 위해 사용
GraphQL 참고영상
(추가) GraphQL과 함께쓰면 좋은 Apollo
- GraphQL을 쉽게 사용하도록 도와주는 라이브러리
- apollo client 와 apollo server 를 제공한다.
- 공식 홈페이지에서 apollo client는 상태관리 라이브러리로 react redux를 apollo server는 REST API 를 대체할 수 있을 것이라고 소개하고 있다.
📌REST API
- Representational State Transfer API
- 여러개의 URL을 활용하여 작동한다.
- 모든 URL은 고유하고, 각기 다른 데이터를 제공한다.
- HTTP 통신규약에 맞춰 API를 요청할때 어떠한 요청에 어떠한 URI를 사용할지에 대한 형식
- 각 요청이 어떤 동작이나 정보를 위한 것인지지를 그 요청의 모습 자체로 추론이 가능하다.
- 해당 버튼을 누르면 해당 음료가 나오는 자판기와 같다.
✔️REST API 의 가장 큰 2가지 문제점
Over Fetching
ex) 숙소에 대한 정보를 받아오려고 하는데, 숙소에 대한 모든 정보가 아니라 특정 정보들만을 추려서 받아오고자 할때, REST API는 새로운 API를 만들거나 불필요한 정보들 까지 다 받아와야 한다.
Under Fetching
ex) 학교에서 1반에 대한 정보와 1반 학생들에 대한 정보를 받아오고자 할 때, REST API는 2번 호출을 해야 한다. 횟수도 문제지만, 비동기로 짜는 코드는 1번의 요청보다 더 복잡해진다.
✔️그렇다면 REST API 는 언제 쓰는가?
- 받아야하는 항목들이 많고, 확실히 정해진 경우에는 REST API가 유리하다.
REST API 참고영상
📌만드는 서비스에 맞게 어떤걸 사용할지 결정해야하는가??
- 결정 할 필요가 없다!!
- 백엔드 서버 하나에 Rest API , GraphQL을 둘 다 구현해 놓으면 된다.
- 각 정보랑 요청마다 유리한걸로 선택해서 OR 그냥 두 가지 방법으로 모두 구현해 놓으면 된다.
📌GraphQL, Restful API 비교
GraphQL | Restful API |
---|
Resource에 대한 형태 정의와 데이터 요청방법이 완전히 분리되어 있다. | Resource에 대한 형태 정의와 데이터 요청방법이 연결되어 있다. |
Resource에 대한 정보만 정의하고, 필요한 크기와 형태는 client단에서 요청 시 결정 | Resource의 크기와 형태를 서버에서 결정 |
GraphQL Schema가 Resource를 나타내고 Query, Mutation 타입이 작업의 유형을 나타낸다. | URI가 Resource를 나타내고 Method가 작업의 유형을 나타낸다. |
한번의 요청에서 여러 Resource에 접근할 수 있다. | 여러 Resource에 접근하고자 할 때 여러 번의 요청이 필요하다. |
📌GraphQL 구현예시 (node.js + express)
✔️server 구현
let express = require('express');
let graphqlHTTP = require('express-graphql');
let { buildSchema } = require('graphql');
let app = express();
app.use("/graphql", graphqlHTTP({
schema: schema,
rootValue: resolver,
graphiql: true,
}));
app.listen(4000, () => console.log('Now browse to localhost:4000/graphql'));
✔️일반적인 데이터 조회 요청
📜요청
query {
pizza_buns {
pb_name,
size,
bread,
sauce,
topping
}
}
query {
pizza_bun(pb_idx: 4) {
pb_name,
size,
bread,
sauce,
topping
}
}
query {
req1: pizza_buns {
pb_name,
size,
}
req2: pizzz_bun(pb_idx: 1) {
pb_name,
size,
bread
}
}
📜schema
let schema = buildSchema(`
type Query {
pizza_buns: [PizzaBun]
pizza_bun[pb_idx: Int]: PizzaBun
}
type PizzaBun {
pb_idx: ID,
pb_name: String,
size: Int,
bread: String,
sauce: String,
topping: String,
}
`};
📜resolver
let resolver = {
pizza_buns: () => {
return [ {
"pb_idx": "1",
"pb_name": "sausage",
"size": 6,
"bread": "wheat",
"sauce": "tomato",
"topping": "sausage"
}, {
"pb_idx": "2",
"pb_name": "seafood",
"size": 7,
"bread": "italian",
"sauce": "arrabiata",
"topping": "shirimp"
} ]
}
}
let resolver = {
pizza_buns: async () => {
return await new Promise((resolve) => {
pool.getConnection(function(err, connection) {
connection.query(
'SELECT * FROM pizza_bun',
(err, results) => {
connection.release();
if (err) throw err;
resolove(results);
})
})
});
},
pizza_bun: async ({pb_idx}) => {
return await new Promise((resolve) => {
pool.getConnection(function(err, connection) {
connection.query(
'SELECT * FROM pizza_bun WHERE pb_idx = ?',
[pb_idx],
(err, results) => {
connection.release();
if (err) throw err;
resolove(results);
})
})
});
},
}
✔️수정 요청을 보낼 때 (예시는 create),
📜요청
mutation {
create_pizza_bun (input: {
pb_name: "spicy",
size: 5,
bread: "parmesan oregano",
sauce: "pomodoro",
topping: "peperoncino"
}) {
pb_idx
}
}
📜schema
type Mutation {
create_pizza_bun(input: PizzaBunInput): Int
}
input PizzaBunInput {
pb_name: String,
size: Int,
bread: String,
sauce: String,
topping: String
}
📜resolver
create_pizza_buns: async ({input}) => {
const insertResultData = await new Promise((resolove) => {
pool.getConnection(function(err, connection) {
connection.query(
'INSERT INTO pizza_bun (pb_name, size, bread, sauce, topping)'
+ 'VALUES (?, ?, ?, ?, ?)',
[input.pb_name, input.size, input.bread, input.sauce, input.topping],
(err, results, fileds) => {
connection.release();
if (err) throw err;
resolove(results);
}
)
})
})
return { pb_idx: insertResultData.insertID }
}
📌Reference