ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ GraphQL ์ž…๋ฌธ

subin
  • #GraphQL
  • #REST
  • #Frontend
  • #Apollo

๋“ค์–ด๊ฐ€๋ฉฐ

์•ˆ๋…•ํ•˜์„ธ์š”. ๋…ธ๋จธ์Šค ํ”„๋ก ํŠธ์—”๋“œ ํŒ€์˜ ํ•˜์ˆ˜๋นˆ์ž…๋‹ˆ๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ์„œ ๋‹ค์–‘ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์ง€๋งŒ, ๊ทธ์ค‘์—์„œ๋„ ์„œ๋ฒ„์™€์˜ ํ†ต์‹ ์€ ๋งค์šฐ ์ค‘์š”ํ•œ ์—…๋ฌด์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ์ €ํฌ ๋…ธ๋จธ์Šค์—์„œ๋„ ์ผ๋ถ€ ์„œ๋น„์Šค์— GraphQL์„ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์„ ๊ฒ€ํ†  ์ค‘์ž…๋‹ˆ๋‹ค. ์ด์— ๋ฐœ๋งž์ถ”์–ด GraphQL์— ๋Œ€ํ•ด ๋น ๋ฅด๊ฒŒ ์•Œ์•„๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

GraphQL์ด๋ž€?

GraphQL์€ Facebook์—์„œ ๊ฐœ๋ฐœํ•œ ์˜คํ”ˆ ์†Œ์Šค ๋ฐ์ดํ„ฐ ์ฟผ๋ฆฌ ์–ธ์–ด์ž…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ํšจ์œจ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜๊ณ , ๋‹จ์ผ ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋“ค์€ ๋ณด๋‹ค ๊ฐ„ํŽธํ•˜๊ณ  ํšจ์œจ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GraphQL ์ฟผ๋ฆฌ ์–ธ์–ด๋ž€?

GraphQL ์ฟผ๋ฆฌ ์–ธ์–ด๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ์กฐํ™”๋œ ๋ฐฉ์‹์œผ๋กœ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ •ํ™•ํžˆ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š”์ง€ ๋ช…์‹œํ•  ์ˆ˜ ์žˆ์–ด, ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก์„ ์ค„์ด๊ณ  ์‘๋‹ต ์‹œ๊ฐ„์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ

์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ์„ ์š”์ฒญํ•˜๋Š” GraphQL ์ฟผ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

query {
  user(id: 1) {
    name
    email
  }
}

์œ„ ์ฟผ๋ฆฌ๋Š” user ์—”ํ‹ฐํ‹ฐ์—์„œ id๊ฐ€ 1์ธ ์‚ฌ์šฉ์ž์˜ name๊ณผ email ํ•„๋“œ๋ฅผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„๋Š” ์ด ์š”์ฒญ์— ๋”ฐ๋ผ ์ •ํ™•ํžˆ ํ•ด๋‹น ํ•„๋“œ๋งŒ ํฌํ•จ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์‘๋‹ต ์˜ˆ์‹œ

์„œ๋ฒ„๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์— ๋”ฐ๋ผ JSON ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์œ„์˜ ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ์‘๋‹ต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

{
  "data": {
    "user": {
      "name": "Luke Skywalker",
      "email": "luke@rebellion.com"
    }
  }
}

์ด ์‘๋‹ต์€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์— ๋งž์ถ”์–ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. data ๊ฐ์ฒด ๋‚ด์— user ๊ฐ์ฒด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉฐ, ์š”์ฒญํ•œ name๊ณผ email ํ•„๋“œ๊ฐ€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ๋Š” ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์ •ํ™•ํžˆ ๋ฐ›์•„๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

REST API์™€์˜ ์ฐจ์ด์ 

GraphQL๊ณผ REST API๋Š” ๋ชจ๋‘ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ํ†ต์‹ ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜์ง€๋งŒ, ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์ฐจ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

1. ๋ฐ์ดํ„ฐ ์š”์ฒญ ๋ฐฉ์‹

  • REST API: ๊ฐ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•ด ๊ณ ์œ ํ•œ URL์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ํŠน์ • ์—”๋“œํฌ์ธํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, /users ์—”๋“œํฌ์ธํŠธ๋Š” ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , /posts ์—”๋“œํฌ์ธํŠธ๋Š” ๊ฒŒ์‹œ๋ฌผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • GraphQL: ๋‹จ์ผ ์—”๋“œํฌ์ธํŠธ(/graphql)๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฟผ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ์š”์ฒญ ์‹œ ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ ๋ช…์‹œํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ์˜ค๋ฒ„ ํŽ˜์นญ ๋ฐ ์–ธ๋” ํŽ˜์นญ

  • REST API: ํŠน์ • ์—”๋“œํฌ์ธํŠธ์—์„œ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•œ ์˜ค๋ฒ„ ํŽ˜์นญ(over-fetching)์ด๋‚˜ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ถ€์กฑํ•œ ์–ธ๋” ํŽ˜์นญ(under-fetching)์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, /users ์—”๋“œํฌ์ธํŠธ๊ฐ€ ๋ชจ๋“  ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ, ํด๋ผ์ด์–ธํŠธ๋Š” ํŠน์ • ์‚ฌ์šฉ์ž ํ•„๋“œ๋งŒ ํ•„์š”๋กœ ํ•˜๋”๋ผ๋„ ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • GraphQL: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์–ด ์˜ค๋ฒ„ ํŽ˜์นญ๊ณผ ์–ธ๋” ํŽ˜์นญ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ๋งŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ํ•ด๋‹น ํ•„๋“œ๋งŒ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ๋ฒ„์ „ ๊ด€๋ฆฌ

  • REST API: ์—”๋“œํฌ์ธํŠธ ๋ฒ„์ „ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ด๋‚˜ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๋ณ€๊ฒฝ ์‹œ, /v1/users, /v2/users์™€ ๊ฐ™์€ ์—”๋“œํฌ์ธํŠธ ๋ฒ„์ „ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • GraphQL: ๋ฒ„์ „ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญํ•˜๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์„œ๋ฒ„ ์ธก์—์„œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ํด๋ผ์ด์–ธํŠธ๋Š” ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

4. ๋„คํŠธ์›Œํฌ ์š”์ฒญ ์ˆ˜

  • REST API: ์—ฌ๋Ÿฌ ์—”๋“œํฌ์ธํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • GraphQL: ๋‹จ์ผ ์ฟผ๋ฆฌ๋กœ ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๋™์‹œ์— ์š”์ฒญํ•  ์ˆ˜ ์žˆ์–ด ๋„คํŠธ์›Œํฌ ์š”์ฒญ ์ˆ˜๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

REST API์™€ GraphQL์˜ ์ฐจ์ด์ : Star Wars API ์˜ˆ์‹œ

์ƒ˜ํ”Œ REST API ์„œ๋ฒ„
์ƒ˜ํ”Œ GraphQL API ์„œ๋ฒ„

REST API์™€ GraphQL์„ ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•ด Star Wars API(SWAPI)๋ฅผ ์‚ฌ์šฉํ•œ ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. SWAPI๋Š” Star Wars ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” API์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” REST์™€ GraphQL์„ ํ†ตํ•ด ์˜ํ™”์™€ ๋“ฑ์žฅ์ธ๋ฌผ์„ ์š”์ฒญํ•˜๋Š” ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ๋‘ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ์ฐจ์ด์ ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

REST API ์˜ˆ์‹œ

REST API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ํ™”์™€ ๊ด€๋ จ๋œ ๋“ฑ์žฅ์ธ๋ฌผ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด ๋‘ ๋ฒˆ์˜ ์š”์ฒญ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

1. ์˜ํ™” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ

GET https://swapi.dev/api/films

์‘๋‹ต ์˜ˆ์‹œ:

{
  "count": 6,
  "results": [
    {
      "title": "A New Hope",
      "episode_id": 4,
      "characters": [
        "https://swapi.dev/api/people/1/",
        "https://swapi.dev/api/people/2/"
      ]
    },
    ...
  ]
}

2. ์˜ํ™”์— ๋“ฑ์žฅํ•˜๋Š” ํŠน์ • ์ธ๋ฌผ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ

GET https://swapi.dev/api/people/1

์‘๋‹ต ์˜ˆ์‹œ:

{
  "name": "Luke Skywalker",
  "height": "172",
  "mass": "77",
  "hair_color": "blond",
  "skin_color": "fair",
  "eye_color": "blue",
  "birth_year": "19BBY",
  "gender": "male"
}

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

GraphQL ์˜ˆ์‹œ

GraphQL์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‹จ์ผ ์ฟผ๋ฆฌ๋กœ ์˜ํ™”์™€ ๊ด€๋ จ๋œ ๋“ฑ์žฅ์ธ๋ฌผ ๋ฐ์ดํ„ฐ๋ฅผ ๋™์‹œ์— ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ๋‹จ์ผ ์ฟผ๋ฆฌ๋กœ ์˜ํ™”์™€ ๋“ฑ์žฅ์ธ๋ฌผ ๋ฐ์ดํ„ฐ ์š”์ฒญ

{
  allFilms {
    films {
      title
      episodeID
      characterConnection {
        characters {
          name
          height
          mass
          skinColor
        }
      }
    }
  }
}

์‘๋‹ต ์˜ˆ์‹œ:

{
  "data": {
    "allFilms": {
      "films": [
        {
          "title": "A New Hope",
          "episodeID": 4,
          "characterConnection": {
            "characters": [
              {
                "name": "Luke Skywalker",
                "height": "172",
                "mass": "77",
                "skinColor": "fair"
              },
              ...
            ]
          }
        },
        ...
      ]
    }
  }
}

ํ”„๋ก ํŠธ์—”๋“œ ์ ์šฉํ•˜๊ธฐ

GraphQL์„ ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋กœ์ ํŠธ์— ๋„์ž…ํ•˜๋Š” ๊ณผ์ •์€ ๋น„๊ต์  ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. React๋ฅผ ์˜ˆ์‹œ๋กœ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

1. Apollo Client ์„ค์น˜ ๋ฐ ์„ค์ •

GraphQL ํด๋ผ์ด์–ธํŠธ๋กœ๋Š” Apollo Client๊ฐ€ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. Apollo Client๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด GraphQL ์„œ๋ฒ„์™€ ์‰ฝ๊ฒŒ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„ค์น˜

๋จผ์ €, ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

npm install @apollo/client graphql

์„ค์ •

ํ”„๋กœ์ ํŠธ์˜ ์ตœ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ Apollo Client๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. Apollo Client๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ด๋ฅผ ApolloProvider๋กœ ๊ฐ์‹ธ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—์„œ GraphQL ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

import React from 'react';
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://your-graphql-endpoint.com/graphql',  // GraphQL ์„œ๋ฒ„์˜ URI๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  cache: new InMemoryCache()
});

function App() {
  return (
    <ApolloProvider client={client}>
      <YourComponent />
    </ApolloProvider>
  );
}

export default App;

2. GraphQL ์ฟผ๋ฆฌ ์ž‘์„ฑ ๋ฐ ๋ฐ์ดํ„ฐ ์š”์ฒญ

์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ ์ž‘์„ฑ

GraphQL ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. gql ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

import { gql } from '@apollo/client';

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      name
      email
    }
  }
`;

๋ฐ์ดํ„ฐ ์š”์ฒญ

React ์ปดํฌ๋„ŒํŠธ์—์„œ useQuery ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. useQuery ํ›…์€ ์ฟผ๋ฆฌ์™€ ์ฟผ๋ฆฌ ๋ณ€์ˆ˜(์˜ต์…˜)๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.

import React from 'react';
import { useQuery } from '@apollo/client';
import { GET_USER } from './queries';

function UserComponent({ userId }) {
  const { loading, error, data } = useQuery(GET_USER, {
    variables: { id: userId }
  });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>{data.user.name}</h1>
      <p>{data.user.email}</p>
    </div>
  );
}

export default UserComponent;

useQuery ํ›…์€ loading, error, data๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๋กœ๋”ฉ ์ƒํƒœ, ์—๋Ÿฌ ์ƒํƒœ, ๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. GraphQL์„ ํ™œ์šฉํ•œ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ

GraphQL์„ ํ™œ์šฉํ•œ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์—๋Š” ๋ฎคํ…Œ์ด์…˜, ์บ์‹ฑ, ์„œ๋ธŒ์Šคํฌ๋ฆฝ์…˜์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ, ์„ฑ๋Šฅ ์ตœ์ ํ™”, ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฎคํ…Œ์ด์…˜ (Mutation)

๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์š”์ฒญ(์˜ˆ: ์ƒ์„ฑ, ์ˆ˜์ •, ์‚ญ์ œ)์€ useMutation ํ›…์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฎคํ…Œ์ด์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { gql, useMutation } from '@apollo/client';

const CREATE_USER = gql`
  mutation CreateUser($name: String!, $email: String!) {
    createUser(name: $name, email: $email) {
      id
      name
      email
    }
  }
`;

function CreateUserComponent() {
  let inputName, inputEmail;
  const [createUser, { data }] = useMutation(CREATE_USER);

  return (
    <div>
      <form
        onSubmit={e => {
          e.preventDefault();
          createUser({ variables: { name: inputName.value, email: inputEmail.value } });
          inputName.value = '';
          inputEmail.value = '';
        }}
      >
        <input
          ref={node => {
            inputName = node;
          }}
          type="text"
          placeholder="Name"
        />
        <input
          ref={node => {
            inputEmail = node;
          }}
          type="email"
          placeholder="Email"
        />
        <button type="submit">Create User</button>
      </form>
    </div>
  );
}

export default CreateUserComponent;

์บ์‹ฑ

Apollo Client๋Š” InMemoryCache๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„œ๋ธŒ์Šคํฌ๋ฆฝ์…˜ (Subscription)

์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ useSubscription ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ GraphQL ์„œ๋ธŒ์Šคํฌ๋ฆฝ์…˜์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { gql, useSubscription } from '@apollo/client';

const USER_ADDED = gql`
  subscription OnUserAdded {
    userAdded {
      id
      name
      email
    }
  }
`;

function NewUsersComponent() {
  const { data, loading } = useSubscription(USER_ADDED);

  if (loading) return <p>Loading...</p>;

  return (
    <div>
      <p>New user added:</p>
      <p>Name: {data.userAdded.name}</p>
      <p>Email: {data.userAdded.email}</p>
    </div>
  );
}

export default NewUsersComponent;

๊ฒฐ๋ก 

GraphQL ํ•™์Šต์„ ํ•˜๋ฉด์„œ GraphQL์€ ํ”„๋ก ํŠธ์—”๋“œ ์ž…์žฅ์—์„œ ๊ฐœ๋ฐœํ•˜๊ธฐ ํŽธํ•œ ํ†ต์‹  ๋ฐฉ์‹์ด๋ผ๊ณ  ๋Š๊ปด์กŒ์Šต๋‹ˆ๋‹ค. ์•„์ง ํ”„๋กœ๋•์…˜ ๋ ˆ๋ฒจ์—์„œ ์‚ฌ์šฉํ•ด๋ณด์ง€ ๋ชปํ–ˆ์ง€๋งŒ, ์•ž์œผ๋กœ ํ™œ์šฉํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์— ๊ธฐ๋Œ€๊ฐ์ด ํฝ๋‹ˆ๋‹ค.

GraphQL์˜ ์žฅ์ ๊ณผ ๋‹จ์ ์„ ์‚ดํŽด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์žฅ์ 

์œ ์—ฐํ•œ ๋ฐ์ดํ„ฐ ์š”์ฒญ: GraphQL์€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์–ด ์‘๋‹ต ์‚ฌ์ด์ฆˆ๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ณ , ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ ๊ฐ„์˜ ์ปคํ”Œ๋ง์„ ์ค„์ž…๋‹ˆ๋‹ค.

๋‹จ์ผ ์—”๋“œํฌ์ธํŠธ ์‚ฌ์šฉ: GraphQL์€ ๋‹จ์ผ ์—”๋“œํฌ์ธํŠธ๋กœ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์–ด ์—ฌ๋Ÿฌ API ์—”๋“œํฌ์ธํŠธ๋ฅผ ๊ด€๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๋‹จ์ 

ํ•™์Šต ๊ณก์„ : GraphQL์˜ ๊ฐœ๋…๊ณผ ์‚ฌ์šฉ๋ฒ•์„ ๋ฐฐ์šฐ๋Š” ๋ฐ ์‹œ๊ฐ„์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ, ๊ธฐ์กด REST API์— ์ต์ˆ™ํ•œ ๊ฐœ๋ฐœ์ž์—๊ฒŒ๋Š” ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์— ์ ์‘ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ณต์žก์„ฑ ์ฆ๊ฐ€: ๊ฐ„๋‹จํ•œ ์š”์ฒญ์˜ ๊ฒฝ์šฐ์—๋„ ์ฟผ๋ฆฌ์™€ ๋ฎคํ…Œ์ด์…˜์„ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ดˆ๊ธฐ ์„ค์ •๊ณผ ๊ตฌํ˜„์— ์‹œ๊ฐ„์ด ๋” ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ ์ž๋ฃŒ

์งง์€ ๊ธ€์— ๋งŽ์€ ๋‚ด์šฉ์„ ๋‹ด์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. GraphQL ์— ๋Œ€ํ•ด์„œ ๋” ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜ ์ž๋ฃŒ๋“ค์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ