들어가기 전에

All about GraphQL #2, GraphQL은 무엇이고 왜 계속 어디선가 들리는 걸까?

만일 당신이 나와 같다면, 새로운 기술에 대해 들었을 때 아마 3가지 단계를 거칠 겁니다.

  1. 묵살

    자바스크립트 라이브러리가 하나 더 나왔다고?! 그냥 jQuery 쓰자!

  2. 흥미

    흠.... 요즘 유행한다고 하는 라이브러리좀 살펴봐야겠어

  3. 패닉

    도와주세요! 지금 당장 새로운 라이브러리를 배우고 싶어요! 안 그러면 완전히 구시대 사람이 될 거에요!

이러한 빠른 변화 속에서 당신의 정신을 유지하는 요령은 일단 흥미가 동하면 당신이 여전히 앞서있는 동안 단계 2, 3처럼 새로운 것을 즉시 배우는 것입니다.

지금이 왜 GraphQL을 배우기 가장 완벽한 시간인지 아시겠죠?

기본

간단명료하게, GraphQL은 어떻게 데이터를 요청할지 기술하는 문법입니다. 그리고 일반적으로 서버에서 클라이언트로 데이터를 가져오는데 사용됩니다. GraphQL은 세가지 특성을 갖습니다.

  • 클라이언트가 정확히 어떤 데이터가 필요한지 상술하게 해줍니다.
  • 여러 소스에서 데이터를 종합하는 것을 더 쉽게 만들어줍니다.
  • 데이터를 상술하는데 타입 시스템을 사용합니다.

GraphQL은 어떻게 시작됐을까요? 실제론 어떤 모습일까요? 그리고 어떻게 사용을 시작해야 할까요? 알아내기 위해 계속 읽어봅시다!

gq1.png

문제점

GraphQL은 거대하고 오래된 페이스북에서 시작했습니다. 하지만 훨씬 더 간단한 앱은 전통적인 REST APIs 방식에 한계를 느낍니다.

예를 들자면, post의 리스트를 보여줘야 한다고 상상해보세요. 그리고 각 포스트의 밑에는 유저 이름과 아바타를 포함한 likes가 있습니다. 충분히 쉽죠, post API가 사용자 객체를 포함하는 likes를 포함하도록 약간만 비틀면 됩니다.

gq2.png

하지만 지금, 이젠 모바일 앱에 대한 작업을 해야합니다. 그리고 추가적인 데이터를 전부 불러오는 것은 결국 많은 것들을 느리게 만듭니다. 그래서 당신은 이제 2개의 엔드포인트가 필요합니다. 하나는 likes와 하나는 likes가 없는 것으로요.

이제 여기에 요소 하나를 더 추가해봅시다. posts가 MySQL 데이터베이스에 저장되는 동안, 한편 likes는 Redis 저장소에 있는 것입니다! 이젠 뭘 해야 할까요?

페이스북에서 관리해야 하는 데이터 소스 및 API 클라이언트로 이 시나리오를 확장해보세요. 왜 좋고 오래된 REST API가 그들의 한계를 보이기 시작했는지 상상할 수 있을 것입니다.

해결법

페이스북이 찾아낸 해결법은 개념적으로 매우 간단합니다. 다중의 멍청한 엔드포인트들을 갖는 것 대신에, 하나의 똑똑한 엔드포인트를 갖는 것입니다. 똑똑한 엔드포인트는 복잡한 쿼리를 받아들일 수 있고, 데이터 아웃풋을 클라이언트가 요구한 어떠한 형태로도 메세징할 수 있습니다.

실용적으로 말하면, GraphQL 계층은 클라이언트와 하나 이상의 데이터 소스 사이에 살고 있습니다. 그리고 클라이언트의 요청을 받고 필요한 데이터를 지시에 따라 전달합니다. 헷갈리시나요? 은유를 통해 알려드리겠습니다!

오래된 REST 모델은 다음과 같습니다. 전화로 피자를 주문하고, 야채를 배달시키고, 세탁소 주인에게 옷을 가져가라고 합니다. 3개의 가게가 있고 전화를 3번 거는 겁니다.

gq3.png

반면에, GraphQL은 개인 비서를 갖는 것과 비슷합니다. 개인 비서에게 3 장소의 주소를 주면, 그냥 원하는 것을 말하면 되는 겁니다. "세탁소 주인에게 옷 좀 맡겨놔, 큰 피자 하나 주문, 계란 한판 사놔" 그리고 그들이 결과를 반환하기를 기다리면 됩니다.

gq4.png

다른 말로 하자면, GraphQL은 이런 마술같은 개인 비서와 대화하기 위한 표준 언어를 만든 거죠.

gq5.png

구글 이미지에 따르면, 일반적인 개인 비서는 8개의 팔을 가진 외계인입니다.

gq6.png

실제로, GraphQL API은 약 3개의 빌딩 블록으로 구조화되어 있습니다. 스키마(Schema), 쿼리(Query), 리졸버(Resolver) 입니다.

쿼리

GraphQL 개인 비서에게 내리는 요청을 쿼리라 합니다. 일반적으로 다음과 같은 모양을 띕니다.

query {
  stuff
}

query 키워드를 이용해 새로운 쿼리를 정의합니다. stuff필드에 대해 문의합시다. GraphQL 쿼리의 대단한 점은 중첩된 필드를 지원한다는 것입니다. 그래서 우리는 한 단계 더 깊게 갈 수 있습니다.

query {
  stuff {
      eggs
    shirt
    pizza
  }
}

보시다시피, 쿼리를 만드는 클라이언트는 어떤 "가게"에서 데이터가 오는지 신경쓸 필요가 없습니다. 그냥 필요한 것을 요청하세요. 그리고 나머지는 GraphQL에게 맡기시면 됩니다.

쿼리 필드가 배열도 가리킬 수 있는 것은 주목할만한 가치가 있습니다. 예를 들면, 포스트의 목록을 가져오는 쿼리의 일반적인 패턴입니다.

query {
  posts { # this is an array
    title
    body
    author { # we can go deeper!
      name
      avatarUrl
      profileUrl
    }
  }
}

쿼리 필드는 인자(argument) 도 지원합니다. 특정한 포스트를 보여주고 싶다면, post 필드에 id를 인자로 집어넣으면 됩니다.

query {
  post(id: "123foo"){
    title
    body
    author{
      name
      avatarUrl
      profileUrl
    }
  }
}

마지막으로, 동적인 id 인자를 만들고 싶으면, 변수(variable) 를 정의할 수 있습니다. 그리고 쿼리 내부에서 재사용할 수 있습니다. 밑의 예제에서는 우리가 쿼리의 이름도 만들어주고 있다는 점을 잘 보세요.

query getMyPost($id: String) {
  post(id: $id){
    title
    body
    author{
      name
      avatarUrl
      profileUrl
    }
  }
}

이 모든 것들을 직접 해볼 수 있습니다. 깃허브 GraphQL API 익스플로러에서 해보시면 됩니다. 예를 들면 아래 나온 쿼리를 수행해보세요.

query {
  repository(owner: "graphql", name: "graphql-js"){
    name
    description
  }
}

gq7.gif

description 아래에 새로운 필드 이름을 타이핑할 때, IDE가 자동으로 GraphQL API로부터 가능한 필드 이름을 직접 자동완성으로 제공합니다! 멋집니다!

gq8.png

GraphQL 쿼리 해부

GraphQL 쿼리에 대해 여기 Anatomy of GraphQL Query에서 더 많이 배울 수 있습니다.

리졸버(Resolvers)

세계 최고의 개인 비서도 주소 없이는 어디도 갈 수 없습니다.

비슷하게, GraphQL 서버도 리졸버(Resolver) 를 사용하라고 말하지 않는다면 쿼리가 전달됐을 때, 무슨 일을 해야할지 모를 것입니다.

리졸버는 GraphQL에게 어떻게 어디로 지정된 필드에 해당하는 데이터를 보내는지 알려줍니다. 예를 들면, post 필드에 대한 리졸버는 다음과 같이 보일 수 있습니다. (아폴로의 GraphQL-Tools 스키마 생성기를 이용합니다.)

Query: {
  post(root, args) {
    return Posts.find({ id: args.id });
  }
}

우리는 리졸버를 Query위에 올렸습니다. 왜냐하면 우리가 루트 레벨에서 post에 대한 쿼리를 하길 원해서입니다. 하지만 서브 필드에 대한 리졸버를 가질 수도 있습니다. postauthor 필드 처럼요.

Query: {
  post(root, args) {
    return Posts.find({ id: args.id });
  }
},
Post: {
  author(post) {
    return Users.find({ id: post.authorId})
  }
}

리졸버는 데이터베이스 도큐먼트를 반환하는 데에 있어 제한이 없다는 것을 알아두세요. 예를 들면, Post 타입에 commetsCount를 추가하길 원할 수도 있습니다

Post: {
  author(post) {
    return Users.find({ id: post.authorId})
  },
  commentsCount(post) {
    return Comments.find({ postId: post.id}).count() 
  }
}

여기서 이해해야 할 GraphQL의 핵심적인 컨셉은, API 스키마와 데이터베이스 스키마가 분리됐다는 것입니다. 다른 말로 하자면, 데이터베이스에는 author, commentsCount 같은 필드가 없을 수도 있습니다. 하지만 리졸버의 힘을 통해 그들을 흉내낼 수 있습니다.

리졸버 내부에 원하는 코드를 작성할 수 있는 것을 봤을 겁니다. 만일 mutation 리졸버의 개념을 안다면, 데이터베이스의 개념을 변경할 수 있는 이유가 바로 그겁니다.

스키마(Schema)

이러한 모든 좋은 것들이 GraphQL의 타입이 있는 스키마 시스템에 의해 가능합니다. 오늘의 목표는 피곤하고 지치는 소개보다는 빠르게 개요를 설명하는 것입니다. 그래서 디테일하게 하진 않겠습니다.

GraphQL 스키마에 대해 더 자세히 알고 싶으면 GraphQL 문서를 찾아보는 것을 추천합니다.

gq9.png

자주 묻는 질문

몇가지 자주 묻는 질문에 대해 답하겠습니다.

GraphQL과 graph databases 사이의 관계는 무엇인가요?

사실 별달리 관계가 없습니다. GraphQL은 graph databases와 연관이 없습니다. "graph" 부분은 필드와 하위 필드를 이용하며 API 그래프를 건너다닌다는 아이디어에서 왔습니다. QL은 Query Language라는 뜻입니다.

REST로 충분한데, 왜 GraphQL로 교체해야되나요?

GraphQL이 해결할 수 있는 REST의 고통스런 엔드포인트들을 쓰면서 아무런 문제도 느끼지 않았다면, 잘했다고 말하겠습니다.

REST 대신 GraphQL을 쓰는 것은 유저의 경험에 아무런 영향도 미치지 못할 수 있습니다. 그래서 변경하는 것은 죽느냐 사느냐의 문제가 아닙니다. 작은 사이드 프로젝트에서 GraphQL을 시도해보길 추천합니다. 기회가 있다면요.

React같은 라이브러리 없이도 GraphQL이 사용가능한가요?

사용할 수 있습니다! GraphQL은 단지 스펙이기 때문입니다. 어떤 플랫폼의 라이브러리에서도 사용할 수 있습니다. 클라이언트도 같이요. 예를 들면 Apollo는 웹, iOS, Angular를 위한 GraphQL 클라이언트를 갖고 있습니다. 아니면 GraphQL 서버를 직접 만드셔도 됩니다.

GraphQL은 페이스북에서 만들었는데, 저는 페이스북을 안믿어요

GraphQL은 스펙입니다. 그 말은 페이스북에서 만든 한 줄의 코드도 없이 GraphQL 구현을 이용할 수 있다는 것입니다.