InfoGrab Docs

GraphQL 구현 가이드

요약

손상된 Personal Access Token(PAT)의 보안 영향을 줄이기 위해, 세분화된(granular) PAT는 사용자가 특정 조직 경계(그룹, 프로젝트, 사용자 또는 인스턴스 수준)에 한정된 세밀한 권한을 가진 토큰을 생성할 수 있도록 합니다.

손상된 Personal Access Token(PAT)의 보안 영향을 줄이기 위해, 세분화된(granular) PAT는 사용자가 특정 조직 경계(그룹, 프로젝트, 사용자 또는 인스턴스 수준)에 한정된 세밀한 권한을 가진 토큰을 생성할 수 있도록 합니다. 이를 통해 사용자는 토큰에 필요한 권한만 부여하는 최소 권한 원칙을 따를 수 있습니다.

Granular PAT는 경계와 특정 리소스 권한으로 구성된 세분화된 스코프를 통해 세밀한 접근 제어를 허용합니다. Granular PAT로 GraphQL 요청을 인증할 때, GitLab은 해당 토큰의 권한에 지정된 경계 수준에서 요청된 리소스에 대한 접근이 포함되어 있는지 검증합니다.

이 문서는 GraphQL 쿼리 및 뮤테이션을 granular PAT 인증과 호환되게 만들려는 커뮤니티 기여자 및 GitLab 개발자를 위해 설계되었습니다.

단계별 구현 가이드#

이 가이드는 GraphQL 유형 및 뮤테이션에 granular PAT 인증을 추가하는 방법을 안내합니다. 시작하기 전에 전반에서 사용되는 용어를 이해하기 위해 권한 명명 규칙 문서를 검토하세요.

Note

이 단계는 GraphQL 유형 및 뮤테이션만 다룹니다. REST API 엔드포인트 보호의 경우 REST API 구현 가이드를 참조하세요.

인증 시스템이 내부적으로 어떻게 작동하는지에 대한 자세한 설명은 GraphQL 아키텍처 문서를 참조하세요.

워크플로우 개요#

구현은 다음 흐름을 따릅니다:

  1. 1-2단계: 계획 - 유형/뮤테이션 식별 및 권한 설계
  2. 3단계: 원시 권한 만들기 (YAML 파일)
  3. 4단계: 원시 권한을 할당 가능한 권한으로 묶기 (YAML 파일)
  4. 5단계: 유형/뮤테이션에 인증 지시자 추가 (Ruby 코드)
  5. 6단계: 인증 테스트 작성 (Ruby 스펙)
  6. 7단계: 로컬 테스트 (수동 유효성 검사)

1단계: 보호할 GraphQL 유형 및 뮤테이션 식별#

목표: 작업 중인 리소스에 대한 모든 GraphQL 유형 및 뮤테이션을 찾습니다.

  1. app/graphql/types/에서 리소스에 대한 GraphQL 유형을 찾습니다.

    예시: 이슈 리소스의 경우 app/graphql/types/issue_type.rb를 엽니다.

  2. app/graphql/mutations/에서 관련 뮤테이션을 찾습니다.

    예시: 이슈의 경우 app/graphql/mutations/issues/를 확인합니다.

  3. 인증이 필요한 유형 및 뮤테이션을 식별합니다:

    • 사용자가 접근하는 리소스를 나타내는 객체 유형 (예: IssueType, ProjectType)
    • 리소스를 만들고, 업데이트하거나 삭제하는 뮤테이션 (예: Mutations::Issues::Create)
    • 리소스를 직접 반환하는 쿼리 필드 (예: QueryTypefield :project)
  4. 유형 또는 뮤테이션에 이미 authorize_granular_token 지시자가 있는지 확인합니다. 지시자가 없는 유형/뮤테이션에 지시자를 추가해야 합니다.

2단계: 필요한 권한 결정#

목표: GitLab 명명 규칙에 따라 세분화된 권한을 정의합니다.

명명 규칙에 대해서는 규칙 문서의 권한 명명을 참조하세요.

유형 및 뮤테이션에 대한 권한 이름 결정#

Granular PAT 인증을 구현할 때 GraphQL 스키마 구조가 아닌 유형이 나타내는 것 또는 뮤테이션이 수행하는 것을 기반으로 권한 이름을 지정합니다.

예시:

  • 유형 IssueType → 이슈 읽기를 나타냄 → 권한 이름은 read_issue
  • 뮤테이션 Mutations::Issues::Create → 이슈 생성 → 권한 이름은 create_issue
  • 유형 ProjectType → 프로젝트 데이터 읽기를 나타냄 → 권한 이름은 read_project

일반적인 패턴#

  • 객체 유형: 유형의 모든 필드를 포함하는 read_resource 권한 사용
    • IssueTyperead_issue
    • ProjectTyperead_project
  • 생성 뮤테이션: create_resource 사용
    • Mutations::Issues::Createcreate_issue
  • 업데이트 뮤테이션: update_resource 사용
    • Mutations::Issues::Updateupdate_issue
  • 삭제 뮤테이션: delete_resource 사용
    • Mutations::Issues::Destroydelete_issue
  • 특수 작업 뮤테이션: 고유한 작업에 대한 특정 권한 만들기
    • 이동, 보관, 전환 등 각각 자체 권한을 가짐

3단계: 권한 정의 파일 만들기#

목표: 아직 존재하지 않는 경우 각 권한에 대한 YAML 정의 파일을 만듭니다.

bin/permission 명령을 사용하여 원시 권한 YAML 파일을 만들려면 권한 정의 파일 섹션의 지침을 따릅니다. 이 단계는 REST API 및 GraphQL 구현 모두에서 동일합니다.

4단계: 할당 가능한 권한 만들기#

목표: 더 간단한 사용자 경험을 위해 원시 권한을 할당 가능한 권한으로 묶습니다.

할당 가능한 권한 YAML 파일을 만들려면 할당 가능한 권한 섹션의 지침을 따릅니다. 이 단계는 REST API 및 GraphQL 구현 모두에서 동일합니다.

5단계: 유형 및 뮤테이션에 인증 지시자 추가#

목표: GraphQL 유형 및 뮤테이션에 granular PAT 인증 지시자를 추가합니다.

authorize_granular_token 메서드를 사용하여 유형 및 뮤테이션에 권한을 선언합니다. 이 메서드는 모든 GraphQL 유형(Types::BaseObject를 통해)과 뮤테이션(Mutations::BaseMutation을 통해)에서 사용할 수 있습니다.

메서드 서명:

authorize_granular_token(permissions:, boundary_type:, boundary: nil, boundary_argument: nil)

매개변수:

매개변수 설명
permissions (필수) 필요한 권한을 나타내는 심볼 (예: :read_issue). 권한 배열도 가능. Authz::PermissionGroups::Assignable.all_permissions의 유효한 권한이어야 함 — gitlab:permissions:validate Rake 작업에 의해 유효성 검사됨.
boundary_type (필수) 인증 경계 유형을 선언하는 심볼 (:project, :group, :user, :instance). gitlab:permissions:validate Rake 작업에 의해 할당 가능한 권한 경계에 대해 유효성 검사됨.
boundary 경계를 추출하기 위해 해석된 객체에서 호출할 메서드를 나타내는 심볼 (예: :project). 독립형 리소스의 경우 :user 또는 :instance 사용.
boundary_argument 경계 경로를 포함하는 인수 이름을 나타내는 심볼 (예: :project_path).

객체 유형의 경우:

class IssueType < BaseObject
  authorize_granular_token permissions: :read_issue, boundary: :project, boundary_type: :project
end

뮤테이션의 경우:

module Mutations
  module Issues
    class Create < BaseMutation
      authorize_granular_token permissions: :create_issue, boundary_argument: :project_path, boundary_type: :project
    end
  end
end

boundary가 적용되는 경우#

  • 유형의 필드 (예: IssueType에 지시자가 있을 때 issue.title)
  • :id 인수와 함께 유형을 반환하는 쿼리 필드 (ID 폴백 활성화)
  • boundary: :user 또는 boundary: :instance를 사용하는 독립형 리소스
  • :id 인수 없이 유형을 반환하는 쿼리 필드에는 적용되지 않음 (객체를 사용할 수 없음, ArgumentError 발생)
boundary_argument가 적용되는 경우#
  • 루트 뮤테이션
  • 루트 쿼리 필드
  • 경계를 인수로 받는 모든 필드
  • boundary_argument 지시자가 있는 유형을 반환하는 필드
독립형 경계#

특정 프로젝트나 그룹에 속하지 않는 리소스에 boundary: :user 또는 boundary: :instance를 사용합니다:

class UserSettingType < BaseObject
  authorize_granular_token permissions: :read_user_settings, boundary: :user, boundary_type: :user
end

boundaryboundary_argument 중 선택#

boundary 사용 시... boundary_argument 사용 시...
유형에 경계를 가져오는 메서드가 있을 때 (예: issue.project) 경계가 필드 인수로 전달될 때 (예: projectPath)
객체 유형의 필드 보호 시 뮤테이션 보호 시
:id 인수가 있는 쿼리 필드 보호 시 경로 인수가 있는 쿼리 필드 보호 시

6단계: 인증 테스트 추가#

목표: GraphQL 유형 및 뮤테이션에 granular PAT 권한이 올바르게 적용되는지 확인합니다.

쿼리의 경우#

'authorizing granular token permissions for GraphQL' 공유 예제를 추가합니다:

it_behaves_like 'authorizing granular token permissions for GraphQL', :<permission_name> do
  let(:user) { current_user }
  let(:boundary_object) { <boundary_object> }
  let(:request) { post_graphql(query, token: { personal_access_token: pat }) }
end

예시:

it_behaves_like 'authorizing granular token permissions for GraphQL', :read_issue do
  let(:user) { current_user }
  let(:boundary_object) { project }
  let(:request) { post_graphql(query, token: { personal_access_token: pat }) }
end

뮤테이션의 경우#

it_behaves_like 'authorizing granular token permissions for GraphQL', :<permission_name> do
  let(:user) { current_user }
  let(:boundary_object) { <boundary_object> }
  let(:request) { post_graphql_mutation(mutation, token: { personal_access_token: pat }) }
end

경계 객체 매핑#

boundary_objectboundary_type과 일치해야 합니다:

경계 유형 경계 객체
:project project
:group group
:user :user
:instance :instance

중요: 경계 객체가 :project 또는 :group인 경우, 인증이 부여되려면 user가 해당 네임스페이스(프로젝트 또는 그룹)의 멤버여야 합니다.

이 테스트가 검증하는 것:

  • 레거시(비세분화) personal access token이 계속 접근 권한을 부여함
  • Granular PAT에서 필요한 권한이 부여된 사용자가 접근을 허용받음
  • 필요한 권한이 없는 사용자가 적절한 오류 메시지와 함께 접근을 거부당함
  • 인증 시스템이 유형/뮤테이션의 권한 요구 사항에 대해 세분화된 스코프를 올바르게 평가함
  • 기능 플래그 granular_personal_access_tokens가 올바르게 적용됨 (비활성화 시 접근 거부)

7단계: 수동 유효성 검사#

목표: 머지 리퀘스트를 만들기 전에 로컬 환경에서 구현을 수동으로 테스트하여 권한이 예상대로 작동하는지 확인합니다.

설정:

Rails 콘솔에서 사용자를 위한 granular PAT를 만듭니다:

# 기능 플래그 활성화
Feature.enable(:granular_personal_access_tokens)

user = User.human.first

# Granular 토큰 만들기
token = PersonalAccessTokens::CreateService.new(
  current_user: user,
  target_user: user,
  organization_id: user.organization_id,
  params: { expires_at: 1.month.from_now, scopes: ['granular'], granular: true, name: 'gPAT' }
).execute[:personal_access_token]

# 적절한 경계 객체 가져오기 (project, group, :user, or :instance)
project = user.projects.first
boundary = Authz::Boundary.for(project)

# 테스트 중인 할당 가능한 권한으로 스코프 만들기
scope = Authz::GranularScope.new(namespace: boundary.namespace, access: boundary.access, permissions: [:read_work_item, :write_work_item])

# 토큰에 스코프 추가
Authz::GranularScopeService.new(token).add_granular_scopes(scope)

# GraphQL 쿼리 테스트용 curl 명령 복사
query = '{ project(fullPath: \"' + project.full_path + '\") { issues { nodes { title } } } }'
IO.popen('pbcopy', 'w') { |f| f.puts "curl \"http://#{Gitlab.host_with_port}/api/graphql\" --request POST --header \"PRIVATE-TOKEN: #{token.token}\" --header \"Content-Type: application/json\" --data '{\"query\": \"#{query}\"}'" }
  1. 다른 터미널에 명령을 붙여넣습니다. 성공해야 합니다.

참조#

GraphQL 구현 가이드

원문 보기
요약

손상된 Personal Access Token(PAT)의 보안 영향을 줄이기 위해, 세분화된(granular) PAT는 사용자가 특정 조직 경계(그룹, 프로젝트, 사용자 또는 인스턴스 수준)에 한정된 세밀한 권한을 가진 토큰을 생성할 수 있도록 합니다.

손상된 Personal Access Token(PAT)의 보안 영향을 줄이기 위해, 세분화된(granular) PAT는 사용자가 특정 조직 경계(그룹, 프로젝트, 사용자 또는 인스턴스 수준)에 한정된 세밀한 권한을 가진 토큰을 생성할 수 있도록 합니다. 이를 통해 사용자는 토큰에 필요한 권한만 부여하는 최소 권한 원칙을 따를 수 있습니다.

Granular PAT는 경계와 특정 리소스 권한으로 구성된 세분화된 스코프를 통해 세밀한 접근 제어를 허용합니다. Granular PAT로 GraphQL 요청을 인증할 때, GitLab은 해당 토큰의 권한에 지정된 경계 수준에서 요청된 리소스에 대한 접근이 포함되어 있는지 검증합니다.

이 문서는 GraphQL 쿼리 및 뮤테이션을 granular PAT 인증과 호환되게 만들려는 커뮤니티 기여자 및 GitLab 개발자를 위해 설계되었습니다.

단계별 구현 가이드#

이 가이드는 GraphQL 유형 및 뮤테이션에 granular PAT 인증을 추가하는 방법을 안내합니다. 시작하기 전에 전반에서 사용되는 용어를 이해하기 위해 권한 명명 규칙 문서를 검토하세요.

Note

이 단계는 GraphQL 유형 및 뮤테이션만 다룹니다. REST API 엔드포인트 보호의 경우 REST API 구현 가이드를 참조하세요.

인증 시스템이 내부적으로 어떻게 작동하는지에 대한 자세한 설명은 GraphQL 아키텍처 문서를 참조하세요.

워크플로우 개요#

구현은 다음 흐름을 따릅니다:

  1. 1-2단계: 계획 - 유형/뮤테이션 식별 및 권한 설계
  2. 3단계: 원시 권한 만들기 (YAML 파일)
  3. 4단계: 원시 권한을 할당 가능한 권한으로 묶기 (YAML 파일)
  4. 5단계: 유형/뮤테이션에 인증 지시자 추가 (Ruby 코드)
  5. 6단계: 인증 테스트 작성 (Ruby 스펙)
  6. 7단계: 로컬 테스트 (수동 유효성 검사)

1단계: 보호할 GraphQL 유형 및 뮤테이션 식별#

목표: 작업 중인 리소스에 대한 모든 GraphQL 유형 및 뮤테이션을 찾습니다.

  1. app/graphql/types/에서 리소스에 대한 GraphQL 유형을 찾습니다.

    예시: 이슈 리소스의 경우 app/graphql/types/issue_type.rb를 엽니다.

  2. app/graphql/mutations/에서 관련 뮤테이션을 찾습니다.

    예시: 이슈의 경우 app/graphql/mutations/issues/를 확인합니다.

  3. 인증이 필요한 유형 및 뮤테이션을 식별합니다:

    • 사용자가 접근하는 리소스를 나타내는 객체 유형 (예: IssueType, ProjectType)
    • 리소스를 만들고, 업데이트하거나 삭제하는 뮤테이션 (예: Mutations::Issues::Create)
    • 리소스를 직접 반환하는 쿼리 필드 (예: QueryTypefield :project)
  4. 유형 또는 뮤테이션에 이미 authorize_granular_token 지시자가 있는지 확인합니다. 지시자가 없는 유형/뮤테이션에 지시자를 추가해야 합니다.

2단계: 필요한 권한 결정#

목표: GitLab 명명 규칙에 따라 세분화된 권한을 정의합니다.

명명 규칙에 대해서는 규칙 문서의 권한 명명을 참조하세요.

유형 및 뮤테이션에 대한 권한 이름 결정#

Granular PAT 인증을 구현할 때 GraphQL 스키마 구조가 아닌 유형이 나타내는 것 또는 뮤테이션이 수행하는 것을 기반으로 권한 이름을 지정합니다.

예시:

  • 유형 IssueType → 이슈 읽기를 나타냄 → 권한 이름은 read_issue
  • 뮤테이션 Mutations::Issues::Create → 이슈 생성 → 권한 이름은 create_issue
  • 유형 ProjectType → 프로젝트 데이터 읽기를 나타냄 → 권한 이름은 read_project

일반적인 패턴#

  • 객체 유형: 유형의 모든 필드를 포함하는 read_resource 권한 사용
    • IssueTyperead_issue
    • ProjectTyperead_project
  • 생성 뮤테이션: create_resource 사용
    • Mutations::Issues::Createcreate_issue
  • 업데이트 뮤테이션: update_resource 사용
    • Mutations::Issues::Updateupdate_issue
  • 삭제 뮤테이션: delete_resource 사용
    • Mutations::Issues::Destroydelete_issue
  • 특수 작업 뮤테이션: 고유한 작업에 대한 특정 권한 만들기
    • 이동, 보관, 전환 등 각각 자체 권한을 가짐

3단계: 권한 정의 파일 만들기#

목표: 아직 존재하지 않는 경우 각 권한에 대한 YAML 정의 파일을 만듭니다.

bin/permission 명령을 사용하여 원시 권한 YAML 파일을 만들려면 권한 정의 파일 섹션의 지침을 따릅니다. 이 단계는 REST API 및 GraphQL 구현 모두에서 동일합니다.

4단계: 할당 가능한 권한 만들기#

목표: 더 간단한 사용자 경험을 위해 원시 권한을 할당 가능한 권한으로 묶습니다.

할당 가능한 권한 YAML 파일을 만들려면 할당 가능한 권한 섹션의 지침을 따릅니다. 이 단계는 REST API 및 GraphQL 구현 모두에서 동일합니다.

5단계: 유형 및 뮤테이션에 인증 지시자 추가#

목표: GraphQL 유형 및 뮤테이션에 granular PAT 인증 지시자를 추가합니다.

authorize_granular_token 메서드를 사용하여 유형 및 뮤테이션에 권한을 선언합니다. 이 메서드는 모든 GraphQL 유형(Types::BaseObject를 통해)과 뮤테이션(Mutations::BaseMutation을 통해)에서 사용할 수 있습니다.

메서드 서명:

authorize_granular_token(permissions:, boundary_type:, boundary: nil, boundary_argument: nil)

매개변수:

매개변수 설명
permissions (필수) 필요한 권한을 나타내는 심볼 (예: :read_issue). 권한 배열도 가능. Authz::PermissionGroups::Assignable.all_permissions의 유효한 권한이어야 함 — gitlab:permissions:validate Rake 작업에 의해 유효성 검사됨.
boundary_type (필수) 인증 경계 유형을 선언하는 심볼 (:project, :group, :user, :instance). gitlab:permissions:validate Rake 작업에 의해 할당 가능한 권한 경계에 대해 유효성 검사됨.
boundary 경계를 추출하기 위해 해석된 객체에서 호출할 메서드를 나타내는 심볼 (예: :project). 독립형 리소스의 경우 :user 또는 :instance 사용.
boundary_argument 경계 경로를 포함하는 인수 이름을 나타내는 심볼 (예: :project_path).

객체 유형의 경우:

class IssueType < BaseObject
  authorize_granular_token permissions: :read_issue, boundary: :project, boundary_type: :project
end

뮤테이션의 경우:

module Mutations
  module Issues
    class Create < BaseMutation
      authorize_granular_token permissions: :create_issue, boundary_argument: :project_path, boundary_type: :project
    end
  end
end

boundary가 적용되는 경우#

  • 유형의 필드 (예: IssueType에 지시자가 있을 때 issue.title)
  • :id 인수와 함께 유형을 반환하는 쿼리 필드 (ID 폴백 활성화)
  • boundary: :user 또는 boundary: :instance를 사용하는 독립형 리소스
  • :id 인수 없이 유형을 반환하는 쿼리 필드에는 적용되지 않음 (객체를 사용할 수 없음, ArgumentError 발생)
boundary_argument가 적용되는 경우#
  • 루트 뮤테이션
  • 루트 쿼리 필드
  • 경계를 인수로 받는 모든 필드
  • boundary_argument 지시자가 있는 유형을 반환하는 필드
독립형 경계#

특정 프로젝트나 그룹에 속하지 않는 리소스에 boundary: :user 또는 boundary: :instance를 사용합니다:

class UserSettingType < BaseObject
  authorize_granular_token permissions: :read_user_settings, boundary: :user, boundary_type: :user
end

boundaryboundary_argument 중 선택#

boundary 사용 시... boundary_argument 사용 시...
유형에 경계를 가져오는 메서드가 있을 때 (예: issue.project) 경계가 필드 인수로 전달될 때 (예: projectPath)
객체 유형의 필드 보호 시 뮤테이션 보호 시
:id 인수가 있는 쿼리 필드 보호 시 경로 인수가 있는 쿼리 필드 보호 시

6단계: 인증 테스트 추가#

목표: GraphQL 유형 및 뮤테이션에 granular PAT 권한이 올바르게 적용되는지 확인합니다.

쿼리의 경우#

'authorizing granular token permissions for GraphQL' 공유 예제를 추가합니다:

it_behaves_like 'authorizing granular token permissions for GraphQL', :<permission_name> do
  let(:user) { current_user }
  let(:boundary_object) { <boundary_object> }
  let(:request) { post_graphql(query, token: { personal_access_token: pat }) }
end

예시:

it_behaves_like 'authorizing granular token permissions for GraphQL', :read_issue do
  let(:user) { current_user }
  let(:boundary_object) { project }
  let(:request) { post_graphql(query, token: { personal_access_token: pat }) }
end

뮤테이션의 경우#

it_behaves_like 'authorizing granular token permissions for GraphQL', :<permission_name> do
  let(:user) { current_user }
  let(:boundary_object) { <boundary_object> }
  let(:request) { post_graphql_mutation(mutation, token: { personal_access_token: pat }) }
end

경계 객체 매핑#

boundary_objectboundary_type과 일치해야 합니다:

경계 유형 경계 객체
:project project
:group group
:user :user
:instance :instance

중요: 경계 객체가 :project 또는 :group인 경우, 인증이 부여되려면 user가 해당 네임스페이스(프로젝트 또는 그룹)의 멤버여야 합니다.

이 테스트가 검증하는 것:

  • 레거시(비세분화) personal access token이 계속 접근 권한을 부여함
  • Granular PAT에서 필요한 권한이 부여된 사용자가 접근을 허용받음
  • 필요한 권한이 없는 사용자가 적절한 오류 메시지와 함께 접근을 거부당함
  • 인증 시스템이 유형/뮤테이션의 권한 요구 사항에 대해 세분화된 스코프를 올바르게 평가함
  • 기능 플래그 granular_personal_access_tokens가 올바르게 적용됨 (비활성화 시 접근 거부)

7단계: 수동 유효성 검사#

목표: 머지 리퀘스트를 만들기 전에 로컬 환경에서 구현을 수동으로 테스트하여 권한이 예상대로 작동하는지 확인합니다.

설정:

Rails 콘솔에서 사용자를 위한 granular PAT를 만듭니다:

# 기능 플래그 활성화
Feature.enable(:granular_personal_access_tokens)

user = User.human.first

# Granular 토큰 만들기
token = PersonalAccessTokens::CreateService.new(
  current_user: user,
  target_user: user,
  organization_id: user.organization_id,
  params: { expires_at: 1.month.from_now, scopes: ['granular'], granular: true, name: 'gPAT' }
).execute[:personal_access_token]

# 적절한 경계 객체 가져오기 (project, group, :user, or :instance)
project = user.projects.first
boundary = Authz::Boundary.for(project)

# 테스트 중인 할당 가능한 권한으로 스코프 만들기
scope = Authz::GranularScope.new(namespace: boundary.namespace, access: boundary.access, permissions: [:read_work_item, :write_work_item])

# 토큰에 스코프 추가
Authz::GranularScopeService.new(token).add_granular_scopes(scope)

# GraphQL 쿼리 테스트용 curl 명령 복사
query = '{ project(fullPath: \"' + project.full_path + '\") { issues { nodes { title } } } }'
IO.popen('pbcopy', 'w') { |f| f.puts "curl \"http://#{Gitlab.host_with_port}/api/graphql\" --request POST --header \"PRIVATE-TOKEN: #{token.token}\" --header \"Content-Type: application/json\" --data '{\"query\": \"#{query}\"}'" }
  1. 다른 터미널에 명령을 붙여넣습니다. 성공해야 합니다.

참조#