메인 콘텐츠로 건너뛰기

GeneratorCommand란

Illuminate\Console\GeneratorCommand는, Laravel의 모든 코드 생성 명령어의 기저가 되는 추상 클래스입니다. make:model, make:controller, make:request 등은 모두 이 클래스를 상속하고 있습니다. 패키지에서 고유의 make:xxx 명령어를 제공하면, 사용자는 클래스를 수동으로 작성하는 대신 php artisan make:handler OrderHandler와 같이 실행하는 것만으로 올바른 네임스페이스의 파일이 생성됩니다.
GeneratorCommand는 공식 문서에 거의 기재가 없고, 프레임워크의 소스 코드를 직접 읽지 않으면 알 수 없습니다. 전형적인 고급 주제입니다.

최소한의 구현

GeneratorCommand를 상속한 클래스에 필요한 구현은 getStub() 메서드뿐입니다. 그 외의 프로퍼티는 옵션이지만, 실제로는 이하를 갖춥니다.
멤버종류역할
$name프로퍼티명령어명(예: make:handler)
$description프로퍼티명령어의 설명문
$type프로퍼티생성물의 종류명. 성공 메시지에 사용됨(예: Handler)
getStub()메서드(필수)stub 파일의 경로를 반환
getDefaultNamespace()메서드생성처의 기본 네임스페이스를 제어
make:handler 명령어를 구현하는 예를 보입니다.
<?php

namespace Vendor\Package\Console\Commands;

use Illuminate\Console\GeneratorCommand;

class HandlerMakeCommand extends GeneratorCommand
{
    protected $name = 'make:handler';

    protected $description = 'Create a new handler class';

    protected $type = 'Handler';

    protected function getStub(): string
    {
        return __DIR__.'/stubs/handler.stub';
    }

    protected function getDefaultNamespace($rootNamespace): string
    {
        return $rootNamespace.'\Handlers';
    }
}
getDefaultNamespace()를 설정하면, php artisan make:handler OrderHandler를 실행했을 때 App\Handlers\OrderHandler 클래스가 app/Handlers/OrderHandler.php에 생성됩니다.

stub 파일 작성

getStub()이 반환하는 경로에 stub 파일을 배치합니다. stub은 템플릿이 되는 PHP 파일로, 플레이스홀더가 네임스페이스와 클래스명으로 치환됩니다.
src
Console
Commands
HandlerMakeCommand.php
stubs
handler.stub
stub 파일의 예:
handler.stub
<?php

namespace {{ namespace }};

class {{ class }}
{
    public function handle(): void
    {
        //
    }
}
GeneratorCommand는 stub 내의 이하 플레이스홀더를 자동적으로 치환합니다.
플레이스홀더치환 후의 값대체 표기
{{ namespace }}생성 클래스의 네임스페이스DummyNamespace
{{ class }}생성 클래스명DummyClass
{{ rootNamespace }}앱의 루트 네임스페이스DummyRootNamespace
{{ namespace }}DummyNamespace는 어느 쪽을 사용해도 같은 결과가 됩니다. Laravel 내장 stub은 양쪽 형식을 포함하고 있었지만, 새로 만드는 경우에는 {{ namespace }} 형식을 권장합니다.

stub 커스터마이즈를 가능하게 하기

사용자가 stub을 덮어쓸 수 있는 구조를 제공하려면, resolveStubPath() 패턴을 사용합니다. 먼저 서비스 프로바이더의 boot()에서 stub을 공개합니다.
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->publishes([
            __DIR__.'/../Console/Commands/stubs' => base_path('stubs'),
        ], 'stubs');
    }
}
다음으로 getStub()에서 사용자가 커스터마이즈한 stub을 우선적으로 사용합니다.
protected function getStub(): string
{
    return $this->resolveStubPath('/stubs/handler.stub');
}

protected function resolveStubPath(string $stub): string
{
    return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
        ? $customPath
        : __DIR__.$stub;
}
php artisan vendor:publish --tag=stubs를 실행한 사용자는 프로젝트 루트의 stubs/handler.stub을 편집하여 템플릿을 변경할 수 있습니다.
resolveStubPath()는 Laravel 내장의 RequestMakeCommand에서도 사용되고 있는 패턴입니다. 패키지를 공개하는 경우에는 이 패턴을 채용해 주세요.

서비스 프로바이더에의 등록

명령어는 서비스 프로바이더의 boot() 메서드에서 등록합니다. runningInConsole()로 확인함으로써, Web 요청 시의 낭비되는 로드를 방지할 수 있습니다.
<?php

namespace Vendor\Package;

use Illuminate\Support\ServiceProvider;
use Vendor\Package\Console\Commands\HandlerMakeCommand;

class PackageServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->commands([
                HandlerMakeCommand::class,
            ]);

            $this->publishes([
                __DIR__.'/../Console/Commands/stubs' => base_path('stubs'),
            ], 'stubs');
        }
    }
}
패키지의 composer.jsonextra.laravel을 설정해 두면, 사용자는 서비스 프로바이더를 수동 등록하지 않아도 됩니다.
"extra": {
    "laravel": {
        "providers": [
            "Vendor\\Package\\PackageServiceProvider"
        ]
    }
}

활용 예

GeneratorCommand가 활약하는 유스케이스를 몇 가지 소개합니다.
인증 체크나 커스텀 검증 로직을 포함하는 자체 기저 클래스를 상속한 Request를 생성하는 명령어. getDefaultNamespace()에서 App\Http\Requests를 반환합니다.
Data Transfer Object의 보일러플레이트를 생성하는 명령어. readonly 프로퍼티나 from() 팩토리 메서드를 포함한 stub을 준비합니다.
단일 책임의 Action 클래스를 생성하는 명령어. App\Actions 네임스페이스에 execute() 메서드를 갖는 클래스를 생성합니다.
make:livewire 명령어는 GeneratorCommand를 상속한 MakeCommand 클래스로 구현되어 있습니다. 컴포넌트 클래스와 Blade 뷰의 2파일을 동시 생성하기 위해 handle()을 오버라이드하고 있습니다.

테스트

Orchestra Testbench를 사용하여, 명령어가 올바르게 파일을 생성하는지 테스트합니다.
<?php

namespace Tests\Feature\Console;

use Illuminate\Support\Facades\File;
use Orchestra\Testbench\TestCase;
use Vendor\Package\PackageServiceProvider;

class HandlerMakeCommandTest extends TestCase
{
    protected function getPackageProviders($app): array
    {
        return [PackageServiceProvider::class];
    }

    protected function tearDown(): void
    {
        File::deleteDirectory(app_path('Handlers'));

        parent::tearDown();
    }

    public function test_make_handler_creates_file(): void
    {
        $path = app_path('Handlers/OrderHandler.php');

        $this->artisan('make:handler', ['name' => 'OrderHandler'])
            ->assertSuccessful();

        $this->assertFileExists($path);
        $this->assertStringContainsString('namespace App\Handlers;', File::get($path));
        $this->assertStringContainsString('class OrderHandler', File::get($path));
    }

    public function test_make_handler_does_not_overwrite_existing_file(): void
    {
        $path = app_path('Handlers/OrderHandler.php');
        File::ensureDirectoryExists(dirname($path));
        File::put($path, '<?php // existing');

        $this->artisan('make:handler', ['name' => 'OrderHandler'])
            ->assertFailed();
    }
}
Generator 명령어는 실제 파일 시스템에 씁니다. tearDown()에서 반드시 정리해 주세요. 테스트가 도중에 실패한 경우에도 확실히 실행됩니다.

관련 페이지

Laravel 패키지 개발

서비스 프로바이더를 사용한 패키지 개발의 기초를 확인합니다.

Orchestra Testbench로 Laravel 패키지를 테스트하기

패키지 테스트 기반의 만드는 법을 확인합니다.
마지막 수정일 2026년 7월 13일