codestates/section4

Unit6 - [API] GraphQL

나아눙 2023. 3. 28. 11:19

GraphQL(Graph + Query Language)

Facebook에서 처음으로 개발했고, 오픈 소스로 제공된 쿼리 언어

API를 위한 쿼리 언어

 

GraphQL은 클라이언트 요청에 따라 유연하게 트리 구조의 JSON 데이터를 응답으로 전송

클라이언트 요청에 따라 유연하게 자원을 가져올 수 있다는 점

 

 

그래프에서 트리 추출하는 방법

쿼리 실행

이방식으로 서버에 요청을 보낸다.

query {
	책(ISBN이 "9780674430006") {
		책 이름
		저자 {
			이름
		}
	}
}

요청 해결 후 돌아온 쿼리

{
	책 : {
		책 이름 : "GraphQL",
		저자 : [
			{ 이름 : "김코딩"},
			{ 이름 : "박해커"},
		]
	}
}

 

GraphQL은 HTTP를 통해 API 서버로 요청을 보내고 응답을 받는다.

응답을 받으면 데이터 결과를 JSON형식으로 받는다.

GraphQL은 서버 개발자가 작성한 각 필드에 대응하는 resolver함수로 각 필드의 데이터를 조회할수 있다.

GraphQL은 GraphQL 라이브러리가 조회 대상 schema가 유효한지 검사

 

REST API의 한계

Overfetch: 필요 없는 데이터까지 제공함

Underfetch: endpoint 가 필요한 정보를 충분히 제공하지 못함

API에서는 각각의 자원에 따라 엔드포인트를 구분하기 때문에 3가지 엔드포인트에 요청을 보내야한다.

 

GraphQL는 정보를 사용하는 측에서 원하는 대로 정보를 가져올 수 있고, 보다 편하게 정보를 수정할 수 있다.

REST API와 GraphQL의 다른점

 

 

GraphQL로 Blog 앱을 구현할 때

장점

하나의 endpoint 요청

/graphql이라는 하나의 endpoint 로 요청을 받고 그 요청에 따라 query , mutation을 resolver 함수로 전달해서 요청에 응답

모든 클라이언트 요청은 post 메소드를 사용한다

 

No! under & overfetching

여러 개의 endpoint 요청을 할 필요없이 하나의 endpoint에서 쿼리를 이용해 원하는 데이터를 정확하게 API에 요청하고 응답

 

강력한 playground

graphql 서버를 실행 => playground GUI를 이용해 

resolver와 schema를 한눈에 보고 test

 

클라이언트 구조 변경에도 지장이 없음

클라이언트 구조가 바뀌어도 필요한 데이터를 결정하고 받는 주체가 클라이언트이기 때문에 서버에 지장X

단점

GraphQL를 학습하는 데 시간이 필요

캐싱이 REST보다 훨씬 복잡 =>보완 :Apollo 엔진의 캐싱과 영속 쿼리 등이 등장

고정된 요청과 응답만 필요할 경우에는 Query 로 인해 요청의 크기가 RESTful API 의 경우보다 더 커진다.

 


 

GraphQL 구조

GraphQL Keywords

  • Query: 저장된 데이터 가져오기 (REST의 GET과 비슷합니다.)
  • Mutation: 저장된 데이터 수정하기
    • Create: 새로운 데이터 생성
    • Update: 기존의 데이터 수정
    • Delete: 기존의 데이터 삭제
  • Subscription: 특정 이벤트가 발생 시 서버가 대응하는 데이터를 실시간으로 클라이언트에게 전송

쿼리(query, 데이터 조회)

{
  person {
    name
    #주석 처리 가능
    friends {
      name
    }
  }
}

 

쿼리 결과

{
  "data": {
    "person": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "So"
        },
        {
          "name": "Yo"
        },
        {
          "name": "LO"
        }
      ]
    }
  }
}

전달인자(Arguments)

{
  human(id: "5") {
    name
    height
  }
}

쿼리 결과

{
  "data": {
    "human": {
      "name": "so",
      "height": 1.60
    }
  }
}

별명(Aliases)

{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

쿼리 결과

{
  "data": {
    "empireHero": {
      "name": "Luke Skywalker"
    },
    "jediHero": {
      "name": "R2-D2"
    }
  }
}

오퍼레이션 네임(Operation name)

query PersonNameAndFriends {
  person {
    name
    friends {
      name
    }
  }
}

쿼리 결과

{
  "data": {
    "person": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "So"
        },
        {
          "name": "Yo"
        },
        {
          "name": "Lo"
        }
      ]
    }
  }
}

변수(Variables)

query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

$변수 이름: 타입 형태

$episode: Episode!가 있으면 반드시 Episode여야한다.

 

뮤테이션(mutation, 데이터 수정)

서버측 데이터 수정

mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

스키마/타입(Schema/Type)

type Character { //필드가 있는 타입
  name: String! //반드시 값이 들어온다.
  appearsIn: [Episode!]! //항상 0개이상의 요소를 포함한 배열이 들어온다.
}

"CreateReviewForEpisode" 변이를 호출하여 지정된 에피소드에 새로운 리뷰를 작성하고, 작성된 리뷰의 평점(stars)과 코멘트(commentary)를 반환

리졸버(Resolver)

그 스키마 필드에 사용되는 함수의 실제 행동을 Resolver에서 정의

const db = require("./../db")
const resolvers = {
  Query: { //Query :저장된 데이터 가져오기 
		getUser: async (_, { email, pw }) => {
			db.findOne({
				where: { email, pw }
			}) ... // 실제 디비에서 데이터를 가져오는 로직을 작성. 
			...
		}
  },
  Mutation: { **Mutation : 저장된 데이터 수정 ( Create , Update , Delete )
		createUser: async (_, { email, pw, name }) => {
			...
		}
  }
  Subscription: { // Subscription : 실시간 업데이트
    newUser: async () => {
      ...
		}
  }
};

GitHub GraphQL API

GitHub GraphQL API는 특별한 explorer를 제공

이 explorer는 일종의 playground라고 생각