본문으로 건너뛰기
버전: master

Package Development

Introduction​

패키지는 Laravel에 기능을 추가하는 주요 방법입니다. 패키지는 Carbon처럼 날짜를 다루기 위한 훌륭한 방법일 수도 있고, Spatie의 Laravel Media Library처럼 Eloquent 모델에 파일을 연동할 수 있도록 해주는 패키지일 수도 있습니다.

패키지에는 여러 종류가 있습니다. 어떤 패키지는 독립 실행형(stand-alone)으로, 모든 PHP 프레임워크에서 동작할 수 있습니다. Carbon과 Pest가 그 예시입니다. 이런 패키지들도 composer.json 파일에 require하여 Laravel에서 사용할 수 있습니다.

반대로, 일부 패키지는 오직 Laravel에서 사용하기 위한 목적으로 만들어집니다. 이런 패키지에는 Laravel 애플리케이션을 확장하는 데 적합한 라우트, 컨트롤러, 뷰, 설정 등이 포함돼 있을 수 있습니다. 이 가이드는 주로 Laravel에 특화된 패키지를 개발하는 방법을 다룹니다.

A Note on Facades​

Laravel 애플리케이션을 개발할 때는 contracts나 facades 모두 테스트 가능성 측면에서 거의 동일하므로 어느 것을 사용해도 큰 문제가 되지 않습니다. 그러나 패키지를 개발할 때는 Laravel의 모든 테스트 헬퍼를 사용할 수 없는 경우가 많습니다. 패키지의 테스트를 일반적인 Laravel 애플리케이션 안에 설치된 것처럼 작성하려면 Orchestral Testbench 패키지를 사용할 수 있습니다.

Package Discovery​

Laravel 애플리케이션의 bootstrap/providers.php 파일에는 Laravel에서 로드해야 하는 서비스 프로바이더 목록이 들어 있습니다. 하지만 사용자에게 서비스 프로바이더를 직접 목록에 추가하도록 요구하지 않고, 패키지의 composer.json 파일 extra 섹션에 프로바이더를 정의해두면 Laravel이 자동으로 이를 로드하도록 할 수 있습니다. 서비스 프로바이더 뿐 아니라 등록하고 싶은 facades도 함께 지정할 수 있습니다:

"extra": {
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
}
},

패키지가 자동 발견에 맞게 구성된 경우, Laravel은 패키지가 설치되는 즉시 서비스 프로바이더와 파사드를 자동으로 등록하여 사용자에게 편리한 설치 경험을 제공합니다.

Opting Out of Package Discovery​

패키지를 사용하는 입장에서 해당 패키지의 자동 발견을 끄고 싶다면, 애플리케이션의 composer.json 파일 extra 섹션에 패키지명을 추가하면 됩니다:

"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar"
]
}
},

애플리케이션의 dont-discover 항목에서 * 문자를 사용하면 모든 패키지의 자동 발견을 비활성화할 수도 있습니다:

"extra": {
"laravel": {
"dont-discover": [
"*"
]
}
},

Service Providers​

Service providers는 패키지와 Laravel을 연결해주는 중개자 역할을 합니다. 서비스 프로바이더는 Laravel의 service container에 다양한 객체를 바인딩하고, 패키지의 뷰, 설정, 언어 파일 등 리소스의 위치를 Laravel에 알려주는 책임을 집니다.

서비스 프로바이더는 Illuminate\Support\ServiceProvider 클래스를 확장하며, register와 boot라는 두 가지 메서드를 가집니다. 기본 ServiceProvider 클래스는 illuminate/support Composer 패키지에 포함되어 있으므로, 이를 패키지 의존성에 추가해야 합니다. 서비스 프로바이더의 구조와 목적에 대해 더 자세히 알고 싶다면 their documentation를 참고하세요.

Resources​

Configuration​

일반적으로 패키지의 설정 파일을 애플리케이션의 config 디렉터리에 퍼블리시해주어야 합니다. 이를 통해 패키지 사용자가 기본 설정 값을 쉽게 오버라이드할 수 있습니다. 설정 파일을 퍼블리시할 수 있도록 하려면, 서비스 프로바이더의 boot 메서드에서 publishes 메서드를 호출하세요:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/courier.php' => config_path('courier.php'),
]);
}

이제 패키지 사용자가 Laravel의 vendor:publish 명령어를 실행하면 설정 파일이 지정된 위치에 복사됩니다. 설정 파일이 퍼블리시된 후에는 일반 설정 파일처럼 값을 읽을 수 있습니다:

$value = config('courier.option');

설정 파일 내에 클로저를 정의하지 마세요. 사용자가 config:cache 아티즌 명령어를 실행할 때 올바르게 직렬화되지 않습니다.

Default Package Configuration​

패키지의 설정 파일을 애플리케이션에 퍼블리시된 복사본과 병합(merge)할 수도 있습니다. 이렇게 하면 사용자는 오버라이드하고 싶은 옵션만 설정 파일에 정의해 두면 됩니다. 설정 값 병합은 서비스 프로바이더의 register 메서드에서 mergeConfigFrom 메서드를 사용하면 됩니다.

mergeConfigFrom 메서드는 첫 번째 인수로 패키지 설정 파일 경로, 두 번째 인수로 애플리케이션에서 사용할 설정 이름을 받습니다:

/**
* Register any package services.
*/
public function register(): void
{
$this->mergeConfigFrom(
__DIR__.'/../config/courier.php', 'courier'
);
}

이 메서드는 설정 배열의 1단계까지만 병합합니다. 사용자가 다차원 배열 설정을 부분적으로 정의할 경우, 누락된 옵션은 병합되지 않습니다.

Routes​

패키지에 라우트가 포함되어 있다면, loadRoutesFrom 메서드를 사용하여 로드할 수 있습니다. 이 메서드는 애플리케이션의 라우트가 캐시되어 있다면 라우트 파일을 불러오지 않으므로 효율적입니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}

Migrations​

패키지에 database migrations이 있다면, publishesMigrations 메서드를 사용하여 해당 디렉터리 또는 파일이 마이그레이션임을 Laravel에 알릴 수 있습니다. Laravel에서 마이그레이션을 퍼블리시할 때 파일 이름의 타임스탬프가 현재 날짜와 시간으로 자동 갱신됩니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishesMigrations([
__DIR__.'/../database/migrations' => database_path('migrations'),
]);
}

Language Files​

패키지에 language files이 있다면, loadTranslationsFrom 메서드를 사용하여 Laravel이 이를 어떻게 로드해야 하는지 지정할 수 있습니다. 예를 들어 패키지명이 courier라면, 서비스 프로바이더의 boot 메서드에 다음을 추가합니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
}

패키지 번역 라인은 package::file.line 형식으로 참조할 수 있습니다. 예를 들어, courier 패키지의 messages 파일에서 welcome 라인을 불러오려면 다음과 같이 합니다:

echo trans('courier::messages.welcome');

패키지용 JSON 번역 파일을 등록하려면 loadJsonTranslationsFrom 메서드를 사용할 수 있습니다. 이 메서드는 패키지의 JSON 번역 파일이 들어있는 디렉터리 경로를 받습니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadJsonTranslationsFrom(__DIR__.'/../lang');
}

Publishing Language Files​

패키지의 언어 파일을 애플리케이션의 lang/vendor 디렉터리로 퍼블리시하려면, 서비스 프로바이더의 publishes 메서드를 사용할 수 있습니다. publishes 메서드는 패키지 경로와 퍼블리시 대상 경로의 배열을 받습니다. 예를 들어 courier 패키지의 언어 파일을 퍼블리시하려면 다음과 같이 할 수 있습니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');

$this->publishes([
__DIR__.'/../lang' => $this->app->langPath('vendor/courier'),
]);
}

이제 패키지 사용자가 Laravel의 vendor:publish 아티즌 명령어를 실행하면, 패키지의 언어 파일이 지정된 위치로 퍼블리시됩니다.

Views​

패키지의 views를 Laravel에 등록하려면, 뷰가 있는 위치를 Laravel에 알려주어야 합니다. 이를 위해 서비스 프로바이더의 loadViewsFrom 메서드를 사용합니다. loadViewsFrom은 뷰 템플릿 경로와 패키지명을 인수로 받습니다. 예를 들어 패키지명이 courier이면, boot 메서드에 다음과 같이 추가합니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
}

패키지 뷰는 package::view 문법으로 참조할 수 있습니다. 따라서 뷰 경로가 등록되면 아래와 같이 courier 패키지의 dashboard 뷰를 불러올 수 있습니다:

Route::get('/dashboard', function () {
return view('courier::dashboard');
});

Overriding Package Views​

loadViewsFrom 메서드를 사용하면, Laravel은 실제로 두 개의 뷰 위치를 등록합니다: 애플리케이션의 resources/views/vendor 디렉터리와 직접 지정한 디렉터리입니다. 즉, courier 패키지의 경우, Laravel은 먼저 개발자가 resources/views/vendor/courier 경로에 커스텀 뷰를 넣었는지 확인하고, 없으면 loadViewsFrom 호출에서 지정한 패키지 뷰 디렉터리를 찾습니다. 이를 통해 패키지 사용자는 뷰를 쉽게 사용자 지정하거나 오버라이드할 수 있습니다.

Publishing Views​

패키지의 뷰 파일을 애플리케이션의 resources/views/vendor 디렉터리로 퍼블리시하고 싶다면, 서비스 프로바이더의 publishes 메서드를 사용할 수 있습니다. publishes 메서드는 패키지 뷰 경로와 퍼블리시 대상 경로의 배열을 인수로 받습니다:

/**
* Bootstrap the package services.
*/
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');

$this->publishes([
__DIR__.'/../resources/views' => resource_path('views/vendor/courier'),
]);
}

이제 패키지 사용자가 vendor:publish 명령어를 실행하면, 패키지 뷰가 지정된 위치로 복사됩니다.

View Components​

패키지에서 Blade 컴포넌트를 제공하거나, 컴포넌트를 일반적이지 않은 디렉터리에 둘 경우 컴포넌트 클래스와 해당 HTML 태그 별칭을 수동으로 등록해야 합니다. 보통 서비스 프로바이더의 boot 메서드에서 컴포넌트를 등록합니다:

use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;

/**
* Bootstrap your package's services.
*/
public function boot(): void
{
Blade::component('package-alert', AlertComponent::class);
}

컴포넌트가 등록된 후에는 별칭 태그를 사용하여 렌더링할 수 있습니다:

<x-package-alert/>

Autoloading Package Components​

또는, componentNamespace 메서드를 사용하면 네임스페이스에 따라 컴포넌트 클래스를 자동으로 로드할 수 있습니다. 예를 들어 Nightshade 패키지에 Calendar와 ColorPicker 컴포넌트가 Nightshade\Views\Components 네임스페이스에 있다면 다음과 같이 등록하면 됩니다:

use Illuminate\Support\Facades\Blade;

/**
* Bootstrap your package's services.
*/
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

이렇게 하면 package-name:: 구문을 사용하여 벤더 네임스페이스로 패키지 컴포넌트를 사용할 수 있습니다:

<x-nightshade::calendar />
<x-nightshade::color-picker />

Blade는 컴포넌트 이름을 파스칼 케이스(Pascal Case)로 변환하여 해당 클래스와 자동으로 연결합니다. "dot" 표기법을 통해 하위 디렉터리도 지원됩니다.

Anonymous Components​

패키지에 익명 컴포넌트가 있다면, loadViewsFrom method로 지정한 "views" 디렉터리 하위에 반드시 components 디렉터리를 만들어 그 안에 넣어야 합니다. 그 다음, 패키지의 뷰 네임스페이스를 접두어로 사용하여 렌더링할 수 있습니다:

<x-courier::alert />

"About" Artisan Command​

Laravel의 기본 about 아티즌 명령어는 애플리케이션의 환경과 설정을 요약해서 보여줍니다. 패키지도 추가 정보를 AboutCommand 클래스를 통해 이 명령어의 출력에 추가할 수 있습니다. 일반적으로 이 정보는 패키지 서비스 프로바이더의 boot 메서드에서 추가합니다:

use Illuminate\Foundation\Console\AboutCommand;

/**
* Bootstrap any package services.
*/
public function boot(): void
{
AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']);
}

Commands​

패키지의 아티즌 명령어를 Laravel과 함께 등록하려면, commands 메서드를 사용합니다. 이 메서드는 명령어 클래스명 배열을 인수로 받습니다. 명령어가 등록되면 Artisan CLI를 통해 사용할 수 있습니다:

use Courier\Console\Commands\InstallCommand;
use Courier\Console\Commands\NetworkCommand;

/**
* Bootstrap any package services.
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
InstallCommand::class,
NetworkCommand::class,
]);
}
}

Optimize Commands​

Laravel의 optimize command는 애플리케이션의 설정, 이벤트, 라우트, 뷰를 캐시합니다. 패키지에서도 optimizes 메서드를 사용하여, optimize 또는 optimize:clear 명령어 실행 시 함께 호출될 자체 아티즌 명령어를 등록할 수 있습니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->optimizes(
optimize: 'package:optimize',
clear: 'package:clear-optimizations',
);
}
}

Reload Commands​

Laravel의 reload command는 실행 중인 서비스를 종료하여 시스템 프로세스 모니터가 자동으로 다시 시작할 수 있도록 합니다. 패키지에서 자체적으로 등록해야 할 아티즌 명령어가 있을 경우, reloads 메서드를 사용하여 reload 명령어 실행 시 호출되도록 할 수 있습니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->reloads('package:reload');
}
}

Public Assets​

패키지에는 JavaScript, CSS, 이미지 등의 에셋이 포함될 수 있습니다. 이런 퍼블릭 에셋을 애플리케이션의 public 디렉터리로 퍼블리시하려면, 서비스 프로바이더의 publishes 메서드를 사용합니다. 아래 예시는 public 에셋 그룹 태그도 추가하여 관련 에셋을 그룹 단위로 쉽게 퍼블리시할 수 있도록 했습니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../public' => public_path('vendor/courier'),
], 'public');
}

이제 패키지 사용자가 vendor:publish 명령어를 실행하면, 에셋이 지정된 퍼블리시 위치로 복사됩니다. 보통 패키지 업데이트 시마다 에셋이 덮어써져야 하므로, 사용자는 --force 플래그를 사용할 수 있습니다:

php artisan vendor:publish --tag=public --force

Publishing File Groups​

패키지의 에셋과 리소스를 그룹별로 개별 퍼블리싱하고 싶을 수 있습니다. 예를 들어, 사용자가 설정 파일만 퍼블리시하고 에셋은 퍼블리시하지 않도록 할 수 있습니다. 이를 위해 서비스 프로바이더에서 publishes 메서드 호출 시 "태그"를 걸어 그룹별로 정의할 수 있습니다. 아래는 패키지 서비스 프로바이더의 boot 메서드에서 courier 패키지에 courier-config와 courier-migrations라는 두 퍼블리시 그룹을 정의하는 예시입니다:

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/package.php' => config_path('package.php')
], 'courier-config');

$this->publishesMigrations([
__DIR__.'/../database/migrations/' => database_path('migrations')
], 'courier-migrations');
}

이제 사용자는 아래와 같이 vendor:publish 명령어의 태그를 지정하여 그룹별로 퍼블리시할 수 있습니다:

php artisan vendor:publish --tag=courier-config

또는 --provider 플래그를 사용하여 패키지 서비스 프로바이더가 정의한 모든 퍼블리시 파일을 한 번에 퍼블리시할 수 있습니다:

php artisan vendor:publish --provider="Your\Package\ServiceProvider"