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

사용자 지정 CLI 모듈

원문 보기 위키 갱신

자체 CLI 모듈을 만들어 사용자 지정 명령으로 Backstage CLI를 확장할 수 있습니다.

출처: 문서

본문

자체 CLI 모듈을 만들어 사용자 지정 명령으로 Backstage CLI를 확장할 수 있습니다. CLI 모듈은 @backstage/cli-node의 createCliModule API를 사용해 하나 이상의 명령을 등록하는 패키지입니다. 프로젝트에 의존성으로 설치되면 CLI가 자동으로 이를 발견하고 로드합니다.

새 모듈 스캐폴딩 (Scaffolding a new module)

모듈을 스캐폴딩하려면 내장 템플릿을 사용합니다.

yarn new

대화형 메뉴에서 cli-module 템플릿을 선택합니다. 그러면 샘플 명령, 독립 실행형 bin 스크립트, 필요한 모든 구성을 포함한 올바른 구조의 새 패키지가 생성됩니다.

모듈 구조 (Module structure)

CLI 모듈 패키지는 다음과 같은 구조를 가집니다.

packages/cli-module-example/  bin/    backstage-cli-module-example    # Standalone bin script  src/    commands/      example.ts                    # Command implementation    index.ts                        # Module definition  package.json

package.json

package.json은 backstage.role을 "cli-module"로 설정해야 합니다. 이것이 CLI가 의존성 스캔 중에 패키지를 모듈로 식별하는 방법입니다.

package.json

{  "name": "@mycompany/cli-module-example",  "version": "0.1.0",  "main": "src/index.ts",  "types": "src/index.ts",  "publishConfig": {    "access": "public",    "main": "dist/index.cjs.js",    "types": "dist/index.d.ts"  },  "backstage": {    "role": "cli-module"  },  "bin": "bin/backstage-cli-module-example",  "files": ["dist", "bin"],  "dependencies": {    "@backstage/cli-common": "...",    "@backstage/cli-node": "...",    "cleye": "..."  },  "devDependencies": {    "@backstage/cli": "..."  }}

모듈 정의 (Module definition)

모듈 진입점은 createCliModule을 사용해 명령을 등록합니다.

src/index.ts

import { createCliModule } from '@backstage/cli-node';import packageJson from '../package.json';export default createCliModule({  packageJson,  init: async reg => {    reg.addCommand({      path: ['example'],      description: 'An example command',      execute: { loader: () => import('./commands/example') },    });  },});

createCliModule API

createCliModule 함수는 두 필드를 가진 옵션 객체를 받아들입니다.

  • packageJson — 최소한 name 필드를 가진 객체로, 일반적으로 가져온 package.json입니다. 명령 충돌이 발생할 때 오류 메시지에서 이름이 사용됩니다.
  • init — 레지스트리 객체를 받는 비동기 콜백입니다. 레지스트리의 addCommand 메서드를 사용해 명령을 등록합니다.

init 콜백은 명령이 실행될 때가 아니라 모듈이 로드될 때 실행됩니다. 가벼운 상태로 유지하고, 무거운 의존성을 가진 명령에는 지연 로더(deferred loader) 패턴을 사용하세요.

명령 정의 (Defining commands)

각 명령은 reg.addCommand에 전달되는 CliCommand 객체로 정의됩니다.

reg.addCommand({  path: ['my-tool', 'run'],  description: 'Run the tool',  execute: async ({ args, info }) => {    // Command implementation  },});

명령 경로 (Command path)

path 배열은 명령이 호출되는 방식을 정의합니다. 각 요소는 명령 계층의 한 수준이 됩니다.

  • ['info']는 backstage-cli info를 등록합니다.
  • ['repo', 'test']는 backstage-cli repo test를 등록합니다.
  • ['actions', 'sources', 'add']는 backstage-cli actions sources add를 등록합니다.

경로의 중간 노드는 자동으로 생성되며 도움말 출력에서 명령 그룹으로 나타납니다.

설명 (Description)

도움말 출력에서 명령 이름 옆에 표시되는 짧은 문자열입니다.

Deprecated 및 experimental 플래그

명령은 deprecated: true 또는 experimental: true로 표시할 수 있습니다. 이 명령들은 --help 출력에서 숨겨지지만 여전히 사용할 수 있습니다.

실행 함수 (Execute function)

execute 필드는 두 가지 패턴을 지원합니다.

직접 실행(Direct execution) — 인라인 비동기 함수입니다.

reg.addCommand({  path: ['greet'],  description: 'Print a greeting',  execute: async ({ args, info }) => {    console.log('Hello!');  },});

지연 로딩(Deferred loading) — 명령 구현을 동적으로 가져오는 로더입니다. 명령이 실제로 호출될 때까지 무거운 의존성을 로드하지 않으므로 권장되는 패턴입니다.

reg.addCommand({  path: ['greet'],  description: 'Print a greeting',  execute: { loader: () => import('./commands/greet') },});

지연 로더 패턴을 사용할 때 명령 파일은 기본 내보내기로 execute 함수를 내보내야 합니다.

src/commands/greet.ts

import type { CliCommandContext } from '@backstage/cli-node';export default async ({ args, info }: CliCommandContext) => {  console.log('Hello!');};

명령 컨텍스트 (Command context)

execute 함수는 두 필드를 가진 CliCommandContext를 받습니다.

  • args — 명령 경로가 해석된 후 남은 명령줄 인수의 배열입니다. 예를 들어 사용자가 backstage-cli greet --name World를 실행하면 args 배열은 ['--name', 'World']가 됩니다.
  • info — usage(도움말에 표시된 전체 명령 경로, 예: "backstage-cli greet")와 name(명령 이름, 예: "greet")을 가진 객체입니다.

플래그 구문 분석 (Parsing flags)

명령은 일반적으로 cleye를 사용해 args 배열에서 플래그를 구문 분석합니다. 이는 모든 내장 CLI 모듈이 사용하는 것과 같은 라이브러리입니다.

src/commands/greet.ts

import { cli } from 'cleye';import type { CliCommandContext } from '@backstage/cli-node';export default async ({ args, info }: CliCommandContext) => {  const { flags } = cli(    {      name: info.usage,      flags: {        name: {          type: String,          description: 'Name to greet',          default: 'World',        },        loud: {          type: Boolean,          description: 'Shout the greeting',        },      },    },    undefined,    args,  );  const greeting = `Hello, ${flags.name}!`;  console.log(flags.loud ? greeting.toUpperCase() : greeting);};

독립 실행 (Standalone execution)

각 CLI 모듈은 전체 @backstage/cli 없이 독립 실행 프로그램으로도 실행할 수 있습니다. 이는 모듈을 독립적으로 배포할 때 유용합니다. 스캐폴딩된 템플릿에는 이를 수행하는 bin 스크립트가 포함됩니다.

bin/backstage-cli-module-example

#!/usr/bin/env nodeconst path = require('node:path');/* eslint-disable-next-line no-restricted-syntax */const isLocal = require('node:fs').existsSync(  path.resolve(__dirname, '../src'),);if (isLocal) {  require('@backstage/cli-node/config/nodeTransform.cjs');}const { runCli } = require('@backstage/cli-node');const cliModule = require(isLocal ? '../src/index' : '..').default;const pkg = require('../package.json');runCli({ modules: [cliModule], name: pkg.name, version: pkg.version });

isLocal 확인은 bin 스크립트 옆에 src/ 디렉터리가 존재하는지 감지합니다. 개발 중 소스에서 실행할 때는 TypeScript 파일을 직접 로드할 수 있도록 Node.js 변환을 등록합니다. 게시된 패키지에서 실행할 때는 컴파일된 출력을 로드합니다.

모듈 설치 (Installing your module)

모듈이 게시되었거나 워크스페이스 패키지로 사용 가능하다면, 프로젝트 루트의 package.json에 의존성으로 추가합니다.

package.json

{  "devDependencies": {    "@backstage/cli": "...",    "@mycompany/cli-module-example": "..."  }}

CLI는 다음 실행 시 자동으로 이를 발견합니다. 사용자 지정 명령이 기본 명령과 함께 --help 출력에 나타납니다.

모듈이 @backstage/cli-defaults의 기본 모듈과 충돌하는 명령 경로를 등록하면, 모듈이 우선하며 충돌하는 기본 모듈은 조용히 건너뜁니다. 충돌 해결에 대한 자세한 내용은 CLI Modules를 참조하세요.

더 알아보기 (Learn more)