개념, 아이디어, 전략
개념, 아이디어, 전략필드와 디렉티브 버전 관리 전략

필드와 디렉티브 버전 관리 전략

먼저 Gato GraphQL의 「필드 버전 관리」 기능을 설명하는 가이드 필드 버전 관리를 통한 스키마 발전을 읽어 주시기 바랍니다.

Gato GraphQL에서는 필드와 디렉티브가 인수 versionConstraint를 받아, 사용할 필드/디렉티브의 특정 버전(구현)을 선택할 수 있습니다:

query GetPosts {
  posts(versionConstraint: "^1.0") {
    id
    title(versionConstraint: ">=2.1")
    excerpt @strUpperCase(versionConstraint: "~1.5.3")
  }
}

versionConstraint 인수를 지정하지 않으면 어떻게 될까요? 예를 들어, 아래 쿼리에서 필드 surname은 어느 버전으로 해석되어야 할까요?

query GetSurname {
  account(id: 1) {
    # 어느 버전을 사용해야 할까요? 1.0.0? 2.0.0?
    surname
  }
}

여기서는 두 가지 과제가 있습니다:

  1. 버전이 지정되지 않았을 때 사용할 기본 버전 결정
  2. 클라이언트에게 여러 버전 중 선택할 수 있음을 알리기

이 과제들을 다루기 전에, GraphQL이 쿼리 실행 시 얼마나 잘 컨텍스트 정보를 제공하는지 확인할 필요가 있습니다.

쿼리 실행 시 컨텍스트 정보 제공

현재 GraphQL에는 이상적이지 않은 부분이 있습니다: 쿼리 실행 시 충분한 컨텍스트 정보를 제공하지 않습니다. 이는 사용 중단(deprecation)에 관해 명확히 드러나는데, 사용 중단 데이터는 Field 타입과 Enum 타입의 isDeprecated 필드와 deprecationReason 필드를 쿼리하는 인트로스펙션을 통해서만 표시됩니다:

{
  __type(name: "Account") {
    name
    fields {
      name
      isDeprecated
      deprecationReason
    }
  }
}

응답은 다음과 같습니다:

{
  "data": {
    "__type": {
      "name": "Account",
      "fields": [
        {
          "name": "id",
          "isDeprecated": false,
          "deprecationReason": null
        },
        {
          "name": "name",
          "isDeprecated": false,
          "deprecationReason": null
        },
        {
          "name": "surname",
          "isDeprecated": true,
          "deprecationReason": "Use `personSurname`"
        },
        {
          "name": "personSurname",
          "isDeprecated": false,
          "deprecationReason": null
        }
      ]
    }
  }
}

그러나 사용 중단된 필드를 포함한 쿼리를 실행하면…

query GetSurname {
  account(id: 1) {
    surname
  }
}

…사용 중단 정보는 응답에 나타나지 않습니다:

{
  "data": {
    "account": {
      "surname": "Owens"
    }
  }
}

이는 쿼리를 실행하는 개발자가 스키마가 업그레이드되었는지, 또는 어떤 필드가 사용 중단되었는지 확인하기 위해 적극적으로 인트로스펙션 쿼리를 실행해야 한다는 것을 의미합니다. 그런 일이 일어나는 것은… 가끔? 아마 거의 없을 수도 있습니다.

사용 중단된 필드를 포함하는 쿼리를 실행할 때 GraphQL API가 사용 중단 정보를 제공한다면, 오래된 쿼리를 수정하는 데 큰 개선이 될 것입니다. 이 정보는 이상적으로는 새 최상위 항목 deprecations 아래에 표시되어, errors 뒤, data 앞에 위치해야 합니다(사양의 응답 형식 제안에 따라).

deprecations 최상위 항목은 사양의 일부가 아니기 때문에, Gato GraphQL의 「Proactive Feedback」 기능은 프로토콜을 필요에 따라 확장할 수 있는 와일드카드 최상위 항목 extensions를 사용하여 쿼리 응답에서 더 나은 피드백을 지원합니다:

쿼리 응답의 사용 중단 정보

warnings를 통한 버전 공지

GraphQL 서버가 최상위 항목 extensions를 사용하여 사용 중단 정보를 제공할 수 있다는 것을 알게 되었습니다. 동일한 방법론을 사용하여 warnings 항목을 추가하고, 필드가 버전 관리되고 있음을 개발자에게 알릴 수 있습니다. 이 정보는 항상 제공하는 것이 아니라, 쿼리가 버전 관리된 필드를 포함하고 versionConstraint 인수가 없는 경우에만 제공합니다.

필드의 기본 버전 정의

사용할 수 있는 접근 방식은 여러 가지가 있습니다:

  1. versionConstraint를 필수로 만들기
  2. 특정 날짜까지 이전 버전을 기본으로 사용하고, 그 날짜부터 새 버전이 기본값이 되도록 하기
  3. 최신 버전을 기본으로 사용하고, 쿼리 개발자가 사용할 버전을 명시적으로 지정하도록 권장하기

각 전략을 살펴보고, 다음 쿼리를 실행했을 때의 응답을 확인해 보겠습니다:

query GetSurname {
  account(id: 1) {
    surname
  }
}

1. versionConstraint를 필수로 만들기

가장 명확한 접근 방식입니다: 필드 인수를 필수로 만들어 클라이언트가 버전 제약을 지정하지 않는 것을 금지합니다. 지정되지 않으면 쿼리는 오류를 반환합니다.

쿼리를 실행하면 다음 응답이 반환됩니다:

{
  "errors": [
    {
      "message": "Argument 'versionConstraint' in field 'surname' cannot be empty"
    }
  ],
  "data": {
    "account": {
      "surname": null
    }
  }
}

2. 특정 날짜까지 이전 버전을 기본으로 사용하고, 그 날짜부터 새 버전이 기본값이 되도록 하기

특정 날짜까지 이전 버전을 계속 사용하고, 그 날짜부터 새 버전이 기본값이 됩니다. 이 전환 기간 동안 쿼리의 새 extensions.warnings 항목을 통해, 해당 날짜 이전에 이전 버전에 버전 제약을 명시적으로 추가하도록 쿼리 개발자에게 요청합니다.

쿼리를 실행하면 다음과 같은 응답이 반환될 수 있습니다:

{
  "extensions": {
    "warnings": [
      {
        "message": "Field 'surname' has a new version: '2.0.0'. This version will become the default one on January 1st. We advise you to use this new version already and test that it works fine; if you find any problem, please report the issue in https://github.com/mycompany/myproject/issues. To do the switch, please add the 'versionConstraint' field argument to your query (using Composer's semver constraint rules; see https://getcomposer.org/doc/articles/versions.md#writing-version-constraints): surname(versionConstraint:\"^2.0\"). If you are unable to switch to the new version, please make sure to explicitly point to the current version '1.0.0' before January 1st: surname(versionConstraint:\"^1.0\"). In case of doubt, please contact us at name@company.com.",
    ]
  },
  "data": {
    "account": {
      "surname": "Owens"
    }
  }
}

3. 최신 버전을 사용하고, 사용자가 사용할 버전을 명시적으로 지정하도록 권장하기

versionConstraint가 설정되지 않은 경우 최신 버전의 필드를 사용하고, 새 extensions.warnings 항목을 통해 해당 필드에서 사용 가능한 모든 버전 목록을 표시하면서 쿼리 개발자가 사용할 버전을 명시적으로 정의하도록 권장합니다:

쿼리를 실행하면 다음과 같은 응답이 반환될 수 있습니다:

{
  "extensions": {
    "warnings": [
      {
        "message": "Field 'surname' has more than 1 version. Please add the 'versionConstraint' field argument to your query to indicate which version to use (using Composer's semver constraint rules; see https://getcomposer.org/doc/articles/versions.md#writing-version-constraints). To use the latest version, use: surname(versionConstraint:\"^2.0\"). Available versions: '2.0.0', '1.0.0'.",
    ]
  },
  "data": {
    "account": {
      "surname": "Owens"
    }
  }
}

디렉티브 버전 관리

동일한 전략을 디렉티브 버전 관리에도 사용할 수 있습니다. 예를 들어, 버전 제약을 지정하지 않고 쿼리를 실행하는 경우:

query {
  post(by: { id: 1 }) {
    title @strTitleCase
  }
}

기본 버전을 가정하여 사용하고, 개발자가 쿼리를 수정할 수 있도록 경고 메시지를 생성할 수 있습니다:

버전 제약 없이 버전 관리된 디렉티브 쿼리하기