플러그인 시스템
이 페이지는 MLightCAD용 플러그인을 빌드하고 등록하고 사용하는 방법을 설명합니다. 플러그인은 명령을 등록하고, 활성 문서와 뷰에 접근하고, 언로드 시 정리함으로써 뷰어를 확장합니다.
플러그인 시스템은 @mlightcad/cad-simple-viewer(packages/cad-simple-viewer/src/plugin)에 있습니다. 공식 플러그인(UI, AI 에이전트, 스크립트 콘솔, HTML/PDF/SVG 내보내기, OneDrive/Google Drive 데이터 소스)은 별도의 npm 패키지로 제공됩니다 — 전체 목록은 내장 플러그인을 참조하십시오.
개요
플러그인은 AcApPlugin을 구현하는 작은 모듈입니다. 플러그인 관리자(AcApPluginManager)는 AcApDocManager가 소유하며 다음을 수행합니다:
- 플러그인이 로드될 때
onLoad()를 호출합니다(명령, 훅 등록 등) - 플러그인이 제거될 때
onUnload()를 호출합니다(명령 등록 해제, 리소스 해제) - 지연 등록을 지원합니다: 사용자가 트리거 명령을 실행할 때까지 플러그인 코드를 다운로드하지 않습니다
핵심 타입
AcApPlugin
모든 플러그인은 다음을 제공해야 합니다:
| 필드 / 메서드 | 설명 |
|---|---|
name | 고유 식별자(필수). 지연 로드를 사용할 때 지연 등록의 name과 일치해야 합니다. |
version? | 선택적 버전 문자열 |
description? | 선택적 사람이 읽을 수 있는 설명 |
onLoad(context, commandManager) | 명령을 등록하고 리소스를 초기화합니다 |
onUnload(context, commandManager) | 명령을 제거하고 리소스를 해제합니다 |
context는 AcApContext(현재 doc와 view)입니다. commandManager는 AcEdCommandStack이며, 내장 명령에서 사용하는 것과 동일한 스택입니다.
참조 구현: packages/cad-simple-viewer/src/plugin/AcApPluginExample.ts의 AcApExamplePlugin.
AcApLazyPluginRegistration
코드 분할을 위해 플러그인을 즉시 로드하는 대신 디스크립터를 등록합니다:
| 필드 | 설명 |
|---|---|
name | loader가 반환하는 AcApPlugin.name과 같아야 합니다 |
triggers | 이 플러그인을 로드하는 명령 이름(대소문자 구분 없음) |
loader | 플러그인 인스턴스를 반환하는 비동기 팩토리 |
단계 1 — 명령 구현
플러그인은 보통 하나 이상의 명령을 노출합니다. AcEdCommand를 확장하고 execute(context)를 구현합니다:
import { AcApContext, AcEdCommand } from '@mlightcad/cad-simple-viewer'
export class AcApMyExportCmd extends AcEdCommand {
async execute(context: AcApContext) {
const fileName = context.doc.fileName || context.doc.docTitle
// ... export logic using context.doc.database, context.view, etc.
}
}프롬프트, 편집기 접근, 엔티티 생성에 대해서는 명령을 참조하십시오.
단계 2 — AcApPlugin 구현
onUnload가 이들을 제거할 수 있도록 등록한 모든 명령을 추적합니다:
import {
AcApContext,
AcApPlugin,
AcEdCommandStack
} from '@mlightcad/cad-simple-viewer'
import { AcApMyExportCmd } from './AcApMyExportCmd'
export class AcApMyPlugin implements AcApPlugin {
name = 'MyPlugin'
version = '1.0.0'
description = 'My custom export command'
private registeredCommands: Array<{ group: string; name: string }> = []
onLoad(_context: AcApContext, commandManager: AcEdCommandStack): void {
const group = AcEdCommandStack.SYSTEMT_COMMAND_GROUP_NAME
commandManager.addCommand(group, 'myexport', 'myexport', new AcApMyExportCmd())
this.registeredCommands.push({ group, name: 'myexport' })
}
onUnload(_context: AcApContext, commandManager: AcEdCommandStack): void {
for (const cmd of this.registeredCommands) {
commandManager.removeCmd(cmd.group, cmd.name)
}
this.registeredCommands = []
}
}공식 플러그인도 동일한 패턴을 따릅니다:
AcApHtmlPlugin—chtml등록AcApPdfPlugin—cpdf,ipdf등록AcApSvgPlugin—csvg등록
뷰어 전역 명령(확대, 레이어 등과 동일한 그룹)에는 AcEdCommandStack.SYSTEMT_COMMAND_GROUP_NAME을 사용합니다.
단계 3 — npm 패키지로 게시(선택 사항)
cad-html-plugin, cad-pdf-plugin 또는 cad-svg-plugin의 구조를 그대로 따릅니다:
my-cad-plugin/
package.json # name: @myorg/cad-my-plugin
src/
AcApMyPlugin.ts
AcApMyExportCmd.ts
registerLazyMyPlugin.ts
index.ts # public exportspackage.json 규칙:
"type": "module"설정@mlightcad/cad-simple-viewer를 peerDependency로 나열(필요 시@mlightcad/data-model,three같은 다른 공유 라이브러리도 포함)- 기존 플러그인처럼
tsc+vite로 빌드 src/index.ts에서 헬퍼 내보내기: 플러그인 클래스,createMyPlugin,registerLazyMyPlugin, 트리거 상수
지연 로더 모듈(registerLazyMyPlugin.ts):
import type { AcApPluginManager } from '@mlightcad/cad-simple-viewer'
export const MY_PLUGIN_NAME = 'MyPlugin'
export const MY_PLUGIN_TRIGGERS = ['myexport'] as const
export async function createMyPlugin() {
const { AcApMyPlugin } = await import('./AcApMyPlugin')
return new AcApMyPlugin()
}
export function registerLazyMyPlugin(pluginManager: AcApPluginManager): void {
pluginManager.registerLazyPlugin({
name: MY_PLUGIN_NAME,
triggers: [...MY_PLUGIN_TRIGGERS],
loader: createMyPlugin
})
}동적 import('./AcApMyPlugin')는 myexport가 실행될 때까지 무거운 플러그인 청크를 초기 번들에서 제외합니다.
플러그인 등록
일반적인 등록 경로는 네 가지입니다. 지연 로딩이 필요한지와 앱 부트스트랩의 소유자가 누구인지에 따라 하나를 선택합니다.
A. 지연 등록(선택적 기능에 권장)
AcApDocManager.createInstance() 이후에 등록합니다. 플러그인 모듈은 첫 트리거 명령 시 로드됩니다.
import { AcApDocManager } from '@mlightcad/cad-simple-viewer'
import { registerLazyHtmlPlugin } from '@mlightcad/cad-html-plugin'
import { registerLazyPdfPlugin } from '@mlightcad/cad-pdf-plugin'
import { registerLazySvgPlugin } from '@mlightcad/cad-svg-plugin'
AcApDocManager.createInstance({
container: document.getElementById('cad-container')!,
htmlViewerRuntimeUrl: './viewer-runtime.iife.js' // required for chtml export
})
const pm = AcApDocManager.instance.pluginManager
registerLazyHtmlPlugin(pm)
registerLazyPdfPlugin(pm)
registerLazySvgPlugin(pm)예제 앱: packages/cad-simple-viewer-example/src/main.ts.
사용자가 트리거 명령을 실행할 때 명령이 아직 등록되지 않았다면 AcApDocManager가 자동으로 loadByTrigger를 호출합니다:
AcApDocManager.instance.sendStringToExecute('chtml')
AcApDocManager.instance.sendStringToExecute('cpdf')sendStringToExecute를 사용할 때는 수동으로 loadByTrigger를 호출할 필요가 없습니다. 단, 실행 전에 플러그인을 미리 로드하려는 경우는 예외입니다(아래 참조).
B. 인라인 지연 등록(cad-viewer 컴포넌트)
@mlightcad/cad-viewer는 registerLazyPlugins()(packages/cad-viewer/src/app/register.ts)에서 PDF/HTML/SVG 플러그인을 등록하며, 이는 app.ts의 initializeCadViewer()에서 호출됩니다. Vue 컴포넌트 소비자는 추가 설정 없이 지연 플러그인을 얻습니다.
C. pluginManager.loadPlugin()을 통한 즉시 로드
문서 관리자를 생성한 직후에 로드합니다:
import { AcApDocManager } from '@mlightcad/cad-simple-viewer'
import { AcApExamplePlugin } from '@mlightcad/cad-simple-viewer' // AcApExamplePlugin export
AcApDocManager.createInstance({ container: el })
await AcApDocManager.instance.pluginManager.loadPlugin(new AcApExamplePlugin())플러그인이 작거나 사용자 작업 전에 반드시 사용 가능해야 할 때 사용합니다.
D. createInstance({ plugins })를 통한 즉시 로드
초기화 시 플러그인을 전달합니다. AcApDocManager가 시작 중에 비동기적으로 로드합니다.
인스턴스 또는 팩토리로부터:
AcApDocManager.createInstance({
container: el,
plugins: {
fromConfig: [
new AcApExamplePlugin(),
() => new AcApAnotherPlugin()
]
}
})폴더로부터(동적 import):
AcApDocManager.createInstance({
container: el,
plugins: {
fromFolder: {
folderPath: './plugins',
pluginList: ['MyPlugin.js'],
continueOnError: true
}
}
})브라우저에서는 pluginList를 반드시 제공해야 합니다(디렉터리 목록 API 없음). 각 파일은 플러그인 인스턴스, 플러그인 클래스 또는 팩토리 함수를 기본 내보내기해야 합니다.
런타임에서 플러그인 사용
플러그인 명령 실행
플러그인이 로드된 후(즉시 또는 지연 트리거를 통해):
// Command line / script style
AcApDocManager.instance.sendStringToExecute('chtml')
// Or via editor API
await AcApDocManager.instance.editor.executeCommand('cpdf')지연 플러그인 미리 로드(선택 사항)
일부 UI 경로는 첫 실행 시 지연을 피하기 위해 sendStringToExecute 전에 미리 로드합니다:
const pluginManager = AcApDocManager.instance.pluginManager
await pluginManager.loadByTrigger('cpdf')
AcApDocManager.instance.sendStringToExecute('cpdf')cad-viewer는 리본에서 PDF 내보내기 시 이 패턴을 사용합니다(MlRibbonCommands.vue → runLazyCommand).
로드 상태 확인
const pm = AcApDocManager.instance.pluginManager
pm.isPluginLoaded('HtmlPlugin') // true after first chtml
pm.isLazyPluginTrigger('chtml') // true if lazy registration exists
pm.getLoadedPlugins() // ['HtmlPlugin', ...]
await pm.unloadPlugin('HtmlPlugin')
await pm.unloadAllPlugins() // called when destroying the view언로드
await AcApDocManager.instance.pluginManager.unloadPlugin('MyPlugin')onUnload는 onLoad에서 등록한 모든 명령을 제거해야 합니다. 그렇지 않으면 스택에 오래된 명령이 남습니다.
내장 플러그인 참조
모노레포에는 여러 공식 플러그인이 포함되어 있습니다: AcApHtmlPlugin(chtml), AcApPdfPlugin(cpdf, ipdf), AcApSvgPlugin(csvg), 그리고 UI, 에이전트, 스크립트 콘솔, 데이터 소스 플러그인이 있습니다.
플러그인별 명령, 공개 API, 구성 및 각 패키지 README 링크가 포함된 전체 목록은 내장 플러그인을 참조하십시오.
통합 체크리스트
@mlightcad/cad-simple-viewer에 의존합니다(게시된 플러그인의 경우 피어 의존성).AcApPlugin+AcEdCommand(들)을 구현합니다.- 패키지 엔트리에서
registerLazy*Plugin+create*Plugin을 내보냅니다. AcApDocManager.createInstance()이후에 등록합니다(또는plugins.fromConfig/fromFolder사용).sendStringToExecute또는 동일한 명령 이름을 호출하는 UI로 명령을 트리거합니다.- 번들링: 지연
loader에서 동적import()를 사용하여 Vite/Rollup이 플러그인 청크를 분할하도록 합니다. - 정리:
onUnload에서 모든 명령을 제거합니다. - 고유 트리거: 하나의 트리거 명령은 두 개의 지연 플러그인에 매핑될 수 없습니다(
registerLazyPlugin은 충돌 시 예외를 발생시킵니다). - 이름 일관성:
AcApLazyPluginRegistration.name은loader()의plugin.name과 일치해야 합니다.
문제 해결
| 증상 | 가능한 원인 |
|---|---|
Command 'chtml' not found | 지연 플러그인이 등록되지 않았거나, destroy 이후에 등록됨 |
| 지연 플러그인이 로드되지 않음 | 트리거 이름 불일치(대문자 정규화 확인); 로더의 name과 등록의 name 불일치 |
| 런타임에서 HTML 내보내기 실패 | htmlViewerRuntimeUrl 누락 또는 viewer-runtime.iife.js가 해당 URL에 배포되지 않음 |
Plugin 'X' is already loaded | unloadPlugin 없이 loadPlugin을 두 번 호출 |
| 트리거가 이미 등록됨 | 두 플러그인이 동일한 트리거 명령을 등록함 |
관련 문서
- 내장 플러그인 — MLightCAD와 함께 제공되는 모든 공식 플러그인
- 명령 — 명령 수명 주기와
AcEdCommand - 데이터 소스 플러그인 — 플러그 가능한 도면 원본(로컬, URL, OneDrive, …)
- Simple Viewer 통합 —
cad-simple-viewer포함 - Vue 컴포넌트 통합 — Vue 컴포넌트와
initializeCadViewer - 아키텍처 개요 — 모듈 구성