Skip to main content

REST์—์„œ GraphQL๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

GitHub์˜ REST API์—์„œ GitHub์˜ GraphQL API๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋ฒ” ์‚ฌ๋ก€ ๋ฐ ๊ณ ๋ ค ์‚ฌํ•ญ์„ ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

API ๋…ผ๋ฆฌ์˜ ์ฐจ์ด์ 

GitHub์€(๋Š”) REST API์™€ GraphQL API๋ผ๋Š” ๋‘ ๊ฐ€์ง€ API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. GitHub์˜ API์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ GitHub์˜ REST API ๋ฐ GraphQL API ๋น„๊ต์„(๋ฅผ) ์ฐธ์กฐํ•˜์„ธ์š”.

REST์—์„œ GraphQL๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ๊ฒƒ์€ API ๋…ผ๋ฆฌ์˜ ์ƒ๋‹นํ•œ ๋ณ€ํ™”๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์Šคํƒ€์ผ๋กœ์„œ์˜ REST์™€ ์‚ฌ์–‘์œผ๋กœ์„œ์˜ GraphQL ๊ฐ„์˜ ์ฐจ์ด๋กœ ์ธํ•ด REST API ํ˜ธ์ถœ์„ ์ผ๋Œ€์ผ ๊ธฐ์ค€์œผ๋กœ GraphQL API ์ฟผ๋ฆฌ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์€ ์–ด๋ ต๊ณ  ์ข…์ข… ๋ฐ”๋žŒ์งํ•˜์ง€๋„ ์•Š์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์—๋Š” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์˜ ํŠน์ • ์˜ˆ์ œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

REST API์—์„œ GraphQL API๋กœ ์ฝ”๋“œ๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

GraphQL์˜ ์ค‘์š”ํ•œ ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๊ฐ๊ฐ์˜ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

์˜ˆ์ œ: ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ

๋‹จ์ผ REST API ํ˜ธ์ถœ์€ ์กฐ์ง ๊ตฌ์„ฑ์›์˜ ๋ชฉ๋ก์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.

curl -v https://api.github.com/orgs/:org/members

๊ตฌ์„ฑ์› ์ด๋ฆ„ ๋ฐ ์•„๋ฐ”ํƒ€์— ๋Œ€ํ•œ ๋งํฌ๋งŒ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ์ธ ๊ฒฝ์šฐ REST ํŽ˜์ด๋กœ๋“œ์—๋Š” ๊ณผ๋„ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ GraphQL ์ฟผ๋ฆฌ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ง€์ •ํ•œ ํ•ญ๋ชฉ๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

query {
    organization(login:"github") {
    membersWithRole(first: 100) {
      edges {
        node {
          name
          avatarUrl
        }
      }
    }
  }
}

๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ ๋ชฉ๋ก์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  ๊ฐ ์š”์ฒญ์ด ๋ณ‘ํ•ฉ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ์˜ˆ์ œ๋ฅผ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค. REST API์— ๋Œ€ํ•œ ํ˜ธ์ถœ์€ ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ ๋ชฉ๋ก๊ณผ ํ•ด๋‹น ์š”์•ฝ ํ‘œํ˜„์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.

curl -v https://api.github.com/repos/:owner/:repo/pulls

๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์„ ๋ณ‘ํ•ฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋ ค๋ฉด ๊ฐ ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์„ ์„ธ๋ถ€ ํ‘œํ˜„(ํฐ ํŽ˜์ด๋กœ๋“œ)์— ๋Œ€ํ•ด ๊ฐœ๋ณ„์ ์œผ๋กœ ๊ฒ€์ƒ‰ํ•˜๊ณ  ํ•ด๋‹น mergeable ํŠน์„ฑ์ด true์ธ์ง€ false์ธ์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

curl -v https://api.github.com/repos/:owner/:repo/pulls/:number

GraphQL์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์— ๋Œ€ํ•œ number ๋ฐ mergeable ํŠน์„ฑ๋งŒ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

query {
    repository(owner:"octocat", name:"Hello-World") {
    pullRequests(last: 10) {
      edges {
        node {
          number
          mergeable
        }
      }
    }
  }
}

์˜ˆ์ œ: ์ค‘์ฒฉ

์ค‘์ฒฉ๋œ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌํ•˜๋ฉด ์—ฌ๋Ÿฌ REST ํ˜ธ์ถœ์„ ๋” ์ ์€ ์ˆ˜์˜ GraphQL ์ฟผ๋ฆฌ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด REST API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปค๋ฐ‹, ๋น„ ๊ฒ€ํ†  ์ฃผ์„, ๊ฒ€ํ† ์™€ ํ•จ๊ป˜ ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์„ ๊ฒ€์ƒ‰ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋„ค ๊ฐ€์ง€ ํ˜ธ์ถœ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

curl -v https://api.github.com/repos/:owner/:repo/pulls/:number
curl -v https://api.github.com/repos/:owner/:repo/pulls/:number/commits
curl -v https://api.github.com/repos/:owner/:repo/issues/:number/comments
curl -v https://api.github.com/repos/:owner/:repo/pulls/:number/reviews

GraphQL API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์ค‘์ฒฉ๋œ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹จ์ผ ์ฟผ๋ฆฌ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{
  repository(owner: "octocat", name: "Hello-World") {
    pullRequest(number: 1) {
      commits(first: 10) {
        edges {
          node {
            commit {
              oid
              message
            }
          }
        }
      }
      comments(first: 10) {
        edges {
          node {
            body
            author {
              login
            }
          }
        }
      }
      reviews(first: 10) {
        edges {
          node {
            state
          }
        }
      }
    }
  }
}

๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ ๋ฒˆํ˜ธ์— ๋Œ€ํ•œ ๋ณ€์ˆ˜๋ฅผ ๋Œ€์ฒดํ•˜์—ฌ ์ด ์ฟผ๋ฆฌ์˜ ์„ฑ๋Šฅ์„ ํ™•์žฅํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ: ๊ฐ•๋ ฅํ•œ ์ž…๋ ฅ

GraphQL ์Šคํ‚ค๋งˆ๋Š” ๊ฐ•๋ ฅํ•œ ํ˜•์‹์ด๋ฏ€๋กœ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๊ฐ€ ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

GraphQL ๋ณ€ํ˜•์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด์Šˆ ๋˜๋Š” ๋Œ์–ด์˜ค๊ธฐ ์š”์ฒญ์— ์ฃผ์„์„ ์ถ”๊ฐ€ํ•˜๊ณ , ์‹ค์ˆ˜๋กœ clientMutationId์˜ ๊ฐ’์— ๋ฌธ์ž์—ด์ด ์•„๋‹ˆ๋ผ ์ •์ˆ˜๋ฅผ ์ง€์ •ํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค.

mutation {
  addComment(input:{clientMutationId: 1234, subjectId: "MDA6SXNzdWUyMjcyMDA2MTT=", body: "Looks good to me!"}) {
    clientMutationId
    commentEdge {
      node {
        body
        repository {
          id
          name
          nameWithOwner
        }
        issue {
          number
        }
      }
    }
  }
}

์ด ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ž‘์—…์— ๋Œ€ํ•œ ์˜ˆ์ƒ๋˜๋Š” ํ˜•์‹์„ ์ง€์ •ํ•˜๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

{
  "data": null,
  "errors": [
    {
      "message": "Argument 'input' on Field 'addComment' has an invalid value. Expected type 'AddCommentInput!'.",
      "locations": [
        {
          "line": 3,
          "column": 3
        }
      ]
    },
    {
      "message": "Argument 'clientMutationId' on InputObject 'AddCommentInput' has an invalid value. Expected type 'String'.",
      "locations": [
        {
          "line": 3,
          "column": 20
        }
      ]
    }
  ]
}

1234๋ฅผ ๋”ฐ์˜ดํ‘œ๋กœ ๋ฌถ์œผ๋ฉด ๊ฐ’์ด ์ •์ˆ˜์—์„œ ๋ฌธ์ž์—ด(์˜ˆ์ƒ๋˜๋Š” ํ˜•์‹)๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.

mutation {
  addComment(input:{clientMutationId: "1234", subjectId: "MDA6SXNzdWUyMjcyMDA2MTT=", body: "Looks good to me!"}) {
    clientMutationId
    commentEdge {
      node {
        body
        repository {
          id
          name
          nameWithOwner
        }
        issue {
          number
        }
      }
    }
  }
}