Skip to main content

GraphQL을 사용하여 호출 형성

GraphQL API에 인증하는 방법을 알아본 다음 쿼리 및 변형을 만들고 실행하는 방법을 알아봅니다.

GraphQL을 사용하여 인증

참고: GraphQL API에 인증하려면 personal access token, GitHub App 또는 OAuth App을 만들어야 합니다. GraphQL API는 fine-grained personal access tokens의 인증을 지원하지 않습니다.

Personal access token을(를) 사용하여 인증

personal access token로 인증하려면 "personal access token만들기"의 단계에 따라 personal access token을(를) 만듭니다. 요청하는 데이터는 필요한 범위를 지정합니다. 예를 들어 "read:user" 범위를 선택하여 사용자에 대한 데이터를 요청합니다. "public_repo" 범위를 선택하여 퍼블릭 리포지토리에 대한 데이터를 요청합니다.

토큰에 리소스에 액세스하는 데 필요한 범위가 없는 경우 API는 토큰에 필요한 범위를 나타내는 오류 메시지를 반환합니다.

GitHub App을(를) 사용하여 인증

조직 또는 다른 사용자를 대신하여 API를 사용하려는 경우 GitHub는 GitHub App를 사용하는 것이 좋습니다. GitHub App로 인증하려면 먼저 PEM 형식으로 프라이빗 키를 생성해야 합니다. 그런 다음 이 키를 사용하여 JWT(JSON 웹 토큰)에 서명해야 합니다. JSON 웹 토큰을 사용하여 GraphQL API에 인증하는 데 사용할 수 있는 GitHub에서 설치 토큰을 요청할 수 있습니다. 자세한 내용은 “GitHub 앱 만들기”, “GitHub 앱을 사용하여 인증” 및 “GitHub 앱의 사용자 식별 및 권한 부여”를 참조하세요.

OAuth App을(를) 사용하여 인증

OAuth App에서 OAuth 토큰으로 인증하려면 먼저 웹 애플리케이션 흐름 또는 디바이스 흐름을 사용하여 OAuth App에 권한을 부여해야 합니다. 그런 다음 받은 액세스 토큰을 사용하여 API에 액세스할 수 있습니다. 자세한 내용은 "OAuth 앱 만들기" 및 " OAuth 앱 권한 부여"를 참조하세요.

GraphQL 엔드포인트

REST API에는 많은 엔드포인트가 있는 반면, GraphQL API에는 단일 엔드포인트가 있습니다.

http(s)://HOSTNAME/api/graphql

어떤 작업을 수행하든 엔드포인트는 일정하게 유지됩니다.

GraphQL과 통신

GraphQL 작업은 여러 줄 JSON으로 구성되므로 GitHub에서는 Explorer를 사용하여 GraphQL을 호출할 것을 권장합니다. 또는 다른 HTTP 말하기 라이브러리를 사용할 curl 수도 있습니다.

REST에서 HTTP 동사는 수행되는 작업을 결정합니다. GraphQL에서는 쿼리를 수행하든 변형을 수행하든 관계없이 JSON으로 인코딩된 본문을 제공하므로, HTTP 동사는 POST입니다. 엔드포인트에 대한 간단한 GET내적 검사 쿼리는 예외입니다. GraphQL과 REST에 대한 자세한 내용은 “REST에서 GraphQL로 마이그레이션”을 참조하세요.

명령 POST 에서 GraphQL을 curl 쿼리하려면 JSON 페이로드를 사용하여 요청합니다. 페이로드에는 query라는 문자열이 포함되어야 합니다.

curl -H "Authorization: bearer TOKEN" -X POST -d " \
 { \
   \"query\": \"query { viewer { login }}\" \
 } \
" http(s)://HOSTNAME/api/graphql

참고: "query"의 문자열 값은 줄 바꿈 문자를 이스케이프해야 하며, 그렇게 하지 않으면 스키마가 이를 올바르게 구문 분석하지 않습니다. POST 본문의 경우 외부 큰따옴표와 이스케이프된 내부 큰따옴표를 사용합니다.

쿼리 및 변형 작업 정보

GitHub GraphQL API에서 허용되는 두 가지 유형의 작업은 쿼리변형입니다. GraphQL과 REST를 비교하면 쿼리는 GET 요청처럼 작동하고 변형은 POST/PATCH/DELETE처럼 작동합니다. 변형 이름은 어떤 수정이 실행되는지를 결정합니다.

속도 제한에 대한 자세한 내용은 “GraphQL 리소스 제한 사항”을 참조하세요.

쿼리 및 변경은 몇 가지 중요한 차이점이 있지만 비슷한 형식을 공유합니다.

쿼리 정보

GraphQL 쿼리는 지정한 데이터만 반환합니다. 쿼리를 구성하려면 스칼라만 반환할 때까지 필드 내의 필드(중첩된 하위 필드라고도 함)를 지정해야 합니다.

쿼리는 다음과 같이 구성됩니다.

query {
  JSON-OBJECT-TO-RETURN
}

실제 예제는 “예제 쿼리”를 참조하세요.

변형 정보

변형을 형성하려면 다음 세 가지를 지정해야 합니다.

  1. 변형 이름. 수행하려는 수정의 유형입니다.
  2. 입력 개체. 입력 필드로 구성된, 서버로 보내려는 데이터입니다. 이것을 변형 이름에 인수로서 전달합니다.
  3. 페이로드 개체. 반환 필드로 구성된, 서버로부터 반환하려는 데이터입니다. 이것을 변형 이름의 본문으로서 전달합니다.

변형은 다음과 같이 구성됩니다.

mutation {
  MUTATION-NAME(input: {MUTATION-NAME-INPUT!}) {
    MUTATION-NAME-PAYLOAD
  }
}

이 예제에서 입력 객체는 MutationNameInput, 페이로드 객체는 MutationNamePayload입니다.

변형 참조에서, 나열된 입력 필드는 입력 개체로서 전달하는 것입니다. 나열된 반환 필드는 페이로드 개체로서 전달하는 것입니다.

실제 예제는 “예제 변형”을 참조하세요.

변수 사용

변수는 쿼리를 더욱 동적이고 강력하게 만들 수 있으며 변형 입력 개체를 전달할 때 복잡성을 줄일 수 있습니다.

참고: Explorer를 사용하는 경우 별도의 쿼리 변수 창에 변수를 입력하고 JSON 개체 앞에 단어 variables를 포함하지 마세요.

다음은 단일 변수를 사용하는 예제 쿼리입니다.

query($number_of_repos:Int!) {
  viewer {
    name
     repositories(last: $number_of_repos) {
       nodes {
         name
       }
     }
   }
}
variables {
   "number_of_repos": 3
}

변수를 사용하는 세 단계가 있습니다.

  1. variables 개체에서 작업 외부의 변수를 정의합니다.

    variables {
       "number_of_repos": 3
    }
    

    개체는 유효한 JSON이어야 합니다. 이 예제에서는 간단한 Int 변수 형식을 보여 주지만, 입력 개체와 같은 더 복잡한 변수 형식을 정의할 수 있습니다. 여기에서 여러 변수를 정의할 수도 있습니다.

  2. 변수를 인수로서 작업에 전달합니다.

    query($number_of_repos:Int!){
    

    인수는 키-값 쌍이며, 여기서 키는 $로 시작하는 이름(예: $number_of_repos)이고 값은 형식(예: Int)입니다. 형식이 필요한지 여부를 나타내는 !를 추가합니다. 여러 변수를 정의한 경우 여기에 여러 인수로서 포함합니다.

  3. 작업 내에서 변수를 사용합니다.

    repositories(last: $number_of_repos) {
    

    이 예제에서는 변수를 검색할 리포지토리의 수로 대체합니다. GraphQL은 강력한 입력을 적용하기 때문에 2단계에서 형식을 지정합니다.

이 프로세스는 쿼리 인수를 동적으로 만듭니다. 이제 단순히 variables 개체의 값을 변경하고 쿼리의 나머지를 동일하게 유지할 수 있습니다.

변수를 인수로서 사용하면 쿼리를 변경하지 않고도 variables 개체의 값을 동적으로 업데이트할 수 있습니다.

예제 쿼리

좀 더 복잡한 쿼리를 살펴보고 이 정보를 컨텍스트에 배치해 보겠습니다.

다음 쿼리는 octocat/Hello-World 리포지토리를 조회하고, 가장 최근에 종료된 20개의 이슈를 찾고, 각 이슈의 제목, URL, 처음 5개 레이블을 반환합니다.

query {
  repository(owner:"octocat", name:"Hello-World") {
    issues(last:20, states:CLOSED) {
      edges {
        node {
          title
          url
          labels(first:5) {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    }
  }
}

컴퍼지션을 한 줄씩 살펴보기:

  • query {

    서버의 데이터를 수정하는 것이 아니라 데이터를 읽고자 하므로, query는 루트 작업입니다. 작업을 지정하지 않으면 query가 기본값이기도 합니다.

  • repository(owner:"octocat", name:"Hello-World") {

    쿼리를 시작하기 위해 repository 개체를 찾으려고 합니다. 스키마 유효성 검사에 따르면 이 개체에 ownername 인수가 필요합니다.

  • issues(last:20, states:CLOSED) {

    리포지토리의 모든 이슈를 설명하기 위해 issues 개체를 호출합니다. repository에서 단일 issue를 쿼리할 수 있지만, 그렇게 할 경우 반환하려는 이슈의 번호를 알아야 하고 이를 인수로서 제공해야 합니다.

    issues 개체에 대한 몇 가지 세부 정보:

    • 문서에 따르면 이 개체의 형식은 IssueConnection입니다.
    • 스키마 유효성 검사에 따르면 이 개체에는 결과의 last 또는 first 숫자가 인수로서 필요합니다. 이 예제에서는 20을 사용합니다.
    • 또한 문서에 따르면 이 개체는 OPEN 또는 CLOSED 값을 허용하는 IssueState 열거형인 states 인수를 허용합니다. 종료된 이슈만 찾으려면 states 키에 CLOSED 값을 제공합니다.
  • edges {

    IssueConnection 형식을 가지고 있으므로 issues가 연결임을 알고 있습니다. 개별 이슈에 대한 데이터를 검색하려면 edges를 통해 노드에 액세스해야 합니다.

  • node {

    여기서는 에지 끝에서 노드를 검색합니다. IssueConnection 문서에 따르면 IssueConnection 형식 끝에 있는 노드는 Issue 개체입니다.

  • 이제 Issue 개체를 검색하고 있다는 것을 알게 되었으므로 문서를 살펴보고 반환할 필드를 지정할 수 있습니다.

    title
    url
    labels(first:5) {
      edges {
        node {
          name
        }
      }
    }
    

    여기서 Issue 개체의 title, url, labels 필드를 지정합니다.

    labels 필드의 형식은 LabelConnection입니다. issues 개체와 마찬가지로 labels도 연결이므로 해당 에지를 연결된 노드인 label 개체로 이동해야 합니다. 노드에서, 반환하려는 label 개체 필드(이 경우 name)를 지정할 수 있습니다.

Octocat의 퍼블릭 Hello-World 리포지토리에서 이 쿼리를 실행하면 많은 레이블이 반환되지는 않습니다. 레이블을 사용하는 자체 리포지토리 중 하나에서 실행해 보면 차이를 확인할 수 있을 것입니다.

예제 변형

변형에는 쿼리를 먼저 수행해야만 알 수 있는 정보가 필요한 경우가 많습니다. 이 예제에서는 두 개의 작업을 보여 줍니다.

  1. 이슈 ID를 가져오기 위한 쿼리.
  2. 이슈에 이모지 반응을 추가하기 위한 변형.
query FindIssueID {
  repository(owner:"octocat", name:"Hello-World") {
    issue(number:349) {
      id
    }
  }
}

mutation AddReactionToIssue {
  addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
    reaction {
      content
    }
    subject {
      id
    }
  }
}

이름(이 예제에서는 FindIssueIDAddReactionToIssue)을 지정하면 동일한 Explorer 창에 쿼리와 변형을 포함할 수 있지만, 작업은 GraphQL 엔드포인트에 대한 별도의 호출로 실행됩니다. 변형과 동시에 쿼리를 수행할 수 없거나 그 반대의 경우도 마찬가지입니다.

예제를 하나 살펴보겠습니다. 간단한 작업처럼 보입니다. 이슈에 이모지 반응을 추가합니다.

그렇다면 쿼리로 시작해야 하는지 어떻게 알 수 있을까요? 아직 하지 않았습니다.

서버의 데이터를 수정하려고 하므로(이슈에 이모지 연결) 유용한 변형을 찾기 위해 스키마를 검색하는 것으로 시작합니다. 참조 문서는 다음 설명과 함께 addReaction 변형을 보여 줍니다. Adds a reaction to a subject. 완벽합니다!

변형에 대한 문서에는 세 개의 입력 필드가 나열됩니다.

  • clientMutationId (String)
  • subjectId (ID!)
  • content (ReactionContent!)

!subjectIdcontent가 필수 필드임을 나타냅니다. 필수 content는 의미가 있습니다. 반응을 추가하려고 하므로 사용할 이모지 지정해야 합니다.

하지만 subjectId가 왜 필요한가요? subjectId가 어떤 리포지토리의 어떤 이슈에 반응해야 하는지 식별하는 유일한 방법이기 때문입니다.

ID를 얻기 위해 쿼리를 사용하여 이 예제를 시작합니다.

쿼리를 한 줄씩 살펴보겠습니다.

  • query FindIssueID {

    여기서는 쿼리를 수행하고 이름을 FindIssueID로 지정합니다. 쿼리에 이름을 지정하는 것은 선택 사항입니다. 변형과 동일한 Explorer 창에 포함할 수 있도록 여기서는 이름을 지정합니다.

  • repository(owner:"octocat", name:"Hello-World") {

    repository 개체를 쿼리하고 ownername 인수를 전달하여 리포지토리를 지정합니다.

  • issue(number:349) {

    issue 개체를 쿼리하고 number 인수를 전달하여 반응할 이슈를 지정합니다.

  • id

    여기에서 https://github.com/octocat/Hello-World/issues/349id를 검색하여 subjectId로 전달합니다.

쿼리를 실행하여 id: MDU6SXNzdWUyMzEzOTE1NTE=를 가져옵니다.

참고: 쿼리에서 반환되는 id가 변형에서 subjectID로서 전달할 값입니다. 문서나 스키마 내적 검사는 이 관계를 나타내지 않습니다. 이를 파악하려면 이름 뒤에 있는 개념을 이해해야 합니다.

ID를 알고 있으면 다음과 같이 변형을 진행할 수 있습니다.

  • mutation AddReactionToIssue {

    여기서는 변형을 수행하고 이름을 AddReactionToIssue로 지정합니다. 쿼리와 마찬가지로 변형에 이름을 지정하는 것도 선택 사항입니다. 쿼리와 동일한 Explorer 창에 포함할 수 있도록 여기서는 이름을 지정합니다.

  • addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {

    다음 줄을 살펴보겠습니다.

    • addReaction은 변형의 이름입니다.

    • input은 필수 인수 키입니다. 변형에 대해서는 항상 input입니다.

    • {subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}는 필수 인수 키입니다. 변형에 대해서는 항상 입력 필드(이 경우 subjectIdcontent)로 구성된 입력 개체(따라서 중괄호)입니다.

      콘텐츠에 사용할 값을 어떻게 알 수 있나요? addReaction 문서에 따르면 content 필드에는 ReactionContent 형식이 있는데, GitHub 이슈에서는 특정 이모지 반응만 지원되므로 이것은 열거형입니다. 다음은 반응에 허용되는 값입니다(일부 값은 해당 이모지 이름과 다름).

      콘텐츠 이모지
      +1 👍
      -1 👎
      laugh 😄
      confused 😕
      heart ❤️
      hooray 🎉
      rocket 🚀
      eyes 👀
  • 호출의 나머지는 페이로드 개체로 구성됩니다. 여기서는 변형을 수행한 후 서버에서 반환하도록 할 데이터를 지정합니다. 이러한 줄은 addReaction 문서에서 오는 것이며, 다음 세 가지 가능한 반환 필드가 있습니다.

    • clientMutationId (String)
    • reaction (Reaction!)
    • subject (Reactable!)

    이 예제에서는 필수 하위 필드(각각 contentid)가 있는 두 개의 필수 필드(reactionsubject)를 반환합니다.

변형을 실행할 때 응답은 다음과 같습니다.

{
  "data": {
    "addReaction": {
      "reaction": {
        "content": "HOORAY"
      },
      "subject": {
        "id": "MDU6SXNzdWUyMTc5NTQ0OTc="
      }
    }
  }
}

정말 간단하죠. 사용자 이름을 찾으려면 🎉를 마우스로 가리키고 이슈에 대한 반응을 확인하세요.

마지막으로, 입력 개체에 여러 필드를 전달하면 구문이 다루기 어려워질 수 있습니다. 필드를 변수로 이동하면 도움이 될 수 있습니다. 변수를 사용하여 원래 변형을 다시 작성하는 방법은 다음과 같습니다.

mutation($myVar:AddReactionInput!) {
  addReaction(input:$myVar) {
    reaction {
      content
    }
    subject {
      id
    }
  }
}
variables {
  "myVar": {
    "subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",
    "content":"HOORAY"
  }
}

이전 예제에서 content 필드 값(변형에서 직접 사용됨)에는 HOORAY 주위에 따옴표가 없지만, 변수에 사용될 때는 따옴표가 있는 것을 알 수 있습니다. 그 이유는 다음과 같습니다.

  • 변형에서 직접 content를 사용하는 경우, 스키마는 값이 문자열이 아니라 열거형ReactionContent 형식일 것으로 예상합니다. 따옴표는 문자열에 예약되어 있으므로 열거형 값 주위에 따옴표를 추가하면 스키마 유효성 검사에서 오류가 발생합니다.
  • 변수에서 content를 사용하는 경우, 변수 섹션은 유효한 JSON이어야 하므로 따옴표가 필요합니다. 스키마 유효성 검사는 실행 중에 변수가 변형으로 전달될 때 ReactionContent 형식을 올바르게 해석합니다.

열거형과 문자열 간의 차이점에 대한 자세한 내용은 공식 GraphQL 사양을 참조하세요.

추가 참고 자료

GraphQL 호출을 구성할 때 할 수 있는 작업이 훨씬 더 많습니다. 다음에 살펴볼 내용은 아래와 같습니다.