패키지 개발
번역일: 2026년 6월 20일
패키지 개발
소개
패키지는 Laravel에 기능을 추가하는 가장 핵심적인 방법입니다. Carbon처럼 날짜 처리를 편리하게 해주는 것부터, Spatie의 Laravel Media Library처럼 Eloquent 모델에 파일을 연결해주는 것까지 패키지의 형태는 매우 다양합니다.
패키지는 크게 두 종류로 나뉩니다. 첫 번째는 독립형(stand-alone) 패키지로, 특정 PHP 프레임워크에 종속되지 않고 어디서든 사용할 수 있습니다. Carbon이나 Pest가 이에 해당하며, composer.json에 추가하는 것만으로 Laravel 프로젝트에서도 활용할 수 있습니다.
두 번째는 Laravel 전용 패키지입니다. 이 패키지들은 라우트, 컨트롤러, 뷰, 설정 파일 등을 포함하며 Laravel 애플리케이션에 특화된 기능을 제공합니다. 이 가이드에서는 주로 이런 Laravel 전용 패키지를 개발하는 방법을 다룹니다.
파사드에 대한 참고 사항
일반적인 Laravel 애플리케이션을 개발할 때는 컨트랙트(contract)와 파사드(facade) 중 무엇을 사용하든 테스트 가능성 면에서 큰 차이가 없습니다. 그러나 패키지를 개발할 때는 다릅니다. 패키지는 Laravel의 테스트 헬퍼 전체에 접근하기 어려운 환경에서 동작하기 때문입니다.
패키지를 마치 실제 Laravel 애플리케이션에 설치된 것처럼 테스트하고 싶다면 Orchestral Testbench 패키지를 활용하세요.
패키지 자동 감지
Laravel 애플리케이션의 bootstrap/providers.php 파일에는 로드할 서비스 프로바이더 목록이 정의되어 있습니다. 하지만 패키지 사용자가 이 파일을 직접 수정하지 않아도 되도록, 패키지의 composer.json 파일의 extra 섹션에 서비스 프로바이더를 선언하면 Laravel이 자동으로 등록해 줍니다. 서비스 프로바이더 외에도 등록할 파사드가 있다면 함께 선언할 수 있습니다.
"extra": {
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
}
},이렇게 설정해두면 패키지가 composer require로 설치될 때 Laravel이 서비스 프로바이더와 파사드를 자동으로 등록하므로, 사용자 입장에서 설치 경험이 훨씬 편리해집니다.
패키지 자동 감지 비활성화
패키지 사용자 입장에서 특정 패키지의 자동 감지를 비활성화하고 싶다면, 애플리케이션의 composer.json 파일의 extra 섹션에 해당 패키지 이름을 추가하면 됩니다.
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar"
]
}
},* 문자를 사용하면 모든 패키지의 자동 감지를 한 번에 비활성화할 수 있습니다.
"extra": {
"laravel": {
"dont-discover": [
"*"
]
}
},서비스 프로바이더
서비스 프로바이더는 패키지와 Laravel을 연결하는 핵심 진입점입니다. 서비스 프로바이더는 서비스 컨테이너에 필요한 것들을 바인딩하고, 뷰·설정·언어 파일 등 패키지 리소스의 위치를 Laravel에 알려주는 역할을 합니다.
서비스 프로바이더는 Illuminate\Support\ServiceProvider 클래스를 상속하며, register와 boot 두 메서드를 가집니다. 기반이 되는 ServiceProvider 클래스는 illuminate/support Composer 패키지에 포함되어 있으므로, 개발 중인 패키지의 의존성에 이를 추가해야 합니다. 서비스 프로바이더의 구조와 역할에 대한 자세한 내용은 서비스 프로바이더 문서를 참고하세요.
리소스
설정
패키지의 설정 파일을 애플리케이션의 config 디렉토리에 퍼블리시할 수 있게 하면, 사용자가 기본 설정 값을 손쉽게 덮어쓸 수 있습니다. 서비스 프로바이더의 boot 메서드에서 publishes 메서드를 호출하면 됩니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/courier.php' => config_path('courier.php'),
]);
}이제 사용자가 vendor:publish 커맨드를 실행하면 설정 파일이 지정한 경로에 복사됩니다. 퍼블리시된 설정 파일은 일반 설정 파일과 동일한 방식으로 접근할 수 있습니다.
$value = config('courier.option');WARNING
설정 파일 안에 클로저(익명 함수)를 정의하지 마세요. 사용자가 config:cache Artisan 커맨드를 실행할 때 클로저는 올바르게 직렬화되지 않습니다.
패키지 기본 설정 병합
패키지의 기본 설정 파일과 애플리케이션에 퍼블리시된 설정 파일을 병합할 수도 있습니다. 이렇게 하면 사용자는 퍼블리시된 설정 파일에서 변경하고 싶은 항목만 정의하면 되며, 나머지는 패키지의 기본값이 자동으로 적용됩니다. 이를 위해 서비스 프로바이더의 register 메서드에서 mergeConfigFrom 메서드를 사용합니다.
mergeConfigFrom의 첫 번째 인자는 패키지 설정 파일의 경로이고, 두 번째 인자는 애플리케이션 설정 파일의 이름입니다.
/**
* 애플리케이션 서비스를 등록합니다.
*/
public function register(): void
{
$this->mergeConfigFrom(
__DIR__.'/../config/courier.php', 'courier'
);
}WARNING
이 메서드는 설정 배열의 첫 번째 레벨만 병합합니다. 다차원 배열의 일부만 사용자가 정의한 경우, 누락된 하위 옵션은 자동으로 채워지지 않으니 주의하세요.
라우트
패키지에 라우트가 포함되어 있다면 loadRoutesFrom 메서드로 불러올 수 있습니다. 이 메서드는 애플리케이션의 라우트가 이미 캐시되어 있는지 자동으로 확인하며, 캐시된 경우 라우트 파일을 다시 로드하지 않습니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}마이그레이션
패키지에 데이터베이스 마이그레이션이 포함되어 있다면 publishesMigrations 메서드를 사용해 해당 디렉토리나 파일이 마이그레이션을 담고 있음을 Laravel에 알릴 수 있습니다. Laravel이 마이그레이션을 퍼블리시할 때, 파일명의 타임스탬프를 현재 날짜와 시간으로 자동 갱신해 줍니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
$this->publishesMigrations([
__DIR__.'/../database/migrations' => database_path('migrations'),
]);
}언어 파일
패키지에 언어 파일이 포함되어 있다면 loadTranslationsFrom 메서드로 로드 방법을 Laravel에 알려줄 수 있습니다. 예를 들어 패키지 이름이 courier라면 서비스 프로바이더의 boot 메서드에 다음과 같이 추가합니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
$this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
}패키지 번역 문자열은 패키지명::파일명.키 형식으로 참조합니다. 예를 들어 courier 패키지의 messages 파일에 있는 welcome 문자열은 다음과 같이 불러옵니다.
echo trans('courier::messages.welcome');JSON 형식의 번역 파일을 사용한다면 loadJsonTranslationsFrom 메서드에 해당 디렉토리 경로를 전달하세요.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
$this->loadJsonTranslationsFrom(__DIR__.'/../lang');
}언어 파일 퍼블리싱
패키지의 언어 파일을 애플리케이션의 lang/vendor 디렉토리에 퍼블리시하려면 서비스 프로바이더의 publishes 메서드를 사용합니다. publishes 메서드는 패키지 경로와 퍼블리시 대상 경로를 배열로 받습니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
$this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
$this->publishes([
__DIR__.'/../lang' => $this->app->langPath('vendor/courier'),
]);
}이제 사용자가 vendor:publish Artisan 커맨드를 실행하면 언어 파일이 지정한 위치에 복사됩니다.
뷰
패키지의 뷰를 Laravel에 등록하려면 뷰 파일이 어디에 있는지 알려줘야 합니다. 서비스 프로바이더의 loadViewsFrom 메서드를 사용하며, 첫 번째 인자는 뷰 템플릿 경로, 두 번째 인자는 패키지 이름입니다. 예를 들어 패키지 이름이 courier라면 다음과 같이 작성합니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
}패키지 뷰는 패키지명::뷰명 형식으로 참조합니다. 서비스 프로바이더에 경로가 등록된 후, courier 패키지의 dashboard 뷰는 다음과 같이 불러올 수 있습니다.
Route::get('/dashboard', function () {
return view('courier::dashboard');
});패키지 뷰 오버라이딩
loadViewsFrom을 호출하면 Laravel은 내부적으로 두 곳을 뷰 탐색 경로로 등록합니다. 애플리케이션의 resources/views/vendor 디렉토리와 메서드에 전달한 패키지 경로입니다. courier 패키지를 예로 들면, 뷰를 렌더링할 때 Laravel은 먼저 resources/views/vendor/courier에 커스텀 뷰가 있는지 확인하고, 없으면 패키지 내부의 뷰 디렉토리를 탐색합니다. 이 구조 덕분에 사용자는 패키지 뷰를 손쉽게 커스터마이징할 수 있습니다.
뷰 퍼블리싱
사용자가 뷰를 직접 수정할 수 있도록 resources/views/vendor 디렉토리에 퍼블리시 가능하게 하려면 서비스 프로바이더의 publishes 메서드를 사용합니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
$this->publishes([
__DIR__.'/../resources/views' => resource_path('views/vendor/courier'),
]);
}이제 사용자가 vendor:publish Artisan 커맨드를 실행하면 패키지의 뷰 파일이 지정한 위치에 복사됩니다.
뷰 컴포넌트
Blade 컴포넌트를 포함하는 패키지를 개발하거나 컴포넌트를 일반적이지 않은 디렉토리에 배치하는 경우, 컴포넌트 클래스와 HTML 태그 별칭을 수동으로 등록해야 합니다. 일반적으로 패키지 서비스 프로바이더의 boot 메서드에서 등록합니다.
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Blade::component('package-alert', AlertComponent::class);
}컴포넌트가 등록되면 태그 별칭을 사용해 렌더링할 수 있습니다.
<x-package-alert/>패키지 컴포넌트 자동 로드
componentNamespace 메서드를 사용하면 네임스페이스 규칙에 따라 컴포넌트 클래스를 자동으로 로드할 수 있습니다. 예를 들어 Nightshade 패키지에 Nightshade\Views\Components 네임스페이스 아래 Calendar와 ColorPicker 컴포넌트가 있다면 다음과 같이 등록합니다.
use Illuminate\Support\Facades\Blade;
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}이렇게 하면 패키지명:: 구문으로 패키지 컴포넌트를 사용할 수 있습니다.
<x-nightshade::calendar />
<x-nightshade::color-picker />Blade는 컴포넌트 이름을 파스칼 케이스(PascalCase)로 변환하여 해당 클래스를 자동으로 찾습니다. 서브디렉토리는 "점(dot)" 표기법으로 지원됩니다.
익명 컴포넌트
패키지에 익명 컴포넌트(클래스 없이 Blade 파일만 존재하는 컴포넌트)가 있다면, loadViewsFrom 메서드에서 지정한 뷰 디렉토리 내의 components 폴더에 배치해야 합니다. 그러면 패키지의 뷰 네임스페이스를 접두사로 붙여 렌더링할 수 있습니다.
<x-courier::alert />"About" Artisan 커맨드
Laravel에 내장된 about Artisan 커맨드는 애플리케이션의 환경과 설정 요약을 보여줍니다. 패키지는 AboutCommand 클래스를 통해 이 출력에 추가 정보를 삽입할 수 있습니다. 일반적으로 패키지 서비스 프로바이더의 boot 메서드에서 추가합니다.
use Illuminate\Foundation\Console\AboutCommand;
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']);
}커맨드
패키지의 Artisan 커맨드를 Laravel에 등록하려면 commands 메서드를 사용합니다. 이 메서드는 커맨드 클래스 이름의 배열을 받으며, 등록된 커맨드는 Artisan CLI를 통해 실행할 수 있습니다.
use Courier\Console\Commands\InstallCommand;
use Courier\Console\Commands\NetworkCommand;
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
InstallCommand::class,
NetworkCommand::class,
]);
}
}최적화 커맨드
Laravel의 optimize 커맨드는 애플리케이션의 설정, 이벤트, 라우트, 뷰를 캐시합니다. optimizes 메서드를 사용하면 optimize 및 optimize:clear 커맨드가 실행될 때 함께 호출될 패키지 전용 Artisan 커맨드를 등록할 수 있습니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->optimizes(
optimize: 'package:optimize',
clear: 'package:clear-optimizations',
);
}
}퍼블릭 에셋
패키지에 JavaScript, CSS, 이미지 같은 에셋이 포함되어 있다면 서비스 프로바이더의 publishes 메서드를 통해 애플리케이션의 public 디렉토리에 퍼블리시할 수 있습니다. 아래 예시에서는 관련 에셋을 쉽게 묶어서 퍼블리시할 수 있도록 public 태그도 함께 지정합니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../public' => public_path('vendor/courier'),
], 'public');
}이제 사용자가 vendor:publish 커맨드를 실행하면 에셋이 지정한 위치에 복사됩니다. 패키지가 업데이트될 때마다 에셋을 덮어써야 하는 경우가 많으므로 --force 플래그를 활용할 수 있습니다.
php artisan vendor:publish --tag=public --force파일 그룹 퍼블리싱
패키지의 에셋과 리소스를 그룹별로 나눠 퍼블리시하고 싶을 때가 있습니다. 예를 들어 사용자가 에셋을 퍼블리시하지 않고도 설정 파일만 선택적으로 퍼블리시할 수 있게 하고 싶을 수 있습니다. 이를 위해 publishes 메서드 호출 시 태그를 지정합니다. 아래 예시는 courier 패키지의 서비스 프로바이더 boot 메서드에서 courier-config와 courier-migrations 두 가지 퍼블리시 그룹을 정의하는 코드입니다.
/**
* 패키지 서비스를 부트스트랩합니다.
*/
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