본문으로 건너뛰기
버전: 11.x

컨트랙트 (Contracts)

소개

라라벨의 "컨트랙트(contracts)"는 프레임워크가 제공하는 핵심 서비스의 동작을 명확히 정의한 일련의 인터페이스 집합입니다. 예를 들어, Illuminate\Contracts\Queue\Queue 컨트랙트는 작업 큐잉 시스템에 필요한 메서드를 정의하고, Illuminate\Contracts\Mail\Mailer 컨트랙트는 이메일 전송에 필요한 메서드를 명시합니다.

각 컨트랙트는 라라벨 프레임워크에서 그에 상응하는 구체 구현을 제공합니다. 예를 들어, 라라벨은 다양한 드라이버를 지원하는 큐 시스템 구현을 제공하고, Symfony Mailer가 내부적으로 동작하는 메일러 구현도 포함하고 있습니다.

라라벨의 모든 컨트랙트는 별도의 GitHub 저장소에 존재합니다. 이 저장소는 어떤 컨트랙트들이 있는지 빠르게 참고할 수 있을 뿐만 아니라, 라라벨 서비스와 연동되는 패키지를 개발할 때 사용할 수 있는, 독립적이고 결합도가 낮은 패키지로 제공됩니다.

컨트랙트와 파사드의 차이

라라벨의 파사드와 헬퍼 함수는 라라벨의 서비스를 간편하게 사용할 수 있도록 하며, 서비스 컨테이너에서 컨트랙트를 타입힌트하고 resolve(해결)할 필요가 없습니다. 대부분의 경우, 각 파사드에는 그에 대응하는 컨트랙트가 존재합니다.

파사드는 클래스의 생성자에서 명시적으로 의존성을 선언하지 않아도 사용할 수 있다는 점이 컨트랙트와 다릅니다. 반면, 컨트랙트를 사용하면 클래스의 의존성을 명확하게 타입힌트로 정의할 수 있습니다. 일부 개발자는 이처럼 의존성을 명확히 하는 방식을 선호하며, 그래서 컨트랙트를 이용하기도 합니다. 반면, 다른 개발자는 파사드가 주는 편리함을 즐깁니다. 일반적으로, 대부분의 애플리케이션은 개발 중에 파사드를 사용해도 전혀 문제가 없습니다.

컨트랙트를 언제 사용해야 할까요?

컨트랙트와 파사드 중 어떤 것을 사용할지 결정하는 것은 개발자 개인 또는 팀의 취향에 달려 있습니다. 두 방식 모두 견고하고 테스트 가능한 라라벨 애플리케이션을 만드는 데 사용할 수 있습니다. 컨트랙트와 파사드는 서로 배타적인 것이 아니므로, 애플리케이션의 일부에서는 파사드를, 다른 부분에서는 컨트랙트에 의존하는 방식이 가능합니다. 클래스의 책임만 잘 분리한다면, 컨트랙트와 파사드 사용에 따른 실제 차이는 거의 없습니다.

일반적으로 대부분의 애플리케이션은 개발 단계에서 파사드를 사용해도 괜찮습니다. 만약 여러 PHP 프레임워크와 연동되는 패키지를 개발한다면, illuminate/contracts 패키지를 통해 라라벨의 구체적인 구현체를 패키지의 composer.json에 의존하지 않고 서비스 연동을 정의할 수 있습니다.

컨트랙트 사용 방법

그렇다면, 컨트랙트의 구현체는 어떻게 얻을 수 있을까요? 사실 매우 간단합니다.

라라벨에서는 컨트롤러, 이벤트 리스너, 미들웨어, 큐 작업, 그리고 라우트 클로저 등 다양한 유형의 클래스가 서비스 컨테이너를 통해 resolve(해결)됩니다. 따라서, 클래스가 resolve될 때 생성자의 타입힌트에 컨트랙트 인터페이스를 선언하기만 하면, 컨테이너가 알아서 해당 구현체를 주입해 줍니다.

예를 들어, 아래와 같은 이벤트 리스너를 살펴보겠습니다.

<?php

namespace App\Listeners;

use App\Events\OrderWasPlaced;
use App\Models\User;
use Illuminate\Contracts\Redis\Factory;

class CacheOrderInformation
{
/**
* Create a new event handler instance.
*/
public function __construct(
protected Factory $redis,
) {}

/**
* Handle the event.
*/
public function handle(OrderWasPlaced $event): void
{
// ...
}
}

이벤트 리스너가 resolve될 때, 서비스 컨테이너는 클래스 생성자의 타입힌트를 읽고 적절한 인스턴스를 자동으로 주입합니다. 서비스 컨테이너에 등록하는 방법 등 더 자세한 내용은 서비스 컨테이너 문서를 참고하시기 바랍니다.

컨트랙트 참고표

아래 표는 라라벨의 모든 컨트랙트와 그에 대응하는 파사드를 빠르게 조회할 수 있도록 정리한 것입니다.

컨트랙트대응 파사드
Illuminate\Contracts\Auth\Access\Authorizable 
Illuminate\Contracts\Auth\Access\GateGate
Illuminate\Contracts\Auth\Authenticatable 
Illuminate\Contracts\Auth\CanResetPassword 
Illuminate\Contracts\Auth\FactoryAuth
Illuminate\Contracts\Auth\GuardAuth::guard()
Illuminate\Contracts\Auth\PasswordBrokerPassword::broker()
Illuminate\Contracts\Auth\PasswordBrokerFactoryPassword
Illuminate\Contracts\Auth\StatefulGuard 
Illuminate\Contracts\Auth\SupportsBasicAuth 
Illuminate\Contracts\Auth\UserProvider 
Illuminate\Contracts\Broadcasting\BroadcasterBroadcast::connection()
Illuminate\Contracts\Broadcasting\FactoryBroadcast
Illuminate\Contracts\Broadcasting\ShouldBroadcast 
Illuminate\Contracts\Broadcasting\ShouldBroadcastNow 
Illuminate\Contracts\Bus\DispatcherBus
Illuminate\Contracts\Bus\QueueingDispatcherBus::dispatchToQueue()
Illuminate\Contracts\Cache\FactoryCache
Illuminate\Contracts\Cache\Lock 
Illuminate\Contracts\Cache\LockProvider 
Illuminate\Contracts\Cache\RepositoryCache::driver()
Illuminate\Contracts\Cache\Store 
Illuminate\Contracts\Config\RepositoryConfig
Illuminate\Contracts\Console\Application 
Illuminate\Contracts\Console\KernelArtisan
Illuminate\Contracts\Container\ContainerApp
Illuminate\Contracts\Cookie\FactoryCookie
Illuminate\Contracts\Cookie\QueueingFactoryCookie::queue()
Illuminate\Contracts\Database\ModelIdentifier 
Illuminate\Contracts\Debug\ExceptionHandler 
Illuminate\Contracts\Encryption\EncrypterCrypt
Illuminate\Contracts\Events\DispatcherEvent
Illuminate\Contracts\Filesystem\CloudStorage::cloud()
Illuminate\Contracts\Filesystem\FactoryStorage
Illuminate\Contracts\Filesystem\FilesystemStorage::disk()
Illuminate\Contracts\Foundation\ApplicationApp
Illuminate\Contracts\Hashing\HasherHash
Illuminate\Contracts\Http\Kernel 
Illuminate\Contracts\Mail\Mailable 
Illuminate\Contracts\Mail\MailerMail
Illuminate\Contracts\Mail\MailQueueMail::queue()
Illuminate\Contracts\Notifications\DispatcherNotification
Illuminate\Contracts\Notifications\FactoryNotification
Illuminate\Contracts\Pagination\LengthAwarePaginator 
Illuminate\Contracts\Pagination\Paginator 
Illuminate\Contracts\Pipeline\Hub 
Illuminate\Contracts\Pipeline\PipelinePipeline
Illuminate\Contracts\Queue\EntityResolver 
Illuminate\Contracts\Queue\FactoryQueue
Illuminate\Contracts\Queue\Job 
Illuminate\Contracts\Queue\MonitorQueue
Illuminate\Contracts\Queue\QueueQueue::connection()
Illuminate\Contracts\Queue\QueueableCollection 
Illuminate\Contracts\Queue\QueueableEntity 
Illuminate\Contracts\Queue\ShouldQueue 
Illuminate\Contracts\Redis\FactoryRedis
Illuminate\Contracts\Routing\BindingRegistrarRoute
Illuminate\Contracts\Routing\RegistrarRoute
Illuminate\Contracts\Routing\ResponseFactoryResponse
Illuminate\Contracts\Routing\UrlGeneratorURL
Illuminate\Contracts\Routing\UrlRoutable 
Illuminate\Contracts\Session\SessionSession::driver()
Illuminate\Contracts\Support\Arrayable 
Illuminate\Contracts\Support\Htmlable 
Illuminate\Contracts\Support\Jsonable 
Illuminate\Contracts\Support\MessageBag 
Illuminate\Contracts\Support\MessageProvider 
Illuminate\Contracts\Support\Renderable 
Illuminate\Contracts\Support\Responsable 
Illuminate\Contracts\Translation\Loader 
Illuminate\Contracts\Translation\TranslatorLang
Illuminate\Contracts\Validation\FactoryValidator
Illuminate\Contracts\Validation\ValidatesWhenResolved 
Illuminate\Contracts\Validation\ValidationRule 
Illuminate\Contracts\Validation\ValidatorValidator::make()
Illuminate\Contracts\View\Engine 
Illuminate\Contracts\View\FactoryView
Illuminate\Contracts\View\ViewView::make()