• 본 시리즈에서는 How to GraphQL의 Tutorial 문서들을 차례대로 번역합니다.
  • 이 글은 GraphQL Fundamentals - Big Picture (Architecture)을 번역한 글입니다.
  • 오역 또는 의역이 있을 수 있습니다. 양해 부탁드리며, 수정이 필요한 부분은 댓글로 요청해주세요.

큰 그림 (설계)

GraphQL은 명세로서 공개되었습니다. 즉, GraphQL이란 사실 GraphQL Server의 작동 방식을 자세히 설명하는 긴 문서에 불과합니다.

사용 예시

이 절에서는 GraphQL 서버가 포함된 3가지 종류의 설계를 하나씩 살펴봅니다.

  1. 데이터베이스 서버와 직접 연결된 GraphQL 서버
  2. 복수의 서드 파티 또는 레거시 시스템을 뒤에 두면서 이들을 단일 GraphQL API로서 통합하는 얆은 계층(Layer)의 GraphQL 서버
  3. 하나의 GraphQL API를 사용하여 데이터베이스 또는 서드 파티 및 레거시 시스템에 접근할 수 있는 하이브리드형 접근 방식 (1번과 2번의 통합)

1. 데이터베이스 서버와 직접 연결된 GraphQL 서버

이 설계는 새로 시작하는 프로젝트에서 가장 쉽게 사용되는 것입니다. 처음 환경을 구축할 때, GraphQL 명세를 구현하는 단일한 (웹) 서버를 두게 됩니다. GraphQL 서버가 쿼리를 받으면, 서버는 쿼리에 전달된 페이로드를 읽고 데이터베이스로부터 필요한 정보를 불러옵니다. 이를 두고 쿼리를 해결(resolve)한다고 말합니다. 이제 불러온 정보를 기반으로 공식 명세에 설명된 바와 같이 응답 객체를 만든 뒤 클라이언트에 반환합니다.

GraphQL은 전송 계층에 대하여 몰라도 된다는 점에 유의하세요. 즉, 어떤 네트워크 프로토콜을 사용하여도 상관이 없습니다. 따라서 TCP, Websocket 등에 기반한 서버에도 사용될 수 있습니다.

또한 GraphQL은 데이터베이스의 종류, 데이터의 저장 형식 등에 구애받지 않습니다. AWS Aurora와 같은 SQL 데이터베이스, MongoDB와 같은 NoSQL 데이터베이스 등을 모두 사용할 수 있습니다.

cRE6oeb.png

[단일 데이터베이스와 연결된 GraphQL 서버로 이루어진, 새로 시작하는 서비스의 표준적인 설계]

2. 이미 존재하는 레거시 시스템을 통합하는 GraphQL 계층

GraphQL의 또다른 주요 사용 예시는 이미 존재해온 복수의 시스템과 밀접하게 결합하면서 이들을 통합해주는 GraphQL API입니다. 이러한 구조는 이미 레거시 인프라와 다양한 API가 존재하여 유지보수 부담이 상당한 기업들에게 매력적입니다. 이러한 레거시 시스템에는 다양한 시스템에서 접근할 수 있는 혁신적인 제품 형태를 만들어내는 것이 현실적으로 불가능하다는 문제가 있습니다.

이런 맥락에서 GraphQL은 레거시 시스템들을 통합하고 복잡성을 숨기는 데에 사용될 수 있습니다. 그러면 새로운 클라이언트는 필요한 데이터를 얻기 위하여 GraphQL 서버와 마치 대화하는 것과 같이 단순하게 구현될 수 있습니다. 여기서 GraphQL 서버는 레거시 시스템으로부터 데이터를 가져와서 GraphQL 응답 형태로 포장하는 역할을 맡게 됩니다.

직전에 언급한 설계에서 GraphQL 서버가 데이터베이스의 종류를 상관하지 않았던 것과 같이, 쿼리를 해결하는 데에 필요한 데이터 출처(source)도 무엇이든 상관하지 않습니다.

zQggcSX.png

[GraphQL을 사용하면 마이크로서비스, 레거시 인프라, 서드 파티 API 등 레거시 시스템의 복잡성을 GraphQL 인터페이스 뒤에 숨길 수 있습니다.]

3. 연결된 데이터베이스와 레거시 시스템의 통합을 통한 하이브리드 형 접근

마지막으로, 앞서 언급한 두 가지 접근을 통합하는 접근도 가능합니다. 이 GraphQL 서버는 연결된 데이터베이스를 가지면서 동시에 레거시 또는 서드 파티 시스템과 상호작용할 수 있습니다.

서버가 쿼리를 받으면, 응답에 필요한 데이터를 연결된 데이터베이스 혹은 통합된 API로부터 가져와서 응답합니다.

73dByTz.png

[두 가지 접근 방식이 결합할 수 있습니다. GraphQL 서버는 단일 데이터베이스는 물론 기존의 시스템으로부터 데이터를 가져올 수 있습니다. 이를 통하여 완전한 유연성을 확보하고, 데이터 관리의 복잡성을 서버에 맡길 수 있습니다.]

Resolver 함수

GraphQL의 이러한 유언성은 어디서 비롯되는 것일까요? 이렇게 다양한 종류의 사용 사례를 모두 만족시키는 방법이 무엇일까요?

바로 직전 장에서 배웠듯이, GraphQL 쿼리(또는 뮤테이션)의 페이로드는 필드들의 집합으로 구성됩니다. GraphQL 서버를 구현할 때, 각각의 필드들은 저마다 리졸버(resolver)라고 불리는 함수에 대응합니다. 리졸버 함수의 유일한 목적은 해당 필드를 위한 데이터를 불러오는 것입니다.

서버가 쿼리를 받았을 때, 쿼리 페이로드 상에 명시된 각 필드들에 대한 함수들을 모두 호출하게 됩니다. 이를 통하여 쿼리를 해결(resolve)하고 각 필드에 대하여 올바른 데이터를 반환할 수 있게 됩니다. 모든 리졸버들이 값을 반환하면, 서버는 쿼리 상에 서술된 형태로 데이터들을 포장한 뒤 클라이언트에 반환해줍니다.

e1gBEP5.png

[위 그림은 해결된 필드의 이름 일부를 보여줍니다. 쿼리에 적힌 각각의 필드들은 저마다 리졸버 함수에 대응합니다. 데이터를 불러오기 위하여 쿼리가 들어오면, GraphQL은 필요한 모든 리졸버들을 호출합니다.]

GraphQL 클라이언트 라이브러리

GraphQL은 프론트엔드 개발자에게 특히 편리합니다. 왜냐하면 REST API를 사용하면서 겪을 수 있는 Under/Overfetch를 비롯한 많은 불편함과 단점들을 해소시켜주기 때문입니다. 복잡성은 막중한 연산 작업을 모두 감당할 수 있는 강력한 머신이 존재하는 서버 측에 맡겨버리면 됩니다. 클라이언트는 불러올 데이터가 어디서 비롯되는지 더 이상 알 필요가 없고, 단지 딱 하나의 유연하면서도 일관성있는 API를 사용하기만 하면 됩니다.

GraphQL를 도입한다는 것은 명령적인 데이터 불러오기로부터 순수하게 선언적인 데이터 불러오기로 전환한다는 의미입니다. 이것은 아주 큰 변화이며, 차이점을 살펴볼 필요가 있습니다. REST API를 사용하여 데이터를 불러올 때에 대부분의 어플리케이션은 다음과 같은 단계를 거칠 것입니다.

  1. HTTP 요청을 구성하고 보낸다 (Javascript의 fetch 등을 사용)
  2. 서버의 응답을 받고 해석한다
  3. 데이터를 로컬에 저장한다 (메모리 또는 저장 장치에)
  4. UI에 데이터를 표시한다

이상적인 형태의 선언적인 데이터 불러오기 접근 방식에서는 클라이언트가 아래의 2단계 이외에는 수행할 필요가 없어질 것입니다.

  1. 필요한 데이터를 서술한다
  2. UI에 데이터를 표시한다

모든 저수준 네트워크 작업, 데이터 보관 작업 등은 추상화되버리고 데이터 의존성의 선언만이 가장 큰 관심사로 거듭나는 것입니다.

이것이 정확히 Relay, Apollo 등의 클라이언트 라이브러리가 해주는 것들입니다. 위와 같은 추상화를 통하여 기반 환경의 반복적인 구현을 신경쓰지 말고 어플리케이션의 중요한 부분에 집중할 수 있도록 해줍니다.

Quiz

리졸버 함수란?

  • 프론트엔드 상에서 쿼리를 해결하고자 GraphQL 클라이언트가 사용하는 함수
  • 당신이 모든 문제를 해결해주는 함수
  • 서버리스 함수의 동의어
  • 단일 필드에 대하여 데이터를 불러와주는 GraphQL 서버 상의 함수