[TIL] Day68 #GraphQL

Beanxxยท2022๋…„ 8์›” 2์ผ
0

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
68/120
post-thumbnail

2022.08.02(Tues)

[TIL] Day68
[SEB FE] Day69

์˜ค๋Š˜์€ ์•„์นจ 8์‹œ๋ถ€ํ„ฐ ์ง‘ ์ด์‚ฌํ•˜๋Š” ๋‚ ์ด์—ฌ์„œ 9์‹œ๋ถ€ํ„ฐ ์นดํŽ˜๋กœ ํ”ผ์‹ ํ–ˆ๋‹ค..
๊ฒŒ๋‹ค๊ฐ€ ๋น„๋„ ์ฃผ๋ฅต์ฃผ๋ฅต ๐Ÿ˜ญ
์ž์ทจ๋ฐฉ 2๋ฒˆ ์ด์‚ฌํ•  ๋•Œ๋งˆ๋‹ค ๋ชจ์กฐ๋ฆฌ ๋น„ ์™”์—ˆ๋Š”๋ฐ ๋ณธ๊ฐ€ ์ด์‚ฌํ•  ๋•Œ๋„ ๋˜๋˜๋˜ ๋น„๋ผ๋‹ˆ.. โ˜”๏ธ
์ง‘ ์™€์ดํŒŒ์ด๋„ ํŽ˜์–ด 2์‹œ ์ „์— ๊ฒจ์šฐ ์—ฐ๊ฒฐํ•˜๊ตฌ, ํŽ˜์–ด ์‹œ๊ฐ„์— ์šฐ๋‹นํƒ•ํƒ• ๋„ˆ๋ฌด ์†Œ๋ž€์Šค๋Ÿฌ์›Œ์„œ ํŽ˜์–ด๋‹˜๊ป˜ ๋„ˆ๋ฌด ์ฃ„์†ก,, โ€ขแท„โŒ“โ€ขแท…
์–ผ๋ฅธ ๋ฐฉ ์ •๋ฆฌํ•˜๊ณ  ์ƒˆ๋กœ์šด ๋งˆ์Œ์œผ๋กœ ๋‹ค์‹œ ์—ด์‹ฌํžˆ ๊ณต๋ถ€ํ•˜์žใ… ๐Ÿฆพ

โ˜‘๏ธย GraphQL

: Graph + Query Language. ํŽ˜์ด์Šค๋ถ์—์„œ ๋งŒ๋“  ์˜คํ”ˆ ์†Œ์Šค ์ฟผ๋ฆฌ ์–ธ์–ด โ‡’ API๋ฅผ ์œ„ํ•œ ์ฟผ๋ฆฌ ์–ธ์–ด

๐Ÿ‘‰ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•ด ๊ทธ๋ž˜ํ”„๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ์ฟผ๋ฆฌ ์–ธ์–ด

๐Ÿ“Žย GraphQL ํŠน์ง•

  • HTTP๋ฅผ ํ†ตํ•ด API ์„œ๋ฒ„๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์‘๋‹ต ๋ฐ›์Œ
  • ์‘๋‹ต ๋ฐ›์„์‹œ, ๋ฐ์ดํ„ฐ ๊ฒฐ๊ณผ๋ฅผ JSON ํ˜•์‹์œผ๋กœ ๋ฐ›์Œ
  • ๊ฐ ํ•„๋“œ์— ๋Œ€์‘ํ•˜๋Š” resolver ํ•จ์ˆ˜๋กœ ๊ฐ ํ•„๋“œ ๋ฐ์ดํ„ฐ ์กฐํšŒ ๊ฐ€๋Šฅ
  • GraphQL ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์กฐํšŒ ๋Œ€์ƒ schema๊ฐ€ ์œ ํšจํ•œ์ง€ ๊ฒ€์‚ฌ

๐Ÿ”นย Graph: ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ ๋“ค์ด ์„œ๋กœ ๋ณต์žกํ•˜๊ฒŒ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ๊ด€๊ณ„๋ฅผ ํ‘œํ˜„ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ

  • Node / ์ •์ (vertex): ํ•˜๋‚˜์˜ ์ 
  • ๊ฐ„์„ (edge): ํ•˜๋‚˜์˜ ์„ 
  • ๊ฐ ๋…ธ๋“œ๊ฐ„์˜ ๊ฐ„์„ ์„ ํ†ตํ•ด ํŠน์ • ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ทธ๋ž˜ํ”„๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํƒ์ƒ‰ ๊ฐ€๋Šฅ
  • ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์— ๋”ฐ๋ผ ์œ ์—ฐํ•˜๊ฒŒ ํŠธ๋ฆฌ ๊ตฌ์กฐ์˜ JSON ๋ฐ์ดํ„ฐ๋ฅผ ์‘๋‹ต์œผ๋กœ ์ „์†ก ๊ฐ€๋Šฅ ๐Ÿ‘

๐Ÿ”ธย ์—”ํ‹ฐํ‹ฐ(Entity): ์‚ฌ๋ฌผ์˜ ๊ตฌ์กฐ๋‚˜ ์ƒํƒœ, ๋™์ž‘ ๋“ฑ์„ ๋ชจ๋ธ๋กœ ํ‘œํ˜„ํ•˜๋Š” ๊ฒฝ์šฐ, ๊ทธ ๋ชจ๋ธ์˜ ๊ตฌ์„ฑ์š”์†Œ

๐Ÿ”นย Tree: ๋ฐฉํ–ฅ์„ฑ ์กด์žฌ โญ•๏ธ, ์‚ฌ์ดํด ์กด์žฌ โŒย โ‡’ ๋น„์ˆœํ™˜ ๊ทธ๋ž˜ํ”„

ใ„ด ๋ฃจํŠธ & ๋ชจ์„œ๋ฆฌ๋ฅผ ํ†ตํ•ด ๋…ธ๋“œ๋ฅผ ๋”ฐ๋ผ ์ˆœํšŒ๊ฐ€๋Šฅํ•˜๋‚˜ ๋™์ผ ๋…ธ๋“œ๋กœ ๋Œ์•„์˜ฌ ์ˆ˜ ์—†๋Š” ์†์„ฑ์˜ ํŠน๋ณ„ํ•œ ๊ทธ๋ž˜ํ”„

๐Ÿ“Žย REST API ํ•œ๊ณ„

  • Overfetch: ํ•„์š”์—†๋Š” ๋ฐ์ดํ„ฐ๊นŒ์ง€ ์ œ๊ณต
  • Underfetch: endpoint๊ฐ€ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์ถฉ๋ถ„ํžˆ ์ œ๊ณต ๋ชปํ•จ
    • ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๋ชจ๋‘ ํ™•๋ณดํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ด์•ผ ํ•จ
  • ํด๋ผ์ด์–ธํŠธ ๊ตฌ์กฐ ๋ณ€๊ฒฝ์‹œ endpoint ๋ณ€๊ฒฝ / ๋ฐ์ดํ„ฐ ์ˆ˜์ • ํ•„์š”
    • ์ž์›ํฌ๊ธฐ&ํ˜•ํƒœ๋ฅผ ์„œ๋ฒ„์—์„œ ๊ฒฐ์ •ํ•˜๋ฏ€๋กœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ง์ ‘ ๋ฐ์ดํ„ฐ ํ˜•ํƒœ ๊ฒฐ์ • ๋ถˆ๊ฐ€๋Šฅ

๐Ÿ“Žย REST API vs GraphQL

REST APIGraphQL
Resource์— ๋Œ€ํ•œ ํ˜•ํƒœ ์ •์˜ & ๋ฐ์ดํ„ฐ ์š”์ฒญ ๋ฐฉ๋ฒ• ์—ฐ๊ฒฐResource์— ๋Œ€ํ•œ ํ˜•ํƒœ ์ •์˜ & ๋ฐ์ดํ„ฐ ์š”์ฒญ ๋ฐฉ๋ฒ• ๋ถ„๋ฆฌ
Resource ํฌ๊ธฐ&ํ˜•ํƒœ๋ฅผ ์„œ๋ฒ„์—์„œ ๊ฒฐ์ •Resource ์ •๋ณด๋งŒ ์ •์˜,
ํ•„์š”ํ•œ ํฌ๊ธฐ&ํ˜•ํƒœ๋Š” ํด๋ผ์ด์–ธํŠธ ๋‹จ์—์„œ ์š”์ฒญ์‹œ ๊ฒฐ์ •
URL๊ฐ€ Resource๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ,
Method๊ฐ€ ์ž‘์—… ์œ ํ˜•์„ ๋‚˜ํƒ€๋ƒ„
GraphQL Schema๊ฐ€ Resource๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ,
Query, Mutation type์ด ์ž‘์—… ์œ ํ˜•์„ ๋‚˜ํƒ€๋ƒ„
์—ฌ๋Ÿฌ Resource์— ์ ‘๊ทผ์‹œ ์—ฌ๋Ÿฌ๋ฒˆ์˜ ์š”์ฒญ ํ•„์š”ํ•œ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ์—ฌ๋Ÿฌ Resource์— ์ ‘๊ทผ ๊ฐ€๋Šฅ
๊ฐ ์š”์ฒญ์€ ํ•ด๋‹น endpoint์— ์ •์˜๋œ ํ•ธ๋“ค๋ง ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ž‘์—… ์ฒ˜๋ฆฌ์š”์ฒญ ๋ฐ›์€ ๊ฐ ํ•„๋“œ์— ๋Œ€ํ•œ resolver๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ž‘์—… ์ฒ˜๋ฆฌ

๐Ÿ‘ย GraphQL ์žฅ์ 

  • ํ•˜๋‚˜์˜ endpoint ์š”์ฒญ
    • ํ•˜๋‚˜์˜ endpoint์ธ /graphql๋กœ ์š”์ฒญ์„ ๋ฐ›๊ณ , ์ด์— ๋”ฐ๋ผ query, mutation์„ resolver ํ•จ์ˆ˜๋กœ ์ „๋‹ฌํ•ด์„œ ์š”์ฒญ์— ์‘๋‹ต (๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์€ POST ๋ฉ”์†Œ๋“œ ์‚ฌ์šฉ)
  • No Under & OverFetching โŒ
    • ํ•˜๋‚˜์˜ endpoint์—์„œ ์ฟผ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ API์— ์š”์ฒญํ•˜๊ณ  ์‘๋‹ต์œผ๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ
  • ๊ฐ•๋ ฅํ•œ playground
    • playground GUI๋ฅผ ์ด์šฉํ•ด resolver & schema๋ฅผ ํ•œ๋ˆˆ์— ๋ณด๊ณ  ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
  • ํด๋ผ์ด์–ธํŠธ ๊ตฌ์กฐ ๋ณ€๊ฒฝ์—๋„ ์ง€์žฅ โŒ
    • ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒฐ์ •ํ•˜๊ณ  ๋ฐ›๋Š” ์ฃผ์ฒด๊ฐ€ ํด๋ผ์ด์–ธํŠธ์ด๋ฏ€๋กœ ์„œ๋ฒ„์— ์ง€์žฅ์—†์Œ
    • ํด๋ผ์ด์–ธํŠธ์—์„  ๋ฌด์Šจ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•œ์ง€๋งŒ ์š”๊ตฌ์‚ฌํ•ญ์„ ์ฟผ๋ฆฌ๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋จ

๐Ÿ‘Žย GraphQL ๋‹จ์ 

  • ์บ์‹ฑ์ด REST๋ณด๋‹ค ํ›จ์”ฌ ๋ณต์žก
    • POST ๋ฉ”์†Œ๋“œ๋งŒ ์ด์šฉํ•ด ์š”์ฒญ์„ ๋ณด๋‚ด๋ฏ€๋กœ ๊ฐ ๋ฉ”์†Œ๋“œ์— ๋”ฐ๋ฅธ ์บ์‹ฑ์„ ์ง€์›๋ฐ›์„ ์ˆ˜ ์—†์Œ
  • ๊ณ ์ •๋œ ์š”์ฒญ๊ณผ ์‘๋‹ต๋งŒ ํ•„์š”ํ•  ๊ฒฝ์šฐ์—” Query๋กœ ์ธํ•ด ์š”์ฒญ ํฌ๊ธฐ๊ฐ€ RESTful API๋ณด๋‹ค ๋” ์ปค์ง โฌ†๏ธ

๐Ÿ“Žย GraphQL ๊ตฌ์กฐ

  • Query: ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ (REST์˜ GET๊ณผ ์œ ์‚ฌ)
  • Mutation: ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ์ˆ˜์ •
    • Create: ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    • Update: ๊ธฐ์กด ๋ฐ์ดํ„ฐ ์ˆ˜์ •
    • Delete: ๊ธฐ์กด ๋ฐ์ดํ„ฐ ์‚ญ์ œ
  • Subscription: ํŠน์ • ์ด๋ฒคํŠธ ๋ฐœ์ƒ์‹œ ์„œ๋ฒ„๊ฐ€ ๋Œ€์‘ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „์†ก
    • ๋ฐœํ–‰/๊ตฌ๋…(pub/sub) ๋ชจ๋ธ์„ ๋”ฐ๋ฆ„

โœจย Query; ๋ฐ์ดํ„ฐ ์กฐํšŒ

๐Ÿ”นย Field

# query๋กœ ๋ฐ์ดํ„ฐ ์กฐํšŒ
{
	hero {
		name
		friends {
			name
		}
	}
}

# ํ•„๋“œ๋ฅผ ์ค‘์ฒฉํ•˜์—ฌ ์ฟผ๋ฆฌํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅ!
# ์œ„์˜ ์ฟผ๋ฆฌ ์‹คํ–‰ ๊ฒฐ๊ณผ
{
  "data": {
    "hero": {
      "name": "Beanxx",
			"friends": [   # friends ํ•„๋“œ -> ๋ฐฐ์—ด ๋ฐ˜ํ™˜
				{
					"name": "Yollki"
				},
				{
					"name": "Zzanggu"
				}
			]
    }
  }
}

๐Ÿ”นย ์ „๋‹ฌ์ธ์ž(Arguments)

# ํ•„๋“œ์— ์ธ์ˆ˜์ „๋‹ฌ ๋ถ€๋ถ„ ์ถ”๊ฐ€ โ†’ ์ฟผ๋ฆฌ์˜ ํ•„๋“œ/์ค‘์ฒฉ๋œ ๊ฐ์ฒด๋“ค์— ์ „๋‹ฌ โ†’ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์Œ
{
  human(id: "10") {
    name
    height
  }
}
# id๊ฐ€ 10์ธ human์˜ name, height ์ฟผ๋ฆฌ
{
  "data": {
    "human": {
      "name": "Beanxx",
      "height": 1.70
    }
  }
}

๐Ÿ”นย ๋ณ„๋ช…(Aliases)

# ํ•„๋“œ ์ด๋ฆ„ ์ค‘๋ณต ์‚ฌ์šฉ ๋ถˆ๊ฐ€ -> ๋ณ„๋ช…์„ ๋ถ™์—ฌ์„œ ์ฟผ๋ฆฌ ์ง„ํ–‰
{ 
  beanHero: hero(episode: BEAN) {
    name
  }
  zzangHero: hero(episode: ZZANG) {
    name
  }
}
# BeanHero, ZzangHero๊ฐ€ ๋ณ„๋ช…!
# ๋ณ„๋ช…์„ ๋ถ™์ธ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ
{
  "data": {
    "beanHero": {
      "name": "Beanxx"
    },
    "zzangHero": {
      "name": "Zzanggu"
    }
  }
}

# ๋‹ค๋ฅธ ๋ณ„๋ช… ์ง€์ • -> 1๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ 2๊ฐœ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ๋‘ ์–ป์–ด๋‚ผ ์ˆ˜ ์žˆ์Œ

๐Ÿ”นย Operation name

# ์ถ•์–‘ํ˜• ๊ตฌ๋ฌธ x -> ์‹ค์ œ ์•ฑ์—์„  ์ฝ”๋“œ ๋ชจํ˜ธํ•˜์ง€ ์•Š๊ฒŒ ์ž‘์„ฑํ•˜๋Š”๊ฒŒ ์ค‘์š”!
# ๋งจ ์•ž 'query'๊ฐ€ Operation name!
# query ์™ธ์—๋„ mutation, subscription, describes ๋“ฑ ์กด์žฌ

query HeroNameAndFriends {
  hero {
    name
    friends {
      name
    }
  }
}
{
  "data": {
    "hero": {
      "name": "Beanxx",
      "friends": [
        {
          "name": "Yollki"
        },
        {
          "name": "Zzanggu"
        },
        {
          "name": "Jane"
        }
      ]
    }
  }
}

๐Ÿ”นย ๋ณ€์ˆ˜(Variables)

# ๋™์ ์œผ๋กœ ์ธ์ˆ˜๋ฅผ ๋ฐ›์•„ ์ฟผ๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ '๋ณ€์ˆ˜' ์‚ฌ์šฉ
query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

# `$๋ณ€์ˆ˜ ์ด๋ฆ„: ํƒ€์ž…`์œผ๋กœ ์ž‘์„ฑ
# `$ep: Episode` ๋’ค์— `!`๊ฐ€ ๋ถ™์œผ๋ฉด ep๋Š” ๋ฐ˜๋“œ์‹œ Episode์—ฌ์•ผ ํ•จ์„ ์˜๋ฏธ (Optional)

โœจย Mutation; ๋ฐ์ดํ„ฐ ์ˆ˜์ •

# server์ธก ๋ฐ์ดํ„ฐ ์ˆ˜์ •
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

โœจย Schema / Type

# GraphQL Schema ๊ธฐ๋ณธ ๊ตฌ์„ฑ ์š”์†Œ: ๊ฐ์ฒด ์ข…๋ฅ˜, ๊ฐ์ฒด ์œ ํ˜•(ํฌํ•จํ•˜๋Š” ํ•„๋“œ๋ฅผ ๋‚˜ํƒ€๋ƒ„)
type Character {
  name: String!
  appearsIn: [Episode!]!
}

# Character: GraphQL ๊ฐ์ฒด ํƒ€์ž… => ์ฆ‰, ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ํƒ€์ž… ์˜๋ฏธ (์Šคํ‚ค๋งˆ ๋Œ€๋ถ€๋ถ„์˜ ํƒ€์ž… - ๊ฐ์ฒด ํƒ€์ž…)
# name, appearIn: Character ํƒ€์ž…์˜ ํ•„๋“œ
# String: ๋‚ด์žฅ๋œ ์Šค์นผ๋ผ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜๋กœ, ๋‹จ์ผ ์Šค์นผ๋ผ ๊ฐ์ฒด (์ฟผ๋ฆฌ์—์„œ ํ•˜์œ„ ์„ ํƒ ๊ฐ€์งˆ ์ˆ˜ ์—†์Œ)
# !: ํ•ด๋‹น ํ•„๋“œ๋Š” nullableํ•˜์ง€ ์•Š๊ณ  ๋ฐ˜๋“œ์‹œ ๊ฐ’์ด ๋“ค์–ด์˜จ๋‹ค๋Š” ์˜๋ฏธ
# [ ]: ๋ฐฐ์—ด ์˜๋ฏธ. ๋ฐฐ์—ด์—๋„ !๊ฐ€ ๋ถ™์„ ์ˆ˜ ์žˆ์Œ. -> ํ•ญ์ƒ 0๊ฐœ ์ด์ƒ์˜ ์š”์†Œ๋ฅผ ํฌํ•จํ•œ ๋ฐฐ์—ด ๊ธฐ๋Œ€

โœจย Resolver

: ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ๊ฒฐ์ •ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๋กœ์จ ๋กœ์ง์„ ์ž‘์„ฑํ•จ.
๐Ÿ‘‰ย ํ•ด๋‹น ์Šคํ‚ค๋งˆ ํ•„๋“œ์— ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜์˜ ์‹ค์ œ ํ–‰๋™์„ Resolver์—์„œ ์ •์˜

const db = require("./../db")
const resolvers = {
  Query: {   # ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
		getUser: async (_, { email, pw }) => {
			db.findOne({
				where: { email, pw }
			}) ... # ์‹ค์ œ DB์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๋Š” ๋กœ์ง ์ž‘์„ฑํ•˜๊ธฐ
			...
		}
  },
  Mutation: {   # ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ์ˆ˜์ •ํ•˜๊ธฐ
		createUser: async (_, { email, pw, name }) => {
			...
		}
  }
  Subscription: {   # ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ
    newUser: async () => {
      ...
		}
  }
};

๐Ÿ“Žย GraphQL Explorer๋กœ ์‹ค์Šต

  1. Github Explorer ์ ‘์†: https://docs.github.com/en/graphql/overview/explorer
  2. Github Login ํ›„, ์ฟผ๋ฆฌ ์ž‘์„ฑํ•ด๋ณด๊ธฐ
  3. Explorer ๋ฒ„ํŠผ ๋ˆ„๋ฅธ ํ›„, ์ง์ ‘ field ํƒ์ƒ‰ ๊ฐ€๋Šฅ

โ˜‘๏ธย [pair] React์—์„œ github GraphQL API ์‚ฌ์šฉํ•ด๋ณด๊ธฐ

์ฒ˜์Œ์— ํŽ˜์–ด๋‹˜๊ณผ GraphQL๋ฅผ React์— ์ ์šฉํ•ด์•ผํ• ์ง€ ๋ง‰๋ง‰ํ•ด์„œ ๊ฑฐ์˜ 1์‹œ๊ฐ„๋™์•ˆ ํ—ค๋งจ๋“ฏ..
graphql.js ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋ณด๊ณ  ๊ทธ๋ƒฅ App.js์— ๋„ฃ์–ด๋ดค๋Š”๋ฐ๋„ Error ๐Ÿ˜ฑ
์•Œ๊ณ  ๋ณด๋‹ˆ github ๋‚ด์—์„œ access token๋„ ๋ฐœ๊ธ‰๋ฐ›์•„์•ผ ํ•˜๊ณ , await๋กœ๋งŒ ์ž‘์„ฑ๋˜์–ด ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ async๋กœ ๊ฐ์‹ธ์ค˜์„œ ์•ฝ๊ฐ„ ์ˆ˜์ •ํ•ด์•ผ ํ–ˆ์—ˆ๋‹ค..! ๋ง‰ํŒ์— ๋ฐ›์•„์˜ค๋Š”๊ฑฐ ์„ฑ๊ณตํ•ด์„œ ์—ฌ๋Ÿฌ ์†์„ฑ๋“ค์„ console๋กœ ํ™•์ธํ•ด๋ณด์•˜๋‹ค.
์•„๋ž˜์™€ ๊ฐ™์ด data๋ฅผ edges ๋‚ด์— ๋ฐฐ์—ด ๋‚ด์˜ ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ๋ฐ›์•„์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

โœจย ์‹œ์ž‘ ํ•˜๊ธฐ ์•ž์„œ ์ผ๋‹จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜๋ถ€ํ„ฐ!

# graphQL์˜ ์ฟผ๋ฆฌ๋ฅผ ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ์‰ฝ๊ฒŒ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

$ npm install @octokit/graphql
import { graphql } from "@octokit/graphql";
import { useEffect, useState } from "react";

async function getRepository() {
  const { repository } = await graphql(
    `
      {
        repository(name: "agora-states-fe", owner: "codestates-seb") {
          discussions(first: 100) { // ๋ฐ์ดํ„ฐ ๋ช‡ ๊ฐœ ์ถœ๋ ฅํ• ์ง€
            edges {
              node {
                id
                title
                createdAt
                url
                author {
                  login
                  avatarUrl
                }
                category {
                  name
                }
                answer {
                  author {
                    login
                  }
                }
              }
            }
          }
        }
      }
    `,
    {
      headers: {
				// ๊ผญ ์•ž์— 'token' ๋ฌธ์ž์—ด ๋ถ™์—ฌ์ฃผ๊ธฐ (์•„๋‹ˆ๋ฉด 'bearer'๋„ ๊ฐ€๋Šฅ!)
        authorization: `token ${MY_TOKEN_ID}`,
      },
    }
  );

  return repository;
}

function App() {
  const [repository, setRepository] = useState({});
  const { discussions } = repository;

	// ์ฒ˜์Œ ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋„๋ก useEffect ์‚ฌ์šฉ!
  useEffect(() => {
    getRepository()
      .then((data) => {
        setRepository(data);
      })
      .catch((error) => {
        console.log(Error, error);
      });
  }, []);

	// map์œผ๋กœ discussions.edges์— ์ ‘๊ทผํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋ฟŒ๋ ค์ฃผ๊ธฐ
  return (
    <div className="main">
			<ul>
        {discussions.edges.map((edge) => {
          return (
            <li key={edge.node.id}>
                <img
                  src={edge.node.author.avatarUrl}
                  alt={`avatar of ${edge.node.author.login}`}
                />
                <div>
                  {`[${edge.node.category.name}]`}
                </div>
                  <a href={edge.node.url}>{edge.node.title}</a>
                <p>{edge.node.answer ? "โ˜‘" : "โ˜’"}</p>
            </li>
          );
        })}
      </ul>
    </div>
  );
}

export default App;
profile
FE developer

0๊ฐœ์˜ ๋Œ“๊ธ€