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

Root HTTP Router 서비스

원문 보기 위키 갱신

root HTTP 라우터는 백엔드 서비스의 루트에 라우트를 등록할 수 있게 해주는 서비스예요. 이는 헬스 체크나 백엔드 서비스의 루트에 노출하고 싶은 다른 라우트 같은 것에 유용해요. httpRouter 서비스를 뒷받침하는 기본 라우터로 사용되며 백엔드 시작 시 Node.js HTTP 서버를 시작해요. 아마 이 서비스를 직접 사용할 일은 없고, 대신 httpRouter 서비스를 사용하게 될 거예요.

출처: 문서

본문

root HTTP 라우터는 백엔드 서비스의 루트에 라우트를 등록할 수 있게 해주는 서비스예요. 이는 헬스 체크나 백엔드 서비스의 루트에 노출하고 싶은 다른 라우트 같은 것에 유용해요. httpRouter 서비스를 뒷받침하는 기본 라우터로 사용되며 백엔드 시작 시 Node.js HTTP 서버를 시작해요. 아마 이 서비스를 직접 사용할 일은 없고, 대신 httpRouter 서비스를 사용하게 될 거예요.

/api/:pluginId/ 경로 접두사는 플러그인이 HttpRouter 서비스를 통해 자신의 라우트를 등록하는 용도로 예약되어 있어요.

각 등록된 root 경로는 서로 구별되어야 해요. 경로가 기존 경로와 겹치면 등록이 실패해요. 비교는 대소문자를 구분하지 않고, 끝 슬래시는 동등하게 취급해요.

서비스 사용하기

다음 예시는 example 백엔드 플러그인에서 root HTTP 라우터 서비스를 가져와 헬스 체크 라우트를 등록하는 방법을 보여줘요.

import {
  coreServices,
  createBackendPlugin,
} from '@backstage/backend-plugin-api';
import { Router } from 'express';

createBackendPlugin({
  pluginId: 'example',
  register(env) {
    env.registerInit({
      deps: {
        rootHttpRouter: coreServices.rootHttpRouter,
      },
      async init({ rootHttpRouter }) {
        const router = Router();
        router.get('/readiness', (request, response) => {
          response.send('OK');
        });
        rootHttpRouter.use('/health', router);
      },
    });
  },
});

속도 제한(Rate limiting)

HTTP Router 문서를 참고하세요.

서비스 구성하기

app-config.yaml을 통해서

app-config.yaml 파일은 RootHttpRouterService의 특정 요구 사항에 맞게 조정할 수 있는 구성 옵션을 제공해요.

backend:
  lifecycle:
    # (Optional) The maximum time that paused requests will wait for the service to start, before returning an error (defaults to 5 seconds).
    # Supported formats:
    # - A string in the format of '1d', '2 seconds' etc. as supported by the `ms` library.
    # - A standard ISO formatted duration string, e.g. 'P2DT6H' or 'PT1M'.
    # - An object with individual units (in plural) as keys, e.g. `{ days: 2, hours: 6 }`.
    startupRequestPauseTimeout: { seconds: 10 }
    # (Optional) The minimum time that the HTTP server will delay the shutdown of the backend. During this delay health checks will be set to failing, allowing traffic to drain (defaults to 0 seconds).
    # Supported formats:
    # - A string in the format of '1d', '2 seconds' etc. as supported by the `ms` library.
    # - A standard ISO formatted duration string, e.g. 'P2DT6H' or 'PT1M'.
    # - An object with individual units (in plural) as keys, e.g. `{ days: 2, hours: 6 }`.
    serverShutdownDelay: { seconds: 20 }
  server:
    # (Optional) HTTP server configuration, Node.js defaults apply otherwise
    # Timeout values support multiple formats:
    # - Numbers (milliseconds): 30000
    # - Duration strings: '30s', '1 minute', '2 hours'
    # - ISO duration strings: 'PT30S', 'PT1M', 'PT2H'
    # - Duration objects: { seconds: 30 }, { minutes: 1 }, { hours: 2 }
    headersTimeout: 60000
    requestTimeout: '30s'
    keepAliveTimeout: { seconds: 5 }
    timeout: 'PT30S'
    # Numeric-only settings
    maxHeadersCount: 2000
    maxRequestsPerSocket: 100

코드를 통해서

root HTTP Router 서비스를 구성하기 위해 전달할 수 있는 추가 옵션이 있어요. 이 옵션들은 createBackend를 호출할 때 전달돼요.

  • indexPath - 일치하지 않는 모든 요청을 전달할 선택적 경로예요. 기본값은 /api/app으로, 백엔드를 통해 프론트엔드 애플리케이션을 서빙하는 app-backend 플러그인을 가리켜요.

  • configure - express 인스턴스를 구성하는 데 사용할 수 있는 선택적 함수예요. root 라우터에 로깅 같은 나만의 미들웨어를 추가하거나, 요청이 백엔드에서 처리되기 전에 수행하고 싶은 다른 작업이 있을 때 유용해요. 미들웨어가 적용되는 순서를 덮어쓰는 데도 유용해요.

createBackend 함수에 옵션을 전달해 root HTTP Router 서비스를 구성할 수 있어요.

import { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';
import { RequestHandler } from 'express';
import morgan from 'morgan';

const backend = createBackend();
backend.add(
  rootHttpRouterServiceFactory({
    configure: ({ app, middleware, routes, config, logger, healthRouter }) => {
      // Refer to https://expressjs.com/en/guide/writing-middleware.html on how to write express middleware
      const customMiddleware = {
        logging(): RequestHandler {
          const middlewareLogger = logger.child({
            type: 'incomingRequest',
          });
          return (req, res, next) => {
            // Custom Logging Implementation
            next();
          };
        },
        // Default logging middleware uses the [morgan](https://github.com/expressjs/morgan) middleware which you can configure with custom formats.
        morganLogging(): RequestHandler {
          const middlewareLogger = logger.child({
            type: 'incomingRequest',
          });
          const customMorganFormat =
            '[:date[clf]] ":method :url HTTP/:http-version" :status ":user-agent"';
          return morgan(customMorganFormat, {
            stream: {
              write(message: string) {
                logger.info(message.trimEnd());
              },
            },
          });
        },
      };
      // The default implementation pretty-prints JSON responses in development
      if (process.env.NODE_ENV === 'development') {
        app.set('json spaces', 2);
      }
      // the built in middleware is provided through an option in the configure function
      app.use(middleware.helmet());
      app.use(middleware.cors());
      app.use(middleware.compression());
      // Optional rate limiting middleware
      app.use(middleware.rateLimit());
      // If you are using rate limiting behind a proxy, you should set the `trust proxy` setting to true
      app.set('trust proxy', true);
      app.use(healthRouter);
      // you can add you your own middleware in here
      app.use(customMiddleware.logging());
      // here the routes that are registered by other plugins
      app.use(routes);
      // some other middleware that comes after the other routes
      app.use(middleware.notFound());
      app.use(middleware.error());
    },
  }),
);

/api/*에 대한 요청은 일치하는 플러그인이 없으면 routes 핸들러가 절대 처리하지 않으며, 대신 일반적으로 middleware.notFound() 핸들러로 넘어간다는 점에 주의하세요. 이는 indexPath가 구성되어 있는지 여부와 관계없이 동일해요.

root HTTP Router 서비스는 기본 Node.js HTTP 서버 객체의 구성도 허용해요. 이는 서버 timeout, keepAliveTimeout, headersTimeout 같은 HTTP 서버 자체의 설정을 수정하는 데 유용해요.

기본 제공되는 applyDefaults 헬퍼를 사용해 기본 app/router 구성은 유지하면서 사용자 지정 서버 구성을 활성화할 수도 있어요.

import { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';

const backend = createBackend();
backend.add(
  rootHttpRouterServiceFactory({
    configure: ({ server, applyDefaults }) => {
      // apply default app/router configuration
      applyDefaults();
      // customize the Node.js HTTP Server timeouts
      server.keepAliveTimeout = 65 * 1000;
      server.headersTimeout = 66 * 1000;
    },
  }),
);

Content Security Policy(CSP) 구성 정의하기

Content Security Policy(CSP)는 Backstage 인스턴스를 다양한 공격, 특히 교차 사이트 스크립팅(XSS)으로부터 보호하는 중요한 보안 기능이에요. Backstage는 app-config.yaml 파일을 통해 CSP 지시문을 구성할 수 있는 유연한 방법을 제공해요.

기본 구성

CSP 지시문은 구성에서 backend.csp 섹션 아래에 정의할 수 있어요.

backend:
  csp:
    default-src: ["'self'"]
    script-src: ["'self'", "'unsafe-inline'", 'example.com']
    connect-src: ["'self'", 'http:', 'https:']
    img-src: ["'self'", 'data:', 'https://backstage.io']
    style-src: ["'self'", "'unsafe-inline'"]
    frame-src: ['https://some-analytics-provider.com']

사용 가능한 지시문

최신 브라우저가 지원하는 모든 CSP 지시문을 구성할 수 있어요. 흔한 지시문은 다음과 같아요.

  • default-src: 다른 CSP 지시문에 대한 대체값
  • script-src: 실행할 수 있는 스크립트를 제어해요
  • style-src: 적용할 수 있는 스타일을 제어해요
  • img-src: 로드할 수 있는 이미지를 제어해요
  • connect-src: fetch, WebSocket 등을 사용해 로드할 수 있는 URL을 제어해요
  • frame-src: iframe에 임베드할 수 있는 URL을 제어해요
  • font-src: 로드할 수 있는 폰트를 제어해요
  • object-src: 플러그인으로 로드할 수 있는 URL을 제어해요
  • media-src: 로드할 수 있는 미디어(오디오, 비디오)를 제어해요
  • upgrade-insecure-requests: 브라우저가 HTTP를 HTTPS로 업그레이드하도록 지시해요

특수 값

지시문 배열 내의 흔한 특수 값은 다음과 같아요.

  • self: 같은 출처의 콘텐츠를 허용해요
  • unsafe-inline: 인라인 스크립트/스타일을 허용해요
  • unsafe-eval: 동적 코드 평가를 허용해요
  • none: 해당 지시문의 모든 콘텐츠를 차단해요
  • data:: data: URI를 허용해요(이미지에 흔히 사용됨)
  • https:: HTTPS를 통한 모든 콘텐츠를 허용해요

사용 가능한 CSP 옵션에 대한 자세한 내용은 다음을 참고하세요.

  • MDN Content Security Policy 문서
  • Helmet Content Security Policy

더 알아보기 (Learn more)