Laravel 파사드

(Laravel 퍼사드에서 넘어옴)

1 개요[ | ]

Facades
파사드

https://laravel.com/docs/11.x/facades

2 소개[ | ]

Laravel 문서 전체에서 "파사드(facades)"를 통해 Laravel의 기능과 상호작용하는 코드 예제를 볼 수 있습니다. 파사드는 애플리케이션의 서비스 컨테이너에서 사용할 수 있는 클래스에 대한 "정적" 인터페이스를 제공합니다. Laravel은 거의 모든 Laravel 기능에 접근할 수 있는 여러 파사드를 기본으로 제공합니다.

Laravel 파사드는 서비스 컨테이너에 있는 클래스에 대한 "정적 프록시" 역할을 하여 전통적인 정적 메소드보다 더 많은 테스트가능성과 유연성을 유지하면서 간결하고 표현력 있는 구문을 제공하는 장점을 가지고 있습니다. 파사드가 어떻게 작동하는지 완전히 이해하지 못해도 괜찮습니다. 계속해서 Laravel에 대해 배우면서 자연스럽게 익히면 됩니다.

모든 Laravel 파사드는 Illuminate\Support\Facades 네임스페이스에 정의되어 있습니다. 따라서 다음과 같이 쉽게 파사드에 접근할 수 있습니다:

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;

Route::get('/cache', function () {
    return Cache::get('key');
});

Laravel 문서 전반에 걸쳐, 다양한 프레임워크 기능을 설명하기 위해 파사드를 사용하는 많은 예시들이 있습니다.

헬퍼 함수

파사드를 보완하기 위해, Laravel은 일반적인 Laravel 기능과 상호작용을 더욱 쉽게 할 수 있도록 다양한 전역 "헬퍼 함수"를 제공합니다. 자주 사용되는 헬퍼 함수로는 view, response, url, config 등이 있습니다. Laravel에서 제공하는 각 헬퍼 함수는 해당 기능과 함께 문서화되어 있으며, 전체 목록은 헬퍼 전용 문서에서 확인할 수 있습니다.

예를 들어, JSON 응답을 생성하기 위해 Illuminate\Support\Facades\Response 파사드를 사용하는 대신, response 함수를 간단히 사용할 수 있습니다. 헬퍼 함수는 전역적으로 사용가능하므로, 이를 사용하기 위해 어떤 클래스를 임포트할 필요가 없습니다:

use Illuminate\Support\Facades\Response;

Route::get('/users', function () {
    return Response::json([
        // ...
    ]);
});
 
Route::get('/users', function () {
    return response()->json([
        // ...
    ]);
});

3 언제 파사드를 사용하는가[ | ]

파사드에는 많은 장점이 있습니다. 파사드는 기억하기 쉬운 간결한 구문을 제공하여 긴 클래스 이름을 일일이 주입하거나 수동으로 구성할 필요 없이 Laravel의 기능을 사용할 수 있게 합니다. 또한 PHP의 동적 메소드를 독특하게 사용하기 때문에 테스트가 용이합니다.

하지만 파사드를 사용할 때는 주의가 필요합니다. 파사드의 주요 위험은 클래스 "스코프 확장(scope creep)"입니다. 파사드는 사용이 매우 간편하고 주입이 필요 없기 때문에, 하나의 클래스에서 여러 파사드를 사용하며 클래스가 계속해서 커지는 경우가 많습니다. 의존성 주입을 사용하면 큰 생성자가 시각적 피드백을 제공하여 클래스가 너무 커지고 있다는 것을 인지할 수 있어 이러한 가능성이 줄어듭니다. 따라서 파사드를 사용할 때는 클래스의 크기에 특히 주의하여 책임의 범위를 좁게 유지해야 합니다. 클래스가 너무 커진다면, 여러 작은 클래스로 분할하는 것을 고려하십시오.

3.1 파사드 vs 의존성 주입[ | ]

의존성 주입의 주요 이점 중 하나는 주입된 클래스의 구현을 교체할 수 있다는 것입니다. 이는 테스트 중에 목(mock)이나 스텁(stub)을 주입하고 해당 스텁의 다양한 메소드가 호출되었는지 확인할 수 있기 때문에 유용합니다.

일반적으로 진짜 정적 클래스 메소드는 목이나 스텁으로 만들 수 없습니다. 그러나 파사드는 서비스 컨테이너에서 해결된 객체에 대한 메소드 호출을 프록시하는 동적 메소드를 사용하기 때문에, 주입된 클래스 인스턴스를 테스트하는 것처럼 파사드를 테스트할 수 있습니다. 예를 들어, 다음과 같은 라우트가 있다고 가정해 봅시다:

use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});

Laravel의 파사드 테스트 메소드를 사용하여 Cache::get 메소드가 예상한 인수와 함께 호출되었는지 확인하는 다음 테스트를 작성할 수 있습니다:

use Illuminate\Support\Facades\Cache;

test('basic example', function () {
    Cache::shouldReceive('get')
         ->with('key')
         ->andReturn('value');

    $response = $this->get('/cache');

    $response->assertSee('value');
});

위 예제에서는 Cache 파사드의 get 메서드가 'key' 인수와 함께 호출되고, 'value'를 반환하도록 설정한 후, '/cache' 경로에 대한 GET 요청을 보내서 응답에 'value'가 포함되어 있는지 확인합니다.

3.2 파사드 vs 헬퍼 함수[ | ]

파사드 외에도, Laravel은 뷰 생성, 이벤트 발생, 작업 디스패치, HTTP 응답 전송과 같은 일반적인 작업을 수행할 수 있는 다양한 "헬퍼" 함수를 포함하고 있습니다. 이러한 헬퍼 함수 중 많은 수는 해당하는 파사드와 동일한 기능을 수행합니다. 예를 들어, 다음의 파사드 호출과 헬퍼 호출은 동일합니다:

return Illuminate\Support\Facades\View::make('profile');
 
return view('profile');

파사드와 헬퍼 함수 사이에는 실질적인 차이가 전혀 없습니다. 헬퍼 함수를 사용할 때에도 해당 파사드와 동일하게 테스트할 수 있습니다. 예를 들어, 다음과 같은 라우트가 주어졌을 때:

Route::get('/cache', function () {
    return cache('key');
});

cache 헬퍼는 Cache 파사드의 기본 클래스의 get 메서드를 호출하게 됩니다. 따라서, 헬퍼 함수를 사용하더라도 예상한 인수로 메소드가 호출되었는지 확인하는 다음과 같은 테스트를 작성할 수 있습니다:

use Illuminate\Support\Facades\Cache;
 
/**
 * 기본적인 기능 테스트 예제입니다.
 */
public function test_basic_example(): void
{
    Cache::shouldReceive('get')
         ->with('key')
         ->andReturn('value');
 
    $response = $this->get('/cache');
 
    $response->assertSee('value');
}

4 파사드의 작동 방식[ | ]

Laravel 애플리케이션에서 파사드는 컨테이너에서 객체에 접근할 수 있게 하는 클래스입니다. 이 기능을 가능하게 하는 메커니즘은 Facade 클래스에 있습니다. Laravel의 파사드와 여러분이 생성하는 커스텀 파사드는 모두 Illuminate\Support\Facades\Facade 기본 클래스를 확장합니다.

Facade 기본 클래스는 __callStatic() 매직 메소드를 사용하여 파사드에서 컨테이너에서 해결된 객체로 호출을 전달합니다. 아래 예제에서는 Laravel 캐시 시스템에 호출이 이루어집니다. 이 코드를 보면, Cache 클래스의 정적 get 메소드가 호출되고 있다고 가정할 수 있습니다:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
 
class UserController extends Controller
{
    /**
     * 주어진 사용자의 프로필을 보여줍니다.
     */
    public function showProfile(string $id): View
    {
        $user = Cache::get('user:'.$id);
 
        return view('profile', ['user' => $user]);
    }
}

파일 상단에서 Cache 파사드를 "임포트"하고 있는 것을 볼 수 있습니다. 이 파사드는 Illuminate\Contracts\Cache\Factory 인터페이스의 기본 구현에 접근하는 프록시 역할을 합니다. 파사드를 사용하여 호출하는 모든 메소드는 Laravel의 캐시 서비스의 기본 인스턴스에 전달됩니다.

Illuminate\Support\Facades\Cache 클래스를 보면, 정적 get 메서드가 없다는 것을 알 수 있습니다:

class Cache extends Facade
{
    /**
     * 컴포넌트의 등록된 이름을 가져옵니다.
     */
    protected static function getFacadeAccessor(): string
    {
        return 'cache';
    }
}

대신, Cache 파사드는 기본 Facade 클래스를 확장하고 getFacadeAccessor() 메소드를 정의합니다. 이 메소드의 역할은 서비스 컨테이너 바인딩의 이름을 반환하는 것입니다. 사용자가 Cache 파사드의 정적 메소드를 참조할 때, Laravel은 서비스 컨테이너에서 cache 바인딩을 해결하고 해당 객체에 대해 요청된 메소드(이 경우 get)를 실행합니다.

5 실시간 파사드[ | ]

실시간 파사드를 사용하여 애플리케이션의 모든 클래스를 파사드처럼 취급할 수 있습니다. 이를 어떻게 사용하는지 보여주기 위해 실시간 파사드를 사용하지 않는 코드를 먼저 살펴보겠습니다. 예를 들어, Podcast 모델에 publish 메소드가 있다고 가정해봅시다. 그런데 팟캐스트를 발행하려면 Publisher 인스턴스를 주입해야 합니다:

namespace App\Models;

use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    /**
     * 팟캐스트를 발행합니다.
     */
    public function publish(Publisher $publisher): void
    {
        $this->update(['publishing' => now()]);

        $publisher->publish($this);
    }
}

메소드에 퍼블리셔 구현을 주입하면 주입된 퍼블리셔를 모킹할 수 있기 때문에 메소드를 쉽게 테스트할 수 있습니다. 하지만 이렇게 하면 매번 publish 메소드를 호출할 때마다 퍼블리셔 인스턴스를 전달해야 합니다. 실시간 파사드를 사용하면 동일한 테스트가능성을 유지하면서도 Publisher 인스턴스를 명시적으로 전달할 필요가 없습니다. 실시간 파사드를 생성하려면 가져온 클래스의 네임스페이스 앞에 Facades를 접두어로 추가합니다:

namespace App\Models;

#use App\Contracts\Publisher;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    /**
     * 팟캐스트를 발행합니다.
     */
    #public function publish(Publisher $publisher): void
    public function publish(): void
    {
        $this->update(['publishing' => now()]);

        #$publisher->publish($this);
        Publisher::publish($this);
    }
}

실시간 파사드를 사용하면 퍼블리셔 구현이 Facades 접두어 뒤에 나타나는 인터페이스 또는 클래스 이름 부분을 사용하여 서비스 컨테이너에서 해결됩니다. 테스트 시 Laravel의 빌트인 파사드 테스트 헬퍼를 사용하여 이 메소드 호출을 모킹할 수 있습니다:

use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('podcast can be published', function () {
    $podcast = Podcast::factory()->create();

    Publisher::shouldReceive('publish')->once()->with($podcast);

    $podcast->publish();
});

6 파사드 클래스 참조[ | ]

다음은 각 퍼사드와 해당 기본 클래스에 대한 설명입니다. 이는 주어진 퍼사드 루트에 대한 API 문서를 빠르게 조사하는 데 유용한 도구입니다. 또한, 서비스 컨테이너 바인딩 키가 해당되는 경우 포함되어 있습니다.

파사드 클래스 서비스 컨테이너 바인딩
App Illuminate\Foundation\Application app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth Illuminate\Auth\AuthManager auth
Auth (Instance) Illuminate\Contracts\Auth\Guard auth.driver
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Broadcast Illuminate\Contracts\Broadcasting\Factory
Broadcast (Instance) Illuminate\Contracts\Broadcasting\Broadcaster
Bus Illuminate\Contracts\Bus\Dispatcher
Cache Illuminate\Cache\CacheManager cache
Cache (Instance) Illuminate\Cache\Repository cache.store
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
Date Illuminate\Support\DateFactory date
DB Illuminate\Database\DatabaseManager db
DB (Instance) Illuminate\Database\Connection db.connection
Event Illuminate\Events\Dispatcher events
Exceptions Illuminate\Foundation\Exceptions\Handler
Exceptions (Instance) Illuminate\Contracts\Debug\ExceptionHandler
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate
Hash Illuminate\Contracts\Hashing\Hasher hash
Http Illuminate\Http\Client\Factory
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\LogManager log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Password (Instance) Illuminate\Auth\Passwords\PasswordBroker auth.password.broker
Pipeline (Instance) Illuminate\Pipeline\Pipeline
Process Illuminate\Process\Factory
Queue Illuminate\Queue\QueueManager queue
Queue (Instance) Illuminate\Contracts\Queue\Queue queue.connection
Queue (Base Class) Illuminate\Queue\Queue
RateLimiter Illuminate\Cache\RateLimiter
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\RedisManager redis
Redis (Instance) Illuminate\Redis\Connections\Connection redis.connection
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory
Response (Instance) Illuminate\Http\Response
Route Illuminate\Routing\Router router
Schedule Illuminate\Console\Scheduling\Schedule
Schema Illuminate\Database\Schema\Builder
Session Illuminate\Session\SessionManager session
Session (Instance) Illuminate\Session\Store session.store
Storage Illuminate\Filesystem\FilesystemManager filesystem
Storage (Instance) Illuminate\Contracts\Filesystem\Filesystem filesystem.disk
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
Vite Illuminate\Foundation\Vite
문서 댓글 ({{ doc_comments.length }})
{{ comment.name }} {{ comment.created | snstime }}