이 세상에 하나는 남기고 가자

세상에 필요한 소스코드 한줄 남기고 가자

laravel(또는 lumen) package 에서 schedule 추가

아사마루

laravel을 사용하는 프로젝트에서 schedule 처리는 Task Scheduling에 안내되어 있는 것처럼 \App\Console\Kernelschedule(Schedule $schedule) 메서드 내에서 정의해서 사용한다.

laravel 기반 프로젝트에 추가한 package(module)에서 직접 schedule을 추가하고 싶다면 어떻게 해야 할까? 이 부분에 대해서는 메뉴얼 상에 안내된 내용이 없다. 하지만 "How to schedule Artisan commands in a package?" 글을 참고하면 아래와 같이 처리할 수 있다.

use Illuminate\Support\ServiceProvider;
use Illuminate\Console\Scheduling\Schedule;

class ScheduleServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->app->booted(function () {
            $schedule = $this->app->make(Schedule::class);
            $schedule->command('some:command')->everyMinute();
        });
    }

    public function register()
    {
    }
}

Application Class의 booted($callback) 함수를 사용해서 schedule을 추가하는 것이다.


그런데 이 booted 함수가 lumen에서는 존재하지 않는다. booted 함수가 없는 것이 문제가 되는 이유는 schedule 개체를 만드는 Kernel Class의 defineConsoleSchedule() 메서드가 아래와 같이 구현되어 있으며 이 메서드는 Provider가 구성된 이후에 실행 되므로 $schedule 개체에 추가적인 schedule을 추가할 수 있는 진입 지점이 없다.

protected function defineConsoleSchedule()
{
  $this->app->instance(
      'Illuminate\Console\Scheduling\Schedule', $schedule = new Schedule($this->app[Cache::class])
  );

  $this->schedule($schedule);
}

따라서 lumen 프로젝트에서는 package 내부에서 직접적으로 schedule을 추가하는 것은 불가능해 보인다. 굳이 해야 한다면 Kernel 클래스의 defineConsoleSchedule() 메서드를 override 하여 아래와 같이 구현해야 한다.

protected function defineConsoleSchedule()
{
  $this->app->instance(
    'Illuminate\Console\Scheduling\Schedule', $schedule = ScheduleHelper::get()
  );

  $this->schedule($schedule);
}

new Schedule($this->app[Cache::class]) 대신에 ScheduleHelper::get()에서 인스턴스를 받아서 사용한다.

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Contracts\Cache\Repository as Cache;

class ScheduleHelper
{
    /** @var Schedule */
    private static $scheduleInstance = null;

    /**
     * @return Schedule
     */
    static public function &get()
    {
        if (empty(static::$scheduleInstance)) {
            static::$scheduleInstance = new Schedule(app(Cache::class));
        }
        return static::$scheduleInstance;
    }
}

ScheduleHelper에서는 schedule 인스턴스를 싱글톤으로 반환한다.

use Illuminate\Support\ServiceProvider;
use Illuminate\Console\Scheduling\Schedule;

class ScheduleServiceProvider extends ServiceProvider
{
  public function boot()
  {
    $schedule = ScheduleHelper::get();
    $schedule->command('some:command')->everyMinute();
  }

  public function register()
  {
  }
}

Provider 에서는 ScheduleHelper::get()를 이용해서 schedule 인스턴스를 받아 schedule을 추가 한다.


위 방법은 lumen 프로젝트와 package 간의 종속 관계가 생기므로 권장할만한 방법은 아니다. 다만, 나의 경우는 lumen 프로젝트 개설시 기본적으로 직접 개발한 package를 사용하고 해당 package에서 기본 제어를 하도록 되어 있기 때문에 위 방법이 유용하게 사용될 수 있다(사실 대부분의 개발자들의 의견은 lumen은 간단한 프로젝트에서만 사용하고 다소 복잡해질 수 있는 프로젝트는 그냥 laravel을 쓰라는 것이다).

Comment