GraphQL

play·2022년 8월 2일
0

Chapter1. GraphQL

1-1 GraphQL
1-2 GraphQL VS REST API

Chapter2. GraphQL 구조



Chapter1. GraphQL(Graph + Query Language)

Facebook이 개발한 오픈 소스로 제공된 API를 위한 쿼리 언어

Query Language 중에서도 Server API 를 통해 정보를 주고받기 위해 사용하는 Query Language

🍛 Graph 사용하는 이유

  • 그래프 : 여러 개의 점들이 서로 복잡하게 연결되어 있는 관계를 표현한 자료구조
  • Node, 정점(vertex) : 하나의 점
  • 두 점 사이를 이어주는 선 : 직접적인 관계가 있을 경우
  • 몇 개의 점과 선을 거쳐 이어지는 선 : 간접적인 관계가 있을 경우
  • GraphQL에선 모든 데이터가 그래프 형태로 연결되어 있다고 전제한다.
    • 트리, 그래프 둘 다 노드와 노드를 연결하는 간선으로 구성된 자료구조 이기 때문이다.
    • 그 그래프를 누구의 입장에서 정렬하는가에 따라 트리 구조를 이룰 수 있다.

  • 이를 통해 GraphQL은 클라이언트 요청에 따라 유연하게 트리구조의 JSON 데이터를 응답으로 전송할 수 있다. = REST API 방식의 고정된 자원이 아닌 클라이언트 요청에 따라 유연하게 자원을 가져올 수 있다

🍛 GraphQL로 그래프 순회

GraphQL로 그래프를 순회하기 위해 도서관의 도서 목록 시스템을 구축한다고 가정한다. 각 책에는 최소 1명의 저자가 있고, 최소한 한 권의 책을 같이 쓴 공동저자도 있을 것이다.

  • 그래프는 데이터의 조각 또는 나타내고자 하는 엔티티 간의 관계를 나타낼 수 있다.
    • 엔티티 : 사물의 구조나 상태, 동작 등을 모델로 표현하는 경우 그 모델의 구성요소.
      ex) 학생이라는 객체는 "학번","이름","학과" 3개의 속성으로 구성됨. 개별적인 속성만으로 정보를 파악하긴 어렵지만, 이들이 모여 "학생"이라는 객체를 구성할 때는 큰 의미를 제공한다.



  • GraphQL을 사용해 앱 데이터 그래프에서 트리를 추출할 수 있다.
  • 트리는 방향성은 존재하지만, 사이클은 존재하지 않는 비순환 그래프.
  • 루트와 모서리를 통해 노드를 따라 순회할 수 있지만,
    동일한 노드로 돌아올 수 없는 속성을 가진 그래프다.

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

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

이 방식으로 서버에 요청을 보내고,
서버가 쿼리를 해결하면 다음 쿼리 결과를 반환한다.

{
	책 : {
		책 이름 : "GraphQL은 어렵지 않다",
		저자 : [
			{ 이름 : "김코딩"},
			{ 이름 : "박해커"},
		]
	}
}

이것을 그래프 관점에서 보면 다음과 같다.

1. ISBN 번호를 사용하여 선택한 "Book" 노드에서 시작
2. GraphQL 중첩된 각 필드로 표시된 간선을 따라 그래프를 탐색, 즉 쿼리 내 중첩된 "title" 필드를 통해 책 제목이 있는 노드로 이동
3. "authors"로 레이블이 지정된 "Book"의 간선을 따라가 "author"노드 가져오고 각 저자의 "name" 얻어옴


GraphQL로 쿼리한 것을 트리 구조로 나타내면,

  • 쿼리가 반환하면 각 정보에는 해당 정보를 얻기 위해 따라간 GraphQL 쿼리의 필드로 구성된 연결 쿼리 정보가 있다.
    • GraphQL 쿼리의 필드(ex. book, Authors, name)는 원하는 결과를 얻기 위해 그래프 간선을 지정한다.

즉, GraphQL은 그래프를 탐색하여 쿼리 결과 트리를 생성하는 쿼리 언어다.


🍛 GraphQL의 특징

  • GraphQL은 HTTP를 통해 API 서버로 요청을 보내고 응답을 받는다.
    • 응답은 JSON 형식으로 받는다.
  • GraphQL은 서버 개발자가 작성한 각 필드에 대응하는 resolver 함수로 각 필드의 데이터를 조회할 수 있다.
  • GraphQL은 GraphQL 라이브러리가 조회 대상 schema가 유효한지 검사한다.



📌 GraphQL VS REST API

REST API 한계

  • Overfetch: 필요 없는 데이터까지 제공함
  • Underfetch: endpoint 가 필요한 정보를 충분히 제공하지 못함
    • 클라이언트는 필요한 정보를 확보하기 위해 추가적인 요청을 보내야 함
    • 그 정보를 가져오려면 REST API에선 각각의 자원에 따라 엔드포인트를 구분하므로 3가지 엔드포인트에 요청을 보내야함
  • 클라이언트 구조 변경 시 엔드포인트 변경 또는 데이터 수정이 필요함
    • REST API에서는 자원의 크기와 형태를 서버에서 결정하기 때문에 클라이언트는 권한이 없다.
      • 만약 클라이언트에서 필요한 데이터의 내용이 변할 경우 다른 endpoint를 통해 변경된 데이터를 가져오거나 수정해야함.

REST API와 GraphQL의 다른점

REST APIGraphQL
Resource에 대한 형태 정의와 데이터 요청 방법연결분리
Resource의 크기와 형태서버에서 결정Resource에 대한 정보만 정의
크기와 형태는 클라이언트 단에서 요청시 결정
Resource / 작업유형URI / MethodGraphQL Schema / Query, Mutation 타입
여러 Resource에 접근할 때여러 번의 요청 필요한 번의 요청
요청 처리엔드포인트에 정의된 핸들링 함수를 호출하여 처리요청 받은 각 필드에 대한 resolver를 호출하여 처리

GraphQL의 장단점

  • 장점
    • 1. 하나의 endpoint 요청
      • /graphql이라는 하나의 endpoint로 요청을 받고 이에 따라 query , mutation을 resolver함수로 전달해 요청 응답.
        모든 클라이언트 요청은 POST 메소드를 사용한다.
    • 2. No! under & overfetching
      여러개의 endpoint 요청 필요X
    • 3. 강력한 playground
      • graphql 서버를 실행하면 playground GUI를 이용해 resolver, schema 를 테스트할 수 있다.
    • 4. 클라이언트 구조 변경에도 지장이 없음
      • 데이터를 결정하고 받는 주체가 클라이언트이므로 서버에 지장X
  • 단점
    • 캐싱이 REST보다 훨씬 복잡하다.
      • HTTP에선 각 메소드에 따라 캐싱이 구현되어 있는 반면, GraphQL에선 POST로 요청을 보내기 떄문에 각 메소드에 따른 캐싱을 지원받을 수 없다.
        • 대안으로 Apollo 엔진의 캐싱과 영속 쿼리 등이 등장
    • Query로 인해 요청의 크기가 RESTful API보다 더 커짐.



Chapter2. GraphQL 구조

GraphQL Keywords

  • Query: 저장된 데이터 가져오기 (REST의 GET과 비슷)
  • Mutation: 저장된 데이터 수정하기
    • Create: 새로운 데이터 생성
    • Update: 기존의 데이터 수정
    • Delete: 기존의 데이터 삭제
  • 구독(Subscription) : 실시간 업데이트 구현 가능
    • Client(요청)-Server(응답) 모델을 따르는 Query 또는 Mutation과 달리, 발행/구독(pub/sub) 모델을 따른다.
    • 특정 이벤트 발생 시 서버가 대응하는 데이터를 실시간으로 클라이언트에게 전송

쿼리(query, 데이터 조회)

필드(field)

// hero의 name과 friends 같이 쿼리
{
  hero {
    name
    # 이런 식으로 GraphQL 내에서 주석 작성도 가능 
    friends {
      name
    }
  }
}

//-------------------//
// 쿼리를 실행했을 때의 결과
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [ // 배열을 반환한다. 
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}
  • 쿼리와 결과가 정확하게 같은 모양
  • GraphQL은 서버에 요청했을 때 예상했던 대로 돌려받고, 서버는 GraphQL을 통해 클라이언트가 요구하는 필드를 정확히 안다.
  • 원하는 필드를 중첩하여 쿼리 가능
  • 다양한 endpoint를 만들어 각기 요청을 보내는 대신 클라이언트가 하나의 요청을 보냄으로써 관련 데이터를 가져올 수 있다

전달인자(Arguments)

{
  human(id: "1000") {
    name
    height
  }
}
// id가 1000인 human의 name과 height를 쿼리
{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 1.72
    }
  }
}
// 쿼리 결과 
  • 필드에 인수를 전달하는 부분을 추가하면, 쿼리의 필드 및 중첩된 객체들에 전달하여 원하는 데이터만 받을 수 있다.

별명(Aliases)

  • 필드 이름 중복 불가
  • 만약 중복으로 사용해서 쿼리를 해야한다면 -> 별명을 붙여서 쿼리한다.
{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

오퍼레이션 네임(Operation name)

query HeroNameAndFriends {
  hero {
    name
    friends {
      name
    }
  }
}
// 이렇게 query keyword와 query name을 작성.
  • query : 오퍼레이션 타입
    • 그 외 mutation, subscription, describes 등이 있다.
  • 쿼리를 약식으로 작성하지 않는 한 오퍼레이션 타입은 반드시 필요하다.

지금까지 고정된 인수를 받아 쿼리했지만, 실제론 동적으로 인수를 받아 쿼리하는 게 대다수다.

변수(Variables)

  • 인수를 동적으로 받고 싶을 때 변수를 사용한다.
query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}
// 변수를 써서 작성된 쿼리
  • 오퍼레이션 네임 옆에 $변수 이름: 타입 형태 로 정의
  • !를 붙일 수도 있음.

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

  • GraphQL은 서버 측 데이터를 수정하기도 한다.
  • REST API가 POST,PUT 요청으로 데이터를 수정하는 것처럼,
    GraphQL도 mutation로 서버 측 데이터를 수정한다.
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

스키마/타입(Schema/Type)

  • GraphQL 스키마의 가장 기본적인 구성 요소
    • 서비스에서 가져올 수 있는 객체의 종류
    • 포함하는 필드를 나타내는 객체 유형
type Character {
  name: String!
  appearsIn: [Episode!]!
}
  • Character : GraphQL 객체 타입, 즉 필드가 있는 타입
  • nameappearIn : Character 타입의 필드
    • GraphQL 쿼리의 Character 타입 어디서든 사용할 수 있다.
  • String : 내장된 스칼라 타입 중 하나. (스칼라 타입: ID, Int)
    • 단일 스칼라 객체로 확인되는 유형
    • 쿼리에서 하위 선택을 가질 수 없다.
  • ! : Non-Null. 서버는 null 값이 아닌 값을 반환할 것을 기대
  • [ ] : 배열. 배열에도 !가 붙을 수 있다.
    • 여기서는 ! 이 뒤에 붙어 있어 null 값을 허용하지 않으므로 항상 0개 이상의 요소를 포함한 배열을 기대할 수 있다.

리졸버(Resolver)

  • 요청에 대한 응답을 결정해주는 함수
  • Query, Mutation, Subscription과 같은 타입의 실제 일하는 방식 즉 로직을 작성한다.
  • 스키마를 정의하면 그 스키마 필드에 사용되는 함수의 실제 행동을 Resolver에서 정의한다.
  • 이러한 함수들이 모여 있기 때문에 보통 Resolvers라 부른다.
  • GraphQL에서는 데이터를 가져오는 구체적인 과정을 직접 구현해야하는데, 이러한 작업(데이터베이스 쿼리, 원격API요청)을 Resolver가 담당한다.
const db = require("./../db")
const resolvers = {
  Query: { // **Query :** 저장된 데이터 가져오기 (REST의 GET 과 비슷)
		getUser: async (_, { email, pw }) => {
			db.findOne({
				where: { email, pw }
			}) ... // 디비에서 데이터를 가져오는 로직 작성 
			...
		}
  },
  Mutation: { // **Mutation :** 저장된 데이터 수정하기 ( Create , Update , Delete )
		createUser: async (_, { email, pw, name }) => {
			...
		}
  }
  Subscription: { // **Subscription :** 실시간 업데이트
    newUser: async () => {
      ...
		}
  }
};

profile
블로그 이사했습니다 🧳

0개의 댓글