Laravel Service Container와 Provider는 애플리케이션에 의존성을 주입하고 관리하는 강력한 기능을 제공해준다.
생성자 등을 통해 외부에서 구체 클래스나 인터페이스를 충족하는 객체를 넣는 것이다. 쉽게 말하면 여러 책을 집필할 때, 반드시 필요한 책의 목차, 저자명, 제조사 등의 정보를 미리 강제해두는 것으로, 사용자는 해당 항목을 반드시 작성해야만 하지만, 각각의 정보를 유연하게 작성할 수 있게 한다.
목차(){},저자명(){}..처럼 빈 메소드만 정의하고 인터페이스를 implements한 클래스에서 비어있는 메소드를 필수로 정의해야한다. 여러 인터페이스의 implements도 가능하다.(다중 상속)
목차(){"1장, 2장"},저자명($저자){echo $저자}..처럼 메소드의 구체적인 동작이 구현되어 있으며, new 키워드를 통해 객체를 생성하여 당장 사용이 가능한 클래스
미완성 클래스, 단어 그대로 미완성이기 때문에, 해당 클래스로는 객체를 만들 수 없으며, 반드시 하위 클래스에 상속을 하여 미완성된 메소드를 완성시켜주어야 한다. abstract라는 개념의 미구현 메소드를 정의하고 인터페이스를 extends한 클래스에서 비어있는 메소드를 필수로 정의해야한다.
인터페이스와는 다르게 단일 상속만을 지원하며 하위 클래스로의 상속을 통해 객체를 생성할 수 있다는 차이가 있다.
Service Container는 의존성을 외부에서 주입하여, 제어의 주체를 로직이 아닌 외부에서 하도록 역할을 분리시키는 것이다. 즉 로직에서 필요한 객체에 대한 정보를 new 키워드를 활용해 하나하나 만들 필요가 없으며 Service Container를 통해 이미 만들어진 인스턴스를 직접 가져와 사용하기만 하면 된다.
use Illuminate\Http\Request;
Route::get('/', function (Request $request) {
// ...
});
서비스 컨테이너를 사용하기 위해, 먼저 클래스의 의존성을 등록하는 작업이 필요하다. 해당 작업을 바인딩(확장)이라고 하며, 아래와 같이 서비스 프로바이더 내에 등록할 수 있다.
use App\Services\PaymentService;
use Illuminate\Support\ServiceProvider;
use App\Services\Transistor;
use App\Services\PodcastParser;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// 의존성이 주입될 때마다 새로운 객체를 생성
$this->app->bind('HelpSpot\API', function ($app) {
return new \HelpSpot\API($app->make('HttpClient'));
});
// 처음 의존성이 주입될 때, 만들어진 객체가 종료시까지 유지
$this->app->singleton('HelpSpot\API', function ($app) {
return new \HelpSpot\API($app->make('HttpClient'));
});
// 의존성 주입된 객체가 한 번의 요청→응답 주기안에서는 동일
$this->app->scoped(Transistor::class, function ($app) {
return new Transistor($app->make(PodcastParser::class));
});
// 존재하는 객체의 인스턴스를 바인딩
$api = new \HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\API', $api);
// PhotoController에서 Filesystem라는 인터페이스의 의존성 주입을 위해
// give내의 클로저를 통해 Filesystem을 바인딩하여 사용한다.
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
}
}
Service Provider는 라라벨 애플리케이션의 부트스트래핑의 핵심이라고 할 수 있다. 라라벨의 코어 서비스는 Service Provider를 통해서 초기 설정된다. Service Provider의 역할인 부트스트래핑이란 서비스 컨테이너에 바인딩을 등록하는 것을 포함, 이벤트 리스터, 미들웨어, 라우터 등을 등록하는 것을 의미한다.
config/app.php의 providers라는 배열에 등록을 함으로써 사용이 가능하며, 각각의 Service Provider는 "지연 프로바이더"로 실제로 필요할 때에 로드된다.
Laravel에서의 Service Provider는 아래의 Artisan 명령어를 통해서 추가할 수 있으며, App\Providers\ 하위에서 관리된다.
php artisan make:provider RiakServiceProvider
생성된 Service Provider 파일
<?php
namespace App\Providers;
use App\Services\Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
// 서비스 컨테이너에 바인딩, 싱글턴으로 서비스를 등록하는 작업을 수행
public function register()
{
$this->app->bind('database', function ($app) {
return new DatabaseConnection($app['config']['database']);
});
$this->app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
// 서비스 등록 이후 추가적인 초기화 작업을 수행
// 서비스 프로바이더들이 등록된 이후 호출
// ex) 이벤트 리스너, 라우터, 뷰 컴포저 등록, 캐시 설정, 모델 관계 설정 등
public function boot()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
}
Facade는 서비스 컨테이너에 등록된 서비스를 간편하게 사용하기 위한 정적인 인터페이스이다. 조금 더 쉽게 이해를 해보자면 디자인 패턴 중 하나이며, 동일한 명칭을 가진 Facade Pattern의 개념을 알아보면 좋다.
Facade Pattern이란 하나의 상위 인터페이스로 하위 인터페이스들을 묶어주는 패턴
위와 같은 의미처럼, 기존에는 다양한 서비스를 하나하나 객체로 생성하고 사용해야 했다면, Facade Pattern을 활용해 여러 객체들을 하나의 인터페이스로 연관 기능들을 통합해서 사용할 수 있게 되었다.
이와 마찬가지로, Laravel의 Facade 또한 상위 인터페이스의 호출만으로도 여러 객체에서 제공하는 기능을 보다 직관적이며 간결한 코드로 제공받을 수 있게 된다.
// Facade를 사용하는 이유
// 기존 : 등록된 서비스의 객체를 인스턴스화하여 사용 (동적 할당)
$service = new service();
$value = $service->get('value');
// Facade : 등록된 서비스의 객체를 Facade를 통해 정적인 형태로 사용 가능
$value = service::get('value');
Laravel에서는 Facade와 비슷한 역할을 가진 헬퍼라는 기능을 제공해 주고 있다. Facade와 겹치는 기능도 있고, 목적 자체도 특정 기능을 간편하게 접근하고 사용하는 것이기 때문에, 개발을 함에 있어서 각각의 특징을 확인하고 사용을 할 필요가 있을 것이다.
// Facade
use Illuminate\Support\Facades\Response;
Response::json([]);
// Helper
response()->json([]);
// todo 이후에 실습 후 작성 예정