플러그인 개발 가이드
📝 초안 (Draft)
이 문서는 검토 중입니다. 내용이 변경될 수 있습니다.
새로운 플러그인을 개발하는 방법을 설명합니다.
개요
Multi-SaaS Kit 플러그인은 독립적인 기능 모듈로, Core 기능을 확장합니다. 이 가이드에서는 플러그인 개발의 전체 과정을 다룹니다.
플러그인 구조
기본 폴더 구조
packages/plugins/{PluginName}/
├── plugin.json # 메타데이터 (필수)
├── README.md # 문서
├── src/
│ ├── {PluginName}Plugin.php # 메인 플러그인 클래스 (필수)
│ ├── {PluginName}ServiceProvider.php # 서비스 프로바이더 (필수)
│ ├── Contracts/ # 인터페이스
│ │ └── {Name}ServiceInterface.php
│ ├── Services/ # 서비스 로직
│ │ └── {Name}Service.php
│ ├── Config/ # 설정 파일
│ │ └── {plugin-slug}.php
│ ├── Models/ # 모델 (필요시)
│ ├── Events/ # 이벤트 (필요시)
│ └── Listeners/ # 리스너 (필요시)
├── database/
│ └── migrations/ # 마이그레이션
├── resources/
│ └── views/ # 뷰 (필요시)
└── tests/
├── Unit/
└── Feature/
Step 1: plugin.json 작성
플러그인 메타데이터를 정의합니다.
{
"slug": "my-awesome-plugin",
"name": "My Awesome Plugin",
"description": "Adds awesome features to Multi-SaaS Kit",
"version": "1.0.0",
"author": "Your Name",
"license": "MIT",
"tier": "L0",
"price": {
"model": "free",
"amount": 0
},
"dependencies": {
"core": ">=1.1.0",
"extensions": []
},
"providers": [
"MyAwesomePluginServiceProvider"
]
}
필수 필드
| 필드 | 설명 | 예시 |
|---|---|---|
slug | 고유 식별자 (소문자, 하이픈) | "my-awesome-plugin" |
name | 표시 이름 | "My Awesome Plugin" |
version | 시맨틱 버전 | "1.0.0" |
tier | 플러그인 티어 | "L0", "L1", "business", "enterprise" |
providers | 서비스 프로바이더 클래스명 | ["MyPluginServiceProvider"] |
가격 모델
// 무료
"price": { "model": "free", "amount": 0 }
// 일회성 구매
"price": { "model": "one_time", "amount": 49.00 }
// 구독
"price": { "model": "subscription", "amount": 9.99, "period": "monthly" }
Step 2: 플러그인 클래스 작성
BasePlugin을 상속하여 메인 플러그인 클래스를 작성합니다.
<?php
declare(strict_types=1);
namespace App\Plugins\MyAwesomePlugin;
use App\Core\Base\Plugin\BasePlugin;
use App\Core\Base\Plugin\Enums\PluginTier;
use App\Core\Base\Plugin\Enums\PriceModel;
class MyAwesomePlugin extends BasePlugin
{
protected string $slug = 'my-awesome-plugin';
protected string $name = 'My Awesome Plugin';
protected string $description = 'Adds awesome features';
protected string $version = '1.0.0';
/**
* 플러그인 부팅 시 실행
*/
public function boot(): void
{
// 라우트 등록, 이벤트 리스너 등록 등
}
/**
* 서비스 등록
*/
public function register(): void
{
// 서비스 컨테이너에 바인딩
}
/**
* 플러그인 설치 시 실행
*/
public function install(): void
{
// 마이그레이션 실행, 초기 데이터 시드 등
}
/**
* 플러그인 제거 시 실행
*/
public function uninstall(): void
{
// 정리 작업
}
public function getTier(): PluginTier
{
return PluginTier::L0;
}
public function getPriceModel(): PriceModel
{
return PriceModel::FREE;
}
public function getDependencies(): array
{
return [
'core' => '>=1.1.0',
];
}
}
Step 3: ServiceProvider 작성
Laravel ServiceProvider를 작성하여 서비스를 등록합니다.
<?php
declare(strict_types=1);
namespace App\Plugins\MyAwesomePlugin;
use App\Plugins\MyAwesomePlugin\Contracts\AwesomeServiceInterface;
use App\Plugins\MyAwesomePlugin\Services\AwesomeService;
use Illuminate\Support\ServiceProvider;
class MyAwesomePluginServiceProvider extends ServiceProvider
{
/**
* Register services
*/
public function register(): void
{
// 설정 파일 병합
$this->mergeConfigFrom(
__DIR__.'/Config/my-awesome-plugin.php',
'my-awesome-plugin'
);
// 서비스 바인딩 (싱글톤)
$this->app->singleton(
AwesomeServiceInterface::class,
AwesomeService::class
);
}
/**
* Bootstrap services
*/
public function boot(): void
{
// 플러그인 비활성화 시 스킵
if (! config('my-awesome-plugin.enabled', false)) {
return;
}
// 마이그레이션 로드
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
// 뷰 로드 (필요시)
$this->loadViewsFrom(__DIR__.'/../resources/views', 'awesome');
// 라우트 로드 (필요시)
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
// 설정 퍼블리싱
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__.'/Config/my-awesome-plugin.php'
=> config_path('my-awesome-plugin.php'),
], 'my-awesome-plugin-config');
}
}
/**
* 제공하는 서비스 목록
*/
public function provides(): array
{
return [
AwesomeServiceInterface::class,
];
}
}
Step 4: Interface 및 Service 작성
Contract (Interface)
<?php
declare(strict_types=1);
namespace App\Plugins\MyAwesomePlugin\Contracts;
interface AwesomeServiceInterface
{
/**
* 주요 기능 메서드
*/
public function doSomethingAwesome(string $input): array;
/**
* 설정 조회
*/
public function getSettings(): array;
}
Service 구현
<?php
declare(strict_types=1);
namespace App\Plugins\MyAwesomePlugin\Services;
use App\Plugins\MyAwesomePlugin\Contracts\AwesomeServiceInterface;
class AwesomeService implements AwesomeServiceInterface
{
protected array $defaultSettings = [
'feature_a' => true,
'feature_b' => false,
'limit' => 100,
];
public function doSomethingAwesome(string $input): array
{
$settings = $this->getSettings();
// 비즈니스 로직 구현
return [
'success' => true,
'result' => "Processed: {$input}",
];
}
public function getSettings(): array
{
return array_merge(
$this->defaultSettings,
config('my-awesome-plugin', [])
);
}
}
Step 5: 설정 파일
<?php
// src/Config/my-awesome-plugin.php
return [
/*
|--------------------------------------------------------------------------
| Plugin Enable
|--------------------------------------------------------------------------
*/
'enabled' => env('MY_AWESOME_PLUGIN_ENABLED', true),
/*
|--------------------------------------------------------------------------
| Feature Settings
|--------------------------------------------------------------------------
*/
'feature_a' => true,
'feature_b' => false,
'limit' => 100,
/*
|--------------------------------------------------------------------------
| SaaS Customization
|--------------------------------------------------------------------------
| SaaS별로 다른 설정이 필요한 경우
*/
'saas_override' => [
// 'saas-slug' => ['limit' => 200],
],
];
Step 6: 마이그레이션
테이블 프리픽스 규칙
플러그인 테이블은 plg_{name}_ 프리픽스를 사용합니다.
<?php
// database/migrations/2026_01_01_000001_create_plg_awesome_logs_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('plg_awesome_logs', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('action');
$table->json('data')->nullable();
$table->timestamps();
$table->index('user_id');
$table->index('created_at');
});
}
public function down(): void
{
Schema::dropIfExists('plg_awesome_logs');
}
};
프리픽스 규칙
| 영역 | 프리픽스 | 예시 |
|---|---|---|
| Core | core_ | core_plugins |
| Extensions | ext_ | ext_guardian_relationships |
| Plugins | plg_{slug}_ | plg_awesome_logs |
Step 7: 테스트 작성
Unit Test
<?php
namespace App\Plugins\MyAwesomePlugin\Tests\Unit;
use App\Plugins\MyAwesomePlugin\Services\AwesomeService;
use PHPUnit\Framework\TestCase;
class AwesomeServiceTest extends TestCase
{
private AwesomeService $service;
protected function setUp(): void
{
parent::setUp();
$this->service = new AwesomeService();
}
public function test_do_something_awesome_returns_success(): void
{
$result = $this->service->doSomethingAwesome('test input');
$this->assertTrue($result['success']);
$this->assertStringContainsString('test input', $result['result']);
}
public function test_get_settings_returns_default_settings(): void
{
$settings = $this->service->getSettings();
$this->assertArrayHasKey('feature_a', $settings);
$this->assertArrayHasKey('limit', $settings);
}
}
Feature Test
<?php
namespace App\Plugins\MyAwesomePlugin\Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class AwesomeFeatureTest extends TestCase
{
use RefreshDatabase;
public function test_plugin_can_be_enabled(): void
{
config(['my-awesome-plugin.enabled' => true]);
$this->assertTrue(config('my-awesome-plugin.enabled'));
}
}
Extensions 의존성 (L1 플러그인)
L1 플러그인은 Extensions에 의존합니다.
// MyL1Plugin.php
public function getTier(): PluginTier
{
return PluginTier::L1;
}
public function getRequiredExtensions(): array
{
return ['guardian']; // Guardian Extension 필요
}
public function getDependencies(): array
{
return [
'core' => '>=1.1.0',
];
}