Laravel의 서비스 컨테이너는 클래스의 의존 관계를 관리하고 의존성 주입을 수행하기 위한 구조입니다. 의존성 주입이란 클래스가 필요로 하는 의존을 생성자나 경우에 따라서는 세터 메서드를 통해 클래스로 “주입”하는 것을 말합니다.다음 예시를 봅시다.
<?phpnamespace App\Http\Controllers;use App\Services\AppleMusic;use Illuminate\View\View;class PodcastController extends Controller{ /** * 새 컨트롤러 인스턴스를 생성한다 */ public function __construct( protected AppleMusic $apple, ) {} /** * 지정된 팟캐스트 정보를 표시한다 */ public function show(string $id): View { return view('podcasts.show', [ 'podcast' => $this->apple->findPodcast($id) ]); }}
이 예시에서 PodcastController는 Apple Music 같은 데이터 소스에서 Podcast를 가져와야 합니다. 그래서 Podcast를 가져올 수 있는 서비스를 주입합니다. 서비스를 주입함으로써 테스트 시에 AppleMusic 서비스의 모의 객체(더미 구현)를 간단하게 교체할 수 있습니다.
서비스 컨테이너를 깊이 이해하는 것은 대규모 Laravel 애플리케이션을 구축할 때 필수적입니다. Laravel 코어 자체에 기여하는 데도 도움이 됩니다.
클래스가 다른 구체 클래스(인터페이스가 아닌)에만 의존하고 있는 경우, 컨테이너에 그 해결 방법을 알려 줄 필요는 없습니다. 예를 들어 다음 코드를 routes/web.php에 작성했다고 합시다.
<?phpclass Service{ // ...}Route::get('/', function (Service $service) { dd($service::class);});
이 예시는 라우트 파일 내에서 클래스를 정의하고 있지만, 이는 데모용입니다. 실제 애플리케이션에서는 서비스 클래스는 app/Services 디렉터리에 정의해 주세요.
이 라우트에 접근하면 Laravel은 자동으로 Service 클래스를 해결해 라우트 핸들러로 주입합니다. 설정 파일을 준비하지 않고도 의존성 주입의 혜택을 누릴 수 있습니다.컨트롤러, 이벤트 리스너, 미들웨어 등 Laravel 애플리케이션에서 작성하는 클래스의 대부분은 컨테이너를 통해 자동으로 의존이 주입됩니다.
use App\Services\Transistor;use App\Services\PodcastParser;use Illuminate\Contracts\Foundation\Application;$this->app->bind(Transistor::class, function (Application $app) { return new Transistor($app->make(PodcastParser::class));});
클로저의 인수로 컨테이너 자체를 받을 수 있습니다. 이를 사용해 하위 의존을 해결할 수 있습니다.서비스 프로바이더 밖에서 컨테이너를 조작하고 싶은 경우에는 App 파사드를 사용합니다.
use App\Services\Transistor;use Illuminate\Contracts\Foundation\Application;use Illuminate\Support\Facades\App;App::bind(Transistor::class, function (Application $app) { // ...});
인터페이스에 의존하지 않는 클래스는 컨테이너에 바인딩할 필요가 없습니다. 컨테이너는 리플렉션을 사용해 이 객체들을 자동으로 해결할 수 있습니다.
singleton 메서드는 클래스나 인터페이스를 한 번만 해결하도록 바인딩합니다. 한 번 해결된 싱글턴은 이후의 컨테이너 호출에서 같은 인스턴스가 반환됩니다.
use App\Services\Transistor;use App\Services\PodcastParser;use Illuminate\Contracts\Foundation\Application;$this->app->singleton(Transistor::class, function (Application $app) { return new Transistor($app->make(PodcastParser::class));});
기존의 객체 인스턴스를 instance 메서드로 컨테이너에 바인딩할 수도 있습니다. 이후 컨테이너 호출에서는 항상 그 인스턴스가 반환됩니다.
use App\Services\Transistor;use App\Services\PodcastParser;$service = new Transistor(new PodcastParser);$this->app->instance(Transistor::class, $service);