정보실

웹학교

정보실

javascript GraphQL을 위한 풀 스택 튜토리얼

본문

무료로 제공되는 오픈 소스 튜토리얼로 GraphQL의 모든 것을 제로에서 프로덕션으로 전환 할 수 있습니다.


기본 튜토리얼-소개 


GraphQL은 REST에 대한 보다 효율적이고 강력하며 유연한 대안을 제공하는 새로운 API 표준입니다. 페이스 북이 개발하고 오픈 소스로 만들었으며 현재 전 세계의 많은 회사와 개인 커뮤니티에서 관리하고 있습니다.


API는 소프트웨어 인프라의 유비쿼터스 구성 요소가 되었습니다. 간단히 말해 API는 클라이언트가 서버에서 데이터를 로드 하는 방법을 정의합니다. 


핵심적으로 GraphQL은 클라이언트가 API에서 필요한 데이터를 정확하게 지정할 수 있는 선언적 데이터 페치를 가능하게 합니다. 고정 데이터 구조를 리턴 하는 여러 엔드 포인트 대신 GraphQL 서버는 단일 엔드 포인트만 노출하고 클라이언트가 요청한 데이터로 정확하게 응답합니다.


GraphQL-API 쿼리 언어 


오늘날 대부분의 응용 프로그램은 해당 데이터가 데이터베이스에 저장된 서버에서 데이터를 가져와야 합니다. 애플리케이션의 요구에 맞는 저장된 데이터에 대한 인터페이스를 제공하는 것은 API의 책임입니다.


GraphQL은 종종 데이터베이스 기술과 혼동됩니다. 이것은 잘못된 생각입니다. GraphQL은 데이터베이스가 아닌 API에 대한 쿼리 언어입니다. 그런 의미에서 데이터베이스는 데이터베이스에 독립적이며 API가 사용되는 모든 상황에서 효과적으로 사용될 수 있습니다.


REST에 대한보다 효율적인 대안 


💡이 블로그 게시물에서 GraphQL을 사용하는 주요 이유에 대해 자세히 알아보십시오. 


REST는 서버에서 데이터를 노출하는 일반적인 방법입니다. REST의 개념이 개발되었을 때 클라이언트 응용 프로그램은 비교적 단순했고 개발 속도는 오늘날의 위치에 거의 미치지 못했습니다. 따라서 REST는 많은 응용 프로그램에 적합했습니다. 그러나 API 환경은 지난 몇 년 동안 급격히 변화했습니다. 특히 API 설계 방식에 어려움을 겪고 있는 세 가지 요소가 있습니다.


1. 모바일 사용량 증가로 효율적인 데이터 로드 필요성 


Facebook이 GraphQL을 개발 한 최초의 이유는 증가 된 모바일 사용, 저전력 장치 및 느슨한 네트워크입니다. GraphQL은 네트워크를 통해 전송해야 하는 데이터의 양을 최소화하여 이러한 조건에서 작동하는 응용 프로그램을 크게 향상 시킵니다.


2. 다양한 프론트 엔드 프레임 워크 및 플랫폼 


클라이언트 응용 프로그램을 실행하는 프런트 엔드 프레임 워크 및 플랫폼의 이기종 환경은 모든 요구 사항에 맞는 하나의 API를 구축하고 유지 관리하기가 어렵습니다. GraphQL을 사용하면 각 클라이언트가 필요한 데이터에 정확하게 액세스 할 수 있습니다.


3. 빠른 기능 개발을 위한 빠른 개발 및 기대 


지속적인 배포는 많은 회사의 표준이 되었으며 신속한 반복 및 빈번한 제품 업데이트는 필수 불가결합니다. REST API를 사용하면 서버에서 데이터가 노출되는 방식을 수정하여 클라이언트 측의 특정 요구 사항과 설계 변경을 설명해야 합니다. 이는 빠른 개발 관행과 제품 반복을 방해합니다.


역사, 상황 및 채택 


GraphQL은 React 개발자 만을 위한 것이 아닙니다 


Facebook은 2012 년 기본 모바일 앱에서 GraphQL을 사용하기 시작했습니다. 흥미롭게도 GraphQL은 주로 웹 기술의 맥락에서 사용되도록 선택되었으며 기본 모바일 공간에서 거의 견인력을 얻지 못했습니다.


페이스 북이 GraphQL에 대해 처음 공개 한 것은 React.js Conf 2015에 있었고 오픈 소스 계획을 발표 한 직후였다. Facebook은 항상 React와 관련하여 GraphQL에 대해 이야기 했었으므로, React 이외의 개발자는 GraphQL이 React 사용에 국한된 기술이 아니라는 것을 이해하는 데 시간이 걸렸습니다.


빠르게 성장하는 커뮤니티 


실제로 GraphQL은 클라이언트가 API와 통신하는 모든 곳에서 사용할 수 있는 기술입니다. 흥미롭게도 Netflix 또는 Coursera와 같은 다른 회사는 API 상호 작용을 보다 효율적으로 만들기 위해 비슷한 아이디어를 연구하고 있었습니다. 

Coursera는 클라이언트가 데이터 요구 사항을 지정할 수 있도록 비슷한 기술을 구상했으며 Netflix는 Falcor라는 솔루션을 공개 소스로 만들었습니다. GraphQL이 오픈 소스 화 된 후 Coursera는 자신의 노력을 완전히 취소하고 GraphQL 열차를 탔습니다.


오늘날 GraphQL은 GitHub, Twitter, Yelp 및 Shopify와 같은 많은 회사에서 생산하는 데 사용됩니다.


2RYRYB3.png 


GraphQL Conf와 같은 GraphQL 전용 컨퍼런스와 GraphQL Weekly 뉴스 레터와 같은 더 많은 리소스가 있습니다.


GraphQL은 REST 더 낫다 



지난 10 년 동안 REST는 웹 API 디자인을 위한 표준이 되었습니다 (아직 퍼지가 아닙니다). 상태 비 저장 서버 및 리소스에 대한 구조적 액세스와 같은 훌륭한 아이디어를 제공합니다. 그러나 REST API는 액세스 할 수 있는 클라이언트의 급변하는 요구 사항을 충족하기에는 너무 융통성이 없는 것으로 나타났습니다.


GraphQL은 더 많은 유연성과 효율성에 대응하기 위해 개발되었습니다! REST API와 상호 작용할 때 개발자가 경험하는 많은 단점과 비 효율성을 해결합니다.


API에서 데이터를 가져올 때 REST와 GraphQL의 주요 차이점을 설명하기 위해 간단한 예제 시나리오를 살펴 보겠습니다. 블로깅 애플리케이션에서 앱은 특정 사용자의 게시물 제목을 표시해야 합니다. 같은 화면에도 해당 사용자의 마지막 3 명의 팔로워 이름이 표시됩니다. REST와 GraphQL로 이러한 상황을 어떻게 해결할 수 있습니까?


💡 개발자가 GraphQL을 좋아하는 이유에 대해 자세히 알아 보려면 이 기사를 확인하십시오


REST vs GraphQL을 사용한 데이터 페치 


REST API를 사용하면 일반적으로 여러 엔드 포인트에 액세스하여 데이터를 수집합니다. 

이 예에서 초기 사용자 데이터를 가져 오려면 /users/<id> 엔드 포인트 일 수 있습니다. 

둘째, 사용자의 모든 게시물을 반환하는 /users/<id>/posts 엔드 포인트가 있을 수 있습니다. 

그런 다음 세 번째 끝점은 사용자 당 팔로어 목록을 반환하는 /users/<id>/followers입니다.


VRyV7Jh.png 

REST를 사용하면 필요한 데이터를 가져 오기 위해 서로 다른 엔드 포인트에 세 번 요청해야 합니다. 엔드 포인트가 필요하지 않은 추가 정보를 반환하므로 오버 페칭 중입니다.


반면 GraphQL에서는 구체적인 데이터 요구 사항이 포함 된 단일 쿼리를 GraphQL 서버에 보내기 만하면 됩니다. 그런 다음 서버는 이러한 요구 사항이 충족되는 JSON 객체로 응답합니다.


z9VKnHs.png 

클라이언트는 GraphQL을 사용하여 쿼리에 필요한 데이터를 정확하게 지정할 수 있습니다. 서버 응답의 구조는 쿼리에 정의 된 중첩 구조를 정확하게 따릅니다.


더 이상 오버 페치 및 언더 페칭 없음 


REST의 가장 일반적인 문제점 중 하나는 오버 페치 및 언더 페치입니다. 클라이언트가 데이터를 다운로드 할 수 있는 유일한 방법은 고정 된 데이터 구조를 리턴하는 엔드 포인트를 때리는 것입니다. 고객에게 정확한 데이터 요구를 제공 할 수 있는 방식으로 API를 설계하는 것은 매우 어렵습니다.


“끝 점이 아닌 그래프를 생각하십시오.”GraphQL 공동 발명자 Lee Byron이 4 년간 GraphQL에서 얻은 교훈. 


오버 페칭 : 불필요한 데이터 다운로드 


오버 페칭은 클라이언트가 실제로 앱에 필요한 것보다 많은 정보를 다운로드 함을 의미합니다. 예를 들어 이름을 가진 사용자 목록만 표시해야 하는 화면을 상상해보십시오. REST API에서 이 앱은 일반적으로 /users 엔드 포인트에 도달하고 사용자 데이터가 포함 된 JSON 배열을 받습니다. 그러나 이 응답에는 반환 된 사용자에 대한 자세한 정보가 포함될 수 있습니다 (예 : 생일이나 주소-사용자 이름 만 표시하면 되므로 클라이언트에게 쓸모없는 정보입니다.


언더 페칭 및 n + 1 문제 


또 다른 문제는 언더 페치 및 n + 1 요청 문제입니다. 언더 페칭은 일반적으로 특정 엔드 포인트가 필요한 정보를 충분히 제공하지 않음을 의미합니다. 클라이언트는 필요한 모든 것을 가져 오기 위해 추가 요청을 해야 합니다. 이는 클라이언트가 요소 목록을 먼저 다운로드해야 하지만 필요한 데이터를 가져 오기 위해 요소 당 하나의 추가 요청을 해야 하는 상황으로 확대 될 수 있습니다.


예를 들어, 동일한 앱에도 사용자 당 마지막 3 명의 팔로워를 표시해야 한다고 가정 해보십시오. API는 추가 엔드 포인트 /users/<user-id>/followers를 제공합니다. 필요한 정보를 표시하려면 앱이 /users 엔드 포인트에 한 번 요청한 다음 각 사용자에 대해 /users/<user-id>/followers 엔드 포인트를 눌러야 합니다.


프론트 엔드에서 신속한 제품 반복 


REST API의 일반적인 패턴은 앱 내부에 있는 뷰에 따라 엔드 포인트를 구성하는 것입니다. 클라이언트가 해당 엔드 포인트에 간단히 액세스하여 특정 보기에 필요한 모든 정보를 얻을 수 있으므로 편리합니다.


이 접근 방식의 주요 단점은 프런트 엔드에서 빠른 반복을 허용하지 않는다는 것입니다. UI가 변경 될 때마다 이제는 이전보다 더 많은 (또는 적은) 데이터가 필요할 위험이 높습니다. 결과적으로, 새로운 데이터 요구를 고려하여 백엔드를 조정해야 합니다. 이로 인해 생산성이 떨어지고 사용자 피드백을 제품에 통합하는 기능이 현저하게 느려집니다.


GraphQL을 사용하면 이 문제가 해결됩니다. GraphQL의 유연한 특성으로 인해 서버에서 추가 작업을 하지 않고도 클라이언트 측을 변경할 수 있습니다. 클라이언트는 정확한 데이터 요구 사항을 지정할 수 있으므로 프런트 엔드 변경시 디자인 및 데이터가 필요할 때 백엔드 엔지니어가 조정할 필요가 없습니다.


백엔드에 대한 통찰력 있는 분석 


GraphQL을 사용하면 백엔드에서 요청한 데이터에 대한 세밀한 통찰력을 얻을 수 있습니다. 각 고객은 관심 있는 정보를 정확하게 지정하므로 사용 가능한 데이터가 어떻게 사용 되는지에 대한 심층적인 이해가 가능합니다. 예를 들어 API를 발전시키고 더 이상 클라이언트가 요청하지 않은 특정 필드를 사용하지 않는 데 도움이 될 수 있습니다.


GraphQL을 사용하면 서버에서 처리되는 요청에 대한 저수준 성능 모니터링을 수행 할 수도 있습니다. GraphQL은 리졸버 함수 개념을 사용하여 클라이언트가 요청한 데이터를 수집합니다. 이러한 리졸버의 성능을 계측하고 측정하면 시스템의 병목 현상에 대한 중요한 통찰력을 얻을 수 있습니다.


스키마 및 타입 시스템의 장점 


GraphQL은 강력한 유형 시스템을 사용하여 API의 함수를 정의합니다. API에 노출 된 모든 유형은 GraphQL 스키마 정의 언어 (SDL)를 사용하여 스키마에 기록됩니다. 이 스키마는 클라이언트와 서버 간의 계약으로 클라이언트가 데이터에 액세스하는 방법을 정의합니다.


스키마가 정의되면 프런트 엔드 및 백엔드에서 작업하는 팀은 네트워크를 통해 전송되는 데이터의 명확한 구조를 알고 있으므로 추가 커뮤니케이션 없이도 작업을 수행 할 수 있습니다.


프론트 엔드 팀은 필요한 데이터 구조를 모의하여 애플리케이션을 쉽게 테스트 할 수 있습니다. 서버가 준비되면 클라이언트 앱이 실제 API에서 데이터를 로드 할 수 있도록 스위치를 전환 할 수 있습니다.


핵심 개념 


이 장에서는 GraphQL의 기본 언어 구성에 대해 배웁니다. 여기에는 형식을 정의하고 쿼리 및 변이를 보내는 구문을 처음으로 엿볼 수 있습니다. 

또한 graphql-up을 기반으로 학습 한 내용을 실험하는 데 사용할 수 있는 샌드 박스 환경을 준비했습니다.


스키마 정의 언어 (SDL) 


GraphQL에는 API의 스키마를 정의하는 데 사용되는 자체 유형 시스템이 있습니다. 스키마 작성 구문을 스키마 정의 언어 (SDL)라고합니다.


다음은 SDL을 사용하여 Person이라는 간단한 유형을 정의하는 방법의 예입니다.


type Person {
  name: String!
  age: Int!
}


이 유형에는 이름과 연령이라는 두 개의 필드가 있으며 각각 String 및 Int 유형입니다. ! 유형을 따르는 것은 이 필드가 필수임을 의미합니다.


유형 간의 관계를 표현할 수도 있습니다. 블로그 애플리케이션의 예에서 Person은 Post와 연관 될 수 있습니다.


type Post {
  title: String!
  author: Person!
}


반대로 관계의 다른 쪽 끝은 Person 유형에 배치해야 합니다.


type Person {
  name: String!
  age: Int!
  posts: [Post!]!
}


Person의 게시물 필드는 실제로는 게시물의 배열이므로 Person과 Post 사이의 일대 다 관계를 만들었습니다.


쿼리로 데이터 가져 오기 


REST API로 작업 할 때 특정 엔드 포인트에서 데이터가 로드 됩니다. 각 끝점에는 반환되는 정보의 구조가 명확하게 정의되어 있습니다. 

이는 클라이언트의 데이터 요구 사항이 연결된 URL에 효과적으로 인코딩됨을 의미합니다.


GraphQL에서 취한 접근 방식은 근본적으로 다릅니다. GraphQL API는 고정 된 데이터 구조를 반환하는 여러 엔드 포인트를 갖는 대신 일반적으로 단일 엔드 포인트만 노출합니다. 이것은 반환 되는 데이터의 구조가 고정되어 있지 않기 때문에 작동합니다. 대신 유연성이 뛰어나고 클라이언트가 실제로 필요한 데이터를 결정할 수 있습니다.


즉, 클라이언트는 데이터 요구를 표현하기 위해 서버에 추가 정보를 보내야 합니다.이 정보를 쿼리라고 합니다.


Basic Queries 


클라이언트가 서버에 보낼 수 있는 예제 쿼리를 살펴 보겠습니다.


{

  allPersons {

    name

  }

}


이 쿼리의 allPersons 필드를 쿼리의 루트 필드라고 합니다. 루트 필드 뒤에 오는 모든 것을 쿼리의 페이로드라고 합니다. 이 쿼리의 페이 로드에 지정된 유일한 필드는 name입니다.


이 쿼리는 현재 데이터베이스에 저장된 모든 사람의 목록을 반환합니다. 응답 예는 다음과 같습니다.


{
  "allPersons": [
    { "name": "Johnny" },
    { "name": "Sarah" },
    { "name": "Alice" }
  ]
}

각 사람은 응답에 이름만 있지만 서버는 나이를 반환하지 않습니다. 정확히 name이 쿼리에 지정된 유일한 필드이기 때문입니다.


고객의 나이도 필요한 경우 쿼리를 약간 조정하고 쿼리 페이 로드에 새 필드를 포함시키기 만하면 됩니다.


{

  allPersons {

    name

    age

  }

}


GraphQL의 주요 장점 중 하나는 중첩 된 정보를 자연스럽게 쿼리 할 수 ​​있다는 것입니다. 예를 들어, Person이 작성한 모든 게시물을 로드 하려는 경우 유형의 구조를 따라 이 정보를 요청하면 됩니다.


{

  allPersons {

    name

    age

    posts {

      title

    }

  }

}


인수가 있는 쿼리 


GraphQL에서 각 필드에 스키마에 지정된 인수가 0 개 이상 있을 수 있습니다. 예를 들어, allPersons 필드에는 특정 인원 만 반환 할 수 있는 마지막 매개 변수가 있을 수 있습니다. 해당 검색어는 다음과 같습니다.


{

  allPersons(last: 2) {

    name

  }

}


변이로 데이터 쓰기 


대부분의 응용 프로그램은 서버에서 정보를 요청하는 것 외에도 현재 백엔드에 저장된 데이터를 변경하는 방법이 필요합니다. GraphQL을 사용하면 이러한 변경은 소위 변이(mutations)를 사용하여 수행됩니다. 일반적으로 세 가지 종류의 변이(mutations)가 있습니다.

  • 새로운 데이터 생성
  • 기존 데이터 업데이트
  • 기존 데이터 삭제

변이는 쿼리와 동일한 구문 구조를 따르지만 항상 변이 키워드로 시작해야 합니다. 다음은 새로운 사람을 만드는 방법에 대한 예입니다.


mutation {

  createPerson(name: "Bob", age: 36) {

    name

    age

  }

}


이전에 작성한 쿼리와 유사하게 변이에도 루트 필드가 있습니다.이 경우에는 createPerson이라고 합니다. 우리는 또한 필드에 대한 논증의 개념에 대해 이미 배웠다. 이 경우 createPerson 필드는 새로운 사람의 이름과 나이를 지정하는 두 개의 인수를 사용합니다.


쿼리와 마찬가지로 새로운 Person 객체의 다른 속성을 요청할 수 있는 변이에 대한 페이 로드를 지정할 수도 있습니다. 우리의 경우, 우리는 이름과 나이를 요구하고 있습니다. 물론 그것은 우리가 그것들을 변이에 전달할 때 그것들을 이미 알고 있기 때문에 우리의 예에서는 그다지 도움이 되지 않습니다. 그러나 변이를 보낼 때 정보를 쿼리 할 수 ​​있다는 것은 단일 왕복으로 서버에서 새로운 정보를 검색 할 수 있는 강력한 도구가 될 수 있습니다!


위의 변이에 대한 서버 응답은 다음과 같습니다.

"createPerson": {
  "name": "Bob",
  "age": 36,
}


자주 발견되는 패턴 중 하나는 GraphQL 유형에 새 객체를 만들 때 서버에서 생성 한 고유 한 ID가 있다는 것입니다. Person 유형을 이전부터 확장하면 다음과 같은 ID를 추가 할 수 있습니다.


type Person {
  id: ID!
  name: String!
  age: Int!
}

이제 새로운 Person이 생성되면 클라이언트에서 사전에 사용할 수 없었던 정보이므로 변이 페이 로드에서 ID를 직접 요청할 수 있습니다.


mutation {

  createPerson(name: "Alice", age: 36) {

    id

  }

}


구독을 통한 실시간 업데이트 


오늘날 많은 응용 프로그램의 또 다른 중요한 요구 사항은 중요한 이벤트에 대해 즉시 알 수 있도록 서버에 실시간으로 연결하는 것입니다. 이 사용 사례의 경우 GraphQL은 구독 개념을 제공합니다.


클라이언트가 이벤트에 가입하면 서버에 대한 연결이 시작되고 유지됩니다. 특정 이벤트가 실제로 발생할 때마다 서버는 해당 데이터를 클라이언트에 푸시합니다. 일반적인 "요청-응답주기"를 따르는 쿼리 및 변이와 달리 가입은 클라이언트로 전송되는 데이터 스트림을 나타냅니다.


구독은 쿼리 및 변이와 동일한 구문을 사용하여 작성됩니다. 다음은 Person 유형에서 발생하는 이벤트를 구독하는 예입니다.


subscription {
  newPerson {
    name
    age
  }
}


클라이언트가 이 서브 스크립 션을 서버에 보낸 후 이들 사이에 연결이 열립니다. 그런 다음 새로운 개인을 작성하는 새로운 변이가 수행 될 때마다 서버는 이 개인에 대한 정보를 클라이언트로 보냅니다.


{
  "newPerson": {
    "name": "Jane",
    "age": 23
  }
}


스키마 정의 


쿼리, 변이 및 구독의 모양에 대한 기본적인 이해가되었으므로 이를 모두 모아서 지금까지 본 예제를 실행할 수 있는 스키마를 작성하는 방법을 알아 보겠습니다.


스키마는 GraphQL API로 작업 할 때 가장 중요한 개념 중 하나입니다. API의 기능을 지정하고 클라이언트가 데이터를 요청할 수 있는 방법을 정의합니다. 종종 서버와 클라이언트 간의 계약으로 간주됩니다.


일반적으로 스키마는 단순히 GraphQL 유형의 모음입니다. 그러나 API의 스키마를 작성할 때 특별한 루트 유형이 있습니다.


type Query { ... }
type Mutation { ... }
type Subscription { ... }


쿼리, 변이 및 구독 유형은 클라이언트가 보낸 요청의 진입 점입니다. 앞에서 본 allPersons-query를 사용하려면 Query 유형을 다음과 같이 작성해야 합니다.


type Query {
  allPersons: [Person!]!
}


allPersons를 API의 루트 필드라고 합니다. allPersons 필드에 마지막 인수를 추가 한 예를 다시 고려하면 다음과 같이 쿼리를 작성해야 합니다.


type Query {
  allPersons(last: Int): [Person!]!
}


마찬가지로 createPerson-mutation의 경우 변이 유형에 루트 필드를 추가해야 합니다.


type Mutation {
  createPerson(name: String!, age: Int!): Person!
}


이 루트 필드에는 새로운 사람의 이름과 나이라는 두 가지 인수가 있습니다. 마지막으로 구독을 위해 newPerson 루트 필드를 추가해야 합니다.


type Subscription {
  newPerson: Person!
}


이 모든 것을 종합하면 이 장에서 보았던 모든 쿼리와 변이에 대한 전체 스키마입니다.


type Query {
  allPersons(last: Int): [Person!]!
}

type Mutation {
  createPerson(name: String!, age: Int!): Person!
}

type Subscription {
  newPerson: Person!
}

type Person {
  name: String!
  age: Int!
  posts: [Post!]!
}

type Post {
  title: String!
  author: Person!
}


더 알아보기 


GraphQL의 핵심 개념에 대해 자세히 알아 보려면 다음 기사 시리즈를 확인하십시오.

Big Picture (Architecture) 


GraphQL은 사양으로만 출시되었습니다. 즉, GraphQL은 실제로 GraphQL 서버의 동작을 자세히 설명하는 긴 문서가 아닙니다.


사용 사례 


이 섹션에서는 GraphQL 서버를 포함하는 3 가지 종류의 아키텍처를 안내합니다.

  1. 연결된 데이터베이스가 있는 GraphQL 서버
  2. 다수의 타사 또는 레거시 시스템 앞에 있는 얇은 계층인 GraphQL 서버는 단일 GraphQL API를 통해 이를 통합합니다
  3. 동일한 GraphQL API를 통해 모두 액세스 할 수 있는 연결된 데이터베이스 및 타사 또는 레거시 시스템의 하이브리드 방식

세 가지 아키텍처는 모두 GraphQL의 주요 사용 사례를 나타내며 사용 가능한 컨텍스트 측면에서 유연성을 보여줍니다.


1. 데이터베이스가 연결된 GraphQL 서버 


이 아키텍처는 그린 필드 프로젝트에서 가장 일반적입니다. 설정에서 GraphQL 사양을 구현하는 단일 (웹) 서버가 있습니다. 쿼리가 GraphQL 서버에 도착하면 서버는 쿼리의 페이 로드를 읽고 데이터베이스에서 필요한 정보를 가져옵니다. 이를 쿼리 해결이라고 합니다. 그런 다음 공식 스펙에 설명 된 대로 응답 오브젝트를 구성하고 이를 클라이언트로 리턴합니다.


GraphQL은 실제로 전송 계층에 구애 받지 않습니다. 이는 사용 가능한 네트워크 프로토콜과 함께 사용될 수 있음을 의미합니다. 따라서 TCP, WebSocket 등을 기반으로 GraphQL 서버를 구현할 수 있습니다.


GraphQL은 데이터베이스 또는 데이터를 저장하는 데 사용되는 형식에 대해서도 신경 쓰지 않습니다. AWS Aurora와 같은 SQL 데이터베이스 또는 MongoDB와 같은 NoSQL 데이터베이스를 사용할 수 있습니다.


cRE6oeb.png 

단일 데이터베이스에 연결하는 하나의 GraphQL 서버가 있는 표준 그린 필드 아키텍처.


2. 기존 시스템을 통합하는 GraphQL 레이어 


GraphQL의 또 다른 주요 사용 사례는 하나의 일관된 GraphQL API 뒤에 여러 기존 시스템을 통합하는 것입니다. 이는 레거시 인프라와 수년에 걸쳐 성장해 왔으며 현재 유지 보수 부담이 큰 다양한 API를 보유한 기업에게 특히 유용합니다. 이러한 레거시 시스템의 주요 문제점 중 하나는 여러 시스템에 액세스해야 하는 혁신적인 제품을 구축 할 수 없다는 것입니다.


이러한 맥락에서 GraphQL을 사용하여 이러한 기존 시스템을 통합하고 멋진 GraphQL API 뒤에 복잡성을 숨길 수 있습니다. 이러한 방식으로 GraphQL 서버와 통신하여 필요한 데이터를 가져 오는 새로운 클라이언트 응용 프로그램을 개발할 수 있습니다. 그런 다음 GraphQL 서버는 기존 시스템에서 데이터를 가져 와서 GraphQL 응답 형식으로 패키지화 합니다.


GraphQL 서버가 사용 중인 데이터베이스 유형을 신경 쓰지 않은 이전 아키텍처와 마찬가지로 이번에는 쿼리를 해결하는 데 필요한 데이터를 가져 오는 데 필요한 데이터 소스를 신경 쓰지 않습니다.


zQggcSX.png 


GraphQL을 사용하면 단일 GraphQL 인터페이스 뒤에 마이크로 서비스, 레거시 인프라 또는 타사 API와 같은 기존 시스템의 복잡성을 숨길 수 있습니다.


3. 데이터베이스 연결 및 기존 시스템의 통합을 통한 하이브리드 접근 


마지막으로, 두 가지 접근 방식을 결합하여 데이터베이스가 연결되어 있지만 레거시 또는 타사 시스템과 통신하는 GraphQL 서버를 구축 할 수 있습니다.


서버가 쿼리를 받으면 쿼리를 해결하고 연결된 데이터베이스 또는 일부 통합 API에서 필요한 데이터를 검색합니다.


73dByTz.png 


두 접근 방식을 결합 할 수 있으며 GraphQL 서버는 기존 시스템 뿐만 아니라 단일 데이터베이스에서 데이터를 가져올 수 있어 유연성이 뛰어나고 모든 데이터 관리 복잡성을 서버에 적용 할 수 있습니다.


리졸버 함수 


그러나 GraphQL을 통해 어떻게 이러한 유연성을 확보 할 수 있습니까? 이처럼 매우 다양한 종류의 사용 사례에 적합한 이유는 무엇입니까?


이전 장에서 배운대로 GraphQL 쿼리 (또는 돌연변이)의 페이 로드는 일련의 필드로 구성됩니다. GraphQL 서버 구현에서 이러한 각 필드는 실제로 리졸버라고 하는 정확히 하나의 함수에 해당합니다. 리졸버 함수의 유일한 목적은 해당 필드의 데이터를 가져 오는 것입니다.


서버는 쿼리를 받으면 쿼리의 페이 로드에 지정된 필드에 대한 모든 기능을 호출합니다. 따라서 조회를 해결하고 각 필드에 대한 올바른 데이터를 검색 할 수 있습니다. 모든 리졸버가 반환되면 서버는 쿼리에서 설명한 형식으로 데이터를 패키지하여 클라이언트로 다시 보냅니다.


e1gBEP5.png 

위 스크린 샷에는 해결 된 일부 필드 이름이 포함되어 있습니다. 쿼리의 각 필드는 리졸버 함수에 해당합니다. GraphQL은 쿼리가 지정된 데이터를 페치하기 위해 들어올 때 필요한 모든 해석자를 호출합니다.


GraphQL 클라이언트 라이브러리 


GraphQL은 프론트 엔드 개발자에게 특히 유용합니다. 오버 페치 및 언더 페치와 같은 REST API에서 발생하는 많은 불편 함과 단점을 완전히 제거합니다. 강력한 기계가 많은 계산 작업을 처리 할 수 있는 서버 측으로 복잡성이 밀려납니다. 클라이언트는 가져 오는 데이터가 실제로 어디에서 오는지 알 필요가 없고, 일관되고 유연한 단일 API를 사용할 수 있습니다.


상당히 중요한 데이터 페치 방식에서 순전히 선언적인 방식으로 갈 때 GraphQL에 도입 된 주요 변경 사항을 고려해 보겠습니다. REST API에서 데이터를 가져올 때 대부분의 애플리케이션은 다음 단계를 수행해야 합니다.


  1. HTTP 요청 생성 및 전송 (예 : 자바 스크립트에서 가져 오기)
  2. 서버 응답 수신 및 구문 분석
  3. 데이터를 로컬에 저장 (단순히 메모리 또는 영구)
  4. UI에 데이터 표시

이상적인 선언적 데이터 가져 오기 접근 방식을 사용하면 고객은 다음 두 단계 이상을 수행하지 않아야 합니다.


  1. 데이터 요구 사항 설명
  2. UI에 데이터 표시


데이터 저장 뿐만 아니라 모든 하위 수준의 네트워킹 작업은 추상화 되어야 하며 데이터 종속성 선언이 지배적인 부분이어야 합니다.


이것이 바로 Relay 또는 Apollo와 같은 GraphQL 클라이언트 라이브러리가 가능하게 하는 것입니다. 이들은 반복적인 인프라 구현을 다루지 않고 애플리케이션의 중요한 부분에 집중할 수 있어야 하는 추상화를 제공합니다.


고급 튜토리얼-클라이언트 


프론트 엔드에서 GraphQL API를 사용하면 새로운 추상화를 개발하고 클라이언트 측에서 공통 기능을 구현할 수 있습니다. 앱에 갖고 싶은 몇 가지 "인프라"기능을 고려해 보겠습니다.

  • HTTP 요청을 구성하지 않고 직접 쿼리 및 변이 보내기
  • 뷰 레이어 통합
  • 캐싱
  • 스키마를 기반으로 쿼리 유효성 검사 및 최적화

물론 일반 HTTP를 사용하여 데이터를 가져온 다음 올바른 정보가 UI에 나타날 때까지 모든 비트를 직접 이동하는 것을 막을 수 있는 것은 없습니다. 그러나 GraphQL은 해당 프로세스 중에 수행해야 하는 많은 수동 작업을 추상화하고 앱의 실제 중요한 부분에 집중할 수 있는 기능을 제공합니다. 다음에서는 이러한 작업이 무엇인지 좀 더 자세히 설명하겠습니다.


현재 사용 가능한 두 가지 주요 GraphQL 클라이언트가 있습니다. 첫 번째는 모든 주요 개발 플랫폼을 위한 강력하고 유연한 GraphQL 클라이언트를 구축하기 위한 커뮤니티 중심의 노력인 Apollo Client입니다. 두 번째는 Relay라고 하며 Facebook에서 자체 개발한 GraphQL 클라이언트로 성능을 크게 최적화하고 웹에서만 사용할 수 있습니다. 


직접 쿼리 및 변이 보내기 


GraphQL의 주요 이점은 선언적인 방식으로 데이터를 가져오고 업데이트 할 수 있다는 것입니다. 다르게 말하자면, 우리는 API 추상화 사다리에서 한 단계 더 올라가므로 더 이상 저수준 네트워킹 작업을 처리 할 필요가 없습니다.


API에서 데이터를 로드 하기 위해 이전에 일반 HTTP (Javascript에서 가져 오기 또는 iOS의 NSURLSession과 같은)를 사용한 경우 GraphQL을 사용하면 데이터 요구 사항을 선언하고 시스템에서 요청 전송을 처리하는 쿼리를 작성하기 만 하면 됩니다. 그리고 당신을 위해 응답을 처리. 이것이 바로 GraphQL 클라이언트가 하는 일입니다.


레이어 통합 및 UI 업데이트 보기 


GraphQL 클라이언트가 서버 응답을 수신하고 처리하면 요청 된 데이터가 어떻게 든 UI에 있어야 합니다. 개발중인 플랫폼 및 프레임 워크에 따라 UI 업데이트가 일반적으로 처리되는 방식에 대한 다양한 접근 방식이 있습니다.


React를 예로 들어, GraphQL 클라이언트는 상위 컴포넌트의 개념을 사용하여 필요한 데이터를 후드 아래에서 가져와 컴포넌트의 props에서 사용할 수 있게 합니다. 일반적으로 GraphQL의 선언적 특성은 특히 함수형 Reactive 프로그래밍 기술과 잘 어울립니다. 이 둘은 뷰가 단순히 데이터 종속성을 선언하고 UI가 선택한 FRP 계층과 연결되는 강력한 조합을 형성 할 수 있습니다.


쿼리 결과 캐싱 : 개념 및 전략 


대부분의 응용 프로그램에서 이전에 서버에서 가져온 데이터의 캐시를 유지하려고 합니다. 유창한 사용자 경험을 제공하고 사용자의 데이터 요금제를 사용하려면 정보를 로컬로 캐시해야 합니다.


일반적으로 데이터를 캐싱 할 때 직관은 원격으로 가져온 정보를 나중에 검색 할 수 있는 로컬 상점으로 가져 오는 것입니다. GraphQL을 사용하는 경우 순진한 접근 방식은 GraphQL 쿼리의 결과를 스토어에 저장하고 정확히 동일한 쿼리가 다시 실행될 때마다 이전에 저장된 데이터를 반환하는 것입니다. 이 방법은 대부분의 응용 프로그램에서 매우 비효율적입니다.


보다 유리한 접근 방식은 미리 데이터를 정규화 하는 것입니다. 즉, (잠재적으로 중첩 된) 쿼리 결과가 평평 해지고 저장소에는 전체적으로 고유 한 ID로 참조 할 수 있는 개별 레코드 만 포함됩니다. 이것에 대해 더 배우고 싶다면 Apollo 블로그에 주제에 대한 훌륭한 글이 있습니다.


빌드 타임 스키마 검증 및 최적화 


스키마에는 클라이언트가 GraphQL API를 사용하여 수행 할 수 있는 작업에 대한 모든 정보가 포함되어 있으므로 클라이언트가 빌드 타임에 이미 보내려는 쿼리의 유효성을 검사하고 최적화 할 수 있는 좋은 기회가 있습니다.


빌드 환경이 스키마에 액세스 할 수 있으면 프로젝트에 있는 모든 GraphQL 코드를 구문 분석하고 스키마의 정보와 비교할 수 있습니다. 이로 인해 응용 프로그램이 실제 사용자의 손에 닿기 전에 오타 및 기타 오류를 포착하여 오류의 결과가 훨씬 더 심각해집니다.


뷰 및 데이터 종속성 배치 


GraphQL의 강력한 개념은 UI 코드와 데이터 요구 사항을 나란히 가질 수 있다는 것입니다. 뷰와 데이터 종속성의 긴밀한 결합은 개발자 경험을 크게 향상 시킵니다. UI의 올바른 부분에서 올바른 데이터가 어떻게 나타 나는지에 대한 정신적 오버 헤드가 제거됩니다.


코로케이션이 얼마나 잘 작동하는지는 개발 중인 플랫폼에 따라 다릅니다. 예를 들어 Javascript 응용 프로그램에서는 실제로 데이터 종속성과 UI 코드를 동일한 파일에 넣을 수 있습니다. Xcode에서는 Assistant Editor를 사용하여 뷰 컨트롤러와 graphql 코드를 동시에 작업 할 수 있습니다.


Server


GraphQL은 종종 클라이언트가 이전보다 훨씬 더 좋은 방식으로 데이터를 얻을 수 있게 하므로 프런트 엔드 중심 API 기술로 설명됩니다. 

그러나 API 자체는 물론 서버 측에서 구현됩니다. GraphQL을 사용하면 서버 개발자가 특정 엔드 포인트를 구현하고 최적화 하는 대신 사용 가능한 데이터를 설명하는 데 집중할 수 있기 때문에 서버에도 많은 이점이 있습니다.


GraphQL 실행 


GraphQL은 스키마와 해당 언어에서 데이터를 검색하는 쿼리 언어를 설명하는 방법을 지정하는 것이 아니라 해당 쿼리가 결과로 변환되는 방법에 대한 실제 실행 알고리즘을 지정합니다. 이 알고리즘은 그 핵심에서 매우 간단합니다. 쿼리는 필드별로 통과하여 각 필드에 대해 "해결 자"를 실행합니다. 따라서 다음과 같은 스키마가 있다고 가정 해 보겠습니다.


type Query {
  author(id: ID!): Author
}

type Author {
  posts: [Post]
}

type Post {
  title: String
  content: String
}


다음은 해당 스키마가 있는 서버로 보낼 수 있는 쿼리입니다.


query {
  author(id: "abc") {
    posts {
      title
      content
    }
  }
}


가장 먼저 볼 것은 쿼리의 모든 필드가 유형과 연결될 수 있다는 것입니다.


query: Query {
  author(id: "abc"): Author {
    posts: [Post] {
      title: String
      content: String
    }
  }
}


이제 서버에서 모든 필드에 대해 실행할 리졸버를 쉽게 찾을 수 있습니다. 실행은 쿼리 유형에서 시작하여 가장 먼저 진행됩니다. 이것은 Query.author에 대한 리졸버를 먼저 실행한다는 것을 의미합니다. 그런 다음 해당 리졸버의 결과를 가져 와서 Author.posts의 리졸버 인 자식에게 전달합니다. 다음 단계에서는 결과가 목록이므로 이 경우 실행 알고리즘은 한 번에 한 항목에서 실행됩니다. 따라서 실행은 다음과 같이 작동합니다.


Query.author(root, { id: 'abc' }, context) -> author
Author.posts(author, null, context) -> posts
for each post in posts
  Post.title(post, null, context) -> title
  Post.content(post, null, context) -> content


결국, 실행 알고리즘은 모든 것을 결과에 대한 올바른 형태로 모아서 반환합니다.


한 가지 주목할 점은 대부분의 GraphQL 서버 구현은 "기본 리졸버"를 제공하므로 모든 단일 필드에 리졸버 기능을 지정할 필요가 없습니다. 예를 들어 GraphQL.js에서는 리졸버의 부모 개체에 올바른 이름의 필드가 포함되어 있으면 리졸버를 지정할 필요가 없습니다.


Apollo 블로그의“GraphQL Explained”게시물에서 GraphQL 실행에 대해 자세히 알아보십시오.


Batched Resolving 


위의 실행 전략에 대해 알 수 있는 한 가지는 다소 순진한 것입니다. 예를 들어 백엔드 API 또는 데이터베이스에서 가져 오는 리졸버가 있는 경우 하나의 쿼리를 실행하는 동안 해당 백엔드가 여러 번 호출 될 수 있습니다. 다음과 같이 여러 게시물의 작성자를 원한다고 가정 해 보겠습니다.


query {
  posts {
    title
    author {
      name
      avatar
    }
  }
}


이 블로그에 게시물이 있다면,이 글의 많은 사람들이 같은 저자를 해야 합니다 가능성이 높습니다. 우리는 각각의 저자 개체를 얻을 수 있는 API 호출을 해야 한다면, 우리는 실수로 같은 일에 대해 여러 요청을 할 수 있습니다. 예를 들면 다음과 같습니다.


fetch('/authors/1')
fetch('/authors/2')
fetch('/authors/1')
fetch('/authors/2')
fetch('/authors/1')
fetch('/authors/2')


우리는 이것을 어떻게 해결합니까? 좀 더 똑똑하게 가져 와서. 모든 리졸버가 실행될 때까지 기다릴 유틸리티에서 페치 함수를 랩핑한 다음 각 항목을 한 번만 페치 해야 합니다.


authorLoader = new AuthorLoader()

// Queue up a bunch of fetches
authorLoader.load(1);
authorLoader.load(2);
authorLoader.load(1);
authorLoader.load(2);

// Then, the loader only does the minimal amount of work
fetch('/authors/1');
fetch('/authors/2');


더 잘할 수 있습니까? 예, API가 일괄 요청을 지원하는 경우 다음과 같이 백엔드에 대해 한 번만 가져올 수 있습니다.


fetch('/authors?ids=1,2')


위의 로더에 캡슐화 할 수도 있습니다.


JavaScript에서 위의 전략은 DataLoader라는 유틸리티를 사용하여 구현할 수 있으며 다른 언어를 위한 유사한 유틸리티가 있습니다.


더 많은 GraphQL 개념 


프래그먼트로 재사용 성 향상 


프래그먼트는 GraphQL 코드의 구조와 재사용 성을 개선하는 데 유용한 편리한 기능입니다. 조각은 특정 유형의 필드 모음입니다.


다음 유형이 있다고 가정합니다.


type User {
  name: String!
  age: Int!
  email: String!
  street: String!
  zipcode: String!
  city: String!
}


여기에서 사용자의 실제 주소와 관련된 모든 정보를 조각으로 나타낼 수 있습니다.


fragment addressDetails on User {
  name
  street
  zipcode
  city
}


이제 사용자의 주소 정보에 액세스하기 위해 쿼리를 작성할 때 다음 구문을 사용하여 조각을 참조하고 실제로 네 개의 필드를 철자 하는 작업을 저장할 수 있습니다.

{
  allUsers {
    ... addressDetails
  }
}


이 쿼리는 다음과 같이 작성합니다.


{
  allUsers {
    name
    street
    zipcode
    city
  }
}


인수를 사용하여 필드 매개 변수화 


GraphQL 유형 정의에서 각 필드는 0 개 이상의 인수를 사용할 수 있습니다. 유형이 지정된 프로그래밍 언어로 함수에 전달되는 인수와 유사하게 각 인수에는 이름과 유형이 있어야 합니다. GraphQL에서는 인수의 기본값을 지정할 수도 있습니다.


예를 들어, 처음에 보았던 스키마의 일부를 생각해 봅시다.


type Query {
  allUsers: [User!]!
}

type User {
  name: String!
  age: Int!
}


이제 allUsers 필드에 인수를 추가하여 인수를 전달하여 사용자를 필터링하고 특정 연령 이상의 사용자 만 포함 할 수 있습니다. 또한 기본적으로 모든 사용자가 반환 되도록 기본값을 지정합니다.


type Query {
  allUsers(olderThan: Int = -1): [User!]!
}


이 olderThan 인수는 이제 다음 구문을 사용하여 쿼리에 전달할 수 있습니다.


{
  allUsers(olderThan: 30) {
    name
    age
  }
}


별명을 가진 명명 된 쿼리 결과 


GraphQL의 주요 장점 중 하나는 단일 요청으로 여러 쿼리를 보낼 수 있다는 것입니다. 그러나 응답 데이터는 요청되는 필드의 구조에 따라 형성되므로 동일한 필드를 요청하는 여러 쿼리를 보낼 때 이름 지정 문제가 발생할 수 있습니다.


{
  User(id: "1") {
    name
  }
  User(id: "2") {
    name
  }
}


실제로 GraphQL 서버는 필드가 같지만 인수가 다르기 때문에 오류가 발생합니다. 이와 같이 쿼리를 보내는 유일한 방법은 별칭을 사용하는 것입니다 (예 : 쿼리 결과의 이름 지정).


{
  first: User(id: "1") {
    name
  }
  second: User(id: "2") {
    name
  }
}


결과적으로 서버는 이제 지정된 별명에 따라 각 User 오브젝트의 이름을 지정합니다.


{
  "first": {
    "name": "Alice"
  },
  "second": {
    "name": "Sarah"
  }
}


고급 SDL 


SDL은 이전 장에서 다루지 않은 몇 가지 언어 기능을 제공합니다. 다음에서는 실제 예를 통해 이에 대해 설명합니다.


객체 및 스칼라 유형 


GraphQL에는 두 가지 유형이 있습니다.

  • 스칼라 유형은 구체적인 데이터 단위를 나타냅니다. GraphQL 스펙에는 5 개의 사전 정의 된 스칼라가 있습니다 : String, Int, Float, Boolean 및 ID.
  • 객체 유형에는 해당 유형의 속성을 표현하고 구성 할 수 있는 필드가 있습니다. 객체 유형의 예는 이전 섹션에서 본 User 또는 Post 유형입니다.

모든 GraphQL 스키마에서 고유 한 스칼라 및 객체 유형을 정의 할 수 있습니다. 사용자 지정 스칼라에 대해 자주 인용되는 예는 구현에서 해당 유형의 유효성 검사, 직렬화 및 역 직렬화 방법을 정의해야 하는 날짜 유형입니다.


Enums 


GraphQL을 사용하면 고정 값 집합이 있는 유형의 의미를 표현하는 언어 기능인 열거 유형 (짧은 열거)을 정의 할 수 있습니다. 따라서 모든 요일을 나타내는 Weekday라는 유형을 정의 할 수 있습니다.


enum Weekday {
  MONDAY
  TUESDAY
  WEDNESDAY
  THURSDAY
  FRIDAY
  SATURDAY
  SUNDAY
}

기술적으로 열거 형은 특수한 종류의 스칼라 유형입니다.


Interface 


인터페이스는 추상적 인 방식으로 유형을 설명하는 데 사용할 수 있습니다. 이 인터페이스를 구현하는 모든 콘크리트 유형에 필요한 필드 집합을 지정할 수 있습니다. 많은 GraphQL 스키마에서 모든 유형에는 id 필드가 있어야 합니다. 인터페이스를 사용하여 이 필드로 인터페이스를 정의한 후 모든 사용자 정의 유형이 이를 구현하도록 하여 이 요구 사항을 표현할 수 있습니다.


interface Node {
  id: ID!
}

type User implements Node {
  id: ID!
  name: String!
  age: Int!
}


Union Types 


조합 유형은 유형이 다른 유형의 모음 중 하나 여야 함을 나타내는 데 사용할 수 있습니다. 그것들은 예를 통해 가장 잘 이해됩니다. 다음 유형을 고려하십시오.


type Adult {
  name: String!
  work: String!
}

type Child {
  name: String!
  school: String!
}


이제 Person 유형을 성인과 아동의 결합으로 정의 할 수 있습니다.


union Person = Adult | Child


이것은 다른 문제를 일으킨다 : Child에 관한 정보를 검색하도록 요청하지만 Person 유형 만 가지고 작업하는 GraphQL 쿼리에서 실제로 이 필드에 액세스 할 수 있는지 어떻게 알 수 있습니까?


이에 대한 답을 조건부 조각이라고 합니다.


{
  allPersons {
    name # works for `Adult` and `Child`
    ... on Child {
      school
    }
    ... on Adult {
       work
    }
  }
}


툴링 및 생태계 


이미 알고 있듯이 GraphQL 생태계는 현재 놀라운 속도로 성장하고 있습니다. 이런 일이 발생하는 이유 중 하나는 GraphQL을 사용하여 훌륭한 도구를 쉽게 개발할 수 있기 때문입니다. 이 섹션에서는 이것이 왜 그런지, 그리고 생태계에 이미 몇 가지 놀라운 도구가 있습니다.


GraphQL 기본 사항에 익숙하다면 GraphQL의 Type System을 사용하여 API의 표면 영역을 빠르게 정의 할 수 있습니다. 이를 통해 개발자는 API의 기능을 명확하게 정의 할 수 있을 뿐만 아니라 스키마에 대해 들어오는 쿼리의 유효성을 검사 할 수 있습니다.


GraphQL의 놀라운 점은 이러한 기능이 서버에만 알려지지 않는다는 것입니다. GraphQL을 통해 클라이언트는 서버에 스키마에 대한 정보를 요청할 수 있습니다. GraphQL은 이 검사를 호출합니다.


Introspection 


스키마 디자이너는 이미 스키마의 모양을 알고 있지만 클라이언트가 GraphQL API를 통해 액세스 할 수 있는 것을 어떻게 알 수 있습니까? __schema 메타 필드를 쿼리하여 GraphQL에이 정보를 요청할 수 있습니다. __schema 메타 필드는 사양에 따라 항상 쿼리의 루트 유형에서 사용할 수 있습니다.


query {
  __schema {
    types {
      name
    }
  }
}


이 스키마 정의를 예로 들어 보겠습니다.


type Query {
  author(id: ID!): Author
}

type Author {
  posts: [Post!]!
}

type Post {
  title: String!
}


위에서 언급 한 내부 검사 쿼리를 보내면 다음과 같은 결과가 나타납니다.


{
  "data": {
    "__schema": {
      "types": [
        {
          "name": "Query"
        },
        {
          "name": "Author"
        },
        {
          "name": "Post"
        },
        {
          "name": "ID"
        },
        {
          "name": "String"
        },
        {
          "name": "__Schema"
        },
        {
          "name": "__Type"
        },
        {
          "name": "__TypeKind"
        },
        {
          "name": "__Field"
        },
        {
          "name": "__InputValue"
        },
        {
          "name": "__EnumValue"
        },
        {
          "name": "__Directive"
        },
        {
          "name": "__DirectiveLocation"
        }
      ]
    }
  }
}


보시다시피 스키마의 모든 유형을 쿼리 했습니다. 정의한 객체 유형과 스칼라 유형을 모두 얻습니다. 우리는 심지어 내성 유형을 조사 할 수 있습니다!


내성 유형에는 사용할 수 있는 이름보다 훨씬 많은 것이 있습니다. 또 다른 예는 다음과 같습니다.


{
  __type(name: "Author") {
    name
    description
  }
}


이 예에서는 __type 메타 필드를 사용하여 단일 유형을 쿼리하고 이름과 설명을 요청합니다. 이 쿼리에 대한 결과는 다음과 같습니다.


{
  "data": {
    "__type": {
      "name": "Author",
      "description": "The author of a post.",
    }
  }
}


보시다시피, 내성 검사는 GraphQL의 매우 강력한 기능이며 표면 만 긁었습니다. 사양은 내부 검사 스키마에서 사용 가능한 필드 및 유형에 대해 훨씬 자세히 설명합니다.


GraphQL 생태계에서 사용할 수 있는 많은 도구는 자체 검사 시스템을 사용하여 놀라운 기능을 제공합니다. 문서 브라우저, 자동 완성, 코드 생성을 생각하면 모든 것이 가능합니다! GraphQL API를 빌드 하고 사용할 때 필요한 가장 유용한 도구 중 하나는 내성을 많이 사용합니다. 이를 GraphiQL이라고 합니다.


GraphQL 놀이터 


GraphQL Playground는 GraphQL API와 대화식으로 작업하기 위한 강력한 "GraphQL IDE"입니다. 자동 완성 및 검증 기능을 갖춘 GraphQL 쿼리, 돌연변이 및 구독 용 편집기와 문서 탐색기를 사용하여 스키마의 구조를 신속하게 시각화합니다 (내성에 의해 구동 됨). 또한 쿼리 기록을 표시하거나 여러 GraphQL API를 나란히 사용할 수 있습니다. 또한 graphql-config와 완벽하게 통합됩니다.


개발을 위한 매우 강력한 도구입니다. 예를 들어 curl을 통해 일반 GraphQL 쿼리를 작성하지 않고도 GraphQL 서버에서 쿼리를 디버깅하고 시도 할 수 있습니다.


Security 


GraphQL은 고객에게 엄청난 힘을 제공합니다. 그러나 큰 힘으로 큰 책임이 옵니다.🕷


고객은 매우 복잡한 쿼리를 작성할 수 있으므로 서버가 올바르게 처리 할 수 ​​있어야 합니다. 이러한 쿼리는 악의적 인 클라이언트의 모욕적 인 쿼리이거나 합법적 인 클라이언트가 사용하는 매우 큰 쿼리 일 수 있습니다. 이 두 경우 모두 클라이언트가 잠재적으로 GraphQL 서버를 중단 시킬 수 있습니다.


이러한 위험을 완화하기 위한 몇 가지 전략이 있습니다. 이 장에서는 가장 간단한 것부터 가장 복잡한 것까지 순서대로 다루고 장단점을 살펴볼 것입니다.


Timeout 


첫 번째 전략과 가장 간단한 전략은 시간 초과를 사용하여 큰 쿼리를 방어하는 것입니다. 이 전략은 서버가 들어오는 쿼리에 대해 아무것도 알 필요가 없기 때문에 가장 간단합니다. 모든 서버는 쿼리에 허용되는 최대 시간을 알고 있습니다.


예를 들어, 5 초 시간 초과로 구성된 서버는 실행하는 데 5 초 이상 걸리는 모든 쿼리의 실행을 중지합니다.


Timeout Pros

  • 구현이 간단합니다.
  • 대부분의 전략은 여전히 ​​타임 아웃을 최종 보호로 사용합니다.

Timeout Cons 

  • 타임 아웃이 시작 되더라도 이미 피해를 입을 수 있습니다.
  • 때로는 구현하기가 어렵습니다. 일정 시간이 지난 후 연결을 끊으면 이상한 동작이 발생할 수 있습니다.

최대 쿼리 깊이 


앞서 다루었 듯이 GraphQL을 사용하는 클라이언트는 원하는 복잡한 쿼리를 만들 수 있습니다. GraphQL 스키마는 종종 주기적 그래프이므로 클라이언트가 다음과 같은 쿼리를 작성할 수 있습니다.


query IAmEvil {
  author(id: "abc") {
    posts {
      author {
        posts {
          author {
            posts {
              author {
                # that could go on as deep as the client wants!
              }
            }
          }
        }
      }
    }
  }
}


클라이언트가 이와 같은 쿼리 깊이를 남용하지 못하게 하려면 어떻게 해야 합니까? 스키마를 알면 합법적 인 쿼리가 얼마나 깊이 진행되는지 알 수 있습니다. 실제로 구현할 수 있으며 최대 쿼리 깊이라고도 합니다.


GraphQL 서버는 쿼리 문서의 추상 구문 트리 (AST)를 분석하여 깊이에 따라 요청을 거부하거나 수락 할 수 있습니다.


최대 쿼리 깊이가 3으로 구성된 서버와 다음 쿼리 문서를 예로 들어 보겠습니다. 빨간색 마커 내의 모든 항목이 너무 깊어 쿼리가 유효하지 않습니다.


Query Depth Example 


최대 쿼리 깊이 설정과 함께 graphql-ruby를 사용하면 다음과 같은 결과가 나타납니다.


{
  "errors": [
    {
      "message": "Query has depth of 6, which exceeds max depth of 3"
    }
  ]
}


Maximum Query Depth Pros 

  • 문서의 AST는 정적으로 분석되므로 쿼리가 실행되지 않아 GraphQL 서버에 로드가 추가되지 않습니다.

Maximum Query Depth Cons 

  • 깊이 만으로는 모든 욕설을 다루기에는 충분하지 않습니다. 예를 들어 루트에서 막대한 양의 노드를 요청하는 쿼리는 비용이 많이 들지만 쿼리 깊이 분석기에 의해 차단되지는 않습니다.

쿼리 복잡성 


때때로, 질의 깊이는 GraphQL 질의가 얼마나 크거나 비싸는지 실제로 알기에 충분하지 않습니다. 많은 경우 스키마의 특정 필드는 다른 필드보다 계산하기가 더 복잡한 것으로 알려져 있습니다.


쿼리 복잡도를 사용하면 이러한 필드의 복잡성을 정의하고 쿼리를 최대 복잡도로 제한 할 수 있습니다. 아이디어는 간단한 숫자를 사용하여 각 필드가 얼마나 복잡한 지를 정의하는 것입니다. 일반적인 기본 값은 각 필드에 1의 복잡성을 부여하는 것입니다.이 쿼리를 예로 들어 보겠습니다.


query {
  author(id: "abc") { # complexity: 1
    posts {           # complexity: 1
      title           # complexity: 1
    }
  }
}


간단한 추가는 이 쿼리의 복잡성을 위해 총 3을 제공합니다. 스키마에서 최대 복잡성을 2로 설정하면 이 쿼리는 실패합니다.


게시물 필드가 실제로 저자 필드보다 훨씬 복잡한 경우 어떻게 합니까? 우리는 현장에 다른 복잡성을 설정할 수 있습니다. 심지어 인수에 따라 다른 복잡성을 설정할 수도 있습니다! 게시물이 인수에 따라 가변적 인 복잡성을 갖는 유사한 검색어를 살펴 보겠습니다.


query {
  author(id: "abc") {    # complexity: 1
    posts(first: 5) {    # complexity: 5
      title              # complexity: 1
    }
  }
}


Query Complexity Pros 

  • 단순한 쿼리 수준보다 더 많은 사례를 다룹니다.
  • 복잡성을 정적으로 분석하여 쿼리를 실행하기 전에 거부하십시오.

Query Complexity Cons 

  • 완벽하게 구현하기 어렵다.
  • 개발자가 복잡성을 추정하는 경우 어떻게 최신 상태를 유지합니까? 우선 비용을 어떻게 찾습니까?
  • 돌연변이는 추정하기 어렵다. 백그라운드 작업 큐잉과 같이 측정하기 어려운 부작용이 있다면 어떻게 해야 합니까?

Throttling 


지금까지 살펴본 솔루션은 악의적 인 쿼리로 인해 서버가 다운되지 않도록 하는 데 유용합니다. 이와 같이 단독으로 사용하는 문제는 큰 쿼리를 중지하지만 중간 크기의 쿼리를 많이 생성하는 클라이언트는 중지하지 않는다는 것입니다!


대부분의 API에서 간단한 스로틀은 클라이언트가 리소스를 너무 자주 요청하지 못하게 하는 데 사용됩니다. GraphQL은 요청 수를 조절하는 것이 실제로 도움이 되지 않기 때문에 약간 특별합니다. 쿼리가 너무 크면 쿼리가 너무 많을 수도 있습니다.


실제로, 요청이 클라이언트에 의해 정의 되었기 때문에 어느 정도의 요청이 수용 가능한지 모릅니다. 그렇다면 클라이언트를 조절하기 위해 무엇을 사용할 수 있습니까?


서버 시간에 따른 제한 


쿼리 완료에 소요되는 시간이 쿼리에 얼마나 비싸는지 추정합니다. 이 휴리스틱을 사용하여 쿼리를 제한 할 수 있습니다. 시스템을 잘 알고 있으면 클라이언트가 특정 기간 동안 사용할 수 있는 최대 서버 시간을 얻을 수 있습니다.


또한 시간이 지남에 따라 얼마나 많은 서버 시간이 클라이언트에 추가되는지 결정합니다. 이것은 고전적인 누출 버킷 알고리즘입니다. 다른 스로틀 알고리즘이 있지만 이 장에서는 다루지 않습니다. 다음 예에서는 새는 버킷 스로틀을 사용합니다.


허용되는 최대 서버 시간 (버킷 크기)이 1000ms로 설정되어 클라이언트가 초당 100ms의 서버 시간 (누설 률)을 얻었고 다음과 같은 돌연변이가 발생한다고 가정 해 보겠습니다.


mutation {
  createPost(input: { title: "GraphQL Security" }) {
    post {
      title
    }
  }
}


완료하는 데 평균 200ms가 걸립니다. 실제로는 시간이 다를 수 있지만 이 예제를 위해 완료하는 데 항상 200ms가 걸린다고 가정합니다.


이는 사용 가능한 서버 시간이 클라이언트에 추가 될 때까지 1 초 내에 이 작업을 5 회 이상 호출하는 클라이언트가 차단됨을 의미합니다.


2 초 후 (100ms가 추가됨) 클라이언트는 한 번만 createPost를 호출 할 수 있습니다.


보시다시피, 시간을 기준으로 조절하는 것은 복잡한 쿼리가 더 많은 시간을 소비하게 되므로 더 자주 호출 할 수 없고 계산이 매우 빠르기 때문에 더 작은 쿼리가 더 자주 호출 될 수 있기 때문에 GraphQL 쿼리를 조절하는 좋은 방법입니다 .


GraphQL API가 공개 인 경우 이러한 제한 제한 조건을 클라이언트에 표현하는 것이 좋습니다. 이 경우 서버 시간이 항상 클라이언트에게 표현하기 가장 쉬운 것은 아니며 클라이언트는 쿼리를 먼저 시도하지 않고 쿼리에 걸리는 시간을 실제로 예측할 수 없습니다.


앞에서 이야기 한 Max Complexity를 기억하십니까? 우리가 대신에 그것을 조절하면 어떻게 될까요?


쿼리 복잡도에 따른 제한 


쿼리 복잡성을 기반으로 한 제한은 클라이언트와 작업하고 스키마의 한계를 존중하도록 돕는 좋은 방법입니다.


검색어 복잡성 섹션에서 사용한 것과 동일한 복잡성 예제를 사용해 보겠습니다.


query {
  author(id: "abc") {    # complexity: 1
    posts {              # complexity: 1
      title              # complexity: 1
    }
  }
}


이 쿼리는 복잡성에 따라 비용이 3이라는 것을 알고 있습니다. 타임 스로틀과 마찬가지로 고객이 사용할 수 있는 시간당 최대 비용 (Bucket Size)을 얻을 수 있습니다.


최대 비용 9로, 고객은 누출 율로 인해 더 많은 쿼리를 금지하기 전에 이 쿼리를 세 번만 실행할 수 있었습니다.


원칙은 타임 스로틀과 동일하지만 이제 이러한 제한을 클라이언트에게 전달하는 것이 훨씬 좋습니다. 클라이언트는 서버 시간을 예측할 필요 없이 쿼리 비용 자체를 계산할 수도 있습니다! 


GitHub 퍼블릭 API는 실제로 이 접근 방식을 사용하여 클라이언트를 제한합니다. 이러한 제한이 사용자에게 어떻게 표현되는지 살펴보십시오 : https://developer.github.com/v4/guides/resource-limitations/.


요약 


GraphQL은 고객에게 더 많은 기능을 제공하기 때문에 고객에게 유용합니다. 그러나 이 기능을 사용하면 매우 비싼 쿼리로 GraphQL 서버를 남용 할 수 있습니다.


이러한 쿼리로부터 GraphQL 서버를 보호하는 방법에는 여러 가지가 있지만 그중 어느 것도 방탄이 아닙니다. 최선의 결정을 내릴 수 있도록 사용 가능한 옵션과 한계를 아는 것이 중요합니다!


일반적인 질문 


GraphQL은 데이터베이스 기술입니까? 


아닙니다. GraphQL은 종종 데이터베이스 기술과 혼동됩니다. 이것은 잘못된 생각입니다. GraphQL은 데이터베이스가 아닌 API에 대한 쿼리 언어입니다. 그런 의미에서 데이터베이스에 관계없이 모든 종류의 데이터베이스와 함께 사용하거나 데이터베이스 없이 사용할 수 있습니다.


GraphQL은 React / Javascript 개발자에게만 해당됩니까? 


아니요. GraphQL은 API 기술이므로 API가 필요한 모든 상황에서 사용할 수 있습니다.


백엔드에서 GraphQL 서버는 웹 서버를 구축하는 데 사용할 수 있는 모든 프로그래밍 언어로 구현 될 수 있습니다. Javascript 옆에는 Ruby, Python, Scala, Java, Clojure, Go 및 .NET에 대한 일반적인 참조 구현이 있습니다.


GraphQL API는 일반적으로 HTTP를 통해 운영되므로 HTTP를 사용할 수 있는 모든 클라이언트는 GraphQL 서버에서 데이터를 쿼리 할 수 ​​있습니다.


참고 : GraphQL은 실제로 전송 계층에 구애 받지 않으므로 서버를 구현하기 위해 HTTP 이외의 프로토콜을 선택할 수 있습니다.


서버 측 캐싱을 수행하는 방법? 


GraphQL과 관련하여 특히 REST와 비교할 때 공통적으로 고려해야 할 사항 중 하나는 서버 측 캐시를 유지 관리하기가 어렵다는 것입니다. REST를 사용하면 데이터 구조가 변경되지 않으므로 각 엔드 포인트에 대한 데이터를 쉽게 캐시 할 수 있습니다.


반면에 GraphQL을 사용하면 클라이언트가 다음에 무엇을 요청할지 명확하지 않으므로 API 바로 뒤에 캐싱 레이어를 배치하는 것은 의미가 없습니다.


서버 측 캐싱은 여전히 ​​GraphQL의 문제입니다. 캐싱에 대한 자세한 정보는 GraphQL 웹 사이트에서 찾을 수 있습니다.


인증 및 권한 부여 방법? 


인증과 권한 부여는 종종 혼동됩니다. 인증은 신원을 주장하는 과정을 설명합니다. 사용자 이름과 비밀번호를 사용하여 서비스에 로그인하면 본인이 인증하는 방식입니다. 반면 권한 부여는 시스템의 특정 부분에 대한 개별 사용자 및 사용자 그룹의 액세스 권한을 지정하는 권한 규칙을 설명합니다.


GraphQL의 인증은 OAuth와 같은 일반적인 패턴으로 구현할 수 있습니다.


권한 부여를 구현하려면 데이터 액세스 논리를 비즈니스 논리 계층에 위임하고 GraphQL 구현에서 직접 처리하지 않는 것이 좋습니다. 인증을 구현하는 방법에 대해 영감을 얻으려면 Graphcool의 권한 규칙을 살펴보십시오.


오류 처리 방법? 


GraphQL 쿼리가 성공하면 "data"라는 루트 필드가 있는 JSON 객체가 반환 됩니다. 요청이 실패하거나 부분적으로 실패하면 (예 : 데이터를 요청하는 사용자에게 올바른 액세스 권한이 없기 때문에) "오류"라는 두 번째 루트 필드가 응답에 추가됩니다.


{
  "data": { ... },
  "errors": [ ... ]
}


자세한 내용은 GraphQL 사양을 참조하십시오.


GraphQL은 오프라인 사용을 지원합니까? 


GraphQL은 (웹) API를 위한 쿼리 언어이며, 그런 의미에서 온라인 상에서만 작동합니다. 그러나 클라이언트 측에서의 오프라인 지원은 유효한 문제입니다. Relay 및 Apollo의 캐싱 기능은 이미 일부 사용 사례에 충분할 수 있지만 실제로 저장된 데이터를 실제로 유지하는 데 널리 사용되는 솔루션은 없습니다. 오프라인 지원이 논의되는 Relay Apollo의 GitHub 문제에 대해 더 많은 통찰력을 얻을 수 있습니다.


오프라인 사용 및 지속성을 위한 흥미로운 접근 방법은 여기에서 찾을 수 있습니다.


좋아하는 기술로 시작하십시오 


축하합니다! 🎉 How To GraphQL의 첫 번째 부분을 통해 GraphQL로 실제 앱을 빌드하는 데 필요한 모든 것을 배웠습니다!


다음 섹션에서 우리가 만든 여러 실습 자습서 중 하나를 선택할 수 있습니다. 모든 자습서는 처음부터 시작하여 본격적인 Hackernews 복제본을 작성하는 방법을 알려줍니다. 배우고 자하는 내용에 따라 프런트 엔드 또는 백엔드 트랙에서 자습서를 선택할 수 있습니다.


나만의 GraphQL 서버 구축 


서버 측에서 GraphQL을 시작하려면 좋아하는 프로그래밍 언어를 선택하십시오 :

  • Node.js (초보자 선택) :이 학습서는 graphql-js 참조 구현을 사용합니다. Node.js, graphql-yoga 서버 라이브러리 및 Prisma를 사용하여 GraphQL 서버를 구축합니다.
  • Ruby : graphql-ruby 라이브러리를 기반으로 하는 이 학습서를 통해 Ruby에서 GraphQL 서버를 빌드하는 방법을 학습하십시오.
  • Java : graphql-java 라이브러리를 기반으로 하는 이 학습서에서 Java로 GraphQL 서버를 빌드 하는 방법을 학습하십시오.
  • Elixir : Absinthe 라이브러리를 기반으로 하는 이 튜토리얼을 통해 Elixir에서 GraphQL 서버를 구축하는 방법을 배웁니다.
  • Python : Graphene 라이브러리를 기반으로 하는 이 학습서에서 Python으로 GraphQL 서버를 빌드 하는 방법을 학습하십시오.
  • 스칼라 : Sangria 라이브러리를 기반으로 하는 이 튜토리얼을 통해 Scala에서 GraphQL 서버를 구축하는 방법을 배웁니다.


프론트 엔드에서 GraphQL을 사용하는 방법 알아보기 


우리가 제공하는 프런트 엔드 자습서에 대한 개요는 다음과 같습니다.

  • React & Apollo (초보자 선택) : GraphQL을 처음 사용하는 경우이 자습서를 참조하십시오. Apollo Client를 사용하면 응용 프로그램을 개발할 때 필요한 모든 것을 제공하는 유연하면서도 강력하고 직관적 인 API를 제공하여 프런트 엔드에서 GraphQL을 쉽게 시작할 수 있습니다.
  • React & urql (Newcomer) : GraphQL을 처음 사용하지만 매우 유연한 솔루션을 찾고 있다면 이 자습서를 참조하십시오. urql은 React 용 GraphQL 클라이언트로 시작하기 쉽고 시간이 지남에 따라 사용자 정의가 가능합니다. 이 학습서에서는 정규화 된 캐싱 및 React Suspense를 사용하는 방법도 설명합니다.
  • React Native & Apollo (출시 예정) : Expo는 JavaScript 및 React로 기본 앱을 빌드 하는 React Native 툴체인입니다. 이 자습서에서는 Expo 및 Apollo Client를 기반으로 GraphQL을 사용하여 모바일 앱을 빌드 하는 방법을 배웁니다.
  • VulcanJS (출시 예정) : VulcanJS는 풀랙 JavaScript 프레임 워크로 React, Apollo ClientMeteor로 GraphQL 앱을 구축 할 수 있습니다.


  • 트위터로 보내기
  • 페이스북으로 보내기
  • 구글플러스로 보내기
  • 카카오톡으로 보내기

페이지 정보

조회 64회 ]  작성일19-09-30 15:41

웹학교