arrow-left

All pages
gitbookPowered by GitBook
1 of 4

Loading...

Loading...

Loading...

Loading...

REST

hashtag
A. REST

REST API ๋งตํ•‘์—๋Š” subscribe๋ฅผ ์ œ์™ธํ•œ call, publish, map ์ปค๋„ฅํ„ฐ๋ฅผ ์ด์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

basePath๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ดํ•˜ REST ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

description์€ ๋ฌธ์„œ ์ƒ์„ฑ์‹œ ํ™œ์šฉ๋˜๋ฉฐ Markdown์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค (์˜ต์…˜).

Call

GET /players/1 ์š”์ฒญ์ด player.get ์•ก์…˜์„ { id: 1 } ํŽ˜์ด๋กœ๋“œ์™€ ํ•จ๊ป˜ ํ˜ธ์ถœํ•˜๊ณ  ์„ฑ๊ณต์‹œ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

depreacted๋Š” ๋ฌธ์„œ ์ƒ์„ฑ์‹œ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค (์˜ต์…˜).

๋ผ์šฐํŠธ path๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ทœ์น™์€ ๋ฅผ ์ฐธ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GET /players/me ์š”์ฒญ์ด player.get ์•ก์…˜์„ { id: <์ธ์ฆ ์ปจํ…์ŠคํŠธ์˜ player.id> } ์ •๋ณด๋กœ๋ถ€ํ„ฐ ํŽ˜์ด๋กœ๋“œ์™€ ํ•จ๊ป˜ ํ˜ธ์ถœํ•˜๊ณ  ์„ฑ๊ณต์‹œ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Map

๋˜๋Š” map ์ปค๋„ฅํ„ฐ (Inline JavaScript Function String)๋ฅผ ํ†ตํ•ด ์ธ์ฆ ์ปจํ…์ŠคํŠธ์˜ player ๊ฐ์ฒด๋ฅผ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„์— ๋‹ค์‹œ ๋‹ค๋ฃจ๋Š” Inline JavaScript Function String์€ API Gateway์˜ Node.js VM ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ ํ•ด์„๋ฉ๋‹ˆ๋‹ค.

Publish

POST /players/1 (body: { message: "blabla" }) ์š”์ฒญ์€ player.message ์ด๋ฒคํŠธ๋ฅผ { userId: id: <์ธ์ฆ ์ปจํ…์ŠคํŠธ์˜ player.id>, message: "blabla" } ํŽ˜์ด๋กœ๋“œ์™€ ํ•จ๊ป˜ publishํ•˜๊ณ  ์„ฑ๊ณต์‹œ ๋ฐœ์†ก๋œ ํŽ˜์ด๋กœ๋“œ๋ฅผ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค.

Params

REST API์˜ params ๋งตํ•‘์—๋Š” @path, @body, @query, @context ๊ฐ์ฒด๋ฅผ ์ด์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

hashtag

GraphQL

hashtag
B. GraphQL

GraphQL API ๋งตํ•‘์—๋Š” call, publish, subscribe, map ์ปค๋„ฅํ„ฐ๋ฅผ ์ด์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

TypeDefs

    REST: {
      basePath: "/players",
      description: "player service REST API",
      routes: [
path-to-regexparrow-up-right
GraphQL ํ”„๋กœํ† ์ฝœ์—์„œ typeDefs ์†์„ฑ์— ์„œ๋น„์Šค์— ํ•„์š”ํ•œ ์ •์˜(scalar๋ฅผ ์ œ์™ธํ•œ ํƒ€์ž…, ์ธํ„ฐํŽ˜์ด์Šค, ์—ด๊ฑฐํ˜• ๋“ฑ ๋ชจ๋“  ํ˜•ํƒœ)์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ธฐ์กด ํƒ€์ž…(API Gateway์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ํƒ€์ž…๊ณผ ๋ถ„์‚ฐ ์„œ๋น„์Šค์—์„œ ์ œ๊ณตํ•œ ํƒ€์ž…๋“ค)์„ ํ™•์žฅ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Resolvers

์ดํ•˜ ๋ฆฌ์กธ๋ฒ„์— ๊ฐ ํƒ€์ž…๋“ค์˜ ํ•„๋“œ๋ฅผ call, publish, subscribe, map ์ปค๋„ฅํ„ฐ์— ๋งตํ•‘ํ•ฉ๋‹ˆ๋‹ค.

๋ฆฌ์กธ๋ฒ„๊ฐ€ ํ• ๋‹น๋˜์ง€ ์•Š์€ ํ•„๋“œ๋“ค์€ source ๊ฐ์ฒด์—์„œ ๋™์ผํ•œ ์ด๋ฆ„์˜ ์†์„ฑ์œผ๋กœ๋ถ€ํ„ฐ ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค.

Call

GraphQL API์˜ Query ๋ฐ Mutation ํƒ€์ž…์˜ ํ•„๋“œ๋“ค์—๋Š” publish ๋ฐ call ๋˜๋Š” map ์ปค๋„ฅํ„ฐ๋ฅผ ์ด์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. params ๋งตํ•‘์—๋Š” @source, @args, @context, @info๋ฅผ ์ด์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Map

GraphQL ํ”„๋กœํ† ์ฝœ์—์„œ map ์ปค๋„ฅํ„ฐ (Inline JavaScript Function String)๋Š” ๊ฐ„๋žตํ•˜๊ฒŒ field: { map: <FN_STRING> } ๋Œ€์‹ ์— field: <FN_STRING> ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„์ฒ˜๋Ÿผ Union, Interface ๊ตฌํ˜„ ํƒ€์ž…์„ ํ•ด์„ํ•˜๊ธฐ ์œ„ํ•œ ํŠน์ˆ˜ ํ•„๋“œ์—๋„ Inline JavaScript Function String๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Batched Call (DataLoader)

์œ„์ฒ˜๋Ÿผ ์ธ์ฆ ์ •๋ณด๋ฅผ ํฌํ•จํ•œ @context๋‚˜ GraphQL ํ•„๋“œ ์ธ์ž์ธ @args๋ฅผ ํ™œ์šฉํ•ด ๋™์ผํ•œ ์•ก์…˜์„ ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๋งตํ•‘ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ call ๋ฉ”์†Œ๋“œ๋Š” GraphQL ์š”์ฒญ์—์„œ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฌ์šด N+1 ์ฟผ๋ฆฌ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์š”์ฒญ์„ ๋ฐฐ์น˜๋กœ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (ref. Dataloaderarrow-up-right)

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

์œ„์™€ ๊ฐ™์€ GraphQL ์š”์ฒญ์€ player.get ์•ก์…˜์„ { id: [context.user.player.id, 1, 2, 3], ...(other common params) } ํŽ˜์ด๋กœ๋“œ์™€ ํ•จ๊ป˜ ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์—ฐ๊ฒฐ๋œ ์•ก์…˜์ด [{ ... }, { ... }, { ... }, { ... }] ๋ฌถ์Œ์œผ๋กœ ์‘๋‹ต์„ ์ฃผ๋ฉด ๊ฐ ํ•„๋“œ์— ํ•ด๋‹นํ•˜๋Š” ์‘๋‹ต์ด ํ• ๋‹น๋ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ id: 3์ธ ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋ฐฐ์น˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณผ์ •์—์„œ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ์ œ์–ด ํ๋ฆ„์„ ๋ฉˆ์ถ”๋Š” ๋Œ€์‹ ์—, ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒํ‚ค์ง€์•Š๊ณ  ๋ฐฐ์น˜ ์‘๋‹ต์— ํฌํ•จ์‹œํ‚ค๊ณ  ๋‚˜๋จธ์ง€ ์ œ์–ด ํ๋ฆ„์„ ๋งˆ๋ฌด๋ฆฌํ•ฉ๋‹ˆ๋‹ค. [{ ... }, { ... }, { ... }, { message: "...", isBatchError: true, ... }] ์ฒ˜๋Ÿผ isBatchError: true ์†์„ฑ์„ ๊ฐ–๋Š” ์—๋Ÿฌ ๊ฐ์ฒด๋ฅผ ์‘๋‹ต์— ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

Subscribe

GraphQL API์˜ Subscription ํƒ€์ž…์˜ ํ•„๋“œ์—์„œ๋Š” subscribe ์ปค๋„ฅํ„ฐ๋ฅผ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. params ๋งตํ•‘์—๋Š” ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ @source, @args, @context, @info๋ฅผ ์ด์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. @source์— ์ด๋ฒคํŠธ ๊ฐ์ฒด๊ฐ€ ๋งตํ•‘๋ฉ๋‹ˆ๋‹ค.

@source ๊ฐ์ฒด๋Š” { event, payload }๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. Broker์— ๋”ฐ๋ผ ๊ธฐํƒ€ ์†์„ฑ์ด ์ถ”๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

subscribe ์ปค๋„ฅํ„ฐ์—์„œ๋Š” ์œ„์ฒ˜๋Ÿผ ์ˆ˜์‹ ๋œ ์ด๋ฒคํŠธ ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋‹ค์‹œ map ์ปค๋„ฅํ„ฐ๋กœ ๋ณ€ํ™˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. subscribe ์ปค๋„ฅํ„ฐ ์•ˆ์—์„œ map ์ปค๋„ฅํ„ฐ๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์ด๋ฒคํŠธ ๊ฐ์ฒด ์ „์ฒด(source)๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

        {
          method: "GET",
          path: "/:id",
          deprecated: false,
          description: "Get player information by id",
          call: {
            action: "player.get",
            params: {
              id: "@path.id",
            },          
          },
        },
        {
          method: "GET",
          path: "/me",
          deprecated: false,
          description: "Get player information of mine",
          call: {
            action: "player.get",
            params: {
              id: "@context.user.player.id",
            },          
          },
        },
        {
          method: "GET",
          path: "/me",
          deprecated: false,
          description: "Get player information of mine",
          map: `({ path, query, body, context }) => context.user.player`,
        },
        {
          method: "POST",
          path: "/message",
          deprecated: false,
          description: "Push notifications to all players",
          publish: {
            event: "player.message",
            broadcast: false,
            params: {
              userId: "@context.user.player.id",
              message: "@body.message",
            },
          },
        },
      ],
    },
// @body ๊ฐ์ฒด ์ „์ฒด๋ฅผ ํŽ˜์ด๋กœ๋“œ๋กœ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜ ์ŠคํŠธ๋ฆผ์„ ์ „๋‹ฌ ํ•  ๋•Œ ์ด์šฉ๋ฉ๋‹ˆ๋‹ค.
params: "@body",

// @ ๋ฌธ์ž์—ด๋กœ ์‹œ์ž‘๋˜์ง€ ์•Š๋Š” ๊ฐ’๋“ค์€ ํ•ด์„๋˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.
params: {
  foo: "@path.foo", // will bar parsed
  bar: "query.bar", // will be "query.bar"
  zzz: ["any", { obj: "ject", can: "be", "use": 2 }],
},

// ํ•ญ์ƒ string ํƒ€์ž…์„ ๊ฐ–๋Š” @query, @path ๊ฐ์ฒด์˜ ์†์„ฑ๋“ค์— ํ•œํ•ด์„œ ํƒ€์ž…์„ boolean์ด๋‚˜ number๋กœ ๋ณ€ํ™˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
params: {
  foo: "@path.foo:number",
  bar: "@query.bar:boolean",
},
    GraphQL: {
      typeDefs: `
        """Soccer Player"""
        type Player implements Node {
          id: ID!
          email: String!
          name: String!
          photoURL: String
          position: String
          """A team player belongs to"""
          team: Team
        }

        extend type Query {
          """Current Player"""
          viewer: Player
          player(id: ID!): Player
        }

        extend type Subscription {
          playerMessage: String!
          playerUpdated: Player
        }
      `,
      resolvers: {
        Player: {
          team: {
            call: {
              action: "team.get",
              params: {
                id: "@source.teamId",
              },
            },
          },
          position: `({ source, args, context, info }) => source.position.toUpperCase()`,
`
          // be noted that special field __isTypeOf got only three arguments
          __isTypeOf: `({ source, context, info }) => return source.someSpecialFieldForThisType != null`,

          // be noted that special field __resolveType got only three arguments
          __resolveType: `
            ({ source, context, info }) => {
              if (source.someSpecialFieldForThisType != null) {
                return "TypeA";
              } else {
                return "TypeB";
              }
            }
          `,
        },
        Query: {
          viewer: {
            call: {
              action: "player.get",
              params: {
                id: "@context.user.player.id[]",
              },
            },
          },
          player: {
            call: {
              action: "player.get",
              params: {
                id: "@args.id[]",
              },
            },
          },
        },
query {
  viewer {
    id
    email
  }
  one: player(id: 1) {
    id
    email
  }
  two: player(id: 2) {
    id
    email
  }
  three: player(id: 3) {
    id
    email
  }
}
        Subscription: {
          playerMessage: {
            subscribe: {
              events: ["player.message"],
            },
          },
          playerMessage: {
            subscribe: {
              events: ["player.message"],
              map: `({ source, args, context, info }) => source.payload.message`,
            },
          },
        },
      },
    },

WebSocket

hashtag
C. WebSocket

    WebSocket: {
      // TODO: WIP
    },

hashtag

Protocol Plugin

  protocol: {

์ดํ•˜ protocol ํ•ญ๋ชฉ์— ์™ธ๋ถ€์— ์ œ๊ณตํ•˜๋ ค๋Š” API๋ฅผ ์ž‘์„ฑํ•˜๊ณ  call, publish, subscribe, map ์ปค๋„ฅํ„ฐ์— ๋งตํ•‘ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์ปค๋„ฅํ„ฐ์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ๋‚ด์šฉ์€ ์•„๋ž˜ Connectors for API Handler ์„น์…˜์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค.