파사드(Facades)

소개

파사드는 애플리케이션의 서비스 컨테이너에서 사용 가능한 클래스들에 대한 "정적" 인터페이스를 제공합니다. 라라벨은 많은 파사드를 가지고 있으며 여러분이 알지 못하는 사이에도 이미 사용하고 있을 것입니다. 라라벨 “파사드”는 서비스 컨테이너에 등록된 클래스들에 대한 일종의 “정적 프록시” 역할을 수행하는 데, 이를 통해서 전통적인 정적 메소드 보다 간결한 문법과 테스트의 용이성 그리고 코드의 유연성을 유지하는 이점을 제공합니다.

때로는 여러분의 애플리케이션이나 패키지에서도 파사드를 만들고 싶어질 것입니다. 따라서 파사드의 개념과 개발 방법, 이러한 클래스들의 사용방법에 대해서 알아보겠습니다.

주의 파사드에 대해서 알아보기 전에 라라벨의 서비스 컨테이너에 익숙질 것을 적극 권장합니다.

설명

라라벨 애플리케이션에의 전체 맥락에서 보자면 파사드는 컨테이너의 객체에 엑세스하는 방법을 제공하는 클래스라고 할 수 있습니다. 이 작업을 수행하는 주요 매커니즘이 파사드 클래스안에 있습니다. 라라벨의 파사드들과 여러분이 작성한 파사드들은 기본 Facade 클래스를 상속받습니다.

여러분이 만드는 파사드 클래스는 getFacadeAccessor라는 하나의 메소드를 구현해야 합니다. getFacadeAccessor메소드의 역할은 컨테이너로부터 어떤 의존성을 해결하는지에 대해서 정의하는 것입니다. Facade 기본 클래스는 __callStatic() 매직 메소드를 사용하여 여러분이 작성한 파사드에 대한 호출을 의존성이 해결된 객체로 전달합니다.

따라서 여러분이 Cache::get과 같이 파사드를 호출할 때, 라라벨은 서비스 컨테이너로부터 의존성을 해결하여 캐시 매니저 획득하고 이 캐시 매니저의 get 메소드를 호출해줍니다. 기술적으로 라라벨의 파사드는 라라벨의 서비스 컨테이너를 하나의 서비스로서 사용하기 위한 편리한 기법입니다.

실제 사용법

다음의 예제에서 라라벨 캐시 시스템을 호출합니다. 이 코드를 보자면, 아마 Cache 클래스의 get static 메소드를 호출한다고 생각할 수 있습니다.

$value = Cache::get('key');

그러나 실제로 Illuminate\Support\Facades\Cache를 살펴보면 get이라는 static 메소드는 찾을 수가 없습니다.

class Cache extends Facade {

    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor() { return 'cache'; }

}

캐시 클래스는 기존 Facade 클래스를 상속하고 getFacadeAccessor() 메소드를 정의하고 있습니다. 유념할 것은 이 메소드의 역할은 컨테이너에 등록된 서비스의 이름을 반환한다는 것입니다.

사용자가 Cache 파사드의 어떤 static 메소드를 사용할 때 라라벨은 서비스 컨테이너에 cache로 바인딩된 객체의 인스턴스에게 요청된 메소드(여기에서는 get)을 수행합니다.

따라서 Cache::get 호출은 다음처럼 다시 쓸 수 있습니다.

$value = $app->make('cache')->get('key');

파사드 가져오기

네임스페이스가 지정된 컨트롤러 안에서 파사드를 사용할 경우 파사드를 use 하도록 해야한다는 것을 기억하십시오. 모든 파사드는 글로벌 네임스페이스에 지정되어 있습니다.

<?php namespace App\Http\Controllers;

use Cache;

class PhotosController extends Controller {

    /**
     * Get all of the application photos.
     *
     * @return Response
     */
    public function index()
    {
        $photos = Cache::get('photos');

        //
    }

}

파사드 생성하기

애플리케이션이나 패키지에 파사드를 생성하는 것은 어렵지 않습니다. 3가지만 기억하십시오.

  • 서비스 컨테이너 바인딩
  • 파사드 클래스
  • 파사드 별칭 설정

다음의 예제를 살펴보겠습니다. PaymentGateway\Payment라는 클래스를 정의하고 있습니다.

namespace PaymentGateway;

class Payment {

    public function process()
    {
        //
    }

}

이 클래스를 서비스 컨테이너에 의해서 의존성이 해결될 수 있도록 해야 합니다. 따라서 서비스 프로바이더에 바인딩을 추가합니다.

App::bind('payment', function()
{
    return new \PaymentGateway\Payment;
});

바인딩을 추가하는 좋은 방법은 PaymentServiceProvider라는 서비스 프로바이더를 생성하고 register 메소드에서 바인딩을 추가하는 것입니다. 그 다음에 config/app.php 설정 파일에서 라라벨에서 로딩하는 서비스 프로바이더 항목을 설정합니다.

이제 파사드 클래스를 생성합니다.

use Illuminate\Support\Facades\Facade;

class Payment extends Facade {

    protected static function getFacadeAccessor() { return 'payment'; }

}

마지막으로 원할 경우, config/app.php 설정 파일의 aliases 배열을 통해서 파사드의 별칭을 지정 할 수 있습니다. 이제 Payment 클래스 인스턴스의 process 메소드를 호출 할 수 있게 되었습니다.

Payment::process();

오토로딩 별칭에 대한 주의사항

PHP는 타입힌트가 정의되지 않은 유형의 클래스들에 대한 오토로드를 시도 하지 않으므로 aliases 배열 안에 클래스가 몇몇 인스턴스들 안에서는 사용 가능하지 않습니다. 만약 \ServiceWrapper\ApiTimeoutExceptionApiTimeoutException 라는 별칭으로 지정되었고, \ServiceWrapper 외부 네임 스페이스에서 catch (ApiTimeoutException $e)를 하려 해도 던져진 예외는 포착되지 않습니다. 별칭으로 타입힌트가 설정된 클래스들 사이에서도 비슷한 문제는 발생됩니다. 이에대한 유일한 해결책은 타입힌트를 사용하려는 클래스들 제일 앞부분에 use를 사용해서 미리 지정해두는 방법입니다.

파사드 Mocking

단위 테스트는 왜 파사드가 저렇게 동작하는지에 대한 매우 중요한 이유입니다. 사실 테스트의 용이성은 파사드가 존재하는 주요한 이유입니다. 보다 자세한 내용에 대해서는 문서의 mocking facades 섹션을 참고하십시오.

파사드 참조 목록

다음은 모든 파사드와 실제 클래스에 대한 목록입니다. 여러분이 특정한 파사드를 기준으로 API 문서를 빠르게 찾고자 하는 경우에 도움이 될것입니다. 응용이 가능하도록 서비스 컨테이너 바인딩도 포함되어 있습니다.

Facade Class Service Container Binding
App Illuminate\Foundation\Application app
Artisan Illuminate\Console\Application artisan
Auth Illuminate\Auth\AuthManager auth
Auth (Instance) Illuminate\Auth\Guard
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Bus Illuminate\Contracts\Bus\Dispatcher
Cache Illuminate\Cache\CacheManager cache
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
DB Illuminate\Database\DatabaseManager db
DB (Instance) Illuminate\Database\Connection
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Hash Illuminate\Contracts\Hashing\Hasher hash
Input Illuminate\Http\Request request
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\Writer log
Mail Illuminate\Mail\Mailer mailer
Password Illuminate\Auth\Passwords\PasswordBroker auth.password
Queue Illuminate\Queue\QueueManager queue
Queue (Instance) Illuminate\Queue\QueueInterface
Queue (Base Class) Illuminate\Queue\Queue
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\Database redis
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Blueprint
Session Illuminate\Session\SessionManager session
Session (Instance) Illuminate\Session\Store
Storage Illuminate\Contracts\Filesystem\Factory filesystem
URL Illuminate\Routing\UrlGenerator url
Validator Illuminate\Validation\Factory validator
Validator (Instance) Illuminate\Validation\Validator
View Illuminate\View\Factory view
View (Instance) Illuminate\View\View