본문 바로가기

개발/graphQL

GraphQL이란?

REST API와는 다른 GraphQL

REST API에서는 url로 요청을 보내서 서버로부터 요청에 맞는 적절한 응답 데이터를 받아온다. 이는 서버가 응답해주기로 정해놓은 틀 내에서 프론트가 데이터를 전달 받을 수 있다는 것이다. 가령 예를 들어

GET /members
GET /memos

라는 rest api가 있을때 프론트 측에서 저 url로 요청을 보내면 서버가 정해서 반환해주는 데이터를 받게된다. 받게 되는 데이터가 어떤 구조인지는 console.log에 찍어서 확인해보거나 브라우저를 통해 url로 접속해서 json 데이터를 직접 확인하거나 일 것이다. 반면 GraphQL 에서는 url이 존재하지 않는다. rest api에는 있는 uri 체계도 없고 /members 이런 url도 없고 그저 하나의 엔드포인트만 있다. 이 엔드포인트로 프론트측에서 GraphQL로 작성한 쿼리를 날려서 원하는 데이터를 받아오는 것이다. 이를 통해 다음의 두가지 문제를 해결한다.

 

GraphQL이 해결해주는 REST api의 문제 2가지

1. over fetching

사용자들의 이름만 필요한데 GET /members 로 요청을 보내면 응답값으로는 member들에 대해 이름, 나이, 주소, 회원 등급 등등 수많은 데이터가 온다. 그저 사용자의 이름만 필요할 뿐인데 쓸데도 없는 방대한 데이터가 응답으로 오게되는 것이다. 물론 서버에서 /members/name 이런식으로 이름만 반환하도록 api를 만들수도 있지만 프론트에서 어떤 형태의 데이터를 원할지는 모르는 것이기 때문에 모든 경우의 수를 열어두고 api를 다 만들어두는 것은 불가능한, 매우 비효율적인 일이다.

 

2. under fetching

under fetching은 필요한 데이터를 한번에 가져오지 못하고 여러번 요청해서 가져오는 경우. 만약 사용자 데이터와 메모 데이터가 필요할 경우에 GET /members,  GET /memos 이렇게 두번의 요청을 보내서 데이터를 받아오고 사용자의 화면에 표시하게 될 것이다. GraphQL을 사용하면 원하는 데이터를 한번에 다 가져올 수 있게 된다.

즉 GraphQL을 사용하면 프론트든 백이든 개발자가 어떤 정보를 가져올지를 통제할 수 있다. GraphQL을 사용해서 프론트가 서버에 데이터를 요청한다면 마치 프론트엔드가 직접 데이터베이스로부터 원하는 형식의 데이터를 가져오는 느낌이다.

마치 이런 느낌...? (비유적인 그림입니다. 프론트에서 데이터베이스로 바로 접근한다는 것이 아닙니다.)

 

 

graphql 시작해보기

공식문서 설명에 따르면 graphql yoga는 설치를 쉽게 하는데에 중점을 둔 완전한 기능을 갖춘 GraphQL 서버라고 한다. 다른건 모르겠고 이걸 쓰면 설치를 쉽게 할 수 있는 듯 하다.

import { GraphQLServer } from 'graphql-yoga';
import resolvers from './graphql/resolvers';

const server = new GraphQLServer({
	typeDefs:"graphql/schema.graphql",
	resolvers
})

server.start(() => console.log("시작!"));

yoga를 사용해서 GraphQL 서버를 만드는 방법이다. typeDefs와 resolvers를 인자로 넘겨야하는데 typeDefs는 어떤 요청에 대해 어떤 형식의 데이터가 반환되는 지를 정의해놓는 것이다. 이렇게 사전에 정의된 것을 바탕으로 resolvers가 실제 데이터를 반환해주는 것이다.

type Member{
    id : Int!
    name:String!
    age:Int!
    address:String!
}

type Query{
    members: [Member]!
    getMember(id:Int!): Member
}

schema.graphql 예제 코드

members 로 요청하면 응답 데이터의 형식은 Member 객체 배열이고 getMember 라는 함수를 호출하면 응답 데이터는 Member 객체임을 알려주는 것이다. 이제 요청에 대해 실제로 응답해주는 것은 resolvers.js에서 해줘야한다.

import { m1, getById } from './db.js';
//m1은 Member 객체의 배열

const resolvers = {
    Query : {
        members : () => m1,
        getMember : (_, { id }) => getById(id)
    }
}

export default resolvers

해당 요청이 들어오면 요청에 맞는 함수를 실행해서 반환값을 만든다.

GraphQL playground를 통해 실습해봤다. 데이터는 이렇게 원하는 형식대로 지정이 가능하다.

query {     //query인지 mutations 인지
  members{  // .graphql에 정의된 key값
    id,
    name,
    age,
    address
  }
}

GraphQL에는 데이터를 받아오기위한 query와 데이터를 변경하기 위한 mutation 이렇게 두가지가 있다. 위의 예제에서는 단순 데이터 조회이니 query를 썼다. 사용자가 원하는 데이터의 형식을 정의해야 하므로 두가지 방식 모두 message body에 JSON 형식의 데이터를 담고서 POST Method로 요청된다.

개발자 탭을 열면 확인이 가능하다 아래쪽에 Request Payload에 요청 메시지 바디에 어떤 형식의 값이 들어가는지 보인다. 저 부분을 참고해서 그렇다면 insomnia에서도 되나? 하고 테스트 해봤다. (포스트맨보다 ui가 깔끔해서 insomnia를 선호한다)

결과는 아주 잘나온다.. 신기하다..

지금까지 알아본것은 Query와 Mutation 중에 단순 데이터 조회를 위한 Query였다. 이제 Mutation에 대해서 알아보자.

 

Mutation

데이터 베이스의 상태가 변할때 사용하는 것.

type Member{
    id : Int!
    name:String!
    age:Int!
    address:String!
}

type Query{
    members: [Member]!
    getMember(id:Int!): Member
}

type Mutation {
    addMember(name: String!, age:Int!, address:String):Member!
    deleteMember(id : Int!):Boolean!
}

schema.graphql 파일에 Mutation 을 추가했다.

const resolvers = {
    Query : {
        members : () => getMembers(),
        getMember : (_, { id }) => getById(id)
    },
    Mutation : {
        addMember : (_, {name, age, address}) => addMember(name, age, address),
        deleteMember : (_, {id}) => deleteMember(id)
    }
}

그에 따라 resolver 에서도 addMember와 deleteMember를 구현한다. 간단하게 filter와 push를 가지고 리스트에 넣고 빼는 걸로 했다. 

playground에서는 이런식으로 호출하면 된다.

메시지 바디에는 위와같이 들어간다. 이를 참고해서 insomnia 에서 해보면 

id 1과 2 가 삭제된 것을 확인할 수 있다.

 

참고하면 좋은 글

https://code-masterjung.tistory.com/19

 

'개발 > graphQL' 카테고리의 다른 글

리액트로 GraphQL 써보기  (0) 2021.09.04
GraphQL로 rest api 감싸기  (0) 2021.09.01