스키마 튜토리얼
스키마 튜토리얼레슨 5: 다양한 사용자에 맞게 콘텐츠 커스터마이징

레슨 5: 다양한 사용자에 맞게 콘텐츠 커스터마이징

쿼리된 데이터(예: 로그인한 사용자의 역할)에 따라 필드에서 다른 응답을 반환할 수 있습니다.

다양한 사용자에 맞게 콘텐츠를 커스터마이징하는 GraphQL 쿼리

다음 GraphQL 쿼리는 게시물 콘텐츠를 가져오며, 관리자 사용자에 한해 콘텐츠 하단에 "이 게시물 편집" 링크를 추가합니다.

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  isAdminUser: _echo(value: false)
    @export(as: "isAdminUser")
    @remove
}
 
query ExportConditionalVariables
  @depends(on: "InitializeDynamicVariables")
{
  me {
    roleNames @remove
    isAdminUser: _inArray(
      value: "administrator",
      array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}
 
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content @remove
    wpAdminEditURL @remove
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}
 
query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id @remove
}

관리자 사용자의 경우 응답은 다음과 같습니다.

{
  "data": {
    "user": {
      "isAdminUser": true
    },
    "post": {
      "content": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n<p><a href=\"https:\/\/mysite.com\/wp-admin\/post.php?post=1&amp;action=edit\">(Admin only) Edit post<\/a><\/p>"
    }
  }
}

관리자가 아닌 사용자의 경우 응답은 다음과 같습니다.

{
  "data": {
    "user": {
      "isAdminUser": false
    },
    "post": {
      "content": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n"
    }
  }
}

GraphQL 서버가 (가능한 모든 조건을 고려하여) 필드에 필요한 값을 동적으로 계산함으로써 다음과 같은 이점을 얻을 수 있습니다.

  • 애플리케이션의 로직이 단순해집니다. 단일 신뢰 소스가 확립되고, 코드가 DRY해지며, 클라이언트 측에서 동일한 로직을 구현할 필요가 없어집니다.
  • 애플리케이션의 신뢰성이 향상됩니다. 여러 클라이언트가 서버 데이터에 접근할 때, 동일한 로직을 서로 다른 구현으로 처리하면 불일치가 발생할 수 있으며 버그의 원인이 됩니다(특히 웹사이트용 JavaScript, Android 앱용 Java, iPhone 앱용 Swift 등 서로 다른 기술 기반의 클라이언트가 혼재하는 경우 더욱 두드러집니다).

단계별 안내: GraphQL 쿼리 작성하기

아래는 이 쿼리가 어떻게 동작하는지에 대한 상세한 분석입니다.

사용자가 관리자인지 확인하기

이 쿼리는 로그인한 사용자가 "administrator" 역할을 가지고 있는지 확인하고, 그 조건을 다이나믹 변수 $isAdminUser로 내보냅니다.

query
{
  me {
    roleNames
    isAdminUser: _inArray(
        value: "administrator",
        array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}

오퍼레이션의 조건부 실행

Multiple Query Execution 이 활성화되면, 디렉티브 @include@skip을 오퍼레이션에도 적용할 수 있습니다. 이를 통해 다이나믹 변수의 값에 따라 오퍼레이션 실행 여부를 제어할 수 있습니다.

아래 쿼리에서는 두 오퍼레이션 중 하나만 실행됩니다.

  • RetrieveContentForAdminUser$isAdminUsertrue일 때만 실행됩니다.
  • RetrieveContentForNonAdminUser$isAdminUserfalse일 때만 실행됩니다.
query RetrieveContentForAdminUser
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  # ...
}
 
query RetrieveContentForNonAdminUser
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  # ...
}

사용자가 관리자인지 여부에 따라 게시물의 content 필드에 대해 서로 다른 응답을 제공해 보겠습니다.

  • 첫 번째 오퍼레이션은 content를 별칭으로 사용하며, _sprintf를 통해 originalContent 필드와 wpAdminEditURL 필드를 연결하여 필드의 값을 동적으로 계산합니다.
  • 두 번째 오퍼레이션은 content 필드를 그대로 가져옵니다.
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content
    wpAdminEditURL
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}

실행할 오퍼레이션 추가하기

이제 실행될 수 있는 두 개의 오퍼레이션이 생겼지만, 쿼리 실행 시 지정할 수 있는 ?operationName=...은 하나뿐입니다.

그래서 RetrieveContentForAdminUserRetrieveContentForNonAdminUser 양쪽에 의존하는 오퍼레이션 ExecuteAll을 추가합니다. 오퍼레이션 내에서 무언가를 쿼리해야 하므로, 간단한 필드 id를 포함합니다.

query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id
}

?operationName=ExecuteAll로 엔드포인트를 호출하면 두 오퍼레이션 모두 로드되지만, 실제로 실행되는 것은 그 중 하나뿐입니다.

불필요한 데이터 제거하기

마지막 단계는 보조적인 필드(응답에 출력할 필요가 없는 필드)를 모두 @remove로 제거하는 것입니다.

통합된 GraphQL 쿼리는 다음과 같습니다.

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  isAdminUser: _echo(value: false)
    @export(as: "isAdminUser")
    @remove
}
 
query ExportConditionalVariables
  @depends(on: "InitializeDynamicVariables")
{
  me {
    roleNames @remove
    isAdminUser: _inArray(
        value: "administrator",
        array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}
 
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content @remove
    wpAdminEditURL @remove
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}
 
query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id @remove
}