플러그인 시스템 — 확장 가능한 앱 아키텍처
"VS Code의 마켓플레이스처럼 사용자가 기능을 추가할 수 있는 앱" — 플러그인 시스템을 만들면 앱의 수명과 가치가 크게 올라갑니다.
플러그인 시스템 설계
플러그인 인터페이스 정의
// 플러그인이 구현해야 할 인터페이스
// plugin-api.js
class PluginAPI {
constructor(app) {
this.app = app;
}
// 메뉴에 항목 추가
addMenuItem(menu, item) {
this.app.menuManager.addItem(menu, item);
}
// 사이드바에 패널 추가
addSidebarPanel(panel) {
this.app.sidebarManager.addPanel(panel);
}
// 명령어 등록
registerCommand(id, handler) {
this.app.commandRegistry.register(id, handler);
}
// 설정 스키마 등록
registerSettings(schema) {
this.app.settingsManager.registerSchema(schema);
}
// 이벤트 구독
on(event, handler) {
this.app.events.on(event, handler);
}
}
플러그인 구조
my-plugin/
├── package.json
├── index.js # 메인 진입점
└── ui/
└── panel.html # UI 컴포넌트
{
"name": "my-plugin",
"version": "1.0.0",
"main": "index.js",
"electronPlugin": {
"displayName": "내 플러그인",
"description": "기능 설명",
"activationEvents": ["onStartup"]
}
}
// index.js — 플러그인 진입점
module.exports = {
activate(api) {
// 플러그인 활성화 시 호출
api.registerCommand('myPlugin.hello', () => {
console.log('Hello from plugin!');
});
api.addMenuItem('tools', {
label: '내 플러그인 실행',
click: () => api.executeCommand('myPlugin.hello'),
});
},
deactivate() {
// 플러그인 비활성화 시 정리
console.log('Plugin deactivated');
},
};
플러그인 로더
class PluginLoader {
constructor(pluginsDir) {
this.pluginsDir = pluginsDir;
this.plugins = new Map();
}
async loadAll() {
const dirs = await fs.promises.readdir(this.pluginsDir, {
withFileTypes: true,
});
for (const dir of dirs) {
if (dir.isDirectory()) {
await this.loadPlugin(path.join(this.pluginsDir, dir.name));
}
}
}
async loadPlugin(pluginPath) {
try {
const pkgPath = path.join(pluginPath, 'package.json');
const pkg = JSON.parse(await fs.promises.readFile(pkgPath, 'utf-8'));
const mainFile = path.join(pluginPath, pkg.main || 'index.js');
// 플러그인 모듈 로드
const plugin = require(mainFile);
this.plugins.set(pkg.name, {
module: plugin,
metadata: pkg.electronPlugin || {},
path: pluginPath,
});
console.log(`플러그인 로드: ${pkg.name}`);
} catch (error) {
console.error(`플러그인 로드 실패: ${pluginPath}`, error);
}
}
activateAll(api) {
for (const [name, plugin] of this.plugins) {
try {
plugin.module.activate(api);
console.log(`플러그인 활성화: ${name}`);
} catch (error) {
console.error(`플러그인 활성화 실패: ${name}`, error);
}
}
}
deactivateAll() {
for (const [name, plugin] of this.plugins) {
try {
plugin.module.deactivate?.();
} catch (error) {
console.error(`플러그인 비활성화 실패: ${name}`, error);
}
}
}
}
보안 고려사항
// 플러그인 샌드박싱 — vm 모듈 사용
const vm = require('vm');
function loadPluginSandboxed(code, api) {
const sandbox = {
api: api, // 제한된 API만 전달
console: console,
setTimeout: setTimeout,
// require는 전달하지 않음 — 시스템 접근 차단
};
const context = vm.createContext(sandbox);
const script = new vm.Script(code);
script.runInContext(context);
}
면접 포인트 정리
- 플러그인 시스템은 인터페이스 정의 → 로더 → 생명주기 관리로 구성
activate/deactivate로 플러그인 생명주기 관리- 보안을 위해 플러그인에 제한된 API만 노출
- VS Code의 Extension API가 좋은 참고 모델
플러그인 시스템을 다뤘으면, 다음은 OS 통합 기능을 알아봅시다.
댓글 로딩 중...