GraphQL을 공부하면 한번은 마주치는 개념이다 바로 over-fetching과 under-fetching이다.
예를 들어 페이스북 myprofile을 예시로 들어 보겠다. 페이스북에 방대한 데이터에서 사용자와 관련된 데이터는 무수히 많을 것이다.
사용자가 좋아요 누른 게시글 혹은 사용자가 가입한 클럽, 작성한 게시글, 등등 많은 양의 데이터가 쏟아진다
하지만 myprofile에 필요한 데이터는 한정적이고 Front에 효과적으로 데이터를 전달해야지 동적인 데이터를 만들기 쉬워질것이고 데이터 통신의 무게도 가벼워질것이다.
이때 Over-fetching과 Under-fetching의 개념을 짚고 들어갈 수 있는것이다.
// over-fetching의 예제 { mypage :{ Name: Baek Birthday : Oct.08 Hobby : Jogging .... }, LikePost:{ Post_uid: 1 Post_uid: 3 ... }, JoinClub:{ ClubName: "Like Run", ClubName: "Make Cook" ... } } // under-fetching의 예제 { mypage :{ Name: Baek Birthday : Oct.08 .... } }
위 예시를 보면 한쪽은 데이터를 너무 받아왔으며 남은 한쪽은 데이터를 부족하게 보내준 예시이다.
- over-fetching: 사용하는 데이터보다 더 많은 데이터를 보내주는 일이다.이는 쓸모없는 데이터를 보내 통신을 무겁게 하고 프론트측에 데이터의 혼동을 줄 수 있다.
- under-fetching: 하나의 EndPoint로 데이터가 충족되지않으니 API를 두번 호출해야하는 상황이 된다. 이는 사용자에게 느린 서비스를 제공할 수 밖에 없게된다.
🤚🏻 때문에 데이터는 필요한 데이터만 만들어서 통신을 해야하는것이다. 이를 위해 REST API에서는 SQL이나 DBMS를 이용해 데이터를 엔드포인트마다 가공하여 보내주거나 받아서 DataBase에 처리한다.
#기존 데이터 호출 Query
select ID, Name from tableA
#GraphQL 데이터 호출 쿼리
{
tableA{
ID
Name
}
}
다양한 Server 프레임워크에 사용가능하다. (Node.js, Django, Spring 등)
API마다 다른 EndPoint를가지고 SQL Query를 처리하는 REST와는 다르게 GraphQL는 하나의 요청으로 여러가지 엔드포인트를 호출한다.(GraphQL은 스키마의 타입마다 데이터베이스 SQL 쿼리가 달라진다.)
- ApolloServer라는 훌륭한 오픈 서버를 통해 GraphQL의 데이터 호출 상태를 확인 할 수 있다.
// typeDefs를 통해 Data 스키마를 정의하고 resolbers를 통해 스키마에 따른 데이터를 처리하는 방법을 넣는다. const server = new ApolloServer({ typeDefs, resolvers }); server.listen().then(({ url }) => { console.log(`Running on ${url}`); });
- typeDefs를 통해 GraphQL의 schema를 정의해준다.
// graphql은 기본적으로 nullable이 된다 따라서 require로 필드를 만들고 싶으면 !를 뒤에 붙이면 Notnull필드가 된다. // (EX : deleteTweet(id: ID!): Boolean!) const typeDefs = gql` type User { id: ID! firstname: String! lastname: String! fullName: String! } type Tweet { id: ID! text: String! author: User! } type Query { // 전체 User의 데이터를 Array로 반환한다. 이떄 return되는 값은 NotNull일수 없으며 안에 데이터도 NotNull일 수 없다. allUser : [User!]! allTweets: [Tweet!]! // argument로 id에 ID를 받고 그에따른 Tweet을 리턴해준다. tweet(id: ID!): Tweet ping : String! } type Mutation { postTweet(text: String, userId: ID): Tweet deleteTweet(id: ID!): Boolean! }
- tweets와 users라는 dummyData를 넣어주고 resolvers에 데이터의 처리 방법을 작성하여 넣어준다.
let tweets =[ { id:"1", text:"first one", userId: "2" }, { id:"2", text:"Bye", userId: "1" } ] let users = [ { id: "1", firstname: "Baek", lastname: "Joon", // fullName: "BaekJoon" }, { id: "2", firstname: "Tom", lastname: "Holand", // fullName: "SpiderMan" } ]
const resolvers = { Query: { //argument로 받는값에 따라 데이터를 보내준다. //argument로 입력받은 tweet을 찾아서 반환해준다. tweet(root, {id}){ return tweets.find(tweet => tweet.id = id) }, // allTweets을 통해서 tweets배열을 return해준다. // allTweets라는 Query를 입력하면 dummyData의 Tweets내역이 출력된다. allTweets(){ return tweets }, }, // Get요청이 아닌 메소드는 Mutation으로 처리된다. Mutation: { // 입력 받은 argument를 처리하는방식 postTweet(__, {text, userId}){ const newTweet = { id: tweets.length + 1, text, } tweets.push(newTweet) // 새로 tweet을 입력받고 입력된 tweet을 반환한다. return newTweet }, deleteTweet(__, args){ const tweet = tweets.find(tweet => tweet.id == args.id) if(!tweet) return false tweets = tweets.filter(tweet => tweet.id !== args.id) return true } }, }
4. 데이터 처리에대해 입력을 했으니 데이터를호출해보자 (localhost:4000으로 이동하면 Apollo가 열리는것을 확인 할 수 있다.)
- allTweets로 데이터를 호출 하였을때 모든 데이터가 출력된다.
- argument로 입력받은 id값의 데이터만 출력된다.
- 새롭게 id:5 라는 tweet이 작성되었다.
GraphQL :
- 사용방법이 어려우나 명확한 데이터 처리가 가능하며 앞서말한 under, over Fetching이 일어날 확률히 현저히 적다.
- Front-End에서 데이터를 정리 할 수 있다.
- 하나의 Query를 통해 데이터를 송수신 할 수 있다. 그리고 Query가 쉽고 직관적이다.
- EndPoint의 모양이 Query의 모양과 일치하도록 실행라이브러리에 의해 구성됩니다.
- 엄격하게 정의된 데이터 유형은 클라이언트와 서버 간의 통신 오류를 줄여줍니다.
REST :
- 사용방법이 간단하나 under, over Fetching이 일어날 확률히 상대적으로 높다.
- Back-End에 의존해야하는 경향이 있다.
- DB의 데이터를 가공하여 보낼 수 있다.
- EndPoint를 통해 구성을 한다.
향후 진행방향
- 외부 API에서 필요한 정보를 가져오는 법을 습득하겠다.
- 향후 프로젝트에 쓰일 Django와 연동하는 법을 실습 할 예정이다.
(https://docs.graphene-python.org/projects/django/en/latest/tutorial-plain/)- RestAPI로 진행된 프로젝트를 GraphQL로 리팩토링 해보겠다.
- DB data 관계를 구현하겠다.
참고