Laravel Horizon
번역일: 2026년 6월 20일
Laravel Horizon
소개
NOTE
Laravel Horizon을 시작하기 전에, Laravel의 기본 큐 서비스를 먼저 익혀두세요. Horizon은 기본 큐 기능 위에 추가 기능을 제공하므로, 큐의 기본 개념을 모르면 혼란스러울 수 있습니다.
Laravel Horizon은 Laravel의 Redis 큐를 위한 아름다운 대시보드와 코드 기반 설정 도구입니다. Job 처리량, 실행 시간, 실패한 Job 등 큐 시스템의 핵심 지표를 한눈에 모니터링할 수 있습니다.
Horizon을 사용하면 큐 워커 관련 설정이 단일 설정 파일 하나에 모두 집약됩니다. 이 파일을 버전 관리 시스템에 포함하면, 배포 시 워커 수나 동작 방식을 코드 변경만으로 손쉽게 조정할 수 있습니다.
설치
WARNING
Laravel Horizon은 반드시 Redis를 큐 드라이버로 사용해야 합니다. config/queue.php 파일에서 큐 연결이 redis로 설정되어 있는지 확인하세요.
Composer로 Horizon을 설치합니다:
composer require laravel/horizon설치 후, horizon:install Artisan 명령어로 필요한 에셋을 퍼블리시합니다:
php artisan horizon:install설정
에셋 퍼블리시가 완료되면 config/horizon.php가 생성됩니다. 이 파일에서 큐 워커 동작 방식을 세부적으로 조정할 수 있으며, 각 옵션마다 설명이 포함되어 있으니 꼼꼼히 살펴보세요.
WARNING
Horizon은 내부적으로 horizon이라는 이름의 Redis 연결을 사용합니다. 이 이름은 예약되어 있으므로, database.php 설정 파일이나 horizon.php의 use 옵션에 다른 Redis 연결 이름으로 사용하지 마세요.
환경 설정 (Environments)
설치 후 가장 먼저 살펴봐야 할 설정은 environments 옵션입니다. 이 옵션은 애플리케이션이 실행될 환경별로 워커 프로세스 동작 방식을 정의하는 배열입니다. 기본적으로 production과 local 환경이 포함되어 있으며, 필요에 따라 환경을 자유롭게 추가할 수 있습니다:
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],일치하는 환경이 없을 경우 사용할 와일드카드 환경(*)도 정의할 수 있습니다:
'environments' => [
// ...
'*' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],Horizon이 시작되면, 현재 APP_ENV 환경 변수 값에 맞는 환경의 워커 설정이 적용됩니다. 예를 들어, 기본 local 환경은 워커 프로세스 3개로 시작하고, production 환경은 최대 10개의 워커를 자동으로 조정합니다.
WARNING
Horizon을 실행할 모든 환경에 대해 horizon 설정 파일의 environments 항목이 반드시 존재해야 합니다.
슈퍼바이저 (Supervisors)
기본 설정 파일을 보면, 각 환경에는 하나 이상의 "슈퍼바이저"가 정의되어 있습니다. 기본값은 supervisor-1이지만 원하는 이름으로 변경할 수 있습니다. 슈퍼바이저는 워커 프로세스 그룹을 감독하고, 큐 간에 프로세스를 균형 있게 분배하는 역할을 합니다.
특정 큐에 다른 밸런싱 전략이나 프로세스 수를 적용하고 싶다면, 해당 환경에 슈퍼바이저를 추가하면 됩니다.
유지보수 모드
애플리케이션이 유지보수 모드로 전환되면, 슈퍼바이저 설정에 force 옵션이 true로 지정되지 않는 한 Horizon은 큐에 쌓인 Job을 처리하지 않습니다:
'environments' => [
'production' => [
'supervisor-1' => [
// ...
'force' => true,
],
],
],기본값 (Default Values)
설정 파일의 defaults 옵션은 슈퍼바이저의 기본값을 지정합니다. 이 기본값은 각 환경의 슈퍼바이저 설정과 병합되므로, 반복적인 설정을 줄일 수 있습니다.
밸런싱 전략
Horizon은 simple, auto, false 세 가지 워커 밸런싱 전략을 제공합니다.
-
simple: 유입되는 Job을 워커 프로세스에 균등하게 분배합니다.'balance' => 'simple', -
auto(기본값): 각 큐의 현재 작업량에 따라 워커 수를 동적으로 조정합니다. 예를 들어notifications큐에 1,000개의 Job이 쌓여 있고render큐가 비어 있다면, Horizon은notifications큐가 처리될 때까지 해당 큐에 더 많은 워커를 배정합니다.auto전략 사용 시,minProcesses와maxProcesses옵션으로 큐당 최소 프로세스 수와 전체 최대 프로세스 수를 제어할 수 있습니다:'environments' => [ 'production' => [ 'supervisor-1' => [ 'connection' => 'redis', 'queue' => ['default'], 'balance' => 'auto', 'autoScalingStrategy' => 'time', 'minProcesses' => 1, 'maxProcesses' => 10, 'balanceMaxShift' => 1, 'balanceCooldown' => 3, 'tries' => 3, ], ], ],autoScalingStrategy옵션은 스케일링 기준을 결정합니다.time은 큐를 모두 처리하는 데 걸리는 총 시간 기준,size는 큐에 쌓인 Job의 수를 기준으로 합니다.balanceMaxShift와balanceCooldown은 스케일링 속도를 제어합니다. 위 예시에서는 3초마다 최대 1개의 프로세스가 추가되거나 제거됩니다. -
false: 설정 파일에 나열된 순서대로 큐를 처리하는 Laravel 기본 동작을 사용합니다.
대시보드 권한
Horizon 대시보드는 /horizon 경로로 접근할 수 있습니다. 기본적으로 local 환경에서만 접근 가능합니다. 비-로컬 환경에서의 접근은 app/Providers/HorizonServiceProvider.php 파일에 정의된 인가 게이트로 제어됩니다. 필요에 따라 이 게이트를 수정하여 접근을 제한하세요:
/**
* Horizon 게이트를 등록합니다.
*
* 이 게이트는 비-로컬 환경에서 Horizon에 접근할 수 있는 사용자를 결정합니다.
*/
protected function gate(): void
{
Gate::define('viewHorizon', function (User $user) {
return in_array($user->email, [
'admin@example.com',
]);
});
}다른 인증 방식 사용 시
IP 제한 등 다른 방법으로 Horizon 접근을 제어하는 경우, 사용자가 별도로 로그인하지 않아도 될 수 있습니다. 이럴 때는 게이트 클로저 시그니처를 function (User $user)에서 function (User $user = null)로 변경하여 Laravel이 인증을 강제하지 않도록 설정하세요.
숨김 처리 Job
특정 Job이 "완료된 Job" 목록에 표시될 필요가 없는 경우, 숨김(silenced) 처리할 수 있습니다. config/horizon.php의 silenced 옵션에 해당 Job 클래스명을 추가하세요:
'silenced' => [
App\Jobs\ProcessPodcast::class,
],또는 Job 클래스에서 Laravel\Horizon\Contracts\Silenced 인터페이스를 구현하면, 설정 파일과 무관하게 자동으로 숨김 처리됩니다:
use Laravel\Horizon\Contracts\Silenced;
class ProcessPodcast implements ShouldQueue, Silenced
{
use Queueable;
// ...
}Horizon 업그레이드
Horizon의 새로운 메이저 버전으로 업그레이드할 때는 업그레이드 가이드를 반드시 꼼꼼히 읽어보세요.
Horizon 실행
config/horizon.php 설정을 마쳤다면, horizon Artisan 명령어로 Horizon을 시작할 수 있습니다. 이 단일 명령어가 현재 환경에 맞는 모든 워커 프로세스를 시작합니다:
php artisan horizon일시 정지 및 재개는 아래 명령어를 사용합니다:
php artisan horizon:pausephp artisan horizon:continue특정 슈퍼바이저만 일시 정지하거나 재개할 수도 있습니다:
php artisan horizon:pause-supervisor supervisor-1php artisan horizon:continue-supervisor supervisor-1현재 Horizon 프로세스 상태 확인:
php artisan horizon:status특정 슈퍼바이저의 상태 확인:
php artisan horizon:supervisor-status supervisor-1현재 처리 중인 Job이 모두 완료된 후 Horizon을 종료하려면 (graceful shutdown):
php artisan horizon:terminateHorizon 배포
실서버에 배포할 때는 php artisan horizon 프로세스를 감시하고, 예기치 않게 종료되면 자동으로 재시작해주는 프로세스 모니터를 설정해야 합니다.
배포 과정에서는 코드 변경사항을 반영하기 위해 Horizon 프로세스를 종료하세요. 프로세스 모니터가 자동으로 재시작해줍니다:
php artisan horizon:terminateSupervisor 설치
Supervisor는 Linux 환경에서 프로세스를 감시하고 자동으로 재시작해주는 도구입니다. Ubuntu에서는 다음 명령어로 설치할 수 있습니다:
sudo apt-get install supervisorNOTE
Supervisor 설정이 번거롭게 느껴진다면 Laravel Forge 사용을 고려해보세요. Forge는 Laravel 프로젝트에 Supervisor를 자동으로 설치하고 설정해줍니다.
Supervisor 설정
Supervisor 설정 파일은 일반적으로 /etc/supervisor/conf.d 디렉토리에 위치합니다. 이 디렉토리에 horizon.conf 파일을 생성하여 Horizon 프로세스를 감시하도록 설정합니다:
[program:horizon]
process_name=%(program_name)s
command=php /home/forge/example.com/artisan horizon
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/horizon.log
stopwaitsecs=3600WARNING
stopwaitsecs 값은 애플리케이션에서 가장 오래 실행되는 Job의 실행 시간(초)보다 반드시 크게 설정해야 합니다. 그렇지 않으면 Supervisor가 Job 처리가 끝나기 전에 프로세스를 강제 종료할 수 있습니다.
WARNING
위 예시는 Ubuntu 기반 서버를 기준으로 합니다. 다른 운영체제에서는 설정 파일 위치나 확장자가 다를 수 있으니, 해당 서버의 문서를 참고하세요.
Supervisor 시작
설정 파일 작성 후, 아래 명령어로 Supervisor 설정을 갱신하고 프로세스를 시작합니다:
sudo supervisorctl rereadsudo supervisorctl updatesudo supervisorctl start horizonNOTE
Supervisor에 대한 자세한 내용은 Supervisor 공식 문서를 참고하세요.
태그
Horizon은 Job, Mailable, 브로드캐스트 이벤트, 알림, 큐 이벤트 리스너에 "태그"를 붙일 수 있습니다. Job에 Eloquent 모델이 포함된 경우, Horizon이 자동으로 태그를 감지하여 붙여줍니다. 다음 예시를 살펴보세요:
<?php
namespace App\Jobs;
use App\Models\Video;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class RenderVideo implements ShouldQueue
{
use Queueable;
/**
* 새 Job 인스턴스를 생성합니다.
*/
public function __construct(
public Video $video,
) {}
/**
* Job을 실행합니다.
*/
public function handle(): void
{
// ...
}
}이 Job을 id가 1인 App\Models\Video 인스턴스와 함께 디스패치하면, Horizon은 자동으로 App\Models\Video:1 태그를 붙입니다. Eloquent 모델의 클래스명과 기본 키를 조합하여 태그를 생성하는 방식입니다:
use App\Jobs\RenderVideo;
use App\Models\Video;
$video = Video::find(1);
RenderVideo::dispatch($video);태그 직접 지정
Job 클래스에 tags 메서드를 정의하면 태그를 직접 지정할 수 있습니다:
class RenderVideo implements ShouldQueue
{
/**
* Job에 부여할 태그 목록을 반환합니다.
*
* @return array<int, string>
*/
public function tags(): array
{
return ['render', 'video:'.$this->video->id];
}
}이벤트 리스너 태그 직접 지정
큐 이벤트 리스너의 경우, Horizon은 tags 메서드에 이벤트 인스턴스를 자동으로 전달합니다. 덕분에 이벤트 데이터를 태그에 활용할 수 있습니다:
class SendRenderNotifications implements ShouldQueue
{
/**
* 리스너에 부여할 태그 목록을 반환합니다.
*
* @return array<int, string>
*/
public function tags(VideoRendered $event): array
{
return ['video:'.$event->video->id];
}
}알림
WARNING
Slack이나 SMS 알림을 설정하기 전에 해당 알림 채널의 사전 요구사항을 확인하세요.
큐의 대기 시간이 길어질 때 알림을 받으려면, App\Providers\HorizonServiceProvider의 boot 메서드에서 아래 메서드를 호출하세요:
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
parent::boot();
Horizon::routeSmsNotificationsTo('15556667777');
Horizon::routeMailNotificationsTo('example@example.com');
Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
}알림 대기 시간 임계값 설정
config/horizon.php의 waits 옵션으로 "대기 시간이 길다"고 판단하는 기준(초)을 연결/큐 조합별로 설정할 수 있습니다. 정의되지 않은 조합은 기본값인 60초가 적용됩니다:
'waits' => [
'redis:critical' => 30,
'redis:default' => 60,
'redis:batch' => 120,
],메트릭
Horizon 대시보드는 Job과 큐의 대기 시간, 처리량 등의 메트릭을 제공합니다. 이 데이터를 대시보드에 채우려면, routes/console.php 파일에서 Horizon의 snapshot 명령어가 5분마다 실행되도록 스케줄을 등록하세요:
use Illuminate\Support\Facades\Schedule;
Schedule::command('horizon:snapshot')->everyFiveMinutes();실패한 Job 삭제
특정 실패 Job을 삭제하려면 horizon:forget 명령어에 해당 Job의 ID 또는 UUID를 전달하세요:
php artisan horizon:forget 5모든 실패한 Job을 한꺼번에 삭제하려면 --all 옵션을 사용하세요:
php artisan horizon:forget --all큐에서 Job 비우기
기본 큐에 쌓인 모든 Job을 삭제하려면 horizon:clear 명령어를 사용하세요:
php artisan horizon:clear특정 큐의 Job만 삭제하려면 --queue 옵션을 지정하세요:
php artisan horizon:clear --queue=emails