[GraphQL] GraphQL에 대하여

김성은·2024년 4월 23일
0

GraphQL-Study

목록 보기
1/2
post-thumbnail

Intro.

모바일 사용량의 증가로 효율적인 데이터 로딩이 필요해졌고, 플랫폼의 다양화로 인해 모든 요구 사항에 맞는 하나의 API를 개발하고 구축 및 유지관리가 어려워졌다. 또한 지속적 배포가 업계의 표준이 됨에 따라 빠른 반복과 빈번한 제품 배포는 필수적이 되었고 이에 따라 프론트엔드와 백엔드의 의사소통 비용은 꾸준히 증가하고 있다.

페이스북은 이러한 3가지 문제점을 해결하고자 GraphQL을 개발하게 되었다.

GraphQL이란?

GraphQL은 API를 위한 쿼리 언어이자 데이터에 대해 정의한 유형 시스템을 사용하여 쿼리를 실행하기 위한 서버 측 런타임입니다. GraphQL은 데이터를 쿼리하고 업데이트하고 생성하고 삭제하기 위한 단일 엔드포인트를 제공하며, 클라이언트가 요청을 체인으로 연결할 필요 없이 필요한 특정 데이터를 요청하고 받을 수 있습니다.

GraphQL은 HTTP를 전송 프로토콜로 사용하며 query, mutations, subscriptions의 세 가지 작업 유형을 수행할 수 있다.

  • query: 쿼리를 사용하면 클라이언트가 정의된 데이터 스키마를 사용하여 필요한 특정 데이터를 요청할 수 있다. 쿼리는 서버에 여러 번 왕복 호출할 필요 없이 여러 데이터 소스 또는 다른 API에서 데이터를 가져오는 데 사용할 수 있다.
  • mutation: GraphQL의 mutaio은 클라이언트가 서버에서 데이터를 변형할 수 있도록 한다. 여기에는 기존 데이터 개체를 만들거나 업데이트하는 것이 포함된다.
  • subscription: subscription은 클라이언트와 서버 간의 양방향, 실시간 통신을 용이하게 한다. 특정 이벤트가 발생시 서버에서 클라이언트로 데이터를 전송해주는 기술이다.

Rest API

/v1/user
/v1/post/{id}

REST API는 REST(Representational State Transfer)로 HTTP Method를 통해 동작을 결정하고 URI를 통해 어떤 자원에 대한 동작을 실행할지 표현한다.

GraphQL은 동일하게 HTTP 요청방식을 사용하지만 POST만 사용하고 단일 end point를 사용한다. 그리고 요청 시 body에 Schema에 맞춰 Query를 사용하여 요청하기 때문에 일관성 있는 통신이 가능하다.

REST API의 문제 (feat. GraphQL이 해결해줄게)

REST API는 underFetching과 OverFetching 문제점이 발생할 수 있다.

  • UnderFetching: 하나의 endpoint로 필요한 모든 데이터를 요청하여 전달받지 못해 여러 번의 네트워크 통신이 필요함
  • OverFetching: endpoint로 응답 받은 정보가 불필요하게 많을 경우 네트워크의 낭비가 존재함

예를 들어 다음과 같은 REST API가 존재한다고 하자

GET /user/1/

{
 "user_id": 1,
 "user_name": "test",
 "usere_grade": "VVIP"
 ...
}

클라이언트가 현재 사용자의 이름만 필요하다고 할 때, user_id와 user_grade는 필요없는 정보이다. 이렇게 불필요하게 많은 정보를 함께 전달받는 것을 overfetching이라고 한다.

반대로 사용자의 장바구니 목록을 확인하는 페이지를 개발하는 클라이언트가 있다고 가정하자. 이 클라이언트는 사용자의 정보와 장바구니 목록이 필요하다위의 API 뿐 아니라 사용자의 주문 목록을 가져오는 GET /cart 등의 추가적인 API 통신을 통해 정보를 가져와야 할 것이다. 이렇게 하나의 endpoint에 원하는 정보가 모두 담겨있지 않아 여러번의 네트워크 통신을 해야하는 경우를 underfetching`이라고 한다.

GraphQL은 필드 수준 요청, Nested 필드, Alias, Fragments 개념들을 사용하여 over/underfetching에 대한 문제점을 해결한다.

각 개념들의 자세한 내용은 글의 뒷 부분에서 설명하겠다.

REST API와 VS GraphQL

출처: https://www.cosmicjs.com/blog/graphql-vs-rest-a-quick-guide

  • 형태 정의 및 데이터 요청 방식: REST에서는 Resource에 대한 형태 정의와 데이터 요청 방법이 연결되어 있지만, GraphQL에서는 Resource에 대한 형태 정의와 데이터 요청이 완전히 분리되어 있다.
  • 자원의 크기와 형태 결정 주체: REST에서는 Resource의 크기와 형태를 서버에서 결정하지만, GraphQL에서는 Resource에 대한 정보만 정의하고, 필요한 크기와 형태는 client 단에서 요청 시 결정한다.
  • 자원과 작업의 유형: REST에서는 URI가 Resource를 나타내고 Method가 작업의 유형을 나타내지만, GraphQL에서는 GraphQL Schema가 Resource를 나타내고 Query, Mutation 타입이 작업의 유형을 나타낸다.
  • 요청 횟수: REST에서는 여러 Resource에 접근하고자 할 때 여러 번의 요청이 필요하지만, GraphQL에서는 한번의 요청에서 여러 Resource에 접근할 수 있다
  • 작업 처리 방식: REST에서 각 요청은 해당 엔드포인트에 정의된 핸들링 함수를 호출하여 작업을 처리하지만, GraphQL에서는 요청 받은 각 필드에 대한 resolver를 호출하여 작업을 처리한다.

GraphQL - Query & Mutation

필드

  • 쿼리와 결과가 동일한 형태인 것을 확인할 수 있다
  • 항상 기대 한 결과를 얻을 수 있다
  • 서버에서 클라이언트가 요청하는 필드를 알 수 있다

인자

  • REST와 같은 시스템에서는 요청에 쿼리 파라미터와 URL 세그먼트 같은 단일 인자들만 전달할 수 있었다
  • GraphQL에서는 모든 필드와 중첩된 객체가 인자를 가질 수 있으므로 GraphQL은 여러번의 API fetch를 대체할 수 있다
  • 필드에 인자를 전달하면, 모든 클라이언트에서 개별적으로 처리하는 대신 서버에서 데이터 변환을 한 번만 구현할 수도 있다

별칭

  • 결과 객체 필드가 쿼리의 필드 이름과 일치하지만 인자는 그렇지 않으므로 다른 인자를 사용하여 같은 쿼리를 직접 쿼리할 수 없다
  • 별칭을 사용하면 필드의 결과를 원하는 이름으로 변경할 수 있다
  • 아래의 예시와 같이 hero 필드가 서로 충돌하지만, 서로 다른 이름의 별칭을 지정하여 한 요청에서 두 결과를 모두 얻을 수 있다

프래그먼트

  • 프래그먼트를 사용하면 필드셋을 구성한 다음 필요한 쿼리에 포함시킬 수 있다
  • 복잡한 응용 프로그램의 데이터 요구사항을 작은 단위로 분할하는데 사용된다
  • 청크가 다른 여러 UI 구성요소를 하나의 초기 데이터 fetch로 통합해야하는 경우에 많이 사용된다

프래그먼트 안에서 변수 사용하기

  • 쿼리나 뮤테이션에 선언된 변수는 프래그먼트에 접근할 수 있다

작업 이름

  • 다음과 같이 작업타입(query), 작업이름(HeroNameAndFriends)를 명시할 수 있다
  • 작업 타입은 query, mutation, subscription이 될 수 있다
  • 작업 이름을 명시하면 디버깅이나 서버 측에서 로깅하는데 유용할 수 있다

변수

  • GraphQL은 동적값을 쿼리에서 없애고, 이를 별도로 전달하는 변수라는 개념이 있다
  • 변수를 사용하기 위해서는 다음 세 가지 작업을 해야 한다
    1. 쿼리 안의 정적 값을 $variableName으로 변경한다
    2. $variableName을 쿼리에서 받는 변수로 선언한다
    3. 별도의 전송규약(일반적으로 json) 변수에 variableName: value를 전달한다

변수 정의

  • 변수 정의는 위의 예제에서 ($episode: Episode) 부분이다
  • 정적 타입 언어의 함수에 대한 인자 정의와 동일하다
  • $ 접두사가 붙은 모든 변수를 나열하고 그 뒤에 타입(ex. Episode)이 온다
  • 선언된 모든 변수는 스칼라, 열거형, input object type이어야 한다

변수 기본 값

  • 타입 선언 다음에 기본값을 명시하여 쿼리의 변수에 기본 값을 할당할 수 있다
  • 모든 변수에 기본값이 제공되면 변수를 전달하지 않고도 쿼리를 호출할 수 있다

지시어

  • 필드나 프래그먼트 안에 삽입될 수 있으며 서버가 원하는 방식으로 쿼리 실행에 영향을 줄 수 있다
  • @Include(if: Boolean): 인자가 true인 경우에만 이 필드를 포함
  • @skip(if: Boolean): 인자가 true면 이 필드를 건너뜀

뮤테이션

  • 기술적으로는 어떠한 쿼리든 데이터를 수정할 수 있지만 변경을 발생시키는 작업이 명시적으로 뮤테이션을 통해 전송되어야 한다는 규칙을 정하는 것이 좋다
  • 뮤테이션 필드가 객체 타입을 반환하면 중첩 필드를 요청할 수 있고 이는 변경된 객체의 새로운 상태를 가져오는데에 유용하다

뮤테이션의 다중 필드

  • 뮤테이션은 쿼리와 마찬가지로 여러 필드를 포함할 수 있다
  • 쿼리 필드는 병렬로 실행되지만 뮤테이션 필드는 하나씩 차례대로 실행된다
  • 즉, 하나의 요청에서 두개의 뮤테이션을 보내면 첫번째는 두번째 요청 전에 완료되는 것이 보장된다

인라인 프래그먼트

  • 인터페이스나 유니언 타입을 반환하는 필드를 쿼리하는 경우, 인라인 프래그먼트를 사용해야 한다
  • 특정한 타입의 필드를 요청하려면 타입 조건과 함께 인라인 프래그먼트를 사용해야 한다
  • 아래 예제에서 ... on Droid 라는 레이블이 붙어있기 때문에 primaryFuction 필드는 hero에서 반환된 CharacterDroid 타입인 경우에만 실행된다

메타 필드

  • GraphQL 서비스에서 리턴될 타입을 모르는 상황이 발생하면 클라이언트에서 해당 데이터를 처리하는 방법을 결정해야 한다.
  • GraphQL을 사용하면 쿼리의 어느 지점에서나 메타 필드인 __typename을 요청하여 그 시점에서 객체 타입의 이름을 얻을 수 있다



참고 자료

rest-graphql-differences
graphql-vs-rest :quick guide
GraphQL 공식문서
graph-vs-rest
profile
백엔드 개발자가 되고 싶은 눈송이입니다

0개의 댓글