arrow-left

Only this pageAll pages
gitbookPowered by GitBook
1 of 84

English

Loading...

Quick Start

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

API Gateway

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Service API Schema

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Development

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Miscellaneous

Loading...

Loading...

Loading...

Loading...

Loading...

Configurations

API Gateway constructor options.

hashtag
1. APIGatewayOptions

APIGatewayOptions type is a kind of container for all the subordinate components' options.

name

default

description

hashtag

hashtag
1.1. APIGatewayOwnOptions

Options for the gateway itself rather inner components.

hashtag
2. ServiceBrokerOptions

Service Broker options are consist of common properties and delegator specific properties. The common properties show below.

hashtag
2.1. ServiceRegistryOptions

hashtag
2.2. BatchingPoolOptions

hashtag
2.3. InlineFunctionOptions

hashtag
2.4. ReporterOptions

hashtag
2.5. ServiceBrokerDelegatorConstructorOptions

Specific options for the Service Broker Delegator. Can choose only one among supported delegators. Currently delegator is supported.

hashtag

hashtag
2.5.1. MoleculerServiceBrokerDelegatorOptions

delegator can be configured with moleculer broker own options and few extra options like below.

hashtag

hashtag
3. SchemaRegistryOptions

Service Registry options are consist of own options for the registry itself and Protocol, Policy, this two type of Plugin constructor options.

hashtag
3.1. ProtocolPluginConstructorOptions

hashtag
3.2. PolicyPluginConstructorOptions

hashtag
4. APIServerOptions

...

hashtag
5. LoggerConstructorOptions

moleculer-api

A dynamic API Gateway which updates REST endpoints, GraphQL schema, WebSocket handlers and access control policies by integrating metadata of discovered remote services.

moleculer-api๋Š” MSA ํ™˜๊ฒฝ์—์„œ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๋“ค์˜ API ์Šคํ‚ค๋งˆ ์กฐ๊ฐ์„ ์ˆ˜์ง‘ํ•˜๊ณ , ๋ฌด์ค‘๋‹จ์œผ๋กœ ํ†ตํ•ฉ API๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ œ๊ณตํ•˜๋Š” ์›น ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.

์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋Š” ๋ถ„์‚ฐ ์„œ๋น„์Šค ํ”„๋กœ์‹œ์ €์˜ ํ˜ธ์ถœ, ๋˜๋Š” ์ค‘์•™ ๋ฉ”์‹œ์ง• ์„œ๋น„์Šค์— ๋Œ€ํ•œ ์ด๋ฒคํŠธ ๋ฐœํ–‰ ๋ฐ ๊ตฌ๋…์„ ์‘์šฉ ํ”„๋กœํ† ์ฝœ(REST, GraphQL, WebSocket ๋“ฑ)์— ๋งตํ•‘ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋Š” JSON ํฌ๋งท์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ์œผ๋ฉฐ, ์‘์šฉ ํ”„๋กœํ† ์ฝœ ๋ณ„ API ๋งตํ•‘๊ณผ ๊ทธ์— ๋Œ€ํ•œ ์ ‘๊ทผ ์ œ์–ด๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

hashtag

type APIGatewayOptions = {
  brokers: RecursivePartial<ServiceBrokerOptions>[],
  schema: RecursivePartial<SchemaRegistryOptions>,
  server: RecursivePartial<APIServerOptions>,
  logger: LoggerConstructorOptions,
} & RecursivePartial<APIGatewayOwnOptions>;

Options for remote service reporter instance which reports API integration status, error or logging in inline function sandbox, etc.

log.event

true

Enable logging event messages.

log.call

true

Enable logging remote procedure call.

Maximum number of examples for a single action (remote procedure).

examples.limitPerEvents

10

Maximum number of examples for a single event.

examples.streamNotation

*STREAM*

Replace stream request and response of an example to given string.

examples.omittedNotation

*OMITTED*

Truncate example object's string property and append given suffix for...

examples.omittedLimit

100

The strings longer than given length.

examples.redactedNotation

*REDACTED*

React example object's string property to given string for...

examples.redactedParamNameRegExps

[

/password/i,

/secret/i,

/credential/i,

/key/i,

Matched strings with given regular expressions.

healthCheck

-

Options for the ServiceRegistry health check feature.

healthCheck.intervalSeconds

10

Health check for every given intervals.

Maximum number of entries for a single batch.

Given Moleculer.ServiceSchema[] would be registered on moleculer service broker started. This is for the testing convenience.

A PolicyPlugin handles access controls (authorization) while calling internal services' procedure, publishing and subscribing event messages.

brokers

-

Service Broker constructor options. Can configure multiple brokers for a single gateway. Service Broker discovers remote services and works as a delegator for calling remote service procedures and also deals with central event messages.

schema

-

Schema Registry constructor options. Schema Registry handles the integration of remote service API schema and creates API handlers. Can disable or configure detailed options for each API Schema Plugins like GraphQL, REST and WebSocket, etc.

server

-

API Server constructor options. Can configure API Server update policy, server components (HTTP, WebSocket server) and network interface (HTTP, HTTPS) detailed options, middleware options and request context factory options.

logger

-

Global logger options. Currently winstonarrow-up-right logger is supported.

name

default

description

skipProcessEventRegistration

false

Set true to not to set default handlers for process interrupt signals like SIGINT.

name

default

description

registry

-

Options for the ServiceRegistry which collect remote services, available procedures and event types, etc.

batching

-

Options for batching feature which utilize data-loaderarrow-up-right for concurrent multiple procedure calls.

function

-

Options for inline function (JS function notation string) sandbox.

reporter

name

default

description

examples

-

Options for the ServiceRegistry action (remote procedure), event example collecting feature.

examples.processIntervalSeconds

5

Consume example queue for every given intervals.

examples.queueLimit

50

Example queue size.

examples.limitPerActions

name

default

description

batchingKey

(hash function)

A keygen function for the key of same batching arguments. Create hash string with args object by default.

entryKey

(hash function)

A keygen function for the variable params of each batched entries.

failedEntryCheck

entry => entry && entry.batchingError

A function to determine whether each entries are failed or not in a batch response. By default, a remote procedure which supports batching should response { ..., batchingError: true } object for failed entry.

entriesLimit

name

default

description

util

{}

Any kind of object which can be accessed as global util variable from inline functions.

name

default

description

tableWidthZoomFactor

1

A reporter sends a report which is consist of raw messages and a string that prints messages as a shape of a table. For that table, set a zoom factor of the width.

name

default

description

moleculer

-

moleculerarrow-up-right Service Broker Delegator options.

name

default

description

batchedCallTimeout

(return 5-60s based on item count)

A function to calculate the timeout options for a batched call.

streamingCallTimeout

3600000 (1 hour)

A timeout options for a streaming call (ms).

streamingToStringEncoding

base64

An encoding which is used to transform streaming data to buffer data. This is for the case that non-root property of payload is a readable stream. Because moleculer service broker can pipe only a single streaming data at once in a single params, a gateway needs to transform non-root streaming data to buffer data before proxy the action call. Try to check your moleculer service broker transporter options and this option for a malformed streaming data issue.

services

name

default

description

maxVersions

10

Maximum number of old versions for each branches.

maxUnusedSeconds

1800

Maximum unused duration until deleting non-master branches.

protocol

-

A ProtocolPlugin handles mapping Public API to calling internal services' procedure, publishing and subscribing event messages.

policy

moleculerarrow-up-right
moleculerarrow-up-right

-

10

100

[]

-

์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ ์˜ˆ์‹œ
{
  branch: "master",

Gateway๋Š” ํŠน์ • ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ์˜ ์ถ”๊ฐ€, ์ œ๊ฑฐ ๋ฐ ์—…๋ฐ์ดํŠธ์‹œ ๊ธฐ์กด ํ†ตํ•ฉ API ์Šคํ‚ค๋งˆ์— ๋ณ‘ํ•ฉ์„ ์‹œ๋„ํ•˜๊ณ , ์„ฑ๊ณต์‹œ ๋ฌด์ค‘๋‹จ์œผ๋กœ ๋ผ์šฐํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉฐ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์›๊ฒฉ ์„œ๋น„์Šค์— ๋‹ค์‹œ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค.

Gateway์˜ ๋ฌด์ค‘๋‹จ ์—”๋“œํฌ์ธํŠธ ์—…๋ฐ์ดํŠธ ๋ฐ ๋ณด๊ณ 

hashtag
์ฃผ์š” ๊ธฐ๋Šฅ

  • ๋ถ„์‚ฐ ์„œ๋น„์Šค์˜ API ์Šคํ‚ค๋งˆ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ๋ณ‘ํ•ฉํ•˜์—ฌ API๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธ

  • Polyglot ํ•˜๊ฑฐ๋‚˜ ์„œ๋กœ ๋‹ค๋ฅธ ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค์— ๊ธฐ๋ฐ˜ํ•œ ์„œ๋น„์Šค๋“ค์˜ API ์Šคํ‚ค๋งˆ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ๋ณ‘ํ•ฉ ํ•  ์ˆ˜ ์žˆ์Œ

  • ๊ฐœ๋ฐœ ํŽธ์˜ ๋ฐ ์ถฉ๋Œ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ API ๋ธŒ๋žœ์นญ ๋ฐ ๋ฒ„์ €๋‹ ๊ธฐ๋Šฅ

  • ์ƒํƒœ ๊ฒ€์‚ฌ ๋ฐ API ๋ฌธ์„œ ์ƒ์„ฑ (WIP)

  • ๋ฏธ๋“ค์›จ์–ด ๋ฐฉ์‹์˜ ์š”์ฒญ ํ๋ฆ„ ์ œ์–ด

    • Error

    • Logging

  • ๋ฏธ๋“ค์›จ์–ด ๋ฐฉ์‹์˜ ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ ์ œ์–ด

    • Authn/Authz

    • Locale

  • ์‘์šฉ ํ”„๋กœํ† ์ฝœ ํ”Œ๋Ÿฌ๊ทธ์ธ

    • REST

    • GraphQL

  • ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ… ํ”Œ๋Ÿฌ๊ทธ์ธ

    • OAuth2 scope ๊ธฐ๋ฐ˜ ์ ‘๊ทผ ์ œ์–ด

    • JavaScript ๊ธฐ๋ฐ˜ ์ ‘๊ทผ ์ œ์–ด

hashtag
License

The project is available under the MIT licensearrow-up-right.

arrow-up-right
arrow-up-right
arrow-up-right
arrow-up-right
arrow-up-right
arrow-up-right
type APIGatewayOwnOptions = {
  skipProcessEventRegistration: boolean;
};
type ServiceBrokerOptions = {
  registry: ServiceRegistryOptions;
  batching: BatchingPoolOptions;
  function: InlineFunctionOptions;
  reporter: ReporterOptions;
  log: {
    event: boolean;
    call: boolean;
  },
} & ServiceBrokerDelegatorConstructorOptions;
type ServiceRegistryOptions = {
  examples: {
    processIntervalSeconds: number;
    queueLimit: number;
    limitPerActions: number;
    limitPerEvents: number;
    streamNotation: string;
    omittedNotation: string;
    omittedLimit: number;
    redactedNotation: string;
    redactedParamNameRegExps: RegExp[];
  };
  healthCheck: {
    intervalSeconds: number;
  };
};
type BatchingPoolOptions = {
  batchingKey: (...args: any[]) => any;
  entryKey: (batchingParams: any) => any;
  failedEntryCheck: (entry: any) => boolean;
  entriesLimit: number;
};
type InlineFunctionOptions = {
  util: {[key: string]: any};
};
type ReporterOptions = {
  tableWidthZoomFactor: number;
};
type ServiceBrokerDelegatorConstructorOptions = {
  moleculer?: MoleculerServiceBrokerDelegatorOptions;
  [otherDelegatorKey]?: any;
};
import * as Moleculer from "moleculer";

type MoleculerServiceBrokerDelegatorOptions = Moleculer.BrokerOptions & {
  batchedCallTimeout: (itemCount: number) => number;
  streamingCallTimeout: number;
  streamingToStringEncoding: "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex";
  services: Moleculer.ServiceSchema[];
};
type SchemaRegistryOptions = {
  maxVersions: number;
  maxUnusedSeconds: number;
  protocol: ProtocolPluginConstructorOptions,
  policy: PolicyPluginConstructorOptions,
};
type ProtocolPluginConstructorOptions = {
  GraphQL: RecursivePartial<GraphQLProtocolPluginOptions> | false;
  REST: RecursivePartial<RESTProtocolPluginOptions> | false;
  WebSocket: RecursivePartial<WebSocketProtocolPluginOptions> | false;
}
export type PolicyPluginConstructorOptions = {
  scope: RecursivePartial<ScopePolicyPluginOptions> | false;
  filter: RecursivePartial<FilterPolicyPluginOptions> | false;
};
{
  branch: "master",
  policy: {},
  protocol: {
    GraphQL: {
      typeDefs: `
        extend type Query {
          storage: StorageQuery!
        }
    
        type StorageQuery {
          files(offset: Int = 0, limit: Int = 10): FileList!
          file(id: ID!): File
        }
    
        type File {
          id: ID!
          name: String!
          contentType: String!
          tags: JSON!
          private: Boolean!
          byteSize: Int!
          size: String!
          url(expiryHours: Int = 2, prompt: Boolean = false, promptAs: String): String!
          updatedAt: DateTime!
          createdAt: DateTime!
        }
    
        type FileList {
          offset: Int!
          limit: Int!
    
          # it is not exact value
          total: Int!
          entries: [File!]!
          hasNext: Boolean!
          hasPrev: Boolean!
        }
    
        extend type Mutation {
          storage: StorageMutation!
        }
    
        type StorageMutation {
          upload(file: Upload!): File!
          update(id: ID!, name: String, tags: JSON, private: Boolean): File!
          delete(id: ID!): Boolean!
        }
      `,
      resolvers: {
        Query: {
          storage: `() => ({})`,
        },
        Mutation: {
          storage: `() => ({})`,
        },
        StorageMutation: {
          upload: {
            call: {
              action: "storage.create",
              params: {
                file: "@args.file",
                meta: {
                  allowedContentTypes: [],
                  private: false,
                },
              },
            },
          },
          update: {
            call: {
              action: "storage.update",
              params: {},
            },
          },
          delete: {
            call: {
              action: "storage.delete",
              params: {},
            }
          },
        },
        StorageQuery: {
          file: {
            call: {
              action: "storage.find",
              params: {
                id: "@args.id[]",
              },
            },
          },
          files: {
            call: {
              action: "storage.get",
              params: {
                offset: "@args.offset",
                limit: "@args.limit",
              },
            },
          },
        },
        File: {
          url: {
            call: {
              action: "storage.getURL",
              params: {
                id: "@source.id[]",
                expiryHours: "@args.expiryHours",
                prompt: "@args.prompt",
                promptAs: "@args.promptAs",
              },
            },
          },
          size: (({ source }: any) => {
            const { byteSize = 0 } = source;
            const boundary = 1000;
            if (byteSize < boundary) {
              return `${byteSize} B`;
            }
            let div = boundary;
            let exp = 0;
            for (let n = byteSize / boundary; n >= boundary; n /= boundary) {
              div *= boundary;
              exp++;
            }
            const size = byteSize / div;
            return `${isNaN(size) ? "-" : size.toLocaleString()} ${"KMGTPE"[exp]}B`;
          }).toString(),
        },
        FileList: {
          hasPrev: `({ source }) => source.offset > 0`,
        }
      },
    },
  },
}
{
  branch: "master",
  policy: {
    call: [
      {
        actions: ["user.update"],
        description: "A user can update the user profile which is belongs to own account.",
        scope: ["user.write"],
        filter: (({ context, params }) => {
          console.log(context);
          if (params.id) {
            return context.auth.identity.sub === params.id;
          }
          return false;
        }).toString(),
      },
    ],
  },
  protocol: {
    GraphQL: {
      typeDefs: `
        extend type Query {
          viewer: User
          user(id: Int, identityId: String, username: String, where: JSON): User
        }
  
        extend type Mutation {
          createUser(input: UserInput!): User!
          updateUser(input: UserInput!): User!
        }
  
        input SportsUserInput {
          id: Int
          username: String
          birthdate: Date
          gender: Gender
          name: String
          pictureFileId: String
        }
        
        type User {
          id: Int!
          username: String!
          name: String!
          birthdate: Date
          gender: Gender
          pictureFile: File
        }
      `,
      resolvers: {
        User: {
          pictureFile: {
            call: {
              if: "({ source }) => !!source.pictureFileId",
              action: "storage.find",
              params: {
                id: "@source.pictureFileId[]",
              },
            },
          },
        },
        Mutation: {
          createUser: {
            call: {
              action: "user.create",
              params: "@args.input",
              implicitParams: false,
            },
          },
          updateUser: {
            call: {
              action: "user.update",
              params: "@args.input",
              implicitParams: false,
            },
          },
        },
      },
    },
  },
}
{
  branch: "master",
  policy: {},
  protocol: {
    REST: {
      description: "update user's FCM registration token",
      basePath: "/notification",
      routes: [
        {
          method: "PUT",
          path: "/update-token",
          call: {
            action: "notification.updateToken",
            params: {
              identityId: "@context.auth.identity.sub",
              token: "@body.token",
            },
          },
        },
      ],
    },
  },
}
{
  branch: "master",
  policy: {},
  protocol: {
    WebSocket: {
      basePath: "/chat",
      description: "...",
      routes: [
        /* bidirectional streaming chat */
        {
          path: "/message-stream/:roomId",
          call: {
            action: "chat.message.stream",
            params: {
              roomId: "@path.roomId",
            },
          },
        },
        /* pub/sub chat */
        {
          path: "/message-pubsub/:roomId",
          subscribe: {
            events: `({ path }) => ["chat.message." + path.roomId]`,
          },
          publish: {
            event: `({ path }) => "chat.message." + path.roomId`,
            params: "@message",
          },
        },
        /* pub/sub video */
        {
          path: "/video-pubsub",
          subscribe: {
            events: ["chat.video"],
          },
          publish: {
            event: "chat.video",
            params: {
              id: "@context.id",
              username: "@query.username",
              data: "@message",
            },
            filter: `({ params }) => params.id && params.username && params.data`,
          },
        },
        /* streaming video */
        {
          path: "/video-stream/:type",
          call: {
            action: "chat.video.stream",
            params: {
              id: "@context.id",
              type: "@path.type",
            },
          },
        },
      ],
    },
  },
}
/token/i,

]

Body Parser
  • Helmet

  • CORS

  • Serve Static File

  • HTTP/HTTPS/HTTP2

  • (ํ™•์žฅ ๊ฐ€๋Šฅ)

  • Correlation ID
  • IP Address

  • User-Agent

  • Request

  • (ํ™•์žฅ ๊ฐ€๋Šฅ)

  • WebSocket
  • (ํ™•์žฅ ๊ฐ€๋Šฅ)

  • (ํ™•์žฅ ๊ฐ€๋Šฅ)
    policy: {},
    protocol: {
    REST: {
    basePath: "/storage",
    routes: [
    {
    path: "/",
    method: "GET",
    call: {
    action: "storage.get",
    params: {
    offset: "@query.offset:number",
    limit: "@query.limit:number",
    },
    },
    },
    {
    path: "/upload",
    method: "POST",
    call: {
    action: "storage.create",
    params: {
    file: "@body.file",
    meta: {
    tags: {
    identityId: "@context.auth.identity.sub",
    },
    allowedContentTypes: ["text/*", "image/*", "application/pdf"],
    private: false,
    },
    },
    },
    },
    {
    path: "/upload-stream",
    method: "POST",
    description: "not a production purpose, need a wrapper action to make this safe",
    call: {
    action: "storage.createWithStream",
    params: "@body.file",
    implicitParams: false,
    },
    },
    {
    path: "/download/:id",
    method: "GET",
    call: {
    action: "storage.getURL",
    params: {
    id: "@path.id",
    expiryHours: "@query.expiryHours:number",
    prompt: "@query.prompt:boolean",
    promptAs: "@query.promptAs:string",
    },
    map: `({ response }) => ({
    $status: response ? 303 : 404,
    $headers: response ? { "Location": response } : undefined,
    })`,
    },
    },
    {
    path: "/:id",
    method: "GET",
    call: {
    action: "storage.find",
    params: {
    id: "@path.id",
    },
    map: `({ response }) => ({
    $status: response ? 200 : 404,
    $body: response,
    })`,
    },
    },
    {
    path: "/:id",
    method: "PUT",
    call: {
    action: "storage.update",
    params: {
    id: "@path.id",
    // id, name, tags, private, contentType
    },
    },
    },
    {
    path: "/:id",
    method: "DELETE",
    call: {
    action: "storage.delete",
    params: {
    id: "@path.id",
    },
    },
    },
    ],
    },
    },
    }
    FBAC; Function Based Access Controlarrow-up-right

    WebSocket Video Server/Client

    Build Status
    Coverage Status
    David
    Known Vulnerabilities
    NPM version

    API Server

    hashtag
    Request Lifecycle

    hashtag
    1. Extension

    Server Extension์€ ์›น ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑ ๋ฐ ํ™•์žฅํ•˜๋Š”๋ฐ ์“ฐ์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ƒ์„ฑ ์˜ต์…˜์„ ํ†ตํ•ด ์ œ์–ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    HTTP

    HTTP Extension์€ HTTP ํ”„๋กœํ† ์ฝœ์„ ์ง€์›ํ•˜๋„๋ก ์›น ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.

    HTTP/2

    HTTP/2 Extension์€ HTTP/2 ํ”„๋กœํ† ์ฝœ์„ ์ง€์›ํ•˜๋„๋ก ์›น ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    TLS

    TLS Extension์€ HTTPS ์š”์ฒญ์„ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์›น ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    2. Middleware

    ์—”๋“œํฌ์ธํŠธ๋กœ์˜ ๋ผ์šฐํŒ… ์ „/ํ›„์— ๋ฏธ๋“ค์›จ์–ด๋“ค์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ƒ์„ฑ ์˜ต์…˜์„ ํ†ตํ•ด ์ œ์–ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    hashtag
    A. Before Middleware

    ์—”๋“œํฌ์ธํŠธ๋กœ์˜ ๋ผ์šฐํŒ… ์ „์— ์ˆ˜ํ–‰๋˜๋Š” ๋ฏธ๋“ค์›จ์–ด์ž…๋‹ˆ๋‹ค.

    Static

    Static Middleware๋Š” ์ตœ์ดˆ๋กœ ์ˆ˜ํ–‰๋˜๋Š” ๋ฏธ๋“ค์›จ์–ด์ž…๋‹ˆ๋‹ค. ํ™œ์„ฑํ™”์‹œ API Gateway ์„œ๋น„์Šค ๋…ธ๋“œ์˜ ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์• ์…‹ ํŒŒ์ผ๋“ค์„ ์„œ๋น™ํ•ฉ๋‹ˆ๋‹ค.

    Body Parser

    Body Parser๋Š” application/json, application/x-www-form-url-encoded, multipart/form-data Content Type ์š”์ฒญ์˜ ๋ฐ”๋””๋ฅผ JavaScript ๊ฐ์ฒด๋กœ ํ•ด์„ํ•ฉ๋‹ˆ๋‹ค.

    Cookie Parser

    Cookie Parser๋Š” Cookie ํ—ค๋”๋ฅผ JavaScript ๊ฐ์ฒด๋กœ ํ•ด์„ํ•˜๊ณ  ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

    CORS

    CORS ๋ฏธ๋“ค์›จ์–ด๋Š” ๊ต์ฐจ ์ถœ์ฒ˜ ์ž์› ๊ณต์œ ์— ๋Œ€ํ•œ ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์— ๋”ฐ๋ผ Preflight ์š”์ฒญ์— ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค.

    Helmet

    Helmet ๋ฏธ๋“ค์›จ์–ด๋Š” ์„œ๋ฒ„์— CSP, HSTS ๋“ฑ ๊ธฐ์ดˆ์ ์ธ ๋ณด์•ˆ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    B. Context Factory

    ์—”๋“œํฌ์ธํŠธ๋กœ์˜ ๋ผ์šฐํŒ… ์ง์ „์— Context Factory ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ†ตํ•ด ์š”์ฒญ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Context๋Š” API Handler์— ์ „๋‹ฌ๋˜๋Š” ์ธ์ฆ, Locale, Language, TimeZone ๋“ฑ์˜ ์ •๋ณด๋ฅผ ํฌํ•จํ•œ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

    Locale

    Locale ์ปจํ…์ŠคํŠธ๋Š” ์š”์ฒญ ํ—ค๋”๋กœ๋ถ€ํ„ฐ Locale, Language, TimeZone ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.

    Auth

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

    hashtag
    C. After Middleware

    ์—”๋“œํฌ์ธํŠธ๋กœ์—์„œ์˜ ์‘๋‹ต ํ›„์— ์ˆ˜ํ–‰๋˜๋Š” ๋ฏธ๋“ค์›จ์–ด์ž…๋‹ˆ๋‹ค.

    Error

    Error ๋ฏธ๋“ค์›จ์–ด๋Š” ๋ถ„์‚ฐ ์„œ๋น„์Šค์˜ ์—๋Ÿฌ๋ฅผ ํŒŒ์•…ํ•˜๊ณ  HTTP ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ์„ค์ •ํ•˜๋ฉฐ ํ‘œ์ค€ํ™”๋œ ์—๋Ÿฌ ํฌ๋งท์˜ ์‘๋‹ต์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    ETag

    ETag ๋ฏธ๋“ค์›จ์–ด๋Š” GET, HEAD ์š”์ฒญ ๋ฐ ์‘๋‹ต(200 OK)์—์„œ If-None-Match ๋ฐ ETag ํ—ค๋”๋ฅผ ํ™œ์šฉํ•ด API ํด๋ผ์ด์–ธํŠธ์˜ ์„ฑ๋Šฅ์„ ๋†’ํž™๋‹ˆ๋‹ค.

    Header

    Header ๋ฏธ๋“ค์›จ์–ด๋Š” Header Context์— ๊ฐ’์ด ํ• ๋‹น๋œ ๊ฒฝ์šฐ ์‘๋‹ต ํ—ค๋”๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    3. API Handler

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

    hashtag
    A. Dynamic Handler

    ๋™์  ํ•ธ๋“ค๋Ÿฌ๋Š” Gateway์— ์ƒ์„ฑ๋œ ๋ธŒ๋žœ์น˜์— ๋”ฐ๋ผ ์š”์ฒญ์„ ๋ธŒ๋žœ์น˜ ํ•ธ๋“ค๋Ÿฌ๋กœ ํ”„๋ก์‹œํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    B. Branch Handler

    ๋ธŒ๋žœ์น˜ ํ•ธ๋“ค๋Ÿฌ๋Š” Gateway์— ์ƒ์„ฑ๋œ ํ•ด๋‹น ๋ธŒ๋žœ์น˜์˜ ํƒœ๊ทธ์— ๋”ฐ๋ผ ์š”์ฒญ์„ ๋ฒ„์ „ ํ•ธ๋“ค๋Ÿฌ๋กœ ํ”„๋ก์‹œํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    C. Version Handler

    ๋ฒ„์ „ ํ•ธ๋“ค๋Ÿฌ๋Š” Gateway API ์Šคํ‚ค๋งˆ์— ๋”ฐ๋ผ ์ปค๋„ฅํ„ฐ ๋ฐ ํ”„๋กœํ† ์ฝœ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์กฐํ•ฉํ•ด ์ƒ์„ฑํ•œ ํ•ธ๋“ค๋Ÿฌ๋กœ ์š”์ฒญ์„ ํ”„๋ก์‹œํ•ฉ๋‹ˆ๋‹ค.

    moleculer-iamarrow-up-right

    Application

    Component

    HTTP

    WebSocket

    Auth

    Cookie

    Context Factory

    Correlation ID

    Middleware

    Locale

    Error

    Helmet

    Logging

    Body Parser

    CORS

    Request

    HTTP

    http, https, http2 (WIP)

    User-Agent

    HTTPS

    GraphQL

    hashtag
    B. GraphQL

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

    TypeDefs

    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. )

    ํ•œ ์ปจํ…์ŠคํŠธ์—์„œ ์—ฌ๋Ÿฌ๋ฒˆ ํ˜ธ์ถœ๋˜๋Š” ์•ก์…˜์— ๋ฐฐ์นญ์„ ์ง€์›ํ•˜๋ฉด ์‘๋‹ต ์†๋„๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ๋†’ํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฐ์นญ์„ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” 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)๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

        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
            }
          `,
    Dataloaderarrow-up-right
          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`,
                },
              },
            },
          },
        },

    Manipulating HTTP Response

    Service Broker Delegator

    Streaming Request/Response

    hashtag
    1. Streaming Request

    Handling streaming request is up to delegator. To support streaming request, delegator should handle { createReadStream(): ReadableStream, ...meta: any } params as their own way.

    hashtag
    1.1. REST file upload example

    When multipart/form-data request with 'file' field is mapped to above REST API schema. REST protocol plugin will delegate action call with params as below.

    hashtag
    1.2. GraphQL file upload example

    When is mapped to above GraphQL API schema. GraphQL protocol plugin will delegate action call with params as below.

    For WebSocket protocol, see 4.

    hashtag
    2. Streaming Response

    Handling streaming response is up to protocol plugins. For REST protocol, any response from delegator like below will be handled as stream response with { "Content-Type": "application/octet-stream", "Transfer-Encoding": "chunked" } headers.

    For GraphQL protocol, above features just ignored.

    For WebSocket protocol, see 4.

    hashtag
    3. Modify Response Header/Status

    Also for REST protocol, can modify response with $headers, status properties in delegator response.

    It is not only for the streaming response. below delegator response will also modify response headers.

    For GraphQL protocol, this feature is just ignored. For WebSocket protocol, this feature is just ignored.

    hashtag
    4. Bidirectional Streaming

    WebSocket protocol supports bidirectional streaming. // TODO: documentation

    GraphQL multipart/form-data requestarrow-up-right
    {
      method: "POST",
      action: "file.upload",
      params: "@body.file",
    }
    {
      filename: string;
      encoding: "utf8"|"7bit"|"base64"|...;
      mimetype: string;
      createReadStream(): ReadableStream;
    }
    {
        typeDefs: `
            extend type Mutation {
                uploadFile(file: Upload): JSON
            }
        `,
        resolvers: {
          Mutation:  {
            uploadFile: {
              call: "file.upload", 
              params: "@args.file",
            },
          },
        },
    }
    {
      filename: string;
      encoding: "utf8"|"7bit"|"base64"|...;
      mimetype: string;
      createReadStream(): ReadableStream;
    }
    {
      createReadStream(): ReadableStream,
      ...,
    }
    {
      createReadStream(): ReadableStream,
      $headers: {
        "Content-Type": "text/html; charset=utf-8",
      },
    }
    {
      $headers: {
        "Status": "301 Moved Permanently",
        "Location": "https://some.where.to.go.com",
      },
      $status: 302,
    }

    Overview

    hashtag
    Development

    hashtag
    1. Yarn Scripts

    • yarn dev [example=simple] - Start development (nodemon with ts-node)

    • yarn build- Uses typescript to transpile service to javascript

    • yarn lint - Run TSLint

    • yarn test - Run tests & generate coverage report

    • yarn test --watch - Watch and run tests

    hashtag
    Contribution

    Please send pull requests improving the usage and fixing bugs, improving documentation and providing better examples, or providing some testing, because these things are important.

    Moleculer

    Protocol Plugin

    Bidirectional Streaming

    Application Component

    API Server

    Project Roadmap

    REST protocol plugin
  • HTTP protocol which mounts HTTP/WS components' modules
    CORS (enabled, including WebSocket)
  • [] HTTP2, HTTP2S

  • RFC7235arrow-up-right

    Contributors

    hashtag
    Maintainers

    hashtag
    Contributors

    Nobody yet

    hashtag

    REST

    GraphQL Resolver with DataLoader

    Quick Examples

    hashtag
    Examples

    ์˜ˆ์‹œ ์ฝ”๋“œ๋Š” ๋ฅผ ์ฐธ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ../src/examplesarrow-up-right

    REST File Upload with streaming

    REST Endpoints

    WebSocket

    Get Started

    Configure and run the gateway server.

    hashtag
    1. Install the package

    yarn add moleculer-api
    npm i --save moleculer-api

    Add moleculer-api package from npm.

    hashtag
    2. Run the gateway

    And write entry script like above. For now, let's skip setting detailed options.

    hashtag
    3. Check the log and few endpoints

    With default options configuration, A gateway will run on localhost 8080 port with HTTP protocol. And basic placeholder scheme for GraphQL plugin, playground for GraphQL and a default status check endpoint will be set.

    • An endpoint to show the status each API integrations. It will show the integration status from oldest version to latest version with detailed integrations belong to each versions by each branches (only master branch initially).

    • A playground endpoint which is set from GraphQL Protocol Plugin.

    WebSocket Chat Server/Client

    GraphQL type extension and reference

    WebSocket Video Broadcasting

    http://localhost:8080/~statusarrow-up-right
    http://localhost:8080/graphqlarrow-up-right
    import { APIGateway, APIGatewayOptions } from "moleculer-api";
    
    const options: APIGatewayOptions = {
        // ...
    };
    const gateway = new APIGateway(options);
    gateway.start()
        .then(() => {
            // ...
        });
    
    const { APIGateway } = require("moleculer-api");
    
    const options = {
        // ...
    };
    const gateway = new APIGateway(options);
    gateway.start()
        .then(() => {
            // ...
        });
    
    2020-11-17T07:25:17.944Z info dwkimqmit.local/broker[0]/moleculer: Moleculer v0.14.11 is starting...
    2020-11-17T07:25:17.946Z info dwkimqmit.local/broker[0]/moleculer: Namespace: <not defined>
    2020-11-17T07:25:17.947Z info dwkimqmit.local/broker[0]/moleculer: Node ID: dwkimqmit.local-58669
    2020-11-17T07:25:17.947Z info dwkimqmit.local/broker[0]/moleculer: Strategy: RoundRobinStrategy
    2020-11-17T07:25:17.947Z info dwkimqmit.local/broker[0]/moleculer: Discoverer: LocalDiscoverer
    2020-11-17T07:25:17.948Z info dwkimqmit.local/broker[0]/moleculer: Serializer: JSONSerializer
    2020-11-17T07:25:17.953Z info dwkimqmit.local/broker[0]/moleculer: Validator: FastestValidator
    2020-11-17T07:25:17.954Z info dwkimqmit.local/broker[0]/moleculer: Registered 13 internal middleware(s).
    2020-11-17T07:25:17.964Z info dwkimqmit.local/server: gateway context factories have been applied id, ip, locale, cookie, userAgent, request, auth
    2020-11-17T07:25:17.968Z info dwkimqmit.local/server: gateway server middleware have been applied: cors, bodyParser, logging, error
    2020-11-17T07:25:17.969Z info dwkimqmit.local/server/application: gateway server application has been started: http<HTTPRoute>, ws<WebSocketRoute>
    2020-11-17T07:25:17.971Z info dwkimqmit.local/server: gateway server protocol has been started: http
    2020-11-17T07:25:17.971Z info dwkimqmit.local/server: gateway server has been started and listening on: http://localhost:8080, ws://localhost:8080
    2020-11-17T07:25:17.972Z info dwkimqmit.local/schema: schema policy plugin has been started: filter, scope
    2020-11-17T07:25:17.972Z info dwkimqmit.local/schema: schema protocol plugin has been started: GraphQL, REST, WebSocket
    2020-11-17T07:25:17.973Z info dwkimqmit.local/schema: master (0 services) branch has been created
    2020-11-17T07:25:17.991Z info dwkimqmit.local/schema/master: master (0 services) branch succeeded 1239eb4a (0 schemata, 0 routes) -> 7a2db312 (0 schemata, 4 routes) version compile:
    (+) /graphql (http:POST): GraphQL HTTP operation endpoint
    (+) /graphql (ws): GraphQL WebSocket operation endpoint
    (+) /graphql (http:GET): GraphQL Playground endpoint
    (+) /~status (http:GET): master branch introspection endpoint
    2020-11-17T07:25:17.991Z info dwkimqmit.local/schema: master (0 services) branch has been updated
    2020-11-17T07:25:17.992Z info dwkimqmit.local/schema: schema registry has been started
    2020-11-17T07:25:17.993Z info dwkimqmit.local/broker[0]/moleculer: '$api' service is registered.
    2020-11-17T07:25:17.995Z info dwkimqmit.local/broker[0]/moleculer: Service '$api' started.
    2020-11-17T07:25:17.997Z info dwkimqmit.local/broker[0]/moleculer: '$node' service is registered.
    2020-11-17T07:25:17.997Z info dwkimqmit.local/broker[0]/moleculer: Service '$node' started.
    2020-11-17T07:25:17.998Z info dwkimqmit.local/broker[0]/moleculer: Service '$api' started.
    2020-11-17T07:25:17.998Z info dwkimqmit.local/broker[0]/moleculer: โœ” ServiceBroker with 2 service(s) is started successfully in 3ms.
    2020-11-17T07:25:17.999Z info dwkimqmit.local/broker[0]: service broker has been started: moleculer
    2020-11-17T07:25:19.997Z info dwkimqmit.local/server/application: master (0 services) handler mounted for http<HTTPRoute> component
    2020-11-17T07:25:19.998Z info dwkimqmit.local/server/application: master (0 services) handler mounted for ws<WebSocketRoute> component
    {
      "branch": "master",
      "latestUsedAt": "2020-11-17T07:45:03.322Z",
      "parentVersion": null,
      "latestVersion": "7a2db312",
      "versions": [
        {
          "version": "7a2db312",
          "fullVersion": "7a2db312e34f6105efb5ec107137763b",
          "routes": [
            "/graphql (http:POST): GraphQL HTTP operation endpoint",
            "/graphql (ws): GraphQL WebSocket operation endpoint",
            "/graphql (http:GET): GraphQL Playground endpoint",
            "/~status (http:GET): master branch introspection endpoint"
          ],
          "integrations": []
        },
        {
          "version": "1239eb4a",
          "fullVersion": "1239eb4a8416af46c0448426b51771f5",
          "routes": [],
          "integrations": []
        }
      ],
      "services": []
    }
    

    Authorization

    GraphQL

    Authentication

    Parse OIDC/OAuth2 context

    Access Control with Auth token scopes

    Access Control with Auth token claims

    Overview

    Project Architecture Diagram

    Schema Registry

    hashtag
    A. Schema Integration

    ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋Š” ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค์— ์˜์กดํ•ด Gateway๋กœ ์ˆ˜์ง‘๋˜๊ณ  ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. Moleculer ์–ด๋Œ‘ํ„ฐ๋กœ ์ž‘์„ฑ๋œ ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค์˜ ๊ฒฝ์šฐ ๊ธฐ๋ณธ์ ์œผ๋กœ ServiceSchema์˜ metadata.api ํ•„๋“œ์—์„œ ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๊ฐ€ ์ˆ˜์ง‘๋˜๊ธฐ๋ฅผ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค.

    ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ์˜ ๋ณ‘ํ•ฉ์€ ์„œ๋น„์Šค ๋…ธ๋“œ์˜ ์—ฐ๊ฒฐ, ์ข…๋ฃŒ, ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์‹œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋Š” ์ž์‹ ์˜ ์Šคํ‚ค๋งˆ๋ฅผ ๋ณ‘ํ•ฉ ํ•  ๋ธŒ๋žœ์น˜๋ฅผ ์Šคํ‚ค๋งˆ์— ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค. Gateway์—๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ master ๋ธŒ๋žœ์น˜๊ฐ€ ์ƒ์„ฑ๋˜์–ด์žˆ์œผ๋ฉฐ, master ๋ธŒ๋žœ์น˜๋Š” ์ œ๊ฑฐ ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

    Access Control with IP address

    Connenctor

    hashtag
    1. Connectors

    hashtag
    A. Context Connectors

    ์š”์ฒญ ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ API ์š”์ฒญ์— ํ™œ์šฉ๋˜๋Š” ์ปค๋„ฅํ„ฐ์ž…๋‹ˆ๋‹ค. ์ ‘๊ทผ ์ œ์–ด ๋ฐ stateless ์ปค๋„ฅํ„ฐ๋ฅผ ์—ฐ๊ณ„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    GraphQL์˜ Subscription ํƒ€์ž…์ด๋‚˜ WebSocket ํ”„๋กœํ† ์ฝœ ๋“ฑ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฑฐ๋‚˜, ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์— ์ค‘์•™ ๋ฉ”์‹œ์ง• ์„œ๋น„์Šค๋ฅผ ์ œ๊ณต ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ์—” publish, subscribe ์ปค๋„ฅํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    B. Stateless Connectors

    ์š”์ฒญ ์ƒํƒœ๊ฐ€ ์—†๋Š” ์ปค๋„ฅํ„ฐ์ž…๋‹ˆ๋‹ค.

    hashtag

    map

    ์ค‘์•™ ๋ฉ”์‹œ์ง• ์„œ๋น„์Šค์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค.

    ๋ถ„์‚ฐ ์„œ๋น„์Šค ๋ฐ ์•ก์…˜, ์ค‘์•™ ๋ฉ”์‹œ์ง• ์„œ๋น„์Šค์˜ ์ƒํƒœ ํ™•์ธ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

    reporter

    O

    ์ถœ์ฒ˜ ๋…ธ๋“œ๋กœ ๋””๋ฒ„๊ทธ ๋ฉ”์„ธ์ง€๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

    logger

    O

    Gateway์˜ ๋กœ๊น… ์ธ์Šคํ„ด์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

    ์ปค๋„ฅํ„ฐ

    ์–ด๋Œ‘ํ„ฐ์— ์œ„์ž„

    ์—ฐ๊ฒฐ ๊ฐ€๋Šฅํ•œ ์ปค๋„ฅํ„ฐ

    ๊ฐœ์š”

    call

    O

    params, map

    ๋ถ„์‚ฐ ์„œ๋น„์Šค ์•ก์…˜์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

    publish

    O

    params

    ์ค‘์•™ ๋ฉ”์‹œ์ง• ์„œ๋น„์Šค์— ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    subscribe

    ์ปค๋„ฅํ„ฐ

    ์–ด๋Œ‘ํ„ฐ์— ์œ„์ž„

    ๊ฐœ์š”

    map

    X

    Inline JavaScript Function String์„ VM์—์„œ ํ•ด์„ํ•˜์—ฌ ์ฃผ์–ด์ง„ ๊ฐ์ฒด๋‚˜ ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    params

    X

    ์š”์ฒญ ํŽ˜์ด๋กœ๋“œ์—์„œ ์œ„์˜ ํƒ€ ์ปค๋„ฅํ„ฐ๋“ค๋กœ ์ „๋‹ฌ ํ•  ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    discover

    O

    ๋ถ„์‚ฐ ์„œ๋น„์Šค์˜ ์—…๋ฐ์ดํŠธ๋‚˜ ์ข…๋ฃŒ๋ฅผ ๊ฐ์ง€ํ•˜๊ณ , ๋…ธ๋“œ, ์„œ๋น„์Šค API Schema, ์•ก์…˜ ๋ฐ ์ด๋ฒคํŠธ ๊ตฌ๋…, ๋ฐœํ–‰ ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.

    health

    O

    O

    Delegator

    hashtag
    1. MoleculerJS

    MoleculerJS ์–ด๋Œ‘ํ„ฐ๋กœ API Gateway ํ•จ๊ป˜์™€ ์•„๋ž˜์˜ ํ”ผ์–ด ๋ชจ๋“ˆ์„ ํ™œ์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • - OIDC ๋ฐ Identity Provider๋ฅผ ์ œ๊ณตํ•˜๋Š” IAM ๋ชจ๋“ˆ, ์ธ์ฆ ์ปจํ…์ŠคํŠธ์— ์—ฐ๋™ ๊ฐ€๋Šฅ

    Health Check

    moleculer-filearrow-up-right - ํŒŒ์ผ ์—…๋กœ๋“œ/๋‹ค์šด๋กœ๋“œ/๊ด€๋ฆฌ ๋ชจ๋“ˆ (GCP Storage bucket backend / File System backend ์ง€์›)

  • moleculer-i18narrow-up-right - ๊ตญ์ œํ™” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๋ฆฌ ๋ฐ ์กฐํšŒ ๋ชจ๋“ˆ (MySQL/Maria DBMS ํ•„์š”)

  • moleculer-consolearrow-up-right - ํ™•์žฅ ๊ฐ€๋Šฅํ•œ Admin Console WebApp (React.js)

    • API document component ๊ธฐ๋ณธ ์ œ๊ณต (moleculer-apiarrow-up-right ํ˜ธํ™˜)

    • IAM component ๊ธฐ๋ณธ ์ œ๊ณต (moleculer-iamarrow-up-right ํ˜ธํ™˜)

    • File management component ๊ธฐ๋ณธ ์ œ๊ณต ( ํ˜ธํ™˜)

    • Translation component ๊ธฐ๋ณธ ์ œ๊ณต ( ํ˜ธํ™˜)

  • moleculer-iamarrow-up-right
    moleculer-filearrow-up-right
    moleculer-i18narrow-up-right

    Service Broker

    hashtag
    Service Brokers

    API Gateway๋Š” ์ตœ์ดˆ์— Moleculerarrow-up-right MSA ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ฐฐ๊ฒฝ์œผ๋กœ ๊ฐœ๋ฐœ๋˜์—ˆ์ง€๋งŒ, ํ™•์žฅ์„ฑ์„ ์œ„ํ•ด์„œ ๊ฐ•ํ•œ ๋””์ปคํ”Œ๋ง์„ ๋ฐฉ์นจ์œผ๋กœ ๊ฐœ๋ฐœ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

    API Gateway๋Š” ๋ถ„์‚ฐ ์„œ๋น„์Šค ๋ฐ ์ค‘์•™ ๋ฉ”์‹œ์ง• ์„œ๋น„์Šค์™€์˜ ๋„คํŠธ์›Œํ‚น์„ ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค์—๊ฒŒ ์œ„์ž„ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค๋Š” ๋ถ„์‚ฐ ์„œ๋น„์Šค๋“ค์˜ ๋„คํŠธ์›Œํ‚น์„ ์œ„์ž„ ๋ฐ›์œผ๋ฉฐ, call, publish, subscribe, discover, report ๋“ฑ์˜ ์ฃผ์š” ๋„คํŠธ์›Œํ‚น ์ธํ„ฐํŽ˜์ด์Šค(์ปค๋„ฅํ„ฐ)๋ฅผ ๊ฐ€์ง„ ์–ด๋Œ‘ํ„ฐ์™€ ๊ฒฐํ•ฉ๋ฉ๋‹ˆ๋‹ค.

    Service Broker

    ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค๋Š” ๋ถ„์‚ฐ ์„œ๋น„์Šค๋“ค์˜ ๋„คํŠธ์›Œํ‚น์„ ์œ„์ž„ ๋ฐ›์œผ๋ฉฐ, call, publish, subscribe, discover, report ๋“ฑ์˜ ์ฃผ์š” ๋„คํŠธ์›Œํ‚น ์ธํ„ฐํŽ˜์ด์Šค(์ปค๋„ฅํ„ฐ)๋ฅผ ๊ฐ€์ง„ ์–ด๋Œ‘ํ„ฐ์™€ ๊ฒฐํ•ฉ๋ฉ๋‹ˆ๋‹ค.

    hashtag
    2. Delegator

    ๋ธŒ๋กœ์ปค๋Š” ์œ„ ์ปค๋„ฅํ„ฐ๋“ค์˜ ํŠน์ • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋‹จ์ผํ•œ ๊ฐ์ฒด๋กœ ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค.

    hashtag
    A. Moleculer

    • MoleculerAPIGateway ์„œ๋น„์Šค๋ฅผ minxin์— ํฌํ•จํ•ด moleculer.ServiceSchema๋ฅผ ํ™•์žฅ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • MoleculerServiceBroker๋ฅผ ์ด์šฉํ•ด ์ง์ ‘ moleculer.ServiceSchema๋ฅผ ๊ตฌํ˜„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    hashtag
    B. Others

    ๊ธฐํƒ€ MSA ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‘์šฉํ•ด ServiceBroker ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    hashtag

    API Handler

    hashtag
    B. API Handler

    Dynamic Handler

    ๋™์  ํ•ธ๋“ค๋Ÿฌ๋Š” ์›น ์„œ๋ฒ„์˜ ๋ชจ๋“  base ๊ฒฝ๋กœ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ, ๋ฏธ๋“ค์›จ์–ด ๋ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ, ์—”๋“œํฌ์ธํŠธ ๊ฐ„์˜ ๊ฒฝ๋กœ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋™์  ํ•ธ๋“ค๋Ÿฌ๋Š” ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋ธŒ๋žœ์น˜ ํ•ธ๋“ค๋Ÿฌ ๋ฐ ๋ฒ„์ „ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ƒ์„ฑ ๋ฐ ์‚ญ์ œํ•˜๋ฉฐ ๋™์ ์œผ๋กœ ๋ผ์šฐํŠธ ํ…Œ์ด๋ธ”์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    ๋™์  ํ•ธ๋“ค๋Ÿฌ๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์— ๋”ฐ๋ผ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ , ๋ธŒ๋žœ์น˜ ํ•ธ๋“ค๋Ÿฌ๋กœ ์š”์ฒญ์„ ํ”„๋ก์‹œํ•ฉ๋‹ˆ๋‹ค.

    Branch Handler

    ๋ธŒ๋žœ์น˜ ํ•ธ๋“ค๋Ÿฌ๋Š” ํƒœ๊ทธ์— ๋”ฐ๋ผ ๋ฒ„์ „ ํ•ธ๋“ค๋Ÿฌ๋กœ ์š”์ฒญ์„ ํ”„๋ก์‹œํ•ฉ๋‹ˆ๋‹ค.

    ์ด ๋ธŒ๋žœ์น˜ ํ•ธ๋“ค๋Ÿฌ๋“ค์€ ์•„๋ž˜์˜ ๊ทœ์น™์— ๋”ฐ๋ผ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.

    • master ๋ธŒ๋žœ์น˜๋ฅผ ์ œ์™ธํ•˜๊ณ , 60๋ถ„ ์ด์ƒ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ๋ธŒ๋žœ์น˜ ํ•ธ๋“ค๋Ÿฌ๋Š” ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.

    Version Handler

    ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ์˜ ๋ณ‘ํ•ฉ์ด ์ผ์–ด๋‚  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ Gateway API ์Šคํ‚ค๋งˆ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๋ณ‘ํ•ฉ์— ์„ฑ๊ณตํ•œ Gateway API ์Šคํ‚ค๋งˆ๋Š” latest ๋ฐ ์ˆํ•ด์‹œ๋กœ ํƒœ๊ทธ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์—…๋ฐ์ดํŠธ๋œ ์—”๋“œํฌ์ธํŠธ๋ณ„๋กœ ์Šคํ‚ค๋งˆ, ํ”„๋กœํ† ์ฝœ ํ”Œ๋Ÿฌ๊ทธ์ธ, ์ปค๋„ฅํ„ฐ๋ฅผ ์—ฐ๊ฒฐํ•ด ๊ฐ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์€ ์Šคํ‚ค๋งˆ์˜ ํ•ธ๋“ค๋Ÿฌ๋Š” ๊ฐ€๋Šฅํ•œ ์žฌ์ฐธ์กฐ๋ฉ๋‹ˆ๋‹ค.

    ์ด ๋ฒ„์ „ ํ•ธ๋“ค๋Ÿฌ๋“ค์€ ์•„๋ž˜์˜ ๊ทœ์น™์— ๋”ฐ๋ผ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.

    • 10๊ฐœ๋ฅผ ์ดˆ๊ณผ(๊ธฐ๋ณธ๊ฐ’)ํ•˜๋Š” ๋ฒ„์ „์ด ์กด์žฌํ•œ๋Š” ๊ฒฝ์šฐ ์˜ค๋ž˜๋œ ์ˆœ์œผ๋กœ ๋ฒ„์ „ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.

    Non-Persistence

    API ์Šคํ‚ค๋งˆ์˜ ๋ธŒ๋žœ์น˜ ๋ฐ ํƒœ๊ทธ ๊ธฐ๋Šฅ์€ ๋ฒ„์ „ ๊ด€๋ฆฌ(/v1, /v2 ๊ฐ™์€)๊ฐ€ ์•„๋‹Œ ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ์˜ ๊ฐœ๋ฐœ ํŽธ์˜๋ฅผ ์œ„ํ•ด ๊ฐœ๋ฐœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

    ์ฃผ์˜์‚ฌํ•ญ

    • Gateway์—๋Š” Persistence Layer๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    • Gateway ์žฌ์‹œ์ž‘์‹œ ๋ธŒ๋žœ์น˜, ๋ฒ„์ „ ๋ฐ ํƒœ๊ทธ ์ •๋ณด๊ฐ€ ๋ณต์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    • Gateway ๋…ธ๋“œ๊ฐ„์— ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™” ์ „๋žต์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    ํ•˜์ง€๋งŒ Gateway ์žฌ์‹œ์ž‘ ์ดํ›„ ๋ชจ๋“  ์„œ๋น„์Šค๋“ค์— ๋Œ€ํ•œ discover๊ฐ€ ์ง„ํ–‰๋˜๋ฉด์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ฐ ๋ธŒ๋žœ์น˜์˜ latest ๋ฒ„์ „์—๋Š” ์ตœ์‹  Gateway API ์Šคํ‚ค๋งˆ๊ฐ€ ๋ณต์›๋ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ธŒ๋žœ์น˜์˜ latest ๋ฒ„์ „์˜ ์‹ ๋ขฐ์„ฑ์€ ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค.

    Gateway ๋…ธ๋“œ๊ฐ„์— ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ ๋ณ‘ํ•ฉ์˜ ์ˆœ์„œ๊ฐ€ ๋™์ผํ•˜๊ฒŒ ๋ณด์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    Policy Plugin

    hashtag
    2. Access Control Policy

    ์ ‘๊ทผ ์ œ์–ด ํ”Œ๋Ÿฌ๊ทธ์ธ๋ณ„ ์Šคํ‚ค๋งˆ ์–‘์‹์€ Access Control Policy ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. ์ด ์„น์…˜์—์„œ๋Š” ๊ธฐ๋ณธ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๊ตฌ๋™ ๋ฐฉ์‹์„ ๊ฐœ๊ด„์ ์œผ๋กœ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

    ํ”„๋กœํ† ์ฝœ์˜ ํ™•์žฅ์„ฑ๊ณผ ์ ‘๊ทผ์ œ์–ด ์ •์ฑ…์˜ ์ •ํ•ฉ์„ฑ์„ ์œ„ํ•ด์„œ, ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์€ ํ”„๋กœํ† ์ฝœ๋ณ„ ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ์•ก์…˜๊ณผ ์ด๋ฒคํŠธ๋ฅผ ์ฃผ์ฒด๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

    ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์˜ ํ‰๊ฐ€๋Š” API Gateway์˜ ๋ฉ”๋ชจ๋ฆฌ์— LRU ๋ฐฉ์‹์œผ๋กœ ์บ์‹œ๋˜๋ฉฐ ํ•œ ์š”์ฒญ์—์„œ ์ค‘๋ณต ์ˆ˜ํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์บ์‹œ ํ‚ค๋ฅผ ์ƒ์„ฑ ํ•  ๋•Œ ์š”์ฒญ์„ ์ •ํ™•ํžˆ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ปจํ…์ŠคํŠธ(์ธ์ฆ ์ •๋ณด) ๋ฐ ํ˜ธ์ถœ ํŽ˜์ด๋กœ๋“œ ๋“ฑ์˜ ์ •๋ณด๊ฐ€ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

    ์ ์šฉ๋˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ˆœ์„œ๋Š” ์œ ํšจํ•ฉ๋‹ˆ๋‹ค. ์šฐ์„ ํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ ์‹คํŒจ ํ•  ๊ฒฝ์šฐ ๋‹ค์Œ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ •์ฑ…์€ ํ‰๊ฐ€๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ ‘๊ทผ ์ œ์–ด ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๊ธฐ๋ณธ ์˜ต์…˜์—์„œ OAuth2 Scope ํ”Œ๋Ÿฌ๊ทธ์ธ(scopes)์ด FBAC ํ”Œ๋Ÿฌ๊ทธ์ธ(filter)๋ณด๋‹ค ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    A. OAuth2 Scope

    OAuth2 Scope ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๊ฐ ์ •์ฑ…์˜ scopes์— ๋‚˜์—ด๋œ ์Šค์ฝ”ํ”„๋ฅผ context.scopes๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ์˜ ์Šค์ฝ”ํ”„๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒฝ์šฐ ์ ‘๊ทผ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    B. FBAC

    FBAC ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๊ฐ ์ •์ฑ…์˜ filter ํ•ญ๋ชฉ์— ๋งตํ•‘๋œ Inline JavaScript Function String์„ VM์—์„œ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ Boolean ๊ฐ’์œผ๋กœ ์ ‘๊ทผ ์ œ์–ด ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค. ํ‰๊ฐ€์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ Boolean ๊ฐ’์ด ๋ฆฌํ„ด๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, API Gateway์—์„œ ์ถœ์ฒ˜ ๋…ธ๋“œ๋กœ ์ „๋‹ฌ๋˜๋ฉฐ ์ ‘๊ทผ์ด ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค.

    ๋””๋ฒ„๊น… ์ค‘์— Inline JavaScript Function String์—์„œ console ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ๋ฉ”์„ธ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ฒฝ์šฐ, ๊ทธ ๋ฉ”์„ธ์ง€๋Š” console ๊ฐ์ฒด์— ๋ฐ”์ธ๋”ฉ๋œ report ์ปค๋„ฅํ„ฐ๊ฐ€ ์ถœ์ฒ˜ ๋…ธ๋“œ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

    Protocol Plugin

    hashtag
    Plugin

    ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ์˜ ํฌ๋งท๊ณผ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๋Š”๋ฐ ์“ฐ์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์œ„์ž„๋œ ์Šคํ‚ค๋งˆ์— ๋Œ€ํ•œ ๊ฒ€์ฆ, ํ•ด์„, ์ž‘๋™ ๋ฐฉ์‹์„ ์ •์˜ํ•˜๊ณ  ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

    ๊ฐ ํ”„๋กœํ† ์ฝœ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ํ•ด๋‹น ํ”„๋กœํ† ์ฝœ ์Šคํ‚ค๋งˆ์˜ ์–‘์‹์„ ์ •์˜ํ•˜๊ณ , ์Šคํ‚ค๋งˆ ๋ณ‘ํ•ฉ, ์š”์ฒญ ํ”„๋ก์‹œ, ์˜์กด์„ฑ ํŒŒ์•…, ์„œ๋ฒ„ ํ™•์žฅ ํ•ธ๋“ค๋Ÿฌ ๋“ฑ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

    ๊ฐ ์ ‘๊ทผ ์ œ์–ด ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ํ•ด๋‹น ์ ‘๊ทผ ์ œ์–ด ์Šคํ‚ค๋งˆ์˜ ์–‘์‹์„ ์ •์˜ํ•˜๊ณ , call, publish, subscribe ์ปค๋„ฅํ„ฐ์˜ ์ •์ฑ…์„ ํ•ด์„ํ•˜๊ณ  ์ ‘๊ทผ ์ œ์–ด๋ฅผ ํŒ๋‹จํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    1. Protocol Plugin

    ํ”„ํ† ํ† ์ฝœ ํ”Œ๋Ÿฌ๊ทธ์ธ๋ณ„ ์Šคํ‚ค๋งˆ ์–‘์‹์€ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. ์ด ์„น์…˜์—์„œ๋Š” ๊ธฐ๋ณธ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๊ตฌ๋™ ๋ฐฉ์‹์„ ๊ฐœ๊ด„์ ์œผ๋กœ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

    hashtag
    A. REST

    REST ํ”„๋กœํ† ์ฝœ์€ ๋ถ„์‚ฐ ์„œ๋น„์Šค์— ๋Œ€ํ•œ call, publish ์ปค๋„ฅํ„ฐ๋ฅผ ํŠน์ • ์—”๋“œํฌ์ธํŠธ์— ๋งตํ•‘ํ•ฉ๋‹ˆ๋‹ค. HTTP ์š”์ฒญ์˜ Paylo,ad๋Š” ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ†ตํ•ด ํŒŒ์‹ฑ๋˜์–ด params ์ปค๋„ฅํ„ฐ๋ฅผ ํ†ตํ•ด ๋ณ€ํ™˜๋˜์–ด call, publish ์ปค๋„ฅํ„ฐ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

    ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ ๋ณ‘ํ•ฉ์„ ๋ฐœ์ƒ์‹œํ‚จ ์ถœ์ฒ˜ ๋…ธ๋“œ๋กœ ๋””๋ฒ„๊ทธ ๋ฉ”์„ธ์ง€๊ฐ€ report๋˜๋ฉฐ ๋ณ‘ํ•ฉ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

    API Catalog๋ฅผ ํ†ตํ•ด์„œ ์—”๋“œํฌ์ธํŠธ ๋ณ„ ์ ์šฉ๋œ ์ •์ฑ… ๋ฐ ์ปค๋„ฅํ„ฐ์™€ ๊ทธ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋Œ€ํ•œ ์„ค๋ช…์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

    TODO: $headers, $status, $body field for REST response

    hashtag
    B. GraphQL

    GraphQL ํ”„๋กœํ† ์ฝœ์€ ๋ถ„์‚ฐ ์„œ๋น„์Šค์— ๋Œ€ํ•œ call ๋ฐ publish, subscribe ์ปค๋„ฅํ„ฐ๋ฅผ ํŠน์ • ํƒ€์ž…์˜ ํ•„๋“œ์— ๋งตํ•‘ํ•ฉ๋‹ˆ๋‹ค. HTTP ์š”์ฒญ์˜ Payload๋Š” ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ†ตํ•ด ํŒŒ์‹ฑ๋˜์–ด params ์ปค๋„ฅํ„ฐ๋ฅผ ํ†ตํ•ด ๋ณ€ํ™˜๋˜์–ด call, publish, subscribe, map ์ปค๋„ฅํ„ฐ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

    GraphQL ์Šคํ‚ค๋งˆ ์ƒ์„ฑ์— ์‹คํŒจํ•˜๋Š” ๊ฒฝ์šฐ ๋ณ‘ํ•ฉ์„ ๋ฐœ์ƒ์‹œํ‚จ ์ถœ์ฒ˜ ๋…ธ๋“œ๋กœ ๋””๋ฒ„๊ทธ ๋ฉ”์„ธ์ง€๊ฐ€ report๋˜๋ฉฐ ๋ณ‘ํ•ฉ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

    API Catalog๋ฅผ ํ†ตํ•ด์„œ ๊ฐ GraphQL Type ๋ณ„ ์ ์šฉ๋œ ์ •์ฑ… ๋ฐ ์ปค๋„ฅํ„ฐ์™€ ๊ทธ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋Œ€ํ•œ ์„ค๋ช…์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

    ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋ฅผ ํ†ตํ•ด์„œ GraphQL Custom Scalar ์ •์˜๋ฅผ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. Gateway ์ƒ์„ฑ์‹œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์˜ต์…˜์„ ํ†ตํ•ด Scalar ์ •์˜๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ DateTime, Date, Time, JSON๊ฐ€ ํฌํ•จ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

    hashtag
    C. WebSocket

    TODO: WIP

    hashtag

    API Document Generation

    hashtag
    C. Reflection

    API Gateway Health Check

    Gateway ์›น์„œ๋ฒ„ ์ž์ฒด์˜ ์ƒํƒœ ํ™•์ธ์šฉ HTTP ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋‚ด์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋‚˜ Kubernetes ๋“ฑ์˜ ์ปจํ…Œ์ด๋„ˆ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ํ™˜๊ฒฝ์—์„œ ํ™œ์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • GET /~health/liveness ์—”๋“œํฌ์ธํŠธ์—์„œ ์›น ์„œ๋ฒ„์˜ ์ƒํƒœ๋ฅผ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • GET /~health/readiness ์—”๋“œํฌ์ธํŠธ์—์„œ ์š”์ฒญ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Gateway ์ƒํƒœ์— ๋”ฐ๋ฅธ ๊ฐ ์—”๋“œํฌ์ธํŠธ์˜ HTTP ์ƒํƒœ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

    API Endpoint Health Check

    Gateway API ์Šคํ‚ค๋งˆ์˜ ๊ฐ ์—”๋“œํฌ์ธํŠธ ๋ณ„ ์ƒํƒœ ํ™•์ธ ๊ธฐ๋Šฅ์„ ๋‚ด์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์—”๋“œํฌ์ธํŠธ์— ์—ฐ๊ฒฐ๋œ ์ปค๋„ฅํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒํƒœ๊ฐ€ ์ธก์ •๋ฉ๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์ปค๋„ฅํ„ฐ์˜ ์ƒํƒœ ํ™•์ธ์€ ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค์—๊ฒŒ ์œ„์ž„๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์‚ฌํ•ญ์€ ์•„๋ž˜์˜ ์„น์…˜์„ ์ฐธ์กฐ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    API ์—”๋“œํฌ์ธํŠธ์˜ ์ƒํƒœ๋Š” ์œ„์ฒ˜๋Ÿผ HTTP๋กœ ์ œ๊ณต๋˜์ง€ ์•Š์œผ๋ฉฐ ์•„๋ž˜์˜ API Catalog๋ฅผ ํ†ตํ•ด์„œ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

    API Catalog

    API Catalog๋Š” REST, GraphQL, WebSocket ๋“ฑ์˜ Protocol Plugin์— ๋”ฐ๋ผ ๊ฐ ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ ๋ฌธ์„œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. API Catalog ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์ด ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์€ API ํƒ€์ž…์ด GraphQL ์Šคํ‚ค๋งˆ์— ํ†ตํ•ฉ๋ฉ๋‹ˆ๋‹ค. ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์€ ํ™œ์„ฑํ™”์‹œ ์˜ต์…˜์œผ๋กœ ์ฃผ์ž… ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Service Catalog

    Service Catalog๋Š” ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์˜ ์„œ๋น„์Šค๋“ค์˜ ๊ฐ ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. Service Catalog ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์ด ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์€ Service ํƒ€์ž…์ด GraphQL ์Šคํ‚ค๋งˆ์— ํ†ตํ•ฉ๋ฉ๋‹ˆ๋‹ค. ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์€ ํ™œ์„ฑํ™”์‹œ ์˜ต์…˜์œผ๋กœ ์ฃผ์ž… ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    API Schema

    IP Address

    200

    503

    500

    Gateway ์ƒํƒœ

    ์‹œ์ž‘์ค‘

    ๋ณ‘ํ•ฉ์ค‘

    ์ž‘๋™์ค‘

    ์ข…๋ฃŒ์ค‘

    ์˜ค๋ฅ˜

    GET /~health/liveness

    200

    200

    200

    200

    500

    GET /~health/readiness

    503

    Connector

    200

    Branch

    ์ œ๊ฑฐ ํ•  ์ˆ˜ ์—†๋Š” ๊ธฐ๋ณธ ๋ธŒ๋žœ์น˜๋Š” master ๋ธŒ๋žœ์น˜์ž…๋‹ˆ๋‹ค. ์ด์™ธ์˜ ๋ธŒ๋žœ์น˜๋ฅผ ๋ช…์‹œํ•˜์—ฌ ๋ธŒ๋žœ์น˜๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ธŒ๋žœ์น˜ ๊ด€๋ จ ๋‚ด์šฉ์€ ์•„๋ž˜์—์„œ ๋‹ค์‹œ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

    Load-Balancing

    ๋™์ ์œผ๋กœ ์Šคํ‚ค๋งˆ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  API๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” Gateway๋Š” ๋™์ผํ•œ ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๊ฐ€ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์— ์—ฌ๋Ÿฌ๊ฐœ ์กด์žฌํ•  ๋•Œ ๋ฌธ์ œ๋ฅผ ๋“œ๋Ÿฌ๋ƒ…๋‹ˆ๋‹ค.

    ์˜ˆ์‹œ

    • player ์„œ๋น„์Šค๊ฐ€ A, B ๋‘ ํ˜ธ์ŠคํŠธ์—์„œ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์— ์—ฐ๊ฒฐ๋˜์–ด ๋””๋ฒ„๊น…๋˜๊ณ  ์žˆ๋‹ค๋ฉด, Gateway๋Š” player ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋ฅผ A ํ˜ธ์ŠคํŠธ์˜ ์Šคํ‚ค๋งˆ, B ํ˜ธ์ŠคํŠธ์˜ ์Šคํ‚ค๋งˆ์— ๋”ฐ๋ผ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋˜์–ด ์›๊ฒฉ์ง€์˜ ๋‘ ๊ฐœ๋ฐœ์ž๋Š” ๋””๋ฒ„๊น…์— ๋ฌธ์ œ๋ฅผ ๊ฒช์Šต๋‹ˆ๋‹ค.

      • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ๋กœ์ปฌ์— ์ „์šฉ ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•˜๊ณ  ๋ถ„์‚ฐ ์‹œ์Šคํ…œ๊ณผ์˜ ์—ฐ๊ฒฐ(eg. VPN ํ„ฐ๋„)์„ ์ข…๋ฃŒํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

    • player ์„œ๋น„์Šค์˜ get ์•ก์…˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ ์‘๋‹ต ์ŠคํŽ™์ด A ํ˜ธ์ŠคํŠธ์—์„œ ๋””๋ฒ„๊น… ์ค‘์ธ ๊ฒฝ์šฐ, ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ด ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์— ๊ธฐ์กด์— ๋ฐฐํฌ๋œ X ๋…ธ๋“œ์˜ player ์„œ๋น„์Šค์˜ get ์•ก์…˜์œผ๋กœ ํ”„๋ก์‹œ๋˜๋Š” ๊ฒฝ์šฐ๋„ ์—ญ์‹œ ๋ฌธ์ œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

      • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์˜ ์šฐ์„  ์ˆœ์œ„๋ฅผ ์ž์‹ ์˜ ํ˜ธ์ŠคํŠธ๋กœ ๊ฐ•์ œํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์š”์ฒญ์‹œ ํด๋ผ์ด์–ธํŠธ IP๋‚˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ด์šฉํ•ด ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ์ด์ „ ๋ฒ„์ „์˜ Gateway์—์„œ ์‹œ๋„๋˜์—ˆ์œผ๋‚˜ ๋งŒ์กฑ์Šค๋Ÿฝ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

    ์œ„์˜ ์—ฌ๋Ÿฌ ์ „๋žต์„ ์‹คํ—˜ํ•ด๋ณธ ํ›„ Gateway๋Š” ๋ธŒ๋žœ์น˜ ๋ฐ ํƒœ๊ทธ์„ ํ†ตํ•ด ๊ฐœ๋ฐœ์‹œ ์ถฉ๋Œ ํšŒํ”ผ๋ฅผ ์ง€์›ํ•˜๊ณ , ์ถ”๊ฐ€๋กœ ๋ธŒ๋žœ์น˜๋ณ„ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ ์ •์ฑ…์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

    • call

      • master ๋ธŒ๋žœ์น˜์—์„œ๋Š” API ์—”๋“œํฌ์ธํŠธ์— ์—ฐ๊ฒฐ๋œ ์„œ๋น„์Šค ์•ก์…˜์„ ํ˜ธ์ถœ ํ•  ๋•Œ master,(none) ๋ธŒ๋žœ์น˜์— API๋ฅผ ์ œ๊ณตํ•œ ์„œ๋น„์Šค ๋…ธ๋“œ๋ฅผ ์šฐ์„ ์œผ๋กœ ์š”์ฒญ์„ ํ”„๋ก์‹œํ•ฉ๋‹ˆ๋‹ค.

    Branch, Version, Integration

    Integration Process

    ๋ณ‘ํ•ฉ ๊ทœ์น™์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

    • discover

      • discover ๋ฐœ์ƒ ์ฆ‰์‹œ ๋ณ‘ํ•ฉ ์š”์ฒญ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

      • ํ”„๋กœํ† ์ฝœ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋”ฐ๋ผ ์Šคํ‚ค๋งˆ ํฌ๋งท์— ๋Œ€ํ•œ ๊ฒ€์ฆ์„ ๊ฑฐ์นฉ๋‹ˆ๋‹ค.

        • ํฌ๋งท ์—๋Ÿฌ๋Š” ๋ณ‘ํ•ฉ ์š”์ฒญ ๋ฉ”์„ธ์ง€ ๋ฆฌ์ŠคํŠธ์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

        • ์ ‘๊ทผ ์ œ์–ด๋ฅผ ์šฐํšŒํ•˜๋Š” ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ ๊ฒฝ๊ณ ๊ฐ€ ๋ณ‘ํ•ฉ ์š”์ฒญ ๋ฉ”์„ธ์ง€ ๋ฆฌ์ŠคํŠธ์— ํฌํ•ฉ๋ฉ๋‹ˆ๋‹ค.

      • ๊ฒ€์ฆ ์„ฑ๊ณต์‹œ ๋ณ‘ํ•ฉ ์š”์ฒญ์ด ํ์— ์‚ฝ์ž…๋˜๋ฉด์„œ ํŠน์ • ์‹œ๊ฐ„(2์ดˆ๋ฅผ ๊ธฐ๋ณธ๊ฐ’) ๋™์•ˆ debounce ํ›„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

        • ํ์—์„œ ํŠน์ • ์„œ๋น„์Šค์— ๋Œ€ํ•œ ๋ณ‘ํ•ฉ ์š”์ฒญ์ด ๋‹ค์ˆ˜์ธ ๊ฒฝ์šฐ ๋งˆ์ง€๋ง‰ ์š”์†Œ๋งŒ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค.

        • ํ”„๋กœํ† ์ฝœ ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ integrationDependencyResolver๊ฐ€ ๊ตฌํ˜„๋œ ๊ฒฝ์šฐ (eg. GraphQL) ํ ์•ˆ์—์„œ ์ฒ˜๋ฆฌ ์ˆœ์„œ๊ฐ€ ์กฐ์ • ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

      • ๊ฒ€์ฆ ์‹คํŒจ์‹œ report ๋‹จ๊ณ„๋กœ ๊ฑด๋„ˆ ๋œ๋‹ˆ๋‹ค.

    • hash

      • ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ์—์„œ branch, description, deprecated๋“ฑ์˜ ๋ฉ”ํƒ€ ์ •๋ณด๋ฅผ ์ œ์™ธํ•œ ์Šคํ‚ค๋งˆ ๊ฐ์ฒด ์ „์ฒด๋ฅผ MD5 ํ•ด์‹ฑํ•˜์—ฌ ๊ณ ์œ ํ•œ ๋ฒ„์ „ ํ•ด์‹œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    • update

      • master ๋ธŒ๋žœ์น˜์˜ ์Šคํ‚ค๋งˆ๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ชจ๋“  ๋ธŒ๋žœ์น˜์— ๋ณ‘ํ•ฉ์ด ์‹œ๋„๋ฉ๋‹ˆ๋‹ค.

        • ์ด ๋•Œ master

    • report

      • ๋ณ‘ํ•ฉ ์š”์ฒญ์˜ ๋ฉ”์„ธ์ง€ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋””๋ฒ„๊ทธ ๋ฉ”์„ธ์ง€๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

      • ๋ณ‘ํ•ฉ ์š”์ฒญ์˜ ์ถœ์ฒ˜ ๋…ธ๋“œ๋กœ ๋””๋ฒ„๊ทธ ๋ฉ”์„ธ์ง€๋ฅผ reportํ•ฉ๋‹ˆ๋‹ค.

    • Branch Strategy Diagram

      ๋ธŒ๋žœ์น˜๊ฐ„ ๋ณ‘ํ•ฉ ์ „๋žต์„ ํ‘œ๋กœ ๋„์‹ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

      ํ‘œ์—์„œ a@v1๋Š” a ์„œ๋น„์Šค ์Šคํ‚ค๋งˆ ์ค‘ v1 ๋ฒ„์ „์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

    {
      branch: "master",

    ๋˜๋Š” ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋กœ์ปฌ์— ๊ฐœ๋ฐœ์šฉ ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์ง€๋งŒ, ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์— ์—ฐ๊ฒฐํ•ด ๋ฐฐํฌ๋œ ํƒ€ ์„œ๋น„์Šค์— ์˜์กด ํ•  ํ•„์š”๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

    ์ฆ‰ ์ด์™ธ(eg. dev)์˜ ๋ธŒ๋žœ์น˜๋กœ์˜ ํŠธ๋ž˜ํ”ฝ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

  • ์ด์™ธ์˜ ๋ธŒ๋žœ์น˜(eg. dev)์—์„œ๋Š” API ์—”๋“œํฌ์ธํŠธ์— ์—ฐ๊ฒฐ๋œ ์„œ๋น„์Šค ์•ก์…˜์„ ํ˜ธ์ถœ ํ•  ๋•Œ dev,master,(none) ๋ธŒ๋žœ์น˜์— API๋ฅผ ์ œ๊ณตํ•œ ์„œ๋น„์Šค ๋…ธ๋“œ๋ฅผ ์šฐ์„ ์œผ๋กœ ์š”์ฒญ์„ ํ”„๋ก์‹œํ•ฉ๋‹ˆ๋‹ค.

    • ์ฆ‰ ํ˜„์žฌ ๋ธŒ๋žœ์น˜(eg. dev)์— ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ master ๋ธŒ๋žœ์น˜์˜ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ฐจ์„ ์œผ๋กœ ์ฐพ์Šต๋‹ˆ๋‹ค.

  • ์ด ๊ทœ์น™์€ API ํ•ธ๋“ค๋Ÿฌ ์ƒ์„ฑ์‹œ์— ๋ธŒ๋žœ์น˜ ์ „๋žต์— ๋”ฐ๋ผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • publish, subscribe

    • ์ด๋ฒคํŠธ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ์—๋Š” ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค์— ์—ฐ๊ฒฐ๋œ ์ค‘์•™ ๋ฉ”์‹œ์ง• ์„œ๋น„์Šค์˜ ์ •์ฑ…์„ ๊ทธ๋Œ€๋กœ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

  • Protocol Plugin

      protocol: {

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

    ๊ฒŒ์ดํŠธ์›จ์ด์— ๋ฒ„์ „์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์—๋Š” ์Šคํ‚ค๋งˆ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ๋กœ ํ•ฉ๋‹ˆ๋‹ค.
  • ์„œ๋น„์Šค ๋…ธ๋“œ์˜ ์—ฐ๊ฒฐ์ด ๋Š๊ธด ๊ฒฝ์šฐ๋Š” ์—ฐ๊ด€๋œ ์Šคํ‚ค๋งˆ์˜ ๋…ธ๋“œํ’€์—์„œ ๋…ธ๋“œ๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.

    • ์ด ๋•Œ ์—ฐ๊ด€๋œ ์Šคํ‚ค๋งˆ๋“ค์—์„œ ๋…ธ๋“œํ’€์ด ๋นˆ ์Šคํ‚ค๋งˆ๋“ค์„ ์ œ๊ฑฐํ•˜๊ธฐ๋กœ ํ•ฉ๋‹ˆ๋‹ค.

  • ์™ธ์˜ ๋ธŒ๋žœ์น˜์—์„œ ์Šคํ‚ค๋งˆ์˜ ์„œ๋น„์Šค๋ช…์ด ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ ๋ณ‘ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ์Šคํ‚ค๋งˆ์˜ ์šฐ์„  ์ˆœ์œ„๊ฐ€ ๋†’์Šต๋‹ˆ๋‹ค.
  • ์ด์™ธ์˜ ๋ธŒ๋žœ์น˜์— ๋ณ‘ํ•ฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ฃผ์–ด์ง„ ๋ธŒ๋žœ์น˜์—๋งŒ ๋ณ‘ํ•ฉ์ด ์‹œ๋„๋ฉ๋‹ˆ๋‹ค.

  • ํ”„๋กœํ† ์ฝœ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋”ฐ๋ผ ๊ฐ ํ”„๋กœํ† ์ฝœ๋ณ„ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    • ๋ณ‘ํ•ฉ์— ์„ฑ๊ณตํ•˜๋ฉด ์ƒ์„ฑ๋œ Gateway API ์Šคํ‚ค๋งˆ ๋ฒ„์ „์— latest ํƒœ๊ทธ ๋ฐ 8 ๊ธ€์ž์˜ ์ˆ ํ•ด์‹œ(eg. abcdefgh)๊ฐ€ ํƒœ๊ทธ๋กœ ๋ถ€์ฐฉ๋ฉ๋‹ˆ๋‹ค.

  • ์˜ต์…˜์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ๋ธŒ๋žœ์น˜๋ณ„ API Catalog๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

  • ์˜ต์…˜์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ๋ธŒ๋žœ์น˜๋ณ„ Service Catalog๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

  • master

    dongwook

    ๋น„๊ณ 

    initial schema

    (empty)

    N/A

    a added to master

    a@v1

    N/A

    a updated to master

    a@v2

    N/A

    b added to dongwook

    a@v2

    a@v2 b@v1

    ์ƒˆ๋กœ์šด b ์„œ๋น„์Šค๋ฅผ dongwook ๋ธŒ๋žœ์น˜๋กœ ๋ถ„๊ธฐํ•ด์„œ ์ž‘์—…; ์ถฉ๋Œํ•˜์ง€ ์•Š๋Š” ์Šคํ‚ค๋งˆ๋“ค์€ ๋ณต์ œ๋˜์ง€ ์•Š๊ณ  ์ฐธ์กฐ๋ฉ๋‹ˆ๋‹ค.

    a updated to master

    a@v3

    a@v3 b@v1

    master ๋ธŒ๋žœ์น˜์˜ ์—…๋ฐ์ดํŠธ๋Š” ๋ชจ๋“  ๋ธŒ๋žœ์น˜๋กœ ์ „ํŒŒ๋ฉ๋‹ˆ๋‹ค.

    a updated to dongwook

    a@v3

    a@v4 b@v1

    master ์ด์™ธ์˜ ๋ธŒ๋žœ์น˜์˜ ์—…๋ฐ์ดํŠธ๋Š” ์ž๊ธฐ ๋ธŒ๋žœ์น˜๋กœ๋งŒ ์ „ํŒŒ๋ฉ๋‹ˆ๋‹ค.

    a updated to master

    a@v3-2

    a@v4 b@v1

    ์ถฉ๋Œํ•˜๋Š” ๊ฒฝ์šฐ ์ž๊ธฐ ๋ธŒ๋žœ์น˜ ์Šคํ‚ค๋งˆ์˜ ์šฐ์„  ์ˆœ์œ„๊ฐ€ ๋†’์Šต๋‹ˆ๋‹ค.

    c added to master

    a@v3-2 c@v1

    a@v5 b@v1 c@v1

    master ๋ธŒ๋žœ์น˜์˜ ์—…๋ฐ์ดํŠธ๋Š” ๋ชจ๋“  ๋ธŒ๋žœ์น˜๋กœ ์ „ํŒŒ๋ฉ๋‹ˆ๋‹ค.

    b added to master

    a@v3-2 b@v1 c@v1

    a@v5 b@v1 c@v1

    ๊ฐœ๋ฐœ๋œ b ์„œ๋น„์Šค๋ฅผ dongwook ๋ธŒ๋žœ์น˜์—์„œ master ๋ธŒ๋žœ์น˜๋กœ ๋ณ€๊ฒฝ

    a updated to master

    a@v5 b@v1 c@v1

    a@v5 b@v1 c@v1

    ์ˆ˜์ •๋œ a ์„œ๋น„์Šค๋ฅผ dongwook ๋ธŒ๋žœ์น˜์—์„œ master ๋ธŒ๋žœ์น˜๋กœ ๋ณ€๊ฒฝ

    dongwook branch removed

    a@v5 b@v1 c@v1

    N/A

    Routing Rule

    Gateway ์›น ์„œ๋ฒ„๋Š” [/~BRANCH[@TAG]]/<ENDPOINT>์˜ ๊ทœ์น™๋Œ€๋กœ API ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋ผ์šฐํŠธํ•ฉ๋‹ˆ๋‹ค. ์ฒซ๋ฒˆ์งธ ๊ฒฝ๋กœ ์กฐ๊ฐ์„ ๋ธŒ๋žœ์น˜(master๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ)๋กœ ๋‘๋ฒˆ์งธ ๊ฒฝ๋กœ ์กฐ๊ฐ์„ ํƒœ๊ทธ(latest๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ)๋กœ ์ดํ•˜ ๊ฒฝ๋กœ๋ฅผ API ์—”๋“œํฌ์ธํŠธ๋กœ ํ•ด์„ํ•ฉ๋‹ˆ๋‹ค.

    ๋ผ์šฐํŠธ ์˜ˆ์‹œ

    ๊ฐ„๋žต

    ๋ธŒ๋žœ์น˜ ํฌํ•จ

    ํƒœ๊ทธ ํฌํ•จ

    GET /players/1

    GET /~master/players/1

    GET /~master@latest/players/1

    -

    -

    GET /~master@h4g3f2e1/players/1

    -

    GET /~dongwook/players/1

    GET /~dongwook@latest/players/1

    -

    ํ…Œ์ด๋ธ”์˜ ๊ฐ ํ–‰์€ ๋™์ผํ•œ ๋ฒ„์ „์˜ ํ•ธ๋“ค๋Ÿฌ๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

    ๋ธŒ๋žœ์น˜ ์ด๋ฆ„ ๊ทœ์น™

    • ์˜๋ฌธ ์†Œ๋ฌธ์ž ๋ฐ ์ˆซ์ž, -, _๋งŒ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค.

    • ๊ธฐ์กด ์Šคํ‚ค๋งˆ ์—”๋“œํฌ์ธํŠธ๋‚˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ base ๊ฒฝ๋กœ์™€ ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

    ํƒœ๊ทธ ์ด๋ฆ„ ๊ทœ์น™

    • ์˜๋ฌธ ์†Œ๋ฌธ์ž ๋ฐ ์ˆซ์ž๋กœ๋งŒ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

    ์ด๋ฒคํŠธ/๋ธŒ๋žœ์น˜

    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

    WebSocket

    hashtag
    C. WebSocket

    hashtag

        REST: {
          basePath: "/players",
          description: "player service REST API",
          routes: [

    -

    GET /~dongwook@a4b3c2d1/players/1

    POST /graphql

    POST /~master/graphql

    GET /~master@latest/graphql

    -

    POST /~dongwook/graphql

    GET /~dongwook@latest/graphql

    -

    GET /~ws-dev/chat

    GET /~ws-dev@latest/chat

    Serve Static

    path-to-regexparrow-up-right
        WebSocket: {
          // TODO: WIP
        },

    Scope

    Scopes

        call: [
          {
            description: "admin can remove player, newbie and admin can create player",
            actions: ["player.**"],
            scopes: ["player", "player.admin"],

    ์œ„ ์ •์ฑ…์€ player.** ํŒจํ„ด(player.get, player.list, player.message.list ๋“ฑ๊ณผ ์ผ์น˜)์˜ ์•ก์…˜์„ ํ˜ธ์ถœํ•˜๋Š” call ์ปค๋„ฅํ„ฐ๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“  ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์ˆ˜ํ–‰๋˜๊ธฐ ์ „์— ๊ณตํ†ต์ ์œผ๋กœ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค. ์šฐ์„  scopes ์ ‘๊ทผ ์ œ์–ด ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋”ฐ๋ผ context์— ์ฃผ์ž…๋œ (moleculer-iam ๊ฐ™์€ ์ปจํ…์ŠคํŠธ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํ†ตํ•ด) OAuth ํ† ํฐ์ด ํš๋“ํ•œ ์Šค์ฝ”ํ”„๋ฅผ ํ™•์ธํ•˜๊ณ  ์ผ์น˜๋˜๋Š” ์Šค์ฝ”ํ”„๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ๋Š” ๊ฒฝ์šฐ ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค.

            {
              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",
    },

    Overview

    hashtag
    API Schema and Handler

    hashtag
    1. Design Principle

    Moleculer API Gateway๋Š” ์•„๋ž˜ ์›์น™์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ณ ์•ˆ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

    • ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์•ˆ์—์„œ ์œ ๋™์ ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

      • Persistence Layer๋ฅผ ๊ฐ–์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    • "๋ถ„์‚ฐ ์„œ๋น„์Šค -> API" ์ข…์†์„ฑ์„ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.

    ์•„์šธ๋Ÿฌ ๋ถ„์‚ฐ ์„œ๋น„์Šค ๋ฐ ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค์—์„œ ๊ธฐ๋Œ€๋˜๋Š” ํŒจํ„ด์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

    • ๋ถ„์‚ฐ ์„œ๋น„์Šค์˜ ํ”„๋กœ์‹œ์ €๋Š” ๋ฌด์ƒํƒœ๋ฅผ ์ง€ํ–ฅํ•ฉ๋‹ˆ๋‹ค.

      • ํ”„๋กœ์‹œ์ €๋Š” ์ธ์ฆ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

      • ํ”„๋กœ์‹œ์ €๋Š” ์ ‘๊ทผ ์ œ์–ด๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    hashtag
    2. API Schema

    ์ดํ•˜์—์„œ ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋Š” ๋ถ„์‚ฐ ํ™˜๊ฒฝ์˜ ๋ถ€๋ถ„์ ์ธ API ์Šคํ‚ค๋งˆ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. Gateway API ์Šคํ‚ค๋งˆ๋Š” Gateway์—์„œ ํ†ตํ•ฉ๋œ API ์Šคํ‚ค๋งˆ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

    ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋Š” JSON ํ…์ŠคํŠธ๋กœ Gateway์— ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ์Šคํ‚ค๋งˆ ๋ฐ์ดํ„ฐ์˜ ์ง๋ ฌํ™” ๋ฐ ๋น„์ง๋ ฌํ™”๋Š” MSA ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋‹ฌ๋ ธ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์˜ˆ์‹œ์—์„œ๋Š” Node.js ํ™˜๊ฒฝ์„ ๊ธฐ์ค€์œผ๋กœ ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋ฅผ JavaScript ๊ฐ์ฒด๋กœ ํ‘œ๊ธฐํ•ฉ๋‹ˆ๋‹ค.

    ์„œ๋น„์Šค API ์Šคํ‚ค๋งˆ๋Š” JSON ํ…์ŠคํŠธ์ž…๋‹ˆ๋‹ค.

  • ๋ถ„์‚ฐ ์„œ๋น„์Šค ํ˜ธ์ถœ์‹œ ์ธ์ฆ ๋“ฑ์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋งตํ•‘ํ•˜๋„๋ก ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค.

  • ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ํŒจํ„ด์„ ์ง€ํ–ฅํ•ฉ๋‹ˆ๋‹ค.

    • ํ”„๋กœํ† ์ฝœ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์„œ๋ฒ„, ๋ฏธ๋“ค์›จ์–ด, ์Šคํ‚ค๋งˆ, ํ•ธ๋“ค๋Ÿฌ์˜ ๋ชจ๋“  ๋ถ€๋ถ„์„ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.

    • ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์€ ํ”„๋กœํ† ์ฝœ๋ณ„ ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์•„๋‹Œ ์•ก์…˜, ์ด๋ฒคํŠธ์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • ๋„คํŠธ์›Œํ‚น ๋ฐ ๋ณต์› ํŒจํ„ด์— ๊ด€์—ฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    • ๋ถ„์‚ฐ ์„œ๋น„์Šค์™€ API Gateway๋Š” ์–ด๋Œ‘ํ„ฐ(Broker)๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

    • ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์„ ์œ ๋„ํ•˜๊ฑฐ๋‚˜ ๊ด€์—ฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • ํ”„๋กœ์‹œ์ €๋Š” ๊ฐ€๋Šฅํ•œ ๋ฉฑ๋“ฑ์„ฑ์„ ๊ฐ–๋„๋ก ๊ณ ๋ ค๋ฉ๋‹ˆ๋‹ค.

  • ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค๋Š” ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์„ ์œ„ํ•œ ๋ณต์› ํŒจํ„ด์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    • ํšŒ๋กœ์ฐจ๋‹จ๊ธฐ

    • ๊ฒฉ๋ฒฝ

    • ์žฌ์‹œ๋„

    • ์š”์ฒญ ํ

  • Policy Plugin

    hashtag
    D. Access Control Policy

      },

    ์œ„์— ์ •์˜ํ•œ ๊ฐ ํ”„๋กœํ† ์ฝœ๋“ค์˜ ์—”๋“œํฌ์ธํŠธ๋Š” ์„œ๋น„์Šค ๋ธŒ๋กœ์ปค์˜ call, publish, subscribe ์ปค๋„ฅํ„ฐ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ๊ฐ ์ปค๋„ฅํ„ฐ๋“ค์— ๋Œ€ํ•ด์„œ ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์„ ์ •์˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

      policy: {

    ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์€ ๋จผ์ € ํ˜ธ์ถœํ•˜๋Š” ์ปค๋„ฅํ„ฐ์— ๋”ฐ๋ผ์„œ action์ด๋‚˜ event์˜ ์ด๋ฆ„์œผ๋กœ ํ•„ํ„ฐ๋ง๋ฉ๋‹ˆ๋‹ค. ์—ฐ๊ด€๋œ ์ •์ฑ…๋“ค์€ ์ˆœ์„œ๋Œ€๋กœ ๋ชจ๋‘ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์ •์ฑ…์„ ํ†ต๊ณผํ•˜๋Š” ๊ฒฝ์šฐ์— ํ•ด๋‹น ์ปค๋„ฅํ„ฐ๊ฐ€ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์„ ํ‰๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์€ ํ”Œ๋Ÿฌ๊ทธ์ธ ํ˜•ํƒœ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ OAuth scope ๋ฐฉ์‹(scopes)๊ณผ Inline JavaScript Function String๋ฅผ ํ™œ์šฉํ•œ FBAC ๋ฐฉ์‹(filter) ๋‘๊ฐ€์ง€๊ฐ€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

    Caching: TODO

    ๋˜ํ•œ ์ ‘๊ทผ ์ œ์–ด ์ •์ฑ…์˜ ํ‰๊ฐ€๋Š” Gateway์˜ ๋ฉ”๋ชจ๋ฆฌ์— LRU ๋ฐฉ์‹์œผ๋กœ ์บ์‹œ๋˜๋ฉฐ ํ•œ ์š”์ฒญ์—์„œ ์ค‘๋ณต ์ˆ˜ํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์บ์‹œ ํ‚ค๋ฅผ ์ƒ์„ฑ ํ•  ๋•Œ ์š”์ฒญ์„ ์ •ํ™•ํžˆ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ปจํ…์ŠคํŠธ(์ธ์ฆ ์ •๋ณด) ๋ฐ ํ˜ธ์ถœ ํŽ˜์ด๋กœ๋“œ ๋“ฑ์˜ ์ •๋ณด๊ฐ€ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

    Policy Plugin

    Schema Registry

    Filter

    Filter

            filter: `({ action, params, context, util }) => {
              if (action === "player.remove") {
                return context.user.player.isAdmin && context.user.player.id != params.id;
              } else if (action === "player.create") {
                return context.user && (!context.user.player || context.user.player.isAdmin); 
              }
              return true;
            }`,
          },

    ๋‹ค์Œ์œผ๋กœ filter ์ ‘๊ทผ ์ œ์–ด ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋”ฐ๋ผ action|event, params, context, util์„ ์ฃผ์ž…ํ•˜์—ฌ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ, true ๊ฐ’์ด ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒฝ์šฐ ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค. FBAC์€ ACL์ด๋‚˜ RBAC์ฒ˜๋Ÿผ ๋Œ€์ค‘ํ™”๋˜์ง€๋Š” ์•Š์•˜์œผ๋‚˜, ABAC์˜ ํ™•์žฅ ๋ชจ๋ธ๋กœ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งค์šฐ ์œ ์—ฐํ•˜์—ฌ ๋ถ„์‚ฐ ํ™˜๊ฒฝ์— ์ ํ•ฉํ•˜๋ฉฐ ํ”„๋กœ๋•์…˜์—์„œ ๊ฒ€์ฆ๋œ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

    filter ์ ‘๊ทผ์ œ์–ด ํ”Œ๋Ÿฌ๊ทธ์ธ ์—ญ์‹œ map ์ปค๋„ฅํ„ฐ์ฒ˜๋Ÿผ Gateway์˜ Node.js VM ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. filter ํ•จ์ˆ˜๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ์ค‘์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ ๋””๋ฒ„๊ทธ ๋ฉ”์‹œ์ง€๊ฐ€ Gateway์—์„œ ์ถœ์ฒ˜ ๋…ธ๋“œ๋กœ ์ „๋‹ฌ๋˜๋ฉฐ ์ ‘๊ทผ์ด ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค.

    ์œ„์ฒ˜๋Ÿผ player ์„œ๋น„์Šค์˜ API ์Šคํ‚ค๋งˆ๋Š” ๊ผญ player ์„œ๋น„์Šค์˜ ์•ก์…˜๋งŒ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ player API์—์„œ ๋…ธ์ถœํ•˜๋Š” team ์„œ๋น„์Šค์˜ ์•ก์…˜์— ๋Œ€ํ•œ ์ ‘๊ทผ ์ œ์–ด ์—ญ์‹œ player ์Šคํ‚ค๋งˆ์—์„œ ์ •์˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

    publish, subscribe ์ปค๋„ฅํ„ฐ์˜ ์ •์ฑ…์—๋Š” actions ๋Œ€์‹  events ํ•„๋“œ๊ฐ€ ์ž‘์„ฑ๋ฉ๋‹ˆ๋‹ค.

    ์œ„์ฒ˜๋Ÿผ filter๊ฐ€ ์ƒ๋žต๋œ ๊ฒฝ์šฐ scopes๋งŒ ์ ์šฉ๋˜๋ฉฐ filter๋Š” ํ†ต๊ณผํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค.

    ์ ‘๊ทผ์ œ์–ด ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์€ ์œ„ ์ •์ฑ…์„ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

    ๋””๋ฒ„๊น… ์ค‘์— Inline JavaScript Function String์—์„œ console ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ๋ฉ”์„ธ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ฒฝ์šฐ, ๊ทธ ๋ฉ”์„ธ์ง€๋Š” Gateway์˜ VM ์•ˆ์—์„œ ์ถœ๋ ฅ๋˜์ง€ ์•Š๊ณ  Gateway๊ฐ€ ์ถœ์ฒ˜ ๋…ธ๋“œ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

          {
            description: "player can get associated team, admin can get all the teams",
            actions: ["team.get"],
            scopes: ["player", "player.admin"],
            filter: (({ action, params, context, util }) => {
              if (context.user.player.isAdmin || params.id === context.user.player.teamId) {
                return true;
              }
              return false;
            }).toString(),
          },
        ],
        publish: [
          {
            description: "Only admins can publish player events",
            events: ["player.**"],
            scopes: ["player"],
            filter: (({ event, params, context, util }) => {
              return context.user.player.isAdmin;
            }).toString(),
          },
        ],
        subscribe: [
          {
            events: ["player.**"],
            description: "Any user can receive player events",
            scopes: ["openid"],
          },
        ],
      },
    }
    {
      actions: ["**"],
      scopes: ["**"],
      filter: `() => true`,
    }
    {
      actions: ["**"],
      scopes: ["**"],
      filter: `(action, params, context) => {
        console.log("policy filter", action, params, context);
      }`,
    }

    Supporters

    hashtag
    QMIT Inc.

    A company which analyzes data of sports players to help recovery, training.

    hashtag

    Application Context Factory

    Middleware

    CHANGELOG

    hashtag
    0.3.2 - 2020-12-13

    hashtag
    Added

    • Created gitbook documentation:

    https://moleculer-api.gitbook.io/api/arrow-up-right

    FAQ

    hashtag
    What is the difference compared to '' project?

    WIP...

    moleculer-webarrow-up-right
    dehypnosis - OverviewGitHubchevron-right
    Logo
    https://en.plco.siteen.plco.sitechevron-right