클래스 기반 기능에는 before 메서드를 정의할 수 있습니다. 이 메서드는 스토리지에서 값을 가져오기 전에 인메모리에서 실행되며, null 이외의 값을 반환하면 그 값이 사용됩니다.
class NewApi{ public function before(User $user): mixed { if (Config::get('features.new-api.disabled')) { return $user->isInternalTeamMember(); } } public function resolve(User $user): mixed { // ... }}
before 메서드는 버그 발생 시 기능을 긴급 비활성화하거나, 특정 일시에 롤아웃을 스케줄링할 때 유용합니다.
active 메서드로 기능이 활성화되어 있는지 확인할 수 있습니다. 기본적으로 현재 인증된 사용자에 대해 체크가 수행됩니다.
use Laravel\Pennant\Feature;if (Feature::active('new-api')) { // 새로운 API를 사용하는 처리}
클래스 기반 기능인 경우 클래스명을 전달합니다.
use App\Features\NewApi;if (Feature::active(NewApi::class)) { // ...}
그 밖에 편리한 메서드도 준비되어 있습니다.
// 모든 기능이 활성화되어 있는지Feature::allAreActive(['new-api', 'site-redesign']);// 어느 하나의 기능이 활성화되어 있는지Feature::someAreActive(['new-api', 'site-redesign']);// 기능이 비활성화되어 있는지Feature::inactive('new-api');// 모든 기능이 비활성화되어 있는지Feature::allAreInactive(['new-api', 'site-redesign']);// 어느 하나의 기능이 비활성화되어 있는지Feature::someAreInactive(['new-api', 'site-redesign']);
@feature('site-redesign') {{-- 'site-redesign'이 활성화된 경우 --}}@else {{-- 'site-redesign'이 비활성화된 경우 --}}@endfeature@featureany(['site-redesign', 'beta']) {{-- 둘 중 하나가 활성화된 경우 --}}@endfeatureany
EnsureFeaturesAreActive 미들웨어를 사용하면 라우트 접근에 기능이 필요함을 지정할 수 있습니다. 기능이 비활성화되어 있는 경우 400 Bad Request가 반환됩니다.
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;Route::get('/api/servers', function () { // ...})->middleware(EnsureFeaturesAreActive::using('new-api', 'servers-api'));
응답을 커스터마이즈하려면 whenInactive 메서드를 사용합니다.
EnsureFeaturesAreActive::whenInactive( function (Request $request, array $features) { return new Response(status: 403); });
루프 안에서 기능 체크를 수행하는 경우 성능 문제가 발생할 수 있습니다. load 메서드를 사용해 미리 값을 가져와 두면 해결할 수 있습니다.
// NG: 루프마다 DB 쿼리가 발생foreach ($users as $user) { if (Feature::for($user)->active('notifications-beta')) { $user->notify(new RegistrationSuccess); }}// OK: 미리 일괄 가져오기Feature::for($users)->load(['notifications-beta']);foreach ($users as $user) { if (Feature::for($user)->active('notifications-beta')) { $user->notify(new RegistrationSuccess); }}
기능을 애플리케이션에서 삭제한 경우나 정의를 변경한 경우, 스토리지에서 값을 삭제(퍼지)할 수 있습니다.
// 단일 기능 퍼지Feature::purge('new-api');// 여러 기능 퍼지Feature::purge(['new-api', 'purchase-button']);// 모든 기능 퍼지Feature::purge();
Artisan 명령으로도 퍼지할 수 있습니다. 배포 파이프라인에 통합하면 편리합니다.
php artisan pennant:purge new-api# 여러 개 지정php artisan pennant:purge new-api purchase-button# 지정한 기능 이외를 모두 퍼지php artisan pennant:purge --except=new-api --except=purchase-button# 서비스 프로바이더에 등록된 기능 이외를 퍼지php artisan pennant:purge --except-registered
테스트에서는 Feature::define으로 기능을 재정의함으로써 반환 값을 제어할 수 있습니다.
tab=Pest
use Laravel\Pennant\Feature;test('it can control feature values', function () { Feature::define('purchase-button', 'seafoam-green'); expect(Feature::value('purchase-button'))->toBe('seafoam-green');});
tab=PHPUnit
use Laravel\Pennant\Feature;public function test_it_can_control_feature_values(): void{ Feature::define('purchase-button', 'seafoam-green'); $this->assertSame('seafoam-green', Feature::value('purchase-button'));}
클래스 기반 기능도 마찬가지로 다룰 수 있습니다.
tab=Pest
test('it can control feature values', function () { Feature::define(NewApi::class, true); expect(Feature::value(NewApi::class))->toBeTrue();});
tab=PHPUnit
use App\Features\NewApi;public function test_it_can_control_feature_values(): void{ Feature::define(NewApi::class, true); $this->assertTrue(Feature::value(NewApi::class));}
기존 드라이버가 요건에 맞지 않는 경우, 커스텀 드라이버를 작성할 수 있습니다. Laravel\Pennant\Contracts\Driver 인터페이스를 구현합니다.
<?phpnamespace App\Extensions;use Laravel\Pennant\Contracts\Driver;class RedisFeatureDriver implements Driver{ public function define(string $feature, callable $resolver): void {} public function defined(): array {} public function getAll(array $features): array {} public function get(string $feature, mixed $scope): mixed {} public function set(string $feature, mixed $scope, mixed $value): void {} public function setForAllScopes(string $feature, mixed $value): void {} public function delete(string $feature, mixed $scope): void {} public function purge(array|null $features): void {}}
서비스 프로바이더의 boot 메서드에서 extend를 호출해 등록합니다.
Feature::extend('redis', function (Application $app) { return new RedisFeatureDriver($app->make('redis'), $app->make('events'), []);});