[Laravel 기초] 5. Service Provider

Service Provider

Service Container 에 적재되는 Service Providers 에 대해 알아본다!!

Service Provider 는 라라벨 어플리케이션 ‘Bootstrap’ 의 핵심 요소라고 합니다. 또한, 라라벨 어플리케이션의 코어 서비스들을 대부분 Service Provider 를 통해 제공된다고 합니다. 여기서 ‘Bootstrap’ 은 단순히 어플리케이션 시작, 흔히 말하는 부팅입니다. 위의 그림처럼 서비스 프로바이더들은 모두 서비스 컨테이너에 적재되어 있습니다. 라라벨 어플리케이션은 요청이 들어오는 경우 등록되어 있는 모든 서비스 프로바이더를 로드하는 것이 아니라 요청에 맞는 서비스 프로바이더만을 로드 하는 ‘지연된’ 프로바이더들을 제공합니다.

config/app.php

<? php

// config/app.php

//...//

'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

    ],

//...//

config/app.php에 이미 등록된 서비스 프로바이더들을 확인할 수 있습니다. 라라벨 프레임워크에서 제공하는 서비스 프로바이더, 사용자가 정의할 라라벨 어플리케이션 서비스 프로바이더 들이 등록되어있고 미리 만들어진 패키지들을 통해 추가될 서비스 프로바이더을 위한 위치도 주석으로 표기되어 있습니다. 서비스 컨테이너에 등록될 서비스 프로바이더들을 config/app.php에 명세한다 정도 기억하시면 되겠습니다.

Using Service Provider

이제 서비스 프로바이더를 사용하는 방법에 대해 알아보겠습니다. 두 가지 작업만 하면 됩니다.

  1. 서비스 프로바이더 클래스를 작성
  2. config/app.php 에 서비스 프로바이더 등록

1. 서비스 프로바이더 작성

Illuminate\Support\ServiceProvider 클래스는 두 가지 메소드를 포함합니다.

  • register => 서비스 컨테이너에 binding (등록, 적재) 하는 작업만 담당합니다.
  • boot => Bootstrap 작업이 끝나고 호출된다. boot 성공적으로 끝났으면 다른 서비스 프로바이더도 호출가능하다.

두 가지 메소드의 차이는 코드 호출 시점에 있습니다. register 는 부트스트랩을 위한 바인딩 작업이며 boot 는 부팅이 끝난 뒤에 호출될 것입니다. 서비스 프로바이더를 만들어보면서 하나씩 익혀보겠습니다.

라라벨에서는 artisan CLI 이라는 커맨드 라인 인터페이스를 제공하고 있습니다. artisan 은 라라벨 개발에 도움을 주는 다양한 명령어들을 제공합니다. artisan CLI 를 사용하여 다음 명령어를 입력해보겠습니다. (커맨드 입력 위치는 설치된 라라벨 어플리케이션 Root 위치에서 사용하면 됩니다.) app/Providers/ 디렉토리 아래 ExampleServiceProvider 라는 이름의 Class 파일이 생성되었습니다.

php artisan make:provider ExampleServiceProvider
artisan CLI 를 이용시 Providers 디렉토리 아래 자동으로 생성된다!
<?php

// App\Providers\ExampleServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class ExampleServiceProvider extends ServiceProvider
{
    // ---------------- Simple 바인딩을 여러개 사용하는 경우 
    public $bindings = [
        ServerProvider::class => DigitalOceanServerProvider::class,
    ];

    public $singletons = [
        DowntimeNotifier::class => PingdomDowntimeNotifier::class,
        ServerToolsProvider::class => ServerToolsProvider::class,
    ];

    public function register()
    {
        // register Method
        // 이 곳에서는 서비스 컨테이너에 바인딩하는 코드만 작성해야한다.

        $this->app->bind('SerivceInterface', function ($app) {
            return new SerivceInterface($app->make('CustomService'));
        });
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        // boot Method
        // 다른 모든 서비스 프로바이더들이 등록된 후 호출됨
        // 즉, Bootstrap 이 완료 된 뒤에 호출, 다른 서비스 프로바이더 사용 가능
        // 생성자에 Type-Hinting 으로 의존성 주입도 가능하다.
    }
}

2. config/app.php 에 서비스 프로바이더 등록

<? php 

// config/app.php

//...//

'providers' => [
        //...//
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
        
        /*
         * Custom Application Service Providers...
         */
        App\Providers\ExampleServiceProvider::class,
       
        //...//
],

단순하게 config/app.php 에 해당 클래스를 한 줄 작성하면 됩니다. 사용자가 작성한 서비스 프로바이더는 기존에 존재하던 프로바이더랑 구분하면 좋을 것 같아서 주석을 추가해서 작성하였습니다.

‘지연된(deferred)’ 서비스 프로바이더

추가로, 앞에서 ‘지연된’ 서비스 프로바이더들을 제공한다고 하였는데 여기서 말하는 ‘지연’은 무엇일까요? 서비스 컨테이너에 등록(register) 을 지연시키는 것입니다. 지연시키게 될 경우 프로바이더가 필요할 때 등록을 진행하기 때문에 어플리케이션 성능 개선에 효과가 있습니다. 아래 소스 처럼 \Illuminate\Contracts\Support\DeferrableProvider 를 구현하여 provides 메소드를 작성하면 됩니다. provides 에서는 프로바이더가 등록한 서비스를 리턴하면 된다.

<?php
class ExampleServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register()
    {
        $this->app->singleton(Connection::class, function ($app) {
            return new Connection($app['config']['riak']);
        });
    }

    public function provides()
    {
        return [Connection::class];
    }
}

TL; DR

  • Service Provider 는 어플리케이션의 대부분의 핵심 기능을 담당한다. 서비스 컨테이너에 바인딩하여 사용한다.
  • config/app.php 에 서비스 프로바이더를 등록하여 사용한다.
  • Service Provider 는 register (서비스 컨테이너에 바인딩), boot (Bootstrap 이후에 동작하는 코드) 메소드를 구현한다.

마침

필자는 사실 서비스 프로바이더를 업무 중에 만들어 사용해 본 경험은 없습니다. 라라벨에서 제공하는 서비스 프로바이더들도 많은 기능을 제공하기도 하며 프로바이더를 작성해야할 만한 규모의 특별한 서비스를 구현해본 적도 없기 때문입니다. 하지만 라라벨 프레임워크의 이해하는데 도움이 되니 사용 방법 정도는 익히고 넘어가겠습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다