본문 바로가기
WIKI 기술 지식 베이스

이벤트 기반 업데이트를 Entity Provider와 통합하기

원문 보기 위키 갱신

이벤트 기반 업데이트를 Entity Provider와 통합하기 (Integrating Event-Driven Updates with Entity Providers)

이 가이드는 이벤트를 수신할 HTTP 엔드포인트를 설정하고 Events System을 EntityProvider에 통합하여 카탈로그를 즉시 업데이트하는 방법을 안내합니다.

출처: 문서

본문

이 가이드는 이벤트를 수신할 HTTP 엔드포인트를 설정하고 Events System을 EntityProvider에 통합하여 카탈로그를 즉시 업데이트하는 방법을 안내합니다. Events 시스템이 다른 사용 사례도 지원하지만, 이 가이드는 특히 EntityProvider에서 HTTP 기반 이벤트를 사용하는 데 초점을 맞춥니다.

제공된 예시에서는 Frobs라는 외부 가상 서비스에서 엔터티를 수집하는 방법을 보여줍니다.

기본 흐름:

  • 외부 서비스(이 예시에서는 Frobs)가 @backstage/plugin-events-backend 플러그인이 노출하는 HTTP 엔드포인트로 이벤트를 보냅니다. 이 엔드포인트는 정의한 토픽(예: frobs)에 해당합니다.
  • @backstage/plugin-events-backend 플러그인을 확장해 사용자 지정 Router를 노출하는 모듈이 이 일반 토픽의 수신 이벤트를 처리하고, 각 이벤트 페이로드의 내용에 따라 더 구체적인 하위 토픽으로 라우팅합니다.
  • EntityProvider가 이러한 특정 하위 토픽 이벤트를 구독하고, 수신 시 카탈로그의 엔터티를 그에 따라 추가/업데이트/삭제하는 작업을 수행합니다.

HTTP 엔드포인트로 이벤트 수신하기 (Receiving Events via HTTP Endpoints)

The @backstage/plugin-events-backend 플러그인은 HTTP 엔드포인트를 통해 이벤트를 수신하기 위한 즉시 사용 가능한(out-of-the-box) 지원을 제공합니다. 이러한 이벤트는 EventsService에 게시됩니다.

특정 토픽에 대한 HTTP 엔드포인트를 만들려면 app-config.yaml에서 구성해야 합니다.

events:  http:    topics:      - frobs

이 구성에 명시적으로 나열된 토픽만 사용 가능한 HTTP 엔드포인트가 됩니다.

위 예시는 다음 엔드포인트를 만듭니다.

POST /api/events/http/frobs

이 URL을 외부 서비스에서 웹훅을 설정할 때 페이로드 URL로 사용할 수 있습니다. 이벤트가 이 엔드포인트로 전송되면 events-backend가 이를 Events Service에 게시하여 구독 중인 모든 엔터티 공급자가 사용할 수 있게 합니다.

일반 토픽을 특정 하위 토픽으로 라우팅하기 (Routing General Topics to Specific Subtopics)

이벤트를 효과적으로 관리하려면 events-backend 플러그인용 모듈을 만들어 일반 토픽을 더 구체적인 하위 토픽으로 라우팅하도록 이벤트 시스템을 확장할 수 있습니다.

예를 들어 POST /api/events/http/frobs 엔드포인트를 사용해 Frobs 서비스용 웹훅을 설정하면 모든 수신 이벤트가 처음에는 일반 frobs 토픽 아래에 게시됩니다. 그러나 페이로드(예: type 필드)를 기반으로 이러한 이벤트를 더 구체적인 하위 토픽 아래에 다시 게시하면 EntityProvider가 더 넓은 frobs 토픽 아래의 모든 이벤트를 처리하는 대신 필요한 관련 하위 토픽만 구독할 수 있게 됩니다.

다음은 일반 frobs 토픽을 구독하고 이벤트 페이로드에 제공된 $.type을 기준으로 더 구체적인 하위 토픽 아래에 이벤트를 게시하는 SubTopicEventRouter의 예시입니다.

import {  EventParams,  EventsService,  SubTopicEventRouter,} from '@backstage/plugin-events-node';/** * Subscribes to the generic `frobs` topic * and publishes the events under the more concrete sub-topic * depending on the `$.type` provided in the event payload. * * @public */export class FrobsEventRouter extends SubTopicEventRouter {  constructor(options: { events: EventsService }) {    super({      events: options.events,      topic: 'frobs',    });  }  protected getSubscriberId(): string {    return 'FrobsEventRouter';  }  protected determineSubTopic(params: EventParams): string | undefined {    if ('type' in (params.eventPayload as object)) {      const payload = params.eventPayload as { type: string };      return payload.type;    }    return undefined;  }}

Events를 Entity Provider에 통합하기 (Integrating Events into an Entity Provider)

엔터티 공급자는 특정 이벤트 토픽을 구독하고 수신 이벤트에 반응할 수 있습니다. 이를 통해 외부 트리거에 기반한 카탈로그의 즉시 업데이트가 가능합니다.

다음은 이벤트 구독을 통합한 EntityProvider의 기본 구조입니다. 아래 번호 표시를 확인해 각 단계를 살펴보세요.

plugins/catalog-backend-module-frobs/src/FrobsProvider.ts

import { Entity } from '@backstage/catalog-model';import {  EntityProvider,  EntityProviderConnection,} from '@backstage/plugin-catalog-node';import {  SchedulerServiceTaskRunner,  UrlReaderService,} from '@backstage/backend-plugin-api';import { EventsService, EventParams } from '@backstage/plugin-events-node';/** * Provides entities from the fictional Frobs service. */export class FrobsProvider implements EntityProvider {  private readonly env: string;  private readonly reader: UrlReaderService;  private readonly taskRunner: SchedulerServiceTaskRunner;  private readonly events?: EventsService;  private connection?: EntityProviderConnection;  constructor(    env: string,    reader: UrlReaderService,    taskRunner: SchedulerServiceTaskRunner,    /** [1] */    events?: EventsService,  ) {    this.env = env;    this.reader = reader;    this.taskRunner = taskRunner;    this.events = events;  }  getProviderName(): string {    return `frobs-${this.env}`;  }  async connect(connection: EntityProviderConnection): Promise<void> {    this.connection = connection;    /** [2] */    await this.events?.subscribe({      id: this.getProviderName(),      topics: ['frobs-add', 'frobs-delete', 'frobs-modify'],      /** [3] */      onEvent: async (params: EventParams) => {        const id = params.eventPayload.id;        const baseUrl = `https://frobs-${id}.example.com/data`;        const response = await this.reader.readUrl(baseUrl);        const data = JSON.parse((await response.buffer()).toString());        const entities: Entity[] = frobsToEntities(data);        if (params.topic === 'frobs-add') {          await this.connection!.applyMutation({            type: 'delta',            added: entities,            removed: [],          });        } else if (params.topic === 'frobs-delete') {          await this.connection!.applyMutation({            type: 'delta',            added: [],            removed: entities,          });        } else if (params.topic === 'frobs-modify') {          ...

이 통합의 핵심 부분을 살펴보겠습니다.

  • EventsService를 의존성으로 추가하기: EntityProvider의 생성자에 EventsService를 선택적 의존성으로 포함합니다. 이를 통해 공급자가 이벤트 시스템과 상호작용할 수 있습니다.
  • connect에서 토픽 구독하기: connect 함수 내에서 플러그인이 반응해야 하는 특정 이벤트 토픽(예: 'frobs-add', 'frobs-delete', 'frobs-modify')을 구독합니다.
  • onEvent 메서드 구현하기: onEvent 메서드는 중요합니다. 공급자가 구독한 토픽에 대한 이벤트를 받을 때마다 호출됩니다. 이 메서드 내에서:
    • 이벤트 페이로드 정보(params.eventPayload로 접근 가능)를 기반으로 어떤 엔터티를 추가, 삭제, 수정해야 할지 결정하는 로직을 구현합니다.
    • 델타(delta) 변형을 사용해 엔터티를 명시적으로 upsert하거나 삭제합니다. 이 접근 방식은 전체 카탈로그를 처음부터 업데이트하는 것보다 더 효율적입니다. 변형에 대한 자세한 내용은 "Provider Mutations" 섹션을 참조하세요.

더 알아보기 (Learn more)