Only this pageAll pages
Powered 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...

GraphQL

Configurations

API Gateway constructor options.

1. APIGatewayOptions

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

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

name

default

description

brokers

-

Options for the gateway itself rather inner components.

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

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

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

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

...

GraphQL Resolver with DataLoader

REST

GraphQL type extension and reference

REST Endpoints

REST File Upload with streaming

Options for batching feature which utilize for concurrent multiple procedure calls.

function

-

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

reporter

-

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.

Consume example queue for every given intervals.

examples.queueLimit

50

Example queue size.

examples.limitPerActions

10

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,

/token/i,

Matched strings with given regular expressions.

healthCheck

-

Options for the ServiceRegistry health check feature.

healthCheck.intervalSeconds

10

Health check for every given intervals.

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

100

Maximum number of entries for a single batch.

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

[]

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

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

-

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

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 winston logger is supported.

type APIGatewayOwnOptions = {
  skipProcessEventRegistration: boolean;
};

name

default

description

skipProcessEventRegistration

false

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

type ServiceBrokerOptions = {
  registry: ServiceRegistryOptions;
  batching: BatchingPoolOptions;
  function: InlineFunctionOptions;
  reporter: ReporterOptions;
  log: {
    event: boolean;
    call: boolean;
  },
} & ServiceBrokerDelegatorConstructorOptions;

name

default

description

registry

-

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

batching

type ServiceRegistryOptions = {
  examples: {
    processIntervalSeconds: number;
    queueLimit: number;
    limitPerActions: number;
    limitPerEvents: number;
    streamNotation: string;
    omittedNotation: string;
    omittedLimit: number;
    redactedNotation: string;
    redactedParamNameRegExps: RegExp[];
  };
  healthCheck: {
    intervalSeconds: number;
  };
};

name

default

description

examples

-

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

examples.processIntervalSeconds

type BatchingPoolOptions = {
  batchingKey: (...args: any[]) => any;
  entryKey: (batchingParams: any) => any;
  failedEntryCheck: (entry: any) => boolean;
  entriesLimit: number;
};

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

type InlineFunctionOptions = {
  util: {[key: string]: any};
};

name

default

description

util

{}

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

type ReporterOptions = {
  tableWidthZoomFactor: number;
};

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.

type ServiceBrokerDelegatorConstructorOptions = {
  moleculer?: MoleculerServiceBrokerDelegatorOptions;
  [otherDelegatorKey]?: any;
};

name

default

description

moleculer

-

moleculer Service Broker Delegator options.

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[];
};

name

default

description

batchedCallTimeout

(return 5-60s based on item count)

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

streamingCallTimeout

type SchemaRegistryOptions = {
  maxVersions: number;
  maxUnusedSeconds: number;
  protocol: ProtocolPluginConstructorOptions,
  policy: PolicyPluginConstructorOptions,
};

name

default

description

maxVersions

10

Maximum number of old versions for each branches.

maxUnusedSeconds

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;
};

1.1. APIGatewayOwnOptions

2. ServiceBrokerOptions

2.1. ServiceRegistryOptions

2.2. BatchingPoolOptions

2.3. InlineFunctionOptions

2.4. ReporterOptions

2.5. ServiceBrokerDelegatorConstructorOptions

2.5.1. MoleculerServiceBrokerDelegatorOptions

3. SchemaRegistryOptions

3.1. ProtocolPluginConstructorOptions

3.2. PolicyPluginConstructorOptions

4. APIServerOptions

5. LoggerConstructorOptions

moleculer
moleculer

-

5

(hash function)

3600000 (1 hour)

1800

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 λ§΅ν•‘κ³Ό 그에 λŒ€ν•œ μ ‘κ·Ό μ œμ–΄λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆ μ˜ˆμ‹œ

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

GatewayλŠ” νŠΉμ • μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆμ˜ μΆ”κ°€, 제거 및 μ—…λ°μ΄νŠΈμ‹œ κΈ°μ‘΄ 톡합 API μŠ€ν‚€λ§ˆμ— 병합을 μ‹œλ„ν•˜κ³ , μ„±κ³΅μ‹œ λ¬΄μ€‘λ‹¨μœΌλ‘œ λΌμš°ν„°λ₯Ό μ—…λ°μ΄νŠΈν•˜λ©° κ·Έ κ²°κ³Όλ₯Ό 원격 μ„œλΉ„μŠ€μ— λ‹€μ‹œ λ³΄κ³ ν•©λ‹ˆλ‹€.

  • λΆ„μ‚° μ„œλΉ„μŠ€μ˜ API μŠ€ν‚€λ§ˆλ₯Ό μˆ˜μ§‘ν•˜κ³  λ³‘ν•©ν•˜μ—¬ APIλ₯Ό μ‹€μ‹œκ°„μœΌλ‘œ μ—…λ°μ΄νŠΈ

  • Polyglot ν•˜κ±°λ‚˜ μ„œλ‘œ λ‹€λ₯Έ μ„œλΉ„μŠ€ λΈŒλ‘œμ»€μ— κΈ°λ°˜ν•œ μ„œλΉ„μŠ€λ“€μ˜ API μŠ€ν‚€λ§ˆλ₯Ό μˆ˜μ§‘ν•˜κ³  병합 ν•  수 있음

  • 개발 편의 및 좩돌 λ°©μ§€λ₯Ό μœ„ν•œ API 브랜칭 및 버저닝 κΈ°λŠ₯

The project is available under the .

Quick Examples

μ˜ˆμ‹œ μ½”λ“œλŠ” λ₯Ό μ°Έκ³  ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Get Started

Configure and run the gateway server.

Add moleculer-api package from npm.

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

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.

]

data-loader

Examples

../src/examples

WebSocket Video Broadcasting

WebSocket Video Server/Client

Authentication

Parse OIDC/OAuth2 context

WebSocket

WebSocket Chat Server/Client

Access Control with Auth token claims

μƒνƒœ 검사 및 API λ¬Έμ„œ 생성 (WIP)
  • 미듀웨어 λ°©μ‹μ˜ μš”μ²­ 흐름 μ œμ–΄

    • Error

    • Logging

    • Body Parser

    • Helmet

    • CORS

    • Serve Static File

    • HTTP/HTTPS/HTTP2

    • (ν™•μž₯ κ°€λŠ₯)

  • 미듀웨어 λ°©μ‹μ˜ μ»¨ν…μŠ€νŠΈ 생성 μ œμ–΄

    • Authn/Authz

    • Locale

    • Correlation ID

    • IP Address

    • User-Agent

    • Request

    • (ν™•μž₯ κ°€λŠ₯)

  • μ‘μš© ν”„λ‘œν† μ½œ ν”ŒλŸ¬κ·ΈμΈ

    • REST

    • GraphQL

    • WebSocket

    • (ν™•μž₯ κ°€λŠ₯)

  • μ ‘κ·Ό μ œμ–΄ μ •μ±… ν”ŒλŸ¬κ·ΈμΈ

    • OAuth2 scope 기반 μ ‘κ·Ό μ œμ–΄

    • JavaScript FBAC; Function Based Access Control 기반 μ ‘κ·Ό μ œμ–΄

    • (ν™•μž₯ κ°€λŠ₯)

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

    μ£Όμš” κΈ°λŠ₯

    License

    MIT license
    Gateway의 무쀑단 μ—”λ“œν¬μΈνŠΈ μ—…λ°μ΄νŠΈ 및 보고

    http://localhost:8080/~status 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).

    • http://localhost:8080/graphql A playground endpoint which is set from GraphQL Protocol Plugin.

    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

    1. Install the package

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

    2. Run the gateway

    3. Check the log and few endpoints

    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(() => {
            // ...
        });
    
    {
      "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": []
    }
    
    Build Status
    Coverage Status
    David
    Known Vulnerabilities
    NPM version

    Access Control with Auth token scopes

    Access Control with IP address

    Overview

    Project Architecture Diagram

    Service Broker

    Service Brokers

    API GatewayλŠ” μ΅œμ΄ˆμ— Moleculer MSA 라이브러리λ₯Ό 배경으둜 κ°œλ°œλ˜μ—ˆμ§€λ§Œ, ν™•μž₯성을 μœ„ν•΄μ„œ κ°•ν•œ λ””μ»€ν”Œλ§μ„ 방침으둜 개발되고 μžˆμŠ΅λ‹ˆλ‹€.

    API GatewayλŠ” λΆ„μ‚° μ„œλΉ„μŠ€ 및 쀑앙 λ©”μ‹œμ§• μ„œλΉ„μŠ€μ™€μ˜ λ„€νŠΈμ›Œν‚Ήμ„ μ„œλΉ„μŠ€ λΈŒλ‘œμ»€μ—κ²Œ μœ„μž„ν•©λ‹ˆλ‹€. μ„œλΉ„μŠ€ λΈŒλ‘œμ»€λŠ” λΆ„μ‚° μ„œλΉ„μŠ€λ“€μ˜ λ„€νŠΈμ›Œν‚Ήμ„ μœ„μž„ λ°›μœΌλ©°, call, publish, subscribe, discover, report λ“±μ˜ μ£Όμš” λ„€νŠΈμ›Œν‚Ή μΈν„°νŽ˜μ΄μŠ€(컀λ„₯ν„°)λ₯Ό κ°€μ§„ μ–΄λŒ‘ν„°μ™€ κ²°ν•©λ©λ‹ˆλ‹€.

    Service Broker

    μ„œλΉ„μŠ€ λΈŒλ‘œμ»€λŠ” λΆ„μ‚° μ„œλΉ„μŠ€λ“€μ˜ λ„€νŠΈμ›Œν‚Ήμ„ μœ„μž„ λ°›μœΌλ©°, call, publish, subscribe, discover, report λ“±μ˜ μ£Όμš” λ„€νŠΈμ›Œν‚Ή μΈν„°νŽ˜μ΄μŠ€(컀λ„₯ν„°)λ₯Ό κ°€μ§„ μ–΄λŒ‘ν„°μ™€ κ²°ν•©λ©λ‹ˆλ‹€.

    λΈŒλ‘œμ»€λŠ” μœ„ 컀λ„₯ν„°λ“€μ˜ νŠΉμ • μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” λ‹¨μΌν•œ 객체둜 κ΅¬ν˜„λ©λ‹ˆλ‹€.

    • MoleculerAPIGateway μ„œλΉ„μŠ€λ₯Ό minxin에 포함해 moleculer.ServiceSchemaλ₯Ό ν™•μž₯ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    • MoleculerServiceBrokerλ₯Ό μ΄μš©ν•΄ 직접 moleculer.ServiceSchemaλ₯Ό κ΅¬ν˜„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    기타 MSA 라이브러리λ₯Ό μ‘μš©ν•΄ ServiceBroker μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    2. Delegator

    A. Moleculer

    B. Others

    Protocol Plugin

    Plugin

    ν”ŒλŸ¬κ·ΈμΈμ€ μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆμ˜ 포맷과 κΈ°λŠ₯을 ν™•μž₯ν•˜λŠ”λ° μ“°μž…λ‹ˆλ‹€. ν”ŒλŸ¬κ·ΈμΈμ€ μœ„μž„λœ μŠ€ν‚€λ§ˆμ— λŒ€ν•œ 검증, 해석, μž‘λ™ 방식을 μ •μ˜ν•˜κ³  κ΅¬ν˜„ν•©λ‹ˆλ‹€.

    각 ν”„λ‘œν† μ½œ ν”ŒλŸ¬κ·ΈμΈμ€ ν•΄λ‹Ή ν”„λ‘œν† μ½œ μŠ€ν‚€λ§ˆμ˜ 양식을 μ •μ˜ν•˜κ³ , μŠ€ν‚€λ§ˆ 병합, μš”μ²­ ν”„λ‘μ‹œ, μ˜μ‘΄μ„± νŒŒμ•…, μ„œλ²„ ν™•μž₯ ν•Έλ“€λŸ¬ 등을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.

    각 μ ‘κ·Ό μ œμ–΄ ν”ŒλŸ¬κ·ΈμΈμ€ ν•΄λ‹Ή μ ‘κ·Ό μ œμ–΄ μŠ€ν‚€λ§ˆμ˜ 양식을 μ •μ˜ν•˜κ³ , call, publish, subscribe 컀λ„₯ν„°μ˜ 정책을 ν•΄μ„ν•˜κ³  μ ‘κ·Ό μ œμ–΄λ₯Ό νŒλ‹¨ν•˜λŠ” ν•Έλ“€λŸ¬λ₯Ό κ΅¬ν˜„ν•©λ‹ˆλ‹€.

    1. Protocol Plugin

    ν”„ν† ν† μ½œ ν”ŒλŸ¬κ·ΈμΈλ³„ μŠ€ν‚€λ§ˆ 양식은 API Schema μ„Ήμ…˜μ„ μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€. 이 μ„Ήμ…˜μ—μ„œλŠ” κΈ°λ³Έ ν”ŒλŸ¬κ·ΈμΈμ˜ ꡬ동 방식을 κ°œκ΄„μ μœΌλ‘œ μ„€λͺ…ν•©λ‹ˆλ‹€.

    A. REST

    REST ν”„λ‘œν† μ½œμ€ λΆ„μ‚° μ„œλΉ„μŠ€μ— λŒ€ν•œ call, publish 컀λ„₯ν„°λ₯Ό νŠΉμ • μ—”λ“œν¬μΈνŠΈμ— λ§΅ν•‘ν•©λ‹ˆλ‹€. HTTP μš”μ²­μ˜ Paylo,adλŠ” 미듀웨어λ₯Ό 톡해 νŒŒμ‹±λ˜μ–΄ params 컀λ„₯ν„°λ₯Ό 톡해 λ³€ν™˜λ˜μ–΄ call, publish 컀λ„₯ν„°λ‘œ μ „λ‹¬λ©λ‹ˆλ‹€.

    μ—”λ“œν¬μΈνŠΈκ°€ μ€‘λ³΅λ˜λŠ” 경우 병합을 λ°œμƒμ‹œν‚¨ 좜처 λ…Έλ“œλ‘œ 디버그 λ©”μ„Έμ§€κ°€ report되며 병합에 μ‹€νŒ¨ν•©λ‹ˆλ‹€.

    API Catalogλ₯Ό ν†΅ν•΄μ„œ μ—”λ“œν¬μΈνŠΈ 별 적용된 μ •μ±… 및 컀λ„₯터와 κ·Έ νŒŒλΌλ―Έν„°μ— λŒ€ν•œ μ„€λͺ…을 μ œκ³΅ν•©λ‹ˆλ‹€.

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

    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κ°€ ν¬ν•¨λ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€.

    TODO: WIP

    B. GraphQL

    C. WebSocket

    Schema Registry

    A. Schema Integration

    μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆλŠ” μ„œλΉ„μŠ€ λΈŒλ‘œμ»€μ— μ˜μ‘΄ν•΄ Gateway둜 μˆ˜μ§‘λ˜κ³  μ²˜λ¦¬λ©λ‹ˆλ‹€. Moleculer μ–΄λŒ‘ν„°λ‘œ μž‘μ„±λœ μ„œλΉ„μŠ€ 브둜컀의 경우 기본적으둜 ServiceSchema의 metadata.api ν•„λ“œμ—μ„œ μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆκ°€ μˆ˜μ§‘λ˜κΈ°λ₯Ό κΈ°λŒ€ν•©λ‹ˆλ‹€.

    μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆμ˜ 병합은 μ„œλΉ„μŠ€ λ…Έλ“œμ˜ μ—°κ²°, μ’…λ£Œ, μŠ€ν‚€λ§ˆ λ³€κ²½μ‹œ λ°œμƒν•©λ‹ˆλ‹€. μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆλŠ” μžμ‹ μ˜ μŠ€ν‚€λ§ˆλ₯Ό 병합 ν•  브랜치λ₯Ό μŠ€ν‚€λ§ˆμ— λͺ…μ‹œν•©λ‹ˆλ‹€. Gatewayμ—λŠ” 기본적으둜 master λΈŒλžœμΉ˜κ°€ μƒμ„±λ˜μ–΄μžˆμœΌλ©°, master λΈŒλžœμΉ˜λŠ” 제거 될 수 μ—†μŠ΅λ‹ˆλ‹€.

    Policy Plugin

    2. Access Control Policy

    μ ‘κ·Ό μ œμ–΄ ν”ŒλŸ¬κ·ΈμΈλ³„ μŠ€ν‚€λ§ˆ 양식은 Access Control Policy μ„Ήμ…˜μ„ μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€. 이 μ„Ήμ…˜μ—μ„œλŠ” κΈ°λ³Έ ν”ŒλŸ¬κ·ΈμΈμ˜ ꡬ동 방식을 κ°œκ΄„μ μœΌλ‘œ μ„€λͺ…ν•©λ‹ˆλ‹€.

    ν”„λ‘œν† μ½œμ˜ ν™•μž₯μ„±κ³Ό μ ‘κ·Όμ œμ–΄ μ •μ±…μ˜ 정합성을 μœ„ν•΄μ„œ, μ ‘κ·Ό μ œμ–΄ 정책은 ν”„λ‘œν† μ½œλ³„ μ—”λ“œν¬μΈνŠΈκ°€ μ•„λ‹ˆλΌ μ•‘μ…˜κ³Ό 이벀트λ₯Ό 주체둜 μ μš©λ©λ‹ˆλ‹€.

    μ ‘κ·Ό μ œμ–΄ μ •μ±…μ˜ ν‰κ°€λŠ” API Gateway의 λ©”λͺ¨λ¦¬μ— LRU λ°©μ‹μœΌλ‘œ μΊμ‹œλ˜λ©° ν•œ μš”μ²­μ—μ„œ 쀑볡 μˆ˜ν–‰λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μΊμ‹œ ν‚€λ₯Ό 생성 ν•  λ•Œ μš”μ²­μ„ μ •ν™•νžˆ κ΅¬λΆ„ν•˜κΈ° μœ„ν•΄μ„œ μ»¨ν…μŠ€νŠΈ(인증 정보) 및 호좜 νŽ˜μ΄λ‘œλ“œ λ“±μ˜ 정보가 λ°˜μ˜λ©λ‹ˆλ‹€.

    μ μš©λ˜λŠ” ν”ŒλŸ¬κ·ΈμΈμ˜ μˆœμ„œλŠ” μœ νš¨ν•©λ‹ˆλ‹€. μš°μ„ ν•˜λŠ” ν”ŒλŸ¬κ·ΈμΈμ—μ„œ μ‹€νŒ¨ ν•  경우 λ‹€μŒ ν”ŒλŸ¬κ·ΈμΈμ˜ 정책은 ν‰κ°€λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μ ‘κ·Ό μ œμ–΄ ν”ŒλŸ¬κ·ΈμΈμ˜ κΈ°λ³Έ μ˜΅μ…˜μ—μ„œ OAuth2 Scope ν”ŒλŸ¬κ·ΈμΈ(scopes)이 FBAC ν”ŒλŸ¬κ·ΈμΈ(filter)보닀 μš°μ„ ν•©λ‹ˆλ‹€.

    A. OAuth2 Scope

    OAuth2 Scope ν”ŒλŸ¬κ·ΈμΈμ€ 각 μ •μ±…μ˜ scopes에 λ‚˜μ—΄λœ μŠ€μ½”ν”„λ₯Ό context.scopesκ°€ ν•˜λ‚˜ μ΄μƒμ˜ μŠ€μ½”ν”„λ₯Ό ν¬ν•¨ν•˜λŠ” 경우 접근을 ν—ˆμš©ν•©λ‹ˆλ‹€.

    FBAC ν”ŒλŸ¬κ·ΈμΈμ€ 각 μ •μ±…μ˜ filter ν•­λͺ©μ— λ§΅ν•‘λœ Inline JavaScript Function String을 VMμ—μ„œ μ‹€ν–‰ν•˜κ³  κ·Έ Boolean κ°’μœΌλ‘œ μ ‘κ·Ό μ œμ–΄ μ—¬λΆ€λ₯Ό νŒλ‹¨ν•©λ‹ˆλ‹€. 평가쀑 μ—λŸ¬κ°€ λ°œμƒν•˜κ±°λ‚˜ Boolean 값이 λ¦¬ν„΄λ˜μ§€ μ•ŠλŠ” 경우, API Gatewayμ—μ„œ 좜처 λ…Έλ“œλ‘œ μ „λ‹¬λ˜λ©° 접근이 κ±°λΆ€λ©λ‹ˆλ‹€.

    디버깅 쀑에 Inline JavaScript Function Stringμ—μ„œ console 객체λ₯Ό μ‚¬μš©ν•΄ λ©”μ„Έμ§€λ₯Ό 좜λ ₯ν•˜λŠ” 경우, κ·Έ λ©”μ„Έμ§€λŠ” console 객체에 λ°”μΈλ”©λœ report 컀λ„₯ν„°κ°€ 좜처 λ…Έλ“œλ‘œ μ „λ‹¬ν•©λ‹ˆλ‹€.

    B. FBAC

    Delegator

    1. MoleculerJS

    MoleculerJS μ–΄λŒ‘ν„°λ‘œ API Gateway ν•¨κ»˜μ™€ μ•„λž˜μ˜ ν”Όμ–΄ λͺ¨λ“ˆμ„ ν™œμš© ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    • moleculer-iam - OIDC 및 Identity Providerλ₯Ό μ œκ³΅ν•˜λŠ” IAM λͺ¨λ“ˆ, 인증 μ»¨ν…μŠ€νŠΈμ— 연동 κ°€λŠ₯

    • moleculer-file - 파일 μ—…λ‘œλ“œ/λ‹€μš΄λ‘œλ“œ/관리 λͺ¨λ“ˆ (GCP Storage bucket backend / File System backend 지원)

    • - κ΅­μ œν™” λ°μ΄ν„°λ² μ΄μŠ€ 관리 및 쑰회 λͺ¨λ“ˆ (MySQL/Maria DBMS ν•„μš”)

    • - ν™•μž₯ κ°€λŠ₯ν•œ Admin Console WebApp (React.js)

      • API document component κΈ°λ³Έ 제곡 ( ν˜Έν™˜)

      • IAM component κΈ°λ³Έ 제곡 ( ν˜Έν™˜)

    File management component κΈ°λ³Έ 제곡 ( ν˜Έν™˜)

  • Translation component κΈ°λ³Έ 제곡 ( ν˜Έν™˜)

  • moleculer-i18n
    moleculer-console
    moleculer-api
    moleculer-iam
    moleculer-file
    moleculer-i18n

    Connenctor

    1. Connectors

    A. Context Connectors

    μš”μ²­ μƒνƒœλ₯Ό 기반으둜 API μš”μ²­μ— ν™œμš©λ˜λŠ” 컀λ„₯ν„°μž…λ‹ˆλ‹€. μ ‘κ·Ό μ œμ–΄ 및 stateless 컀λ„₯ν„°λ₯Ό 연계 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    컀λ„₯ν„°

    μ–΄λŒ‘ν„°μ— μœ„μž„

    μ—°κ²° κ°€λŠ₯ν•œ 컀λ„₯ν„°

    κ°œμš”

    call

    GraphQL의 Subscription νƒ€μž…μ΄λ‚˜ WebSocket ν”„λ‘œν† μ½œ 등을 μ‚¬μš©ν•˜μ§€ μ•Šκ±°λ‚˜, λΆ„μ‚° μ‹œμŠ€ν…œμ— 쀑앙 λ©”μ‹œμ§• μ„œλΉ„μŠ€λ₯Ό 제곡 ν•  수 μ—†λŠ” κ²½μš°μ—” publish, subscribe 컀λ„₯ν„°λ₯Ό κ΅¬ν˜„ν•˜μ§€ μ•Šμ•„λ„ λ¬΄κ΄€ν•©λ‹ˆλ‹€.

    μš”μ²­ μƒνƒœκ°€ μ—†λŠ” 컀λ„₯ν„°μž…λ‹ˆλ‹€.

    μš”μ²­ νŽ˜μ΄λ‘œλ“œμ—μ„œ μœ„μ˜ 타 컀λ„₯ν„°λ“€λ‘œ 전달 ν•  객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

    discover

    O

    λΆ„μ‚° μ„œλΉ„μŠ€μ˜ μ—…λ°μ΄νŠΈλ‚˜ μ’…λ£Œλ₯Ό κ°μ§€ν•˜κ³ , λ…Έλ“œ, μ„œλΉ„μŠ€ API Schema, μ•‘μ…˜ 및 이벀트 ꡬ독, λ°œν–‰ 정보λ₯Ό μˆ˜μ§‘ν•©λ‹ˆλ‹€.

    health

    O

    λΆ„μ‚° μ„œλΉ„μŠ€ 및 μ•‘μ…˜, 쀑앙 λ©”μ‹œμ§• μ„œλΉ„μŠ€μ˜ μƒνƒœ 확인을 μ œκ³΅ν•©λ‹ˆλ‹€.

    reporter

    O

    좜처 λ…Έλ“œλ‘œ 디버그 λ©”μ„Έμ§€λ₯Ό μ „λ‹¬ν•©λ‹ˆλ‹€.

    logger

    O

    Gateway의 λ‘œκΉ… μΈμŠ€ν„΄μŠ€λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

    O

    params, map

    λΆ„μ‚° μ„œλΉ„μŠ€ μ•‘μ…˜μ„ ν˜ΈμΆœν•©λ‹ˆλ‹€.

    publish

    O

    params

    쀑앙 λ©”μ‹œμ§• μ„œλΉ„μŠ€μ— 이벀트λ₯Ό λ°œν–‰ν•©λ‹ˆλ‹€.

    subscribe

    O

    map

    쀑앙 λ©”μ‹œμ§• μ„œλΉ„μŠ€μ—μ„œ 이벀트λ₯Ό κ΅¬λ…ν•©λ‹ˆλ‹€.

    컀λ„₯ν„°

    μ–΄λŒ‘ν„°μ— μœ„μž„

    κ°œμš”

    map

    X

    Inline JavaScript Function String을 VMμ—μ„œ ν•΄μ„ν•˜μ—¬ μ£Όμ–΄μ§„ κ°μ²΄λ‚˜ 응닡 객체λ₯Ό λ³€ν™˜ν•©λ‹ˆλ‹€.

    params

    B. Stateless Connectors

    X

    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 버전을 μ˜λ―Έν•©λ‹ˆλ‹€.

    κ²Œμ΄νŠΈμ›¨μ΄μ— 버전이 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” κ²½μš°μ—λŠ” μŠ€ν‚€λ§ˆλ₯Ό μ—…λ°μ΄νŠΈν•˜κΈ°λ‘œ ν•©λ‹ˆλ‹€.
  • μ„œλΉ„μŠ€ λ…Έλ“œμ˜ 연결이 끊긴 κ²½μš°λŠ” μ—°κ΄€λœ μŠ€ν‚€λ§ˆμ˜ λ…Έλ“œν’€μ—μ„œ λ…Έλ“œλ₯Ό μ‚­μ œν•©λ‹ˆλ‹€.

    • 이 λ•Œ μ—°κ΄€λœ μŠ€ν‚€λ§ˆλ“€μ—μ„œ λ…Έλ“œν’€μ΄ 빈 μŠ€ν‚€λ§ˆλ“€μ„ μ œκ±°ν•˜κΈ°λ‘œ ν•©λ‹ˆλ‹€.

  • μ΄μ™Έμ˜ λΈŒλžœμΉ˜μ— λ³‘ν•©ν•˜λŠ” κ²½μš°μ—λŠ” μ£Όμ–΄μ§„ λΈŒλžœμΉ˜μ—λ§Œ 병합이 μ‹œλ„λ©λ‹ˆλ‹€.

  • ν”„λ‘œν† μ½œ ν”ŒλŸ¬κ·ΈμΈμ„ 따라 각 ν”„λ‘œν† μ½œλ³„ ν•Έλ“€λŸ¬λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

    • 병합에 μ„±κ³΅ν•˜λ©΄ μƒμ„±λœ Gateway API μŠ€ν‚€λ§ˆ 버전에 latest νƒœκ·Έ 및 8 κΈ€μžμ˜ 숏 ν•΄μ‹œ(eg. abcdefgh)κ°€ νƒœκ·Έλ‘œ λΆ€μ°©λ©λ‹ˆλ‹€.

  • μ˜΅μ…˜μ΄ ν™œμ„±ν™”λœ 경우 λΈŒλžœμΉ˜λ³„ API Catalogλ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.

  • μ˜΅μ…˜μ΄ ν™œμ„±ν™”λœ 경우 λΈŒλžœμΉ˜λ³„ Service Catalogλ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.

  • 이벀트/브랜치

    master

    dongwook

    λΉ„κ³ 

    initial schema

    (empty)

    N/A

    a added to master

    Routing Rule

    Gateway μ›Ή μ„œλ²„λŠ” [/~BRANCH[@TAG]]/<ENDPOINT>의 κ·œμΉ™λŒ€λ‘œ API μ—”λ“œν¬μΈνŠΈλ₯Ό λΌμš°νŠΈν•©λ‹ˆλ‹€. 첫번째 경둜 쑰각을 브랜치(masterλ₯Ό κΈ°λ³Έκ°’μœΌλ‘œ)둜 λ‘λ²ˆμ§Έ 경둜 쑰각을 νƒœκ·Έ(latestλ₯Ό κΈ°λ³Έκ°’μœΌλ‘œ)둜 μ΄ν•˜ 경둜λ₯Ό API μ—”λ“œν¬μΈνŠΈλ‘œ ν•΄μ„ν•©λ‹ˆλ‹€.

    라우트 μ˜ˆμ‹œ

    κ°„λž΅

    브랜치 포함

    νƒœκ·Έ 포함

    GET /players/1

    GET /~master/players/1

    GET /~master@latest/players/1

    -

    ν…Œμ΄λΈ”μ˜ 각 행은 λ™μΌν•œ λ²„μ „μ˜ ν•Έλ“€λŸ¬λ‘œ μ—°κ²°λ©λ‹ˆλ‹€.

    브랜치 이름 κ·œμΉ™

    • 영문 μ†Œλ¬Έμž 및 숫자, -, _만 ν—ˆμš©λ©λ‹ˆλ‹€.

    • κΈ°μ‘΄ μŠ€ν‚€λ§ˆ μ—”λ“œν¬μΈνŠΈλ‚˜ ν”ŒλŸ¬κ·ΈμΈμ˜ base κ²½λ‘œμ™€ 쀑볡될 수 μ—†μŠ΅λ‹ˆλ‹€.

    νƒœκ·Έ 이름 κ·œμΉ™

    • 영문 μ†Œλ¬Έμž 및 숫자둜만 κ΅¬μ„±λ©λ‹ˆλ‹€.

    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

    -

    GET /~master@h4g3f2e1/players/1

    -

    GET /~dongwook/players/1

    GET /~dongwook@latest/players/1

    -

    -

    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

    Health Check

    API Document Generation

    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 μŠ€ν‚€λ§ˆμ— ν†΅ν•©λ©λ‹ˆλ‹€. μ ‘κ·Ό μ œμ–΄ 정책은 ν™œμ„±ν™”μ‹œ μ˜΅μ…˜μœΌλ‘œ μ£Όμž… ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    200

    200

    500

    GET /~health/readiness

    503

    200

    200

    503

    500

    Gateway μƒνƒœ

    μ‹œμž‘μ€‘

    병합쀑

    μž‘λ™μ€‘

    μ’…λ£Œμ€‘

    였λ₯˜

    GET /~health/liveness

    200

    Connector

    200

    API Handler

    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 μŠ€ν‚€λ§ˆ λ³‘ν•©μ˜ μˆœμ„œκ°€ λ™μΌν•˜κ²Œ 보μž₯λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

    API Server

    Request Lifecycle

    1. Extension

    Server Extension은 μ›Ή μ„œλ²„λ₯Ό 생성 및 ν™•μž₯ν•˜λŠ”λ° μ“°μž…λ‹ˆλ‹€. μ„œλ²„ 생성 μ˜΅μ…˜μ„ 톡해 μ œμ–΄ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    HTTP

    HTTP Extension은 HTTP ν”„λ‘œν† μ½œμ„ μ§€μ›ν•˜λ„λ‘ μ›Ή μ„œλ²„λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. 기본적으둜 ν™œμ„±ν™”λ©λ‹ˆλ‹€.

    HTTP/2

    HTTP/2 Extension은 HTTP/2 ν”„λ‘œν† μ½œμ„ μ§€μ›ν•˜λ„λ‘ μ›Ή μ„œλ²„λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

    TLS

    TLS Extension은 HTTPS μš”μ²­μ„ 처리 ν•  수 μžˆλ„λ‘ μ›Ή μ„œλ²„λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

    2. 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 λ“± 기초적인 λ³΄μ•ˆ μš”μ†Œλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

    μ—”λ“œν¬μΈνŠΈλ‘œμ˜ λΌμš°νŒ… 직전에 Context Factory 미듀웨어λ₯Ό 톡해 μš”μ²­ μ»¨ν…μŠ€νŠΈλ₯Ό μƒμ„±ν•˜κ²Œ λ©λ‹ˆλ‹€. ContextλŠ” API Handler에 μ „λ‹¬λ˜λŠ” 인증, Locale, Language, TimeZone λ“±μ˜ 정보λ₯Ό ν¬ν•¨ν•œ κ°μ²΄μž…λ‹ˆλ‹€.

    Locale

    Locale μ»¨ν…μŠ€νŠΈλŠ” μš”μ²­ ν—€λ”λ‘œλΆ€ν„° Locale, Language, TimeZone λ“±μ˜ 정보λ₯Ό μΆ”μΆœν•©λ‹ˆλ‹€.

    Auth

    인증 μ»¨ν…μŠ€νŠΈλ₯Ό ν™œμ„±ν™”ν•˜κΈ° μœ„ν•΄μ„œλŠ” μš”μ²­μœΌλ‘œλΆ€ν„° μ„œλ²„ 생성 μ˜΅μ…˜μ„ 톡해 인증 정보λ₯Ό μƒμ„±ν•˜λŠ” ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•΄μ•Όν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ Bearer 토큰을 ν”Όμ–΄ λͺ¨λ“ˆμ„ 톡해 톡해 νŒŒμ‹±ν•΄ idToken을 νšλ“ν•˜κ±°λ‚˜, λ³„λ„μ˜ 인증 μ„œλ²„μ— 전달해 μ›ν•˜λŠ” λ°©μ‹λŒ€λ‘œ 해석 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    μ—”λ“œν¬μΈνŠΈλ‘œμ—μ„œμ˜ 응닡 후에 μˆ˜ν–‰λ˜λŠ” λ―Έλ“€μ›¨μ–΄μž…λ‹ˆλ‹€.

    Error

    Error λ―Έλ“€μ›¨μ–΄λŠ” λΆ„μ‚° μ„œλΉ„μŠ€μ˜ μ—λŸ¬λ₯Ό νŒŒμ•…ν•˜κ³  HTTP μƒνƒœ μ½”λ“œλ₯Ό μ„€μ •ν•˜λ©° ν‘œμ€€ν™”λœ μ—λŸ¬ 포맷의 응닡을 μƒμ„±ν•©λ‹ˆλ‹€.

    ETag

    ETag λ―Έλ“€μ›¨μ–΄λŠ” GET, HEAD μš”μ²­ 및 응닡(200 OK)μ—μ„œ If-None-Match 및 ETag 헀더λ₯Ό ν™œμš©ν•΄ API ν΄λΌμ΄μ–ΈνŠΈμ˜ μ„±λŠ₯을 λ†’νž™λ‹ˆλ‹€.

    Header

    Header λ―Έλ“€μ›¨μ–΄λŠ” Header Context에 값이 ν• λ‹Ήλœ 경우 응닡 헀더λ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.

    μš”μ²­μ΄ μ „μ²˜λ¦¬ 미듀웨어, μ»¨ν…μŠ€νŠΈ 미듀웨어λ₯Ό 톡과 ν•œ 후에 λ™μ μœΌλ‘œ μƒμ„±λœ μ—”λ“œν¬μΈνŠΈλ‘œ λΌμš°νŒ…λ©λ‹ˆλ‹€. API ν•Έλ“€λŸ¬λ₯Ό 톡과 ν•œ ν›„ ν›„μ²˜λ¦¬ 미듀웨어λ₯Ό μ§€λ‚˜ μš”μ²­μ΄ μ™„λ£Œλ©λ‹ˆλ‹€.

    동적 ν•Έλ“€λŸ¬λŠ” Gateway에 μƒμ„±λœ λΈŒλžœμΉ˜μ— 따라 μš”μ²­μ„ 브랜치 ν•Έλ“€λŸ¬λ‘œ ν”„λ‘μ‹œν•©λ‹ˆλ‹€.

    브랜치 ν•Έλ“€λŸ¬λŠ” Gateway에 μƒμ„±λœ ν•΄λ‹Ή 브랜치의 νƒœκ·Έμ— 따라 μš”μ²­μ„ 버전 ν•Έλ“€λŸ¬λ‘œ ν”„λ‘μ‹œν•©λ‹ˆλ‹€.

    버전 ν•Έλ“€λŸ¬λŠ” Gateway API μŠ€ν‚€λ§ˆμ— 따라 컀λ„₯ν„° 및 ν”„λ‘œν† μ½œ ν”ŒλŸ¬κ·ΈμΈμ„ μ‘°ν•©ν•΄ μƒμ„±ν•œ ν•Έλ“€λŸ¬λ‘œ μš”μ²­μ„ ν”„λ‘μ‹œν•©λ‹ˆλ‹€.

    A. Before Middleware

    B. Context Factory

    C. After Middleware

    3. API Handler

    A. Dynamic Handler

    B. Branch Handler

    C. Version Handler

    moleculer-iam

    Authorization

    Component

    WebSocket

    HTTP

    Application

    Context Factory

    Auth

    Cookie

    Correlation ID

    Locale

    IP Address

    User-Agent

    Error

    Logging

    Middleware

    Body Parser

    Helmet

    CORS

    HTTPS

    HTTP

    http, https, http2 (WIP)

    Moleculer

    Overview

    API Schema and Handler

    1. Design Principle

    Moleculer API GatewayλŠ” μ•„λž˜ 원칙을 기반으둜 κ³ μ•ˆλ˜μ—ˆμŠ΅λ‹ˆλ‹€.

    • λΆ„μ‚° μ‹œμŠ€ν…œμ•ˆμ—μ„œ μœ λ™μ μœΌλ‘œ μž‘λ™ν•©λ‹ˆλ‹€.

      • Persistence Layerλ₯Ό κ°–μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

    • "λΆ„μ‚° μ„œλΉ„μŠ€ -> API" 쒅속성을 μ΅œμ†Œν™”ν•©λ‹ˆλ‹€.

      • μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆλŠ” JSON ν…μŠ€νŠΈμž…λ‹ˆλ‹€.

      • λΆ„μ‚° μ„œλΉ„μŠ€ ν˜ΈμΆœμ‹œ 인증 λ“±μ˜ μ»¨ν…μŠ€νŠΈλ₯Ό νŒŒλΌλ―Έν„°λ‘œ λ§΅ν•‘ν•˜λ„λ‘ μœ λ„ν•©λ‹ˆλ‹€.

    • ν™•μž₯ κ°€λŠ₯ν•œ μ»΄ν¬λ„ŒνŠΈ νŒ¨ν„΄μ„ μ§€ν–₯ν•©λ‹ˆλ‹€.

      • ν”„λ‘œν† μ½œ ν”ŒλŸ¬κ·ΈμΈμ€ μ„œλ²„, 미듀웨어, μŠ€ν‚€λ§ˆ, ν•Έλ“€λŸ¬μ˜ λͺ¨λ“  뢀뢄을 ν™•μž₯ν•©λ‹ˆλ‹€.

      • μ ‘κ·Ό μ œμ–΄ 정책은 ν”„λ‘œν† μ½œλ³„ μ—”λ“œν¬μΈνŠΈκ°€ μ•„λ‹Œ μ•‘μ…˜, μ΄λ²€νŠΈμ— μ μš©λ©λ‹ˆλ‹€.

    • λ„€νŠΈμ›Œν‚Ή 및 볡원 νŒ¨ν„΄μ— κ΄€μ—¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

      • λΆ„μ‚° μ„œλΉ„μŠ€μ™€ API GatewayλŠ” μ–΄λŒ‘ν„°(Broker)둜 μ—°κ²°λ©λ‹ˆλ‹€.

      • λΆ„μ‚° νŠΈλžœμž­μ…˜μ„ μœ λ„ν•˜κ±°λ‚˜ κ΄€μ—¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

    μ•„μšΈλŸ¬ λΆ„μ‚° μ„œλΉ„μŠ€ 및 μ„œλΉ„μŠ€ λΈŒλ‘œμ»€μ—μ„œ κΈ°λŒ€λ˜λŠ” νŒ¨ν„΄μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

    • λΆ„μ‚° μ„œλΉ„μŠ€μ˜ ν”„λ‘œμ‹œμ €λŠ” λ¬΄μƒνƒœλ₯Ό μ§€ν–₯ν•©λ‹ˆλ‹€.

      • ν”„λ‘œμ‹œμ €λŠ” 인증 μ»¨ν…μŠ€νŠΈλ₯Ό κ³ λ €ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

      • ν”„λ‘œμ‹œμ €λŠ” μ ‘κ·Ό μ œμ–΄λ₯Ό κ³ λ €ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

    μ΄ν•˜μ—μ„œ μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆλŠ” λΆ„μ‚° ν™˜κ²½μ˜ 뢀뢄적인 API μŠ€ν‚€λ§ˆλ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€. Gateway API μŠ€ν‚€λ§ˆλŠ” Gatewayμ—μ„œ ν†΅ν•©λœ API μŠ€ν‚€λ§ˆλ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€.

    μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆλŠ” JSON ν…μŠ€νŠΈλ‘œ Gateway에 μ „λ‹¬λ©λ‹ˆλ‹€. μŠ€ν‚€λ§ˆ λ°μ΄ν„°μ˜ 직렬화 및 λΉ„μ§λ ¬ν™”λŠ” MSA λΌμ΄λΈŒλŸ¬λ¦¬μ— λ‹¬λ ΈμŠ΅λ‹ˆλ‹€. μ•„λž˜ μ˜ˆμ‹œμ—μ„œλŠ” Node.js ν™˜κ²½μ„ κΈ°μ€€μœΌλ‘œ μ„œλΉ„μŠ€ API μŠ€ν‚€λ§ˆλ₯Ό JavaScript 객체둜 ν‘œκΈ°ν•©λ‹ˆλ‹€.

    ν”„λ‘œμ‹œμ €λŠ” κ°€λŠ₯ν•œ 멱등성을 갖도둝 κ³ λ €λ©λ‹ˆλ‹€.
  • μ„œλΉ„μŠ€ λΈŒλ‘œμ»€λŠ” λΆ„μ‚° μ‹œμŠ€ν…œμ„ μœ„ν•œ 볡원 νŒ¨ν„΄μ„ κ΅¬μ„±ν•©λ‹ˆλ‹€.

    • νšŒλ‘œμ°¨λ‹¨κΈ°

    • 격벽

    • μž¬μ‹œλ„

    • μš”μ²­ 큐

  • 2. API Schema

    Protocol Plugin

      protocol: {

    μ΄ν•˜ protocol ν•­λͺ©μ— 외뢀에 μ œκ³΅ν•˜λ €λŠ” APIλ₯Ό μž‘μ„±ν•˜κ³  call, publish, subscribe, map 컀λ„₯터에 λ§΅ν•‘ν•©λ‹ˆλ‹€. 각 컀λ„₯터에 λŒ€ν•œ 좔가적인 λ‚΄μš©μ€ μ•„λž˜ Connectors for API Handler μ„Ήμ…˜μ—μ„œ λ‹€λ£Ήλ‹ˆλ‹€.

    REST

    A. REST

    REST API λ§΅ν•‘μ—λŠ” subscribeλ₯Ό μ œμ™Έν•œ call, publish, map 컀λ„₯ν„°λ₯Ό 이용 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

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

    basePathλ₯Ό 기반으둜 μ΄ν•˜ REST μ—”λ“œν¬μΈνŠΈκ°€ μƒμ„±λ©λ‹ˆλ‹€.

    description은 λ¬Έμ„œ μƒμ„±μ‹œ ν™œμš©λ˜λ©° Markdown을 μ§€μ›ν•©λ‹ˆλ‹€ (μ˜΅μ…˜).

    Call

            {
              method: "GET",
              path: "/:id",
              deprecated: false,
              description: "Get player information by id",
              call: {
                action: "player.get",
                params: {
                  id: "@path.id",
                },          
              },
            },

    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 객체λ₯Ό 이용 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    path-to-regexp
            {
              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

    B. GraphQL

    GraphQL API λ§΅ν•‘μ—λŠ” call, publish, subscribe, map 컀λ„₯ν„°λ₯Ό 이용 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    TypeDefs

        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
            }
          `,

    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)λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

    Dataloader
          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`,
                },
              },
            },
          },
        },

    Serve Static

    Overview

    Development

    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

    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.

    Contribution

    WebSocket

    C. WebSocket

        WebSocket: {
          // TODO: WIP
        },

    Policy Plugin

    D. Access Control Policy

      },

    μœ„μ— μ •μ˜ν•œ 각 ν”„λ‘œν† μ½œλ“€μ˜ μ—”λ“œν¬μΈνŠΈλŠ” μ„œλΉ„μŠ€ 브둜컀의 call, publish, subscribe 컀λ„₯ν„°λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€. 이 λ•Œ ν˜ΈμΆœλ˜λŠ” 각 컀λ„₯터듀에 λŒ€ν•΄μ„œ μ ‘κ·Ό μ œμ–΄ 정책을 μ •μ˜ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

      policy: {

    μ ‘κ·Ό μ œμ–΄ 정책은 λ¨Όμ € ν˜ΈμΆœν•˜λŠ” 컀λ„₯터에 λ”°λΌμ„œ actionμ΄λ‚˜ event의 μ΄λ¦„μœΌλ‘œ ν•„ν„°λ§λ©λ‹ˆλ‹€. μ—°κ΄€λœ 정책듀은 μˆœμ„œλŒ€λ‘œ λͺ¨λ‘ μ μš©λ©λ‹ˆλ‹€. λͺ¨λ“  정책을 ν†΅κ³Όν•˜λŠ” κ²½μš°μ— ν•΄λ‹Ή 컀λ„₯ν„°κ°€ 호좜될 수 μžˆμŠ΅λ‹ˆλ‹€.

    μ ‘κ·Ό μ œμ–΄ 정책을 ν‰κ°€ν•˜λŠ” 방식은 ν”ŒλŸ¬κ·ΈμΈ ν˜•νƒœλ‘œ μ œκ³΅λ©λ‹ˆλ‹€. 기본적으둜 OAuth scope 방식(scopes)κ³Ό Inline JavaScript Function Stringλ₯Ό ν™œμš©ν•œ FBAC 방식(filter) 두가지가 μ œκ³΅λ©λ‹ˆλ‹€.

    Caching: TODO

    λ˜ν•œ μ ‘κ·Ό μ œμ–΄ μ •μ±…μ˜ ν‰κ°€λŠ” Gateway의 λ©”λͺ¨λ¦¬μ— LRU λ°©μ‹μœΌλ‘œ μΊμ‹œλ˜λ©° ν•œ μš”μ²­μ—μ„œ 쀑볡 μˆ˜ν–‰λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μΊμ‹œ ν‚€λ₯Ό 생성 ν•  λ•Œ μš”μ²­μ„ μ •ν™•νžˆ κ΅¬λΆ„ν•˜κΈ° μœ„ν•΄μ„œ μ»¨ν…μŠ€νŠΈ(인증 정보) 및 호좜 νŽ˜μ΄λ‘œλ“œ λ“±μ˜ 정보가 λ°˜μ˜λ©λ‹ˆλ‹€.

    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);
      }`,
    }

    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 토큰이 νšλ“ν•œ μŠ€μ½”ν”„λ₯Ό ν™•μΈν•˜κ³  μΌμΉ˜λ˜λŠ” μŠ€μ½”ν”„κ°€ ν•˜λ‚˜λΌλ„ μžˆλŠ” 경우 ν†΅κ³Όν•©λ‹ˆλ‹€.

    Streaming Request/Response

    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.

    1.1. REST file upload example

    {
      method: "POST",
      action: "file.upload",
      params: "@body.file",
    }

    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.

    {
      filename: string;
      encoding: "utf8"|"7bit"|"base64"|...;
      mimetype: string;
      createReadStream(): ReadableStream;
    }

    1.2. GraphQL file upload example

    {
        typeDefs: `
            extend type Mutation {
                uploadFile(file: Upload): JSON
            }
        `,
        resolvers: {
          Mutation:  {
            uploadFile: {
              call: "file.upload", 
              params: "@args.file",
            },
          },
        },
    }

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

    {
      filename: string;
      encoding: "utf8"|"7bit"|"base64"|...;
      mimetype: string;
      createReadStream(): ReadableStream;
    }

    For WebSocket protocol, see 4.

    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.

    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.

    WebSocket protocol supports bidirectional streaming. // TODO: documentation

    3. Modify Response Header/Status

    4. Bidirectional Streaming

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

    Bidirectional Streaming

    Service Broker Delegator

    Manipulating HTTP Response

    Protocol Plugin

    Schema Registry

    API Server

    Project Roadmap

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

    Application Context Factory

    Middleware

    CHANGELOG

    0.3.2 - 2020-12-13

    Added

    • Created gitbook documentation: https://moleculer-api.gitbook.io/api/

    Policy Plugin

    Request

    Branch

    {
      branch: "master",

    제거 ν•  수 μ—†λŠ” κΈ°λ³Έ λΈŒλžœμΉ˜λŠ” 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λ₯Ό μ œκ³΅ν•œ μ„œλΉ„μŠ€ λ…Έλ“œλ₯Ό μš°μ„ μœΌλ‘œ μš”μ²­μ„ ν”„λ‘μ‹œν•©λ‹ˆλ‹€.

    λ˜λŠ” λ§ˆμ°¬κ°€μ§€λ‘œ λ‘œμ»¬μ— 개발용 κ²Œμ΄νŠΈμ›¨μ΄λ₯Ό 직접 μ‹€ν–‰ν•˜λŠ” λ°©λ²•μ΄μ§€λ§Œ, λΆ„μ‚° μ‹œμŠ€ν…œμ— μ—°κ²°ν•΄ 배포된 타 μ„œλΉ„μŠ€μ— 의쑴 ν•  ν•„μš”κ°€ μžˆλŠ” 경우 λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.

    즉 이외(eg. dev)의 브랜치둜의 νŠΈλž˜ν”½μ„ λ°©μ§€ν•©λ‹ˆλ‹€.

  • μ΄μ™Έμ˜ 브랜치(eg. dev)μ—μ„œλŠ” API μ—”λ“œν¬μΈνŠΈμ— μ—°κ²°λœ μ„œλΉ„μŠ€ μ•‘μ…˜μ„ 호좜 ν•  λ•Œ dev,master,(none) λΈŒλžœμΉ˜μ— APIλ₯Ό μ œκ³΅ν•œ μ„œλΉ„μŠ€ λ…Έλ“œλ₯Ό μš°μ„ μœΌλ‘œ μš”μ²­μ„ ν”„λ‘μ‹œν•©λ‹ˆλ‹€.

    • 즉 ν˜„μž¬ 브랜치(eg. dev)에 μ—”λ“œν¬μΈνŠΈκ°€ μ—†λŠ” 경우 master 브랜치의 μ—”λ“œν¬μΈνŠΈλ₯Ό μ°¨μ„ μœΌλ‘œ μ°ΎμŠ΅λ‹ˆλ‹€.

  • 이 κ·œμΉ™μ€ API ν•Έλ“€λŸ¬ μƒμ„±μ‹œμ— 브랜치 μ „λž΅μ— 따라 μžμ—°μŠ€λŸ½κ²Œ μ μš©λ©λ‹ˆλ‹€.

  • publish, subscribe

    • 이벀트 λ©”μ‹œμ§€ μ „λ‹¬μ—λŠ” μ„œλΉ„μŠ€ λΈŒλ‘œμ»€μ— μ—°κ²°λœ 쀑앙 λ©”μ‹œμ§• μ„œλΉ„μŠ€μ˜ 정책을 κ·ΈλŒ€λ‘œ λ”°λ¦…λ‹ˆλ‹€.

  • Supporters

    QMIT Inc.

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

    FAQ

    What is the difference compared to '' project?

    WIP...

    moleculer-web

    Contributors

    Maintainers

    Contributors

    Nobody yet

    Application Component

    dehypnosis - OverviewGitHub
    Logo
    https://en.plco.siteen.plco.site