[LARAVEL] 큐 & 스케줄러

김세연·2025년 9월 23일

Laravel

목록 보기
12/14
post-thumbnail

큐 & 스케줄러 (Queues & Scheduler)

큐 (Queues - 백그라운드 작업)

바쁜 레스토랑의 '주문 접수대와 주방'과 같다.
손님(사용자)이 주문(요청)을 하면, 웨이터는 주문표를 주방에 전달하고 바로 다른 손님을 응대하러 간다.
주방(백그라운드)에서는 요리사들(워커)이 주문표 순서대로 요리(작업)를 만든다.
이 덕분에 손님은 요리가 완성될 때까지 카운터에서 하염없이 기다릴 필요가 없다.

스케줄러 (Scheduler - 주기적인 작업)

정원의 '자동 스프링클러' 시스템과 같다.
"매일 아침 6시에 10분간 물을 뿌려라" 라고 한 번만 설정해두면, 사람이 없어도 매일 정해진 시간에 알아서 작업을 수행한다.


큐 (Queues) - 오래 걸리는 작업은 나중에

사용자가 회원가입을 했을 때 환영 이메일을 보내는 상황을 생각해 보면 이메일 서버가 느리면 사용자는 이메일이 발송될 때까지 몇 초간 '로딩 중' 화면만 봐야 한다.
큐는 이런 문제를 해결한다.

동작 방식

  • 작업(Job) 클래스 생성:
    큐에서 처리할 작업을 정의하는 클래스를 만든다.
php artisan make:job SendWelcomeEmail
  • 작업 내용 작성 (app/Jobs/SendWelcomeEmail.php):
    handle 메서드 안에 실제로 수행할 로직(이메일 발송 등)을 작성한다.
public function handle()
{
    // 시간이 오래 걸릴 수 있는 이메일 발송 로직
    Mail::to($this->user->email)->send(new WelcomeEmail($this->user));
}
  • 컨트롤러에서 작업 전달 (Dispatch):
    컨트롤러에서는 이 작업을 직접 실행하는 대신, 큐에 보내기만 한다.
public function register(Request $request)
{
    // ... 사용자 생성 로직 ...

    // 이메일 발송 작업을 직접 하지 않고 큐에 맡김
    SendWelcomeEmail::dispatch($newUser);

    // 사용자는 즉시 "가입 완료!" 메시지를 보게 됨
    return redirect('/welcome');
}
  • 워커(Worker) 실행:
    터미널에서 워커(일꾼) 프로세스를 실행시켜 둔다.
    이 워커가 큐에 들어온 작업들을 하나씩 꺼내서 백그라운드에서 처리한다.
php artisan queue:work

이렇게 하면 사용자는 이메일 발송 시간과 상관없이 즉시 응답을 받게 되어 사용자 경험이 크게 향상된다.


스케줄러 (Scheduler) - 반복 작업 자동화

"매일 자정마다 활동이 없는 사용자를 휴면 계정으로 전환"하는 등의 반복 작업을 자동화한다.

동작 방식

라라벨 스케줄러를 사용하면, 서버에 복잡한 Cron 설정을 여러 개 할 필요 없이, 단 하나의 Cron 설정만으로 모든 주기적인 작업을 관리할 수 있다.

  • 작업 로직 정의 (커맨드 생성):
    스케줄러가 실행할 로직을 보통 커맨드(Command) 클래스로 만든다.
php artisan make:command DeactivateDormantUsers
  • 스케줄 정의 (app/Console/Kernel.php):
    schedule 메서드 안에 어떤 작업을 언제, 얼마나 자주 실행할지 PHP 코드로 우아하게 정의한다.
protected function schedule(Schedule $schedule)
{
    // DeactivateDormantUsers 커맨드를 매일 자정에 실행
    $schedule->command('users:deactivate-dormant')->daily();

    // 매주 월요일 오전 8시에 리포트 이메일 발송
    $schedule->command('reports:send')->weekly()->mondays()->at('8:00');

    // 매시간 5분마다 캐시 정리
    $schedule->command('cache:clear-custom')->cron('5 * * * *');
}
  • 서버에 Cron 설정 (단 한 번!):
    서버의 crontab에 다음 한 줄만 추가해두면, 라라벨 스케줄러가 매분마다 Kernel.php를 확인하여 실행할 작업이 있는지 검사하고 알아서 실행한다.
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

큐 (Queues) 심화

큐 드라이버 (Queue Drivers) - 어떤 창고에 보관할까?

큐에 작업을 보낼 때, 그 작업은 잠시 동안 어딘가에 저장되어야 한다.
이 '저장소'를 드라이버라고 하며, 어떤 드라이버를 선택하느냐에 따라 시스템의 성능과 안정성이 크게 달라진다.

  • sync (동기):
    큐를 사용하지 않는 것과 같다.
    즉시 작업을 처리하므로 개발 환경에서 테스트용으로만 사용한다.

  • database:
    작업을 데이터베이스 테이블에 저장한다.
    설정이 매우 간단하지만, 작업이 많아지면 DB에 부하를 줄 수 있다.
    중소 규모 애플리케이션에 적합하다.

  • redis:
    Redis 메모리 서버에 작업을 저장한다.
    매우 빠르고 안정적이어서 실제 운영 환경에서 가장 널리 사용되는 방식이다.

  • SQS:
    아마존 웹 서비스(AWS)의 SQS 서비스를 사용한다.
    대규모 분산 시스템에 적합하다.

config/queue.php.env 파일에서 어떤 드라이버를 사용할지 설정할 수 있다.

실패한 작업 (Failed Jobs) 처리 - 응급 조치

만약 큐에서 실행되던 작업이 예외를 만나 실패하면 라라벨은 이 실패한 작업을 버리지 않고, failed_jobs라는 데이터베이스 테이블에 자동으로 기록해 둔다.

  • 실패한 작업 확인:
    php artisan queue:failed 명령어로 실패한 작업 목록을 볼 수 있다.

  • 실패한 작업 재시도:
    php artisan queue:retry [id] 명령어로 특정 실패 작업을 다시 큐에 넣어 재시도할 수 있다.
    버그를 수정한 후 사용하면 매우 유용하다.

우선순위 큐 (Prioritization)

어떤 작업은 다른 작업보다 더 중요할 수 있다.
(예: '비밀번호 재설정 이메일' > '주간 뉴스레터 발송') 큐에 우선순위를 두어 중요한 작업을 먼저 처리하게 할 수 있다.

  • 구현 방법:
    작업을 보낼 때, 다른 큐 이름을 지정한다.
SendPasswordResetEmail::dispatch($user)->onQueue('high');
SendNewsletter::dispatch()->onQueue('low');

워커를 실행할 때, 처리할 큐의 순서를 지정한다.

# high 큐를 먼저 확인하고, 없으면 low 큐를 처리
php artisan queue:work --queue=high,low

작업 체이닝 & 배치 (Job Chaining & Batches) - 협업 시스템

  • 체이닝 (Chaining):
    하나의 작업이 성공적으로 끝나면, 그 다음 작업을 이어서 실행하도록 묶는 기능이다.
    "동영상 인코딩이 끝나면, 사용자에게 알림을 보내라"와 같은 순차적인 작업에 유용하다.

  • 배치 (Batches):
    여러 작업을 하나의 묶음(배치)으로 실행하고, 모든 작업이 성공적으로 끝났을 때 또는 중간에 하나라도 실패했을 때 특정 로직을 실행하는 기능이다.
    "100개의 CSV 파일을 모두 처리한 후에, 최종 리포트를 생성해라"와 같은 시나리오에 완벽하다.


스케줄러 (Scheduler) 심화

중복 실행 방지 (withoutOverlapping) - 한 번에 한 명씩만

어떤 스케줄 작업이 예상보다 오래 걸려서(예: 5분마다 실행되는데 7분이 걸림), 이전 작업이 끝나기도 전에 다음 작업이 시작되는 경우가 있다.
이는 심각한 데이터 오류를 유발할 수 있다.

withoutOverlapping()을 사용하면, 이전 작업이 아직 실행 중일 경우 새로운 작업을 시작하지 않도록 막아주는 '잠금(Lock)' 기능이 활성화된다.

$schedule->command('reports:generate')->everyFiveMinutes()->withoutOverlapping();

단일 서버에서 실행 (onOneServer) - 대표 한 명만

여러 대의 서버로 로드 밸런싱을 하는 환경에서는, 모든 서버가 동시에 스케줄러를 실행하여 동일한 작업이 여러 번 실행되는 문제가 발생할 수 있다.

onOneServer()를 사용하면, 여러 서버 중 오직 한 대의 서버에서만 해당 스케줄 작업이 실행되도록 보장한다.

점검 모드에서 실행 (evenInMaintenanceMode)

기본적으로 php artisan down으로 사이트를 점검 모드로 전환하면 스케줄러는 동작하지 않는다.
하지만 서버 상태 점검이나 데이터 백업처럼 점검 중에도 반드시 실행되어야 하는 작업이 있을 수 있다.
이때 evenInMaintenanceMode()를 추가하면 된다.

작업 후크 (Task Hooks) - 전/후 처리

스케줄 작업이 실행되기 전이나 후에 특정 로직을 실행하고 싶을 때가 있다.
예를 들어, 작업 시작과 끝을 슬랙으로 알리는 경우이다.

$schedule->command('backup:run')
         ->daily()
         ->before(function () {
             // 작업 시작 전 로직
             Log::info('데이터베이스 백업을 시작합니다...');
         })
         ->after(function () {
             // 작업 성공 후 로직
             Log::info('데이터베이스 백업이 성공적으로 완료되었습니다.');
         });

마지막 한 걸음

큐 관리 시스템: Laravel Horizon (호라이즌)

큐는 백그라운드에서 조용히 동작하기 때문에, 지금 얼마나 많은 작업이 대기 중인지, 실패한 작업은 없는지, 워커는 잘 작동하는지 눈으로 확인하기 어렵다.
Laravel Horizon은 이러한 큐 시스템을 위한 아름다운 대시보드이자 강력한 관리 도구이다.

  • 역할:
    Horizon을 설치하면 웹 브라우저를 통해 실시간으로 큐의 상태를 모니터링할 수 있습니다.
  • 주요 기능:
    • 실시간 작업 현황: 현재 처리 중인 작업, 대기 중인 작업, 실패한 작업 수를 한눈에 파악.
    • 성능 지표: 작업 처리량, 평균 대기 시간 등 성능 관련 지표 제공.
    • 실패한 작업 관리: 실패한 작업을 검색하고 대시보드에서 바로 재시도.

실제 운영 환경에서 큐를 안정적으로 사용하려면 Horizon은 거의 필수적인 도구이다.

비동기적으로 생각하기 (Thinking Asynchronously)

가장 중요한 것은 개발자의 사고방식 전환이다.
어떤 기능을 구현할 때 항상 "이 작업이 지금 당장 끝나야만 하는가?"라고 질문하는 습관을 들이는 것이다.

  • 사용자 프로필 업데이트 → 즉시 처리 (동기)

  • 업데이트 후 환영 이메일 발송 → 나중에 처리 (비동기, 큐)

  • 매월 말 사용자 활동 리포트 생성 → 예약 처리 (스케줄러)

이처럼 동기, 비동기, 예약 작업을 적절히 구분하여 설계하는 것이 확장성 있는 애플리케이션을 만드는 핵심이다.


결론

큐와 스케줄러는 라라벨 애플리케이션의 '자율 신경계'와 같다.
우리가 의식하지 않아도 심장이 뛰고 호흡을 하는 것처럼, 이 시스템은 애플리케이션의 핵심적인 백그라운드 프로세스와 반복 작업을 자동으로 처리하여 생명력을 유지한다.

이 두 가지 기둥을 통해 얻는 궁극적인 가치는 다음과 같다.

  • 최상의 사용자 경험:
    사용자는 시간이 오래 걸리는 작업을 기다릴 필요 없이 즉각적인 피드백을 받는다.

  • 최고의 서버 효율성:
    서버는 사용자의 요청을 빠르게 처리하고, 무거운 작업은 부담이 적은 시간에 비동기적으로 수행한다.

profile
공부 재밌따

0개의 댓글