Skip to content

Graphql๊ณผ REST API, ๊ทธ๋ฆฌ๊ณ  BFFย #43

@yuiseo

Description

@yuiseo

๐Ÿ“ Graphql๊ณผ REST API, ๊ทธ๋ฆฌ๊ณ  BFF

๐Ÿ“š ์ฃผ์ œ:
GraphQL, REST API, ๊ทธ๋ฆฌ๊ณ  Backend for Frontend(BFF)์˜ ๊ฐœ๋…๊ณผ ์ฐจ์ด์ ์„ ๋น„๊ตํ•˜๊ณ , ๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ ๊ณผ ํ™œ์šฉ ์‚ฌ๋ก€๋ฅผ ์กฐ์‚ฌํ•œ๋‹ค.


๐ŸŽฏ ์Šคํ„ฐ๋”” ๋ชฉํ‘œ
GraphQL๊ณผ REST API์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ์ดํ•ดํ•˜๊ณ , BFF์˜ ํ•„์š”์„ฑ๊ณผ ์—ญํ• ์„ ๋ถ„์„ํ•˜์—ฌ ์‹ค๋ฌด์—์„œ ์ ์ ˆํ•œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.


๐Ÿ“– ํ•ต์‹ฌ ๋‚ด์šฉ:

Grpahql


GraphQL์€ ์ฟผ๋ฆฌ(Query) ๊ธฐ๋ฐ˜ API๋กœ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์„ ํƒ์ ์œผ๋กœ ์š”์ฒญ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹

  • ๋‹จ์ผ ์—”๋“œํฌ์ธํŠธ(/grpahql)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ ์„ ํƒ ํ•˜๊ณ , ํ•œ ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

์žฅ์ 

  • ์˜ค๋ฒ„ํŽ˜์นญ ๋ฐฉ์ง€ โ†’ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ
  • ์–ธ๋”ํŽ˜์นญ ๋ฐฉ์ง€ โ†’ ํ•œ ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ์—ฐ๊ด€ ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ
  • ์œ ์—ฐํ•œ API ํ™•์žฅ ๊ฐ€๋Šฅ โ†’ ํ•„๋“œ ์ถ”๊ฐ€ ์‹œ ๊ธฐ์กด API ๋ณ€๊ฒฝ ์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅ

๋‹จ์ 

  • ์ดˆ๊ธฐ ์„ค์ •์ด ๋ณต์žก โ†’ ์Šคํ‚ค๋งˆ ์ •์˜ ๋ฐ ๋ฐฑ์—”๋“œ ์„ค์ • ํ•„์š”
  • ์บ์‹ฑ์ด ์–ด๋ ต๋‹ค โ†’ ๋ชจ๋“  ์š”์ฒญ์ด POST๋กœ ์ฒ˜๋ฆฌ๋  ๊ฒฝ์šฐ CDN ์บ์‹ฑ ํ™œ์šฉ์ด ์–ด๋ ค์›€
  • N+1 ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ โ†’ ๋ฆฌ์กธ๋ฒ„ ์ตœ์ ํ™” ํ•„์š” (DataLoader ์‚ฌ์šฉ)

grpahql์˜ ๋ฆฌ์กธ๋ฒ„(resolver)?


๐Ÿ‘‰ย graphql์—์„œ ์ฟผ๋ฆฌ๋ฅผ ํ•ด์„ํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜

์ฆ‰, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฑ์—”๋“œ์—์„œ ์ฐพ์•„์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ญํ• 

๊ทผ๋ฐ ์™œ ๋ฆฌ์กธ๋ฒ„๊ฐ€ ๋” ๋งŽ์•„์ ธ?

  • graphql์€ ๊ฐ ํ•„๋“œ๋งˆ๋‹ค ๋ฆฌ์กธ๋ฒ„๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ!

  • ์˜ˆ์‹œ

    โœ” ์‚ฌ์šฉ์ž ์ •๋ณด์™€ ๊ฒŒ์‹œ๊ธ€์„ ํ•จ๊ป˜ ์š”์ฒญํ•˜๋Š” GraphQL ์ฟผ๋ฆฌ

    query {
      user(id: 1) {
        name
        posts {
          title
          content
        }
      }
    }
    

    โœ” ๋ฐฑ์—”๋“œ ๋ฆฌ์กธ๋ฒ„ (๊ฐ ํ•„๋“œ๋ณ„๋กœ ๋”ฐ๋กœ ์กด์žฌ)

    const resolvers = {
      Query: {
        user: (_, { id }) => getUserById(id) // ์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๋Š” ๋ฆฌ์กธ๋ฒ„
      },
      User: {
        posts: (parent) => getPostsByUserId(parent.id) // ์‚ฌ์šฉ์ž์˜ ๊ฒŒ์‹œ๊ธ€ ๊ฐ€์ ธ์˜ค๋Š” ๋ฆฌ์กธ๋ฒ„
      }
    };
    

    ๐Ÿ“Œ โžก user ๋ฆฌ์กธ๋ฒ„์™€ posts ๋ฆฌ์กธ๋ฒ„๊ฐ€ ๋”ฐ๋กœ ์žˆ์Œ!

    ๐Ÿ“Œ โžก ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ๋ฆฌ์กธ๋ฒ„๊ฐ€ ๊ณ„์† ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋จ.

    ๐Ÿ“Œ โžก ์œ ์ง€ ๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์›Œ์ง!!

    ๐Ÿ“Œ โžก ๋ฐ์ดํ„ฐ ํƒ€์ž…๋งˆ๋‹ค ๋ฆฌ์กธ๋ฒ„๊ฐ€ ๊ณ„์† ์ถ”๊ฐ€๋จ

    ๐Ÿ“Œโžก ์ฝ”๋“œ๊ฐ€ ๋งŽ์•„์ง€๊ณ , ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์›Œ์ง

    ๐Ÿ“Œโžก ๊ฐ ๋ฆฌ์กธ๋ฒ„๊ฐ€ ๋”ฐ๋กœ ๋™์ž‘ํ•˜๋‹ค ๋ณด๋ฉด N+1 ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ

Graphql์˜ N+1 ๋ฌธ์ œ


๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•  ๋•Œ ๋น„ํšจ์œจ์ ์ธ ์ฟผ๋ฆฌ ์‹คํ–‰์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜๋Š” ์„ฑ๋Šฅ ๋ฌธ์ œ์ธ๋ฐ, ๋ฆฌ์กธ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ๋ฐœ์ƒ

์˜ˆ์‹œ ์‚ฌ์šฉ์ž ๋ชฉ๋ก๊ณผ ๊ฐ ์‚ฌ์šฉ์ž์˜ ๊ฒŒ์‹œ๊ธ€ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” graphql ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑ

query {
  users {
    id
    name
    posts {
      id
      title
    }
  }
}

๐Ÿ’ก ์‹คํ–‰ ํ๋ฆ„ (๋ฌธ์ œ ๋ฐœ์ƒ ๊ณผ์ •)

  • users ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด 1๊ฐœ์˜ ์ฟผ๋ฆฌ ์‹คํ–‰

    SELECT * FROM users;  -- (1๊ฐœ์˜ ์ฟผ๋ฆฌ ์‹คํ–‰)
    
  • posts ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด N๊ฐœ์˜ ์ฟผ๋ฆฌ ์‹คํ–‰

    SELECT * FROM posts WHERE user_id = 1;  -- (N๋ฒˆ ์‹คํ–‰)
    SELECT * FROM posts WHERE user_id = 2;
    SELECT * FROM posts WHERE user_id = 3;
    ...
    SELECT * FROM posts WHERE user_id = N;
    

๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป

users ๋ฐ์ดํ„ฐ๋ฅผ ๋จผ์ € ๊ฐ€์ ธ์˜จ ํ›„, ๊ฐ ์‚ฌ์šฉ์ž๋ณ„ posts๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ๊ฐ€์ ธ์˜ค๋ฉด ์ด N+1๊ฐœ์˜ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒ(1๊ฐœ์˜ users ์กฐํšŒ + N๊ฐœ์˜ posts ์กฐํšŒ)

์‚ฌ์šฉ์ž๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก (N์ด ์ปค์งˆ์ˆ˜๋ก) ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์š”์ฒญ์ด ๊ธฐํ•˜๊ธ‰์ˆ˜์ ์œผ๋กœ ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋˜๊ณ , ์ด๋Š” ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ดˆ๋ž˜ํ•˜๊ฒŒ ๋œ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

  • DataLoader(Facebook์ด ๋งŒ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)๋ฅผ ํ™œ์šฉ - ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ
    • ์›๋ฆฌ๋Š” ์ฟผ๋ฆฌ ๊ฐฏ์ˆ˜๋ฅผ 1๊ฐœ๋กœ ์ค„์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ ์‹œํ‚ค๋Š” ๊ฒƒ
      • data loader ์‚ฌ์šฉ์‹œ batching(์ผ๊ด„ ์ฒ˜๋ฆฌ)์™€ caching(์บ์‹ฑ)์„ ์ ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ ๊ฐฏ์ˆ˜๋ฅผ ์ค„์ด๋Š” ๊ฒƒ

REST API


REST API๋Š” ๋ฆฌ์†Œ์Šค ๊ธฐ๋ฐ˜์˜ HTTP ์š”์ฒญ ๋ฐฉ์‹์œผ๋กœ, ๊ฐ ๋ฆฌ์†Œ์Šค(๋ฐ์ดํ„ฐ)์— ๋Œ€ํ•ด ๊ณ ์ •๋œ ์—”๋“œํฌ์ธํŠธ(URL)์„ ์‚ฌ์šฉํ•ด ์š”์ฒญํ•˜๋Š” ๋ฐฉ์‹

์žฅ์ 

  • ๊ตฌ์กฐ๊ฐ€ ๋‹จ์ˆœํ•˜๊ณ  ์ต์ˆ™ํ•จ (RESTful ์›์น™ ๋”ฐ๋ฆ„)
  • ์บ์‹ฑ ํ™œ์šฉ ๊ฐ€๋Šฅ (๋ธŒ๋ผ์šฐ์ € ๋ฐ CDN ์ง€์›)
  • HTTP ์ƒํƒœ ์ฝ”๋“œ ํ™œ์šฉ ๊ฐ€๋Šฅ (200, 404, 500 ๋“ฑ)

๋‹จ์ 

  • ์˜ค๋ฒ„ํŽ˜์นญ(Over-fetching) โ†’ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๊นŒ์ง€ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Œ
  • ์–ธ๋”ํŽ˜์นญ(Under-fetching) โ†’ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๋ฒˆ ์š”์ฒญํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Œ
  • API ๋ฒ„์ „ ๊ด€๋ฆฌ ํ•„์š” โ†’ ์ƒˆ๋กœ์šด ํ•„๋“œ ์ถ”๊ฐ€ ์‹œ /v1/, /v2/ ๊ฐ™์€ ์—”๋“œํฌ์ธํŠธ ์ถ”๊ฐ€ ํ•„์š”

์˜ค๋ฒ„ ํŽ˜์นญ

๐Ÿ‘‰ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ณด๋‹ค ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๋ฌธ์ œ

์ฆ‰, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์›ํ•˜์ง€ ์•Š๋Š” ์ •๋ณด๊นŒ์ง€ ๊ฐ™์ด ์ „๋‹ฌ๋˜๋Š” ์ƒํ™ฉ

์˜ˆ์‹œ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„(name)๋งŒ ํ•„์š”ํ•œ ์ƒํ™ฉ

REST ๋ฐฉ์‹ (์˜ค๋ฒ„ํŽ˜์นญ ๋ฌธ์ œ ๋ฐœ์ƒ)


GET /users/1
{
  "id": 1,
  "name": "Alice",
  "email": "[email protected]",
  "age": 30,
  "address": {
    "street": "123 Main St",
    "city": "Seoul"
  },
  "phone": "010-1234-5678"
}
  • ํด๋ผ์ด์–ธํŠธ๋Š” ์ด๋ฆ„(name)๋งŒ ํ•„์š”ํ•œ๋ฐ,
  • ์ด๋ฉ”์ผ, ๋‚˜์ด, ์ฃผ์†Œ, ์ „ํ™”๋ฒˆํ˜ธ ๋“ฑ ๋ถˆํ•„์š”ํ•œ ์ •๋ณด๊นŒ์ง€ ๋ฐ›์•„์˜ด
  • ๐Ÿ‘‰ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก์œผ๋กœ ๋„คํŠธ์›Œํฌ ๋น„์šฉ ์ฆ๊ฐ€ & ์„ฑ๋Šฅ ์ €ํ•˜

GraphQL ๋ฐฉ์‹ (์˜ค๋ฒ„ํŽ˜์นญ ํ•ด๊ฒฐ)


query {
  user(id: 1) {
    name
  }
}
{
  "data": {
    "user": {
      "name": "Alice"
    }
  }
}

์˜ค์ง ํ•„์š”ํ•œ name ํ•„๋“œ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฒ„ํŽ˜์นญ ๋ฐฉ์ง€ ๊ฐ€๋Šฅ! ๐Ÿ”ฅ

์–ธ๋”ํŽ˜์นญ

๐Ÿ‘‰ย ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•˜๊ณ , ์—ฌ๋Ÿฌ ๋ฒˆ ์š”์ฒญํ•ด์•ผ ํ•˜๋Š” ๋ฌธ์ œ

์˜ˆ์‹œ ํด๋ผ์ด์–ธํŠธ์—์„œ ํŠน์ • ์‚ฌ์šฉ์ž(user)์™€ ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ๊ฒŒ์‹œ๊ธ€(posts)์„ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ๋‹ค๋ฉด?

REST ๋ฐฉ์‹


  1. GET /users/1 โ†’ ์‚ฌ์šฉ์ž ์ •๋ณด ์š”์ฒญ
  2. GET /users/1/posts โ†’ ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ๊ฒŒ์‹œ๊ธ€ ์š”์ฒญ

์ด๋ ‡๊ฒŒ 2๋ฒˆ์˜ API ํ˜ธ์ถœ์ด ํ•„์š”

๋งŒ์•ฝ ์‚ฌ์šฉ์ž์™€ ๋Œ“๊ธ€ ์ •๋ณด๊นŒ์ง€ ๊ฐ™์ด ๊ฐ€์ ธ์˜ค๋ ค๋ฉด 3๊ฐœ์˜ ์š”์ฒญ์ด ํ•„์š”!

์–ธ๋”ํŽ˜์นญ์€ API๊ฐ€ ๋ถ„๋ฆฌ ๋˜์–ด ์žˆ์–ด ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐœ์ƒ

GraphQL ๋ฐฉ์‹


query {
  user(id: 1) {
    name
    posts {
      title
      comments {
        text
      }
    }
  }
}

ํ•œ ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ.

BFF(backend for frontend)


  • ํ”„๋ก ํŠธ์—”๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์„ฑ์— ๋งž๊ฒŒ ๋ฐฑ์—”๋“œ API๋ฅผ ์ œ๊ณตํ•˜๋Š” ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด
  • BFF๋Š” ํŠน์ • ํ”„๋ก ํŠธ์—”๋“œ ํด๋ผ์ด์–ธํŠธ(์›น, ๋ชจ๋ฐ”์ผ, ๋ฐ์Šคํฌํ†ฑ ๋“ฑ)์— ์ตœ์ ํ™”๋œ API๋ฅผ ์ œ๊ณต
  • BFF๋Š” ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ ์‚ฌ์ด์˜ '๋งž์ถคํ˜• API ๋ ˆ์ด์–ด' ์—ญํ• 

๐Ÿ“Œ BFF์™€ GraphQL ๋น„๊ต

ย  BFF GraphQL
๊ฐœ๋ฐœ ๋ฐฉ์‹ ํ”„๋ก ํŠธ์—”๋“œ๋ณ„ ๋งž์ถคํ˜• API ์„œ๋ฒ„ ๊ฐœ๋ฐœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ ์š”์ฒญ ๊ฐ€๋Šฅ
๋ฐ์ดํ„ฐ ๊ฐ€๊ณต BFF๊ฐ€ ๋ฐ์ดํ„ฐ ์กฐํ•ฉ ๋ฐ ๊ฐ€๊ณต GraphQL ์„œ๋ฒ„๊ฐ€ ๋ฐ์ดํ„ฐ ์กฐํ•ฉ
์˜ค๋ฒ„ํŽ˜์นญ ํ•ด๊ฒฐ ๋งž์ถค API ์ œ๊ณต ์›ํ•˜๋Š” ํ•„๋“œ๋งŒ ์š”์ฒญ ๊ฐ€๋Šฅ
์–ธ๋”ํŽ˜์นญ ํ•ด๊ฒฐ ์—ฌ๋Ÿฌ API๋ฅผ ์กฐํ•ฉํ•ด์„œ ์ œ๊ณต ํ•œ ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ œ๊ณต

๐Ÿ“Œ BFF vs GraphQL ์ฐจ์ด์ 

  • GraphQL์€ ๋‹จ์ผ API๋กœ ํ•ด๊ฒฐ, ํ•˜์ง€๋งŒ BFF๋Š” ๋งž์ถคํ˜• API ์„œ๋ฒ„๋ฅผ ์ถ”๊ฐ€๋กœ ์šด์˜
  • GraphQL์„ ์‚ฌ์šฉํ•˜๋ฉด BFF ์—†์ด๋„ ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์š”์ฒญ ๊ฐ€๋Šฅ

๐Ÿ’ก ์ฐธ๊ณ  ์ž๋ฃŒ:
https://graphql.org/learn/
https://tech.kakao.com/posts/364
https://blog.toktokhan.dev/rest-api-vs-graphql-7348f54a220b
https://blog.postman.com/graphql-vs-rest/
https://fe-developers.kakaoent.com/2022/220310-kakaopage-bff/
https://docs.github.com/ko/rest/about-the-rest-api/comparing-githubs-rest-api-and-graphql-api?apiVersion=2022-11-28
https://kosaf04pyh.tistory.com/341

Metadata

Metadata

Assignees

Labels

๋ฐฑ์—”๋“œ์งˆ๋ฌธ ๋ฆฌ์ŠคํŠธ์˜ ์ง๋ฌด ๊ตฌ๋ถ„์„ ์œ„ํ•œ ๋ผ๋ฒจํ”„๋ก ํŠธ์—”๋“œ์งˆ๋ฌธ ๋ฆฌ์ŠคํŠธ์˜ ์ง๋ฌด ๊ตฌ๋ถ„์„ ์œ„ํ•œ ๋ผ๋ฒจ

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions