본문 바로가기

개발이야기

GraphQL In Action | GraphQL 작업 수정 및 구성

GraphQL 작업 수정 및 구성

서버로 부터 받아온 요청을 재분류하거나 재구성하는 일이 있다.
그래프QL은 기본 탑재된 기능을 사용해서 모든 것을 해결할 수 있다.

인수를 사용해서 필드 변경하기

그래프QL의 필드는 함수와 같다. 함수는 입력과 출력을 매핑하며, 입력은 여러개의 인수를 통해 받는다.

함수처럼 그래프QL필드에서도 여러 인수값을 전달할 수 있다. 백엔드 상의 그래프QL스키마는 이 인수값에 접근할 수 있으며 인수를 사용해서 응답을 변경할 수도 있다. 변경된 응답은 해당 인수를 지정한 필드로 반환한다.

단일 레코드를 요구하는 모든 API 요청은 해당 레코드용 식별자를 지정해야한다.
보통 식별자로는 데이터베이스에서 해당 레코드를 식별할 수 있는 고유 ID를 사용하는 것이 일반적이지만, ID외에도 레코드를 식별할 수 있는 고유한 값이라면 어떠한 것이든 괜찮다.

예를 들어 API에서 특정 사용자 정보를 요청한다면 보통 해당하는 사용자의 ID를 함께 요청해서 보낸다.

별칭을 사용해서 필드 이름 변경하기

그래프QL의 별칭 기능은 아주 간단하지만 유용하다. 요청 자체를 사용해 서버가 반환하는 응답을 변경할 수 있기 때문이다.

별칭을 사용하면 응답된 이후의 데이터 처리를 최소화할 수 있다.
별칭을 사용할 수 없다면 응답 객체를 사용할 때마다 응답 데이터를 변경해야할 수 있다.

응답 객체를 UI에 구조에 맞추서 변환하는 것을 별칭기능이라고 한다.
만약 응답 객체가 처리해야할 구조가 깊은 다단계 구조라면 변경 작업에 시간이 많이 소요된다.

그래프QL에서는 별칭기능을 통해 API 서버가 다른 이름으로 필드를 반환하도록 지시할 수 있다. 다음과 같이 원하는 필드의 별칭만 지정하면 된다.

별칭명: 필드명

단순히 필드에 별칭을 지정해두면, 서버가 해당 별칭을 사용한 새로운 필드 이름을 반환한다.

query ProfileInfo {
  user(login: "hyunjinee") {
    name
    companyName: company
    bio
  }
}

지시문을 사용한 응답 변경

경우에 따라선 단순히 응답의 필드명을 바꾼다고 문제가 해결되지 않을 수도 있다. 응답데이터 일부를 조건에 따라 포함하거나 제외해야할 수도 있다. 이때 유용한 것이 그래프QL 지시문이다. (directive)

그래프QL 요청에서 지시문은 그래프QL 서버에 추가 정보를 제공하기 위한 한가지 방법으로 실행지시나 타입 유효성 검사 등의 정보를 그래프QL 문서로 제공한다. 필드 인수와 유사하지만 더 강력한 기능을 제공한다고 생각할 수 있다.

지시문은 그래프QL 문서에 @로 시작하는 문자열을 사용해 작성한다.

  • @include
  • @skip
  • @deprecated

다음과 같이 내향성 쿼리를 통해서 특정 스키마가 지원하는 지시문 리스트를 확인할 수 있다.

query AllDirectives {
  __schema {
    directives {
      name
      description
      locations
      args {
        name
        description
        defaultValue
      }
    }
  }
}

이 쿼리는 각 지시문의 이름과 설명을 보여주며, 해당 지시문이 사용되는 모든 위치를 배열로 보여준다. 또한 해당 지시문이 사용할 수 있는 모든 인수 리스트도 볼 수 있다.

필수 인수처럼 어떤 인숫값은 API 서버가 필수로 요구한다. 모든 그래프QL 스키마에서 @include, @skip, 지시문은 if를 인수로 사용한다.
@deprecated 지시문은 reason이라는 인수를 필요로한다.

변수와 입력값

변수는 그래프QL 문서에서 $기호로 시작하며, $login, $showRepositories 형태이다. $기호 뒤에 지정하는 이름은 어떤 것이든 상관이 없다. 그래프QL 작업을 재사용하거나 값이나 문자를 하드코딩하는 것을 방지하기위해 변수를 사용한다.

변수는 필드나 지시문에도 사용해서 입력값을 받게 만들 수 있다. 입력값은 Int, Float, String, Boolean, Null 같은 스칼라값을 사용할 수 있으며 ENUM이나 리스트 객체등도 사용할 수 있다.

@include 지시문

@include 지시문은 필드 또는 조각 뒤에 조건(if 인수)을 지정해 사용할 수 있다. 이 조건은 해당 필드 또는 조각을 응답에 포함시킬지 정하기 위해 사용한다. @include는 다음과 같은 형식으로 사용한다.

fieldName @include(if: $someTest)

$someTest가 true인 상태로 쿼리를 실행하면 해당 필드를 포함하고 $someTest가 false인 상태면 해당 필드를 제외한다.

query OrgInfo($orgLogin: String!, $fullDetails: Boolean!) {
  oraganization(login: $orgLogin) {
    name
    description
    websiteUrl @include(if: $fullDetails)
  }
}

@skip 지시문

@include와 반대되는 개념이다. @include와 마찬가지로 필드 또는 조각 뒤에 if 인수와 함꼐 사용한다. 조건에 따라 필드ㅗ 또는 조각을 응답에서 제외시키는 것이다. @skip 사용방법은 다음과 같다.

fieldName @skip(if: $someTest)

$someTest가 true인 상태에서 쿼리를 실행하면 해당 필드를 응답에서 제외한다. 반대로 $someTest가 false이면 해당 필드를 포함한다.
이 지시문은 불린값을 반대로 해석해야할 때 유용하다. 이 지시문은 불린값을 반대로 해석해야할 때 유용하다.

예를 들어 불린 변수를 $fullDetails 대신에 $partialDetails라고 가정했다고 하자. JSON 객체에서 해당 불린값을 바꿀 필요없이, @skip지시문을 사용해 $partialDetails값을 바로 사용할 수 있다. @skip을 사용한 OrgInfo 쿼리는 다음과 같다.

query OrgInfo($orgLogin: String!, $partialDeatils: Boolean!) {
  organization(login: $orgLogin) {
    name
    description
    websiteUrl @skip(if: $partialDetails)
  }
}

@include나 @skip은 서로간에 우선순위가 정해져있지 않다. 같이사용한다면 @inlcude가 true이고 @skip이 false일 때만 해당 필드가 포함된다.

@deprecated 지시문

그래프QL 서버에선 특수한 지시문을 사용해서 그래프 QL 서비스의 폐지된 스키마를 사용자에게 알려줄 수 있다. 예를 들면 특정 타입의 필드나 ENUM 값 등이 폐지된 경우이다.

그래프 QL 스키마에서 필드를 폐지할 때는 @deprecated 지시문과 reason 인수를 함께 사용해서 폐지된 이유도 표시할 수 있다. 다음은 그래프 QL 스키마 언어를 이용해 타입을 정의한 예시로 폐지된 필드를 포함하고 있다.

type User {
  emailAddress: String
  eamil: String @deprecated(reason: 'use emailAddress instead')
}

그래프QL 조각

무엇이든 복잡한 것을 만들 때는 작은 부분으로 나누어 한번에 한 부분만 집중하는 것은 좋은 접근법이다.
작은 부분들은 가능하면 서로 의존하지 않게 독립적으로 설계해야 바람직하며, 각 부분별로 테스트 및 재사용이 가능해야한다.

규모가 큰 시스템은 이런 작은 부분들을 조합해 서로간 커뮤니케이션이 가능해야 한다. 예를 들어 UI 영역에선 리액트가 작은 부품들을 조합해서 큰 UI를 만드는 것으로 유명하다.

그래프QL에서 조각이란 언어의 조합 단위로 큰 그래프QL 작업을 작은 부분으로 나눌 수 있게 해준다.
간단히 말하자면 조각은 그래프QL을 작은 부품으로 나누어 재사용할 수 있게 만든것이다.

fragment는 그래프QL 작업의 컴포넌트와 같다.
큰 그래프QL 문서를 작은 단위로 나눌 때 조각이 유용하게 사용된다. 또한 조각은 그래프QL작업에서 필드 그룹의 중복을 방지하기 위해서도 사용된다.

그래프QL조각을 정의하려면 그래프QL 문서의 최상위 계층에서 fragment라는 키워드를 사용한다. 그리고 해당 조각의 이름과 조각이 사용될 타입을 지정한 후 해당 조각을 나타내는 부분 쿼리를 작성한다.

다음은 간단한 깃허브의 조직 정보 추출 쿼리이다.

query OrgInfo {
  organization(login:'jscomplete') {
    name
    description
    websiteUrl
  }
}

이 쿼리를 조각으로 만들려면 먼저 조각을 정의해야한다.

fragments orgFields on Organization {
  name
  description
  websiteUrl
}

orgFiels 조각을 정의하고 있으며 이 조각을 organization 필드를 확장한 선택 세트 내에서 사용할 수 있다.
onOrganization 부분을 조각의 타입 조건이라고 한다. 조각은 본질적으로 선택 세트이므로 객체 타입에만 정의할 수 있다.
다른 말로하면 조각을 스칼라값으로 정의할 수는 없다.

조각을 이해하기 위해서 쿼리 내에 필드가 있던 자리에 조각 이름을 전개해보도록 하자.

query OrgInfoWithFragment {
  organization(login:'jscomplete') {
    ...orgFields
  }
}

orgFields 처럼 3점 연산와 조각이름을 함께 사용하는 방법을 조각 전개라고 한다. 조각 전개는 조각의 타입이 해당 조각을 사용하고자 하는 객체의 타입과 일치할 때만 사용할 수 있다.

그래프QL 문서에서 조각을 정의했다면 해당 조각은 반드시 사용해야한다. 정의했지만 사용되지 않는 조각을 포함하고 있는 문서를 그래프QL 서버로 보낼 수 없다.

조각을 잘 사용하면 중복을 제거할 수 있다. (DRY)
조각을 잘 사용해서 얻을 수 있는 가장 큰 효과는 UI 컴포넌트 같은 조립 단위와 연동시킬 수 있다는 것이다.

컴포넌트라는 용어는 사람마다 생각하는 의미가 다르다. UI 영역에서 컴포넌트는 입력 텍스트 박스가 될 수도 있고 트위터의 140글자와 버튼, 카운터로 구성된 폼이 될 수도 있다. 애플리케이션에서 눈에 보이는 아무 부분이나 선택해서 컴포넌트라고 부를수도 있다. 컴포넌트는 작을 수도 있고 클수도 있으며, 자체적으로 기능을 가지거나 서로 조합해서 특정 기능을 구현할 수도 있다.

모든 HTML 요소는 속성과 동작을 지정할 수는 있지만, 동적 데이터를 사용할 수 없다는 점에서 제약을 가지고 있다.
UI 컴포넌트와 관련된 이야기는 컴포넌트가 데이터와 연계되기 시작할 때 흥미로워진다. 데이터 연계는 리액트나 앵귤러등의 최신 라이브러리를 사용해서 구현할 수 있다. 이런 데이터 컴포넌트는 데이터 형태만 동일하다면 어떤 데이터건 연계해서 재사용할 수 있다는 이점이 있다.

데이터 컴포넌트는 어떤 데이터가 오는지는 관심이 없다. 중요한 것은 데이터의 형태이다.

컴포넌트가 요구하는 정확한 데이터만 제공할 수 있다면 어데에 있든 컴포넌트가 동작할 것이다. 이런 특성을 잘 살려주는 것이 그래프QL이다. 그래프QL을 이용하면 애플리케이션이 요구하는 데이터 형태를 기술할 수 있다.

그래프QL 쿼리를 사용해서 애플리케이션의 데이터 요건을 정의할 수 있다. 애플리케이션이 필요로하는 데이터는 애플리케이션의 개별 컴포넌트가 필요로하는 모든 데이터를 조합한 것으로, 그래프QL 조각은 작은 단위의 쿼리를 조합해서 하나의 큰 쿼리를 만들 수 있게 해준다. 이것이 그래프QL 조각이 완벽하게 컴포넌트와 연계되는 이유이다. 조각을 자용해서 단일 컴포넌트의 데이터 요건을 표현하고 이 조각을 모아서 전체 애플리케이션용 데이터 요건을 만드는 것이다.

트위터 페이지에서 전체 페이지가 필요로 하는 데이터를 만들려만 다음과 같이 조각을 전개해서 하나로 합친 그래프QL 쿼리를 만들 수 있다.

query ProfilePageData {
  user(handle: "ManningBooks") {
    ...headerData
    ...sidebarData
    ...tweetListData
  }
}

fragment를 사용하는 가장 큰 이점은, 모든 컴포넌트가 필요한 데이터를 자율적으로 선언하게 하므로 컴포넌트별로 부모 컴포넌트에 의지하지 않고 필요한 경우 데이터 요건을 자유롭게 변경할 수 있다는 것이다.

조각은 UI 컴포넌트가 필요한 것을 요청해서 전체 애플리케이션을 구상하게 해준다. 모든 UI 컴포넌트를 그래프QL 조각과 연동시키므로 각 컴포넌트에게 강력한 독립성을 부여할 수 있다. 개별 컴포넌트가 자신만의 데이터 요건을 선언할 수 있고, 조각들을 결합해서 전체 애플리케이션이 필요로하는 데이터를 구성할 수 있는 것이다.

인터페이스와 유니온용 인라인 조각

인라인조각은 익명 함수와 비슷하다.
인라인 조각은 이름이 없는 조각으로 정의한 위치에 바로 인라인으로 전개할 수 있다.

인라인 조각은 인터페이스나 유니온을 요청할 때 타입 조건으로 사용할 수 있다.

인터페이스와 유니온은 그래프QL의 추상타입이다. 인터페이스는 공유 필드의 리스트를 정의하고 유니온은 사용할 수 있는 객체 타입의 리스트를 정의한다.
그래프QL 스키마의 객체 타입은 인터페이스를 구현할 수 있으며 객체 타입이 구현한 해당 인터페이스가 정의한 필드 리스트도 함께 구현된다.
객체 타입을 유니온으로 정의하면 객체가 반환하는 것이 해당 유니온중 하나라는 것을 보장한다.

유니온은 기본저긍로 OR 로직과 같다. 즉, 어떤 타입이 A또는 B가 될 수 있다는 뜻으로 실제로 어떤 유니온 타입은 XOrY라는 이름을 가지고 있다.

정리

  • 요청을 보낼 때 그리프QL 필드에 인수를 지정할 수 있다. 그래프QL 서버는 이 인수를 사용해서 단일 레코드를 찾거나 반환하는 레코드 수를 제한할 수 있다. 또한 레코드 순서를 지정하거나 페이지 매김을 할 수 있으며, 검색과 필터, 변경을 위한 입력값 제공등도 할 수 있다.
  • 모든 그래프QL 필드에 별칭을 지정할 수 있다. 이를 통해 클라이언트 요청 텍스트만으로도 서버 응답 구조를 변경할 수 있다.
  • 그래프QL 지시문을 사용하면 그래프QL 서버의 응답 구조를 애플리케이션이 선별적으로 변경할 수 있다.
  • 지시문과 필드 인수는 요청 변수와 같이 사용할 수 있다. 이 변수를 통해 문자열 결합에 의존하지 않고 그래프QL 요청을 동적값과 함께 재사용할 수 있다.
  • 그래프QL의 조립 단위인 조각을 사용해서 그래프QL 쿼리의 공통 부분만을 재사용할 수 있으며, 여러개의 조각을 합쳐서 전체 쿼리를 구성할 수 있다. 이것은 UI 컴포넌트와 데이터를 연동시킬 때 아주 유용한 접근법이다. 또한 인라인 조각을 사용하면 유니온 객체 타입이나 인터페이스를 구현한 객체 타입으로부터 정보를 선별적으로 취할 수 있다.