본문 바로가기

Laravel Horizon

번역일: 2026년 6월 20일

Laravel Horizon

소개

NOTE

Laravel Horizon을 사용하기 전에 먼저 Laravel의 기본 큐 서비스에 익숙해지는 것을 권장합니다. Horizon은 Laravel 큐의 확장이므로, 기본 큐 개념을 모르면 Horizon의 기능이 낯설게 느껴질 수 있습니다.

Laravel Horizon은 Laravel의 Redis 큐를 위한 아름다운 대시보드와 코드 기반 설정을 제공합니다. Horizon을 사용하면 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.phpuse 옵션에 동일한 이름을 다른 Redis 커넥션에 사용하면 안 됩니다.

환경 설정

설치 후 가장 먼저 살펴봐야 할 설정은 environments 옵션입니다. 이 옵션은 애플리케이션이 실행될 환경별로 워커 프로세스 옵션을 정의하는 배열입니다. 기본적으로 productionlocal 환경이 포함되어 있으며, 필요에 따라 환경을 추가할 수 있습니다:

'environments' => [
    'production' => [
        'supervisor-1' => [
            'maxProcesses' => 10,
            'balanceMaxShift' => 1,
            'balanceCooldown' => 3,
        ],
    ],

    'local' => [
        'supervisor-1' => [
            'maxProcesses' => 3,
        ],
    ],
],

Horizon이 시작되면 현재 애플리케이션 환경(APP_ENV 환경 변수 값)에 맞는 워커 설정이 적용됩니다. 예를 들어 local 환경에서는 워커 프로세스를 최대 3개 시작하고 큐 간 자동 밸런싱을 적용하며, production 환경에서는 최대 10개의 워커 프로세스를 운용합니다.

WARNING

horizon 설정 파일의 environments 항목에 Horizon을 실행할 모든 환경이 반드시 포함되어야 합니다.

Supervisor

기본 설정 파일에서 볼 수 있듯이, 각 환경에는 하나 이상의 "supervisor"를 정의할 수 있습니다. 기본값은 supervisor-1이지만, 원하는 이름을 자유롭게 사용할 수 있습니다. 각 supervisor는 워커 프로세스 그룹을 관리하고 큐 간의 워커 밸런싱을 담당합니다.

특정 큐에 대해 다른 밸런싱 전략이나 워커 수를 지정하고 싶다면, 해당 환경에 supervisor를 추가하면 됩니다.

점검 모드

애플리케이션이 점검 모드일 때는 기본적으로 Horizon이 큐에 들어온 Job을 처리하지 않습니다. 점검 모드에서도 Job을 계속 처리하려면 해당 supervisor에 force 옵션을 true로 설정하세요:

'environments' => [
    'production' => [
        'supervisor-1' => [
            // ...
            'force' => true,
        ],
    ],
],

기본값

기본 설정 파일에는 defaults 옵션이 있습니다. 이 옵션은 supervisor의 기본값을 지정하며, 각 환경의 supervisor 설정과 병합됩니다. 덕분에 supervisor마다 동일한 설정을 반복하지 않아도 됩니다.

밸런싱 전략

Horizon은 Laravel 기본 큐 시스템과 달리 세 가지 워커 밸런싱 전략을 제공합니다: simple, auto, false.

simple 전략은 들어오는 Job을 워커 프로세스에 균등하게 분배합니다:

'balance' => 'simple',

auto 전략(기본값)은 큐의 현재 부하에 따라 워커 프로세스 수를 자동으로 조정합니다. 예를 들어 notifications 큐에 1,000개의 Job이 쌓여 있고 render 큐가 비어 있다면, Horizon은 notifications 큐가 빌 때까지 해당 큐에 더 많은 워커를 할당합니다.

auto 전략 사용 시 minProcessesmaxProcesses 옵션으로 워커 프로세스의 최솟값과 최댓값을 제어할 수 있습니다:

'environments' => [
    'production' => [
        'supervisor-1' => [
            'connection' => 'redis',
            'queue' => ['default'],
            'balance' => 'auto',
            'autoScalingStrategy' => 'time',
            'minProcesses' => 1,
            'maxProcesses' => 10,
            'balanceMaxShift' => 1,
            'balanceCooldown' => 3,
            'tries' => 3,
        ],
    ],
],

autoScalingStrategy 옵션은 Horizon이 워커 수를 조정할 때 기준으로 삼는 방식을 결정합니다. time 전략은 큐를 모두 처리하는 데 걸리는 총 시간을 기준으로, size 전략은 큐에 쌓인 Job의 총 개수를 기준으로 스케일을 조정합니다.

balanceMaxShiftbalanceCooldown 옵션은 Horizon이 워커 수를 얼마나 빠르게 조정할지를 결정합니다. 위 예시에서는 3초마다 최대 1개의 프로세스가 생성되거나 종료됩니다. 애플리케이션의 특성에 맞게 이 값을 조정하세요.

balance 옵션이 false로 설정되면, Laravel 기본 동작인 설정 파일에 나열된 순서대로 큐를 처리합니다.

대시보드 접근 권한

Horizon 대시보드는 /horizon 라우트로 접근할 수 있습니다. 기본적으로는 local 환경에서만 접근이 허용됩니다. 비로컬 환경에서의 접근은 app/Providers/HorizonServiceProvider.php인가 게이트(authorization gate)로 제어됩니다. 필요에 따라 이 게이트를 수정해 접근 권한을 제한할 수 있습니다:

/** * Horizon 게이트를 등록합니다. * * 이 게이트는 비로컬 환경에서 Horizon에 접근할 수 있는 사용자를 결정합니다. */ protected function gate(): void { Gate::define('viewHorizon', function (User $user) { return in_array($user->email, [ 'admin@example.com', ]); }); }

다른 인증 방식 사용

Laravel은 게이트 클로저에 인증된 사용자를 자동으로 주입합니다. IP 제한 등 다른 방식으로 Horizon 접근을 제어하는 경우, 사용자가 로그인하지 않아도 대시보드에 접근할 수 있어야 할 수 있습니다. 이 경우 위의 function (User $user) 시그니처를 function (User $user = null)로 변경하여 Laravel이 인증을 강제하지 않도록 하세요.

무시할 Job 설정

특정 Job이 "완료된 Job" 목록에 표시되지 않도록 숨기고 싶을 때는, horizon 설정 파일의 silenced 옵션에 해당 Job 클래스명을 추가하세요:

'silenced' => [
    App\Jobs\ProcessPodcast::class,
],

또는 해당 Job 클래스에서 Laravel\Horizon\Contracts\Silenced 인터페이스를 구현하면, 설정 파일에 별도로 추가하지 않아도 자동으로 무시됩니다:

use Laravel\Horizon\Contracts\Silenced; class ProcessPodcast implements ShouldQueue, Silenced { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // ... }

Horizon 업그레이드

Horizon의 새로운 메이저 버전으로 업그레이드할 때는 업그레이드 가이드를 꼼꼼히 확인하세요. 또한 어떤 버전으로 업그레이드하든 애셋을 반드시 다시 퍼블리시해야 합니다:

php artisan horizon:publish

애셋을 항상 최신 상태로 유지하려면 composer.jsonpost-update-cmd 스크립트에 다음 명령어를 추가하세요:

{ "scripts": { "post-update-cmd": [ "@php artisan vendor:publish --tag=laravel-assets --ansi --force" ] } }

Horizon 실행

config/horizon.php에서 supervisor와 워커 설정을 마쳤다면, horizon Artisan 명령어로 Horizon을 시작할 수 있습니다. 이 명령어 하나로 현재 환경에 맞는 모든 워커 프로세스가 시작됩니다:

php artisan horizon

Horizon 프로세스를 일시 중지하거나 재개하려면 다음 명령어를 사용하세요:

php artisan horizon:pausephp artisan horizon:continue

특정 supervisor만 일시 중지하거나 재개할 수도 있습니다:

php artisan horizon:pause-supervisor supervisor-1php artisan horizon:continue-supervisor supervisor-1

현재 Horizon 프로세스의 상태를 확인하려면:

php artisan horizon:status

Horizon 프로세스를 안전하게 종료하려면 horizon:terminate 명령어를 사용하세요. 현재 처리 중인 Job을 모두 완료한 뒤 종료됩니다:

php artisan horizon:terminate

Horizon 배포

실제 서버에 Horizon을 배포할 때는 프로세스 모니터를 설정하여 php artisan horizon 명령어가 예기치 않게 종료될 경우 자동으로 재시작되도록 해야 합니다.

배포 과정에서는 코드 변경 사항을 반영하기 위해 Horizon 프로세스를 종료해야 합니다. 프로세스 모니터가 자동으로 재시작해 줍니다:

php artisan horizon:terminate

Supervisor 설치

Supervisor는 Linux 운영 체제의 프로세스 모니터로, horizon 프로세스가 중단되면 자동으로 재시작합니다. Ubuntu에서는 다음 명령어로 설치할 수 있습니다:

sudo apt-get install supervisor

NOTE

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=3600

stopwaitsecs 값은 가장 오래 실행되는 Job의 처리 시간(초)보다 크게 설정해야 합니다. 그렇지 않으면 Supervisor가 Job 처리가 끝나기 전에 프로세스를 강제 종료할 수 있습니다.

WARNING

위 예시는 Ubuntu 기반 서버 기준입니다. 다른 운영 체제에서는 설정 파일의 위치와 확장자가 다를 수 있으니 해당 서버의 문서를 참고하세요.

Supervisor 시작

설정 파일을 생성한 후 다음 명령어로 Supervisor 설정을 갱신하고 프로세스를 시작합니다:

sudo supervisorctl rereadsudo supervisorctl updatesudo supervisorctl start horizon

NOTE

Supervisor에 대한 더 자세한 내용은 Supervisor 공식 문서를 참고하세요.

태그

Horizon은 메일러블, 브로드캐스트 이벤트, 알림, 큐 이벤트 리스너 등 다양한 Job에 "태그"를 붙일 수 있습니다. 실제로 Horizon은 Job에 연결된 Eloquent 모델을 감지해 대부분의 Job에 태그를 자동으로 부여합니다. 다음 Job을 예로 살펴보겠습니다:

<?php namespace App\Jobs; use App\Models\Video; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class RenderVideo implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * 새로운 Job 인스턴스 생성 */ public function __construct( public Video $video, ) {} /** * Job 실행 */ public function handle(): void { // ... } }

이 Job이 id1App\Models\Video 인스턴스와 함께 큐에 추가되면, Horizon은 자동으로 App\Models\Video:1 태그를 부여합니다. Horizon은 Job의 프로퍼티에서 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

Horizon에서 Slack 또는 SMS 알림을 설정하기 전에 해당 알림 채널의 사전 요구 사항을 확인하세요.

큐의 대기 시간이 길어질 때 알림을 받고 싶다면, App\Providers\HorizonServiceProviderboot 메서드에서 Horizon::routeMailNotificationsTo, Horizon::routeSlackNotificationsTo, Horizon::routeSmsNotificationsTo 메서드를 사용하세요:

/** * 애플리케이션 서비스 부트스트랩 */ public function boot(): void { parent::boot(); Horizon::routeSmsNotificationsTo('15556667777'); Horizon::routeMailNotificationsTo('example@example.com'); Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel'); }

알림 대기 시간 임계값 설정

"오래 대기 중"으로 간주할 시간(초)은 config/horizon.phpwaits 옵션에서 설정합니다. 커넥션/큐 조합별로 임계값을 지정할 수 있으며, 정의되지 않은 조합의 기본값은 60초입니다:

'waits' => [
    'redis:critical' => 30,
    'redis:default' => 60,
    'redis:batch' => 120,
],

메트릭

Horizon의 메트릭 대시보드는 Job과 큐의 대기 시간 및 처리량 정보를 제공합니다. 대시보드에 데이터를 채우려면 애플리케이션의 스케줄러에서 horizon:snapshot Artisan 명령어를 5분마다 실행하도록 설정해야 합니다:

/** * 애플리케이션의 커맨드 스케줄 정의 */ protected function schedule(Schedule $schedule): void { $schedule->command('horizon:snapshot')->everyFiveMinutes(); }

실패한 Job 삭제

실패한 Job을 삭제하려면 horizon:forget 명령어를 사용하세요. 실패한 Job의 ID 또는 UUID를 인수로 전달합니다:

php artisan horizon:forget 5

큐에서 Job 비우기

기본 큐의 모든 Job을 삭제하려면 horizon:clear Artisan 명령어를 사용하세요:

php artisan horizon:clear

특정 큐의 Job만 삭제하려면 queue 옵션을 지정하세요:

php artisan horizon:clear --queue=emails

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 6월 20일