프로세스

번역일: 2026년 6월 25일

프로세스

소개

Laravel은 Symfony Process 컴포넌트를 기반으로, 외부 프로세스를 간결하게 실행할 수 있는 API를 제공합니다. 일반적인 사용 사례에 집중한 직관적인 인터페이스를 통해 쉘 명령어 실행, 결과 확인, 테스트 등의 작업을 편리하게 처리할 수 있습니다.

프로세스 실행

프로세스를 실행하려면 Process 파사드의 run 또는 start 메서드를 사용합니다. run은 프로세스가 완료될 때까지 기다리는 동기 방식이고, start는 백그라운드에서 실행하는 비동기 방식입니다. 먼저 동기 방식부터 살펴봅니다.

use Illuminate\Support\Facades\Process; $result = Process::run('ls -la'); return $result->output();

run 메서드는 Illuminate\Contracts\Process\ProcessResult 인스턴스를 반환하며, 실행 결과를 다양한 방법으로 확인할 수 있습니다.

$result = Process::run('ls -la'); $result->successful(); // 성공 여부 $result->failed(); // 실패 여부 $result->exitCode(); // 종료 코드 $result->output(); // 표준 출력 (stdout) $result->errorOutput(); // 표준 에러 (stderr)

예외 발생

종료 코드가 0보다 크면(즉, 프로세스가 실패하면) Illuminate\Process\Exceptions\ProcessFailedException 예외를 던지고 싶을 때는 throw 또는 throwIf 메서드를 사용합니다. 프로세스가 성공했다면 결과 인스턴스를 그대로 반환합니다.

$result = Process::run('ls -la')->throw(); $result = Process::run('ls -la')->throwIf($condition);

프로세스 옵션

프로세스를 실행하기 전에 작업 디렉터리, 타임아웃, 환경 변수 등 다양한 옵션을 설정할 수 있습니다.

작업 디렉터리 지정

path 메서드로 프로세스가 실행될 디렉터리를 지정합니다. 지정하지 않으면 현재 PHP 스크립트의 작업 디렉터리를 상속합니다.

$result = Process::path(__DIR__)->run('ls -la');

표준 입력

input 메서드로 프로세스의 표준 입력(stdin)에 데이터를 전달할 수 있습니다.

$result = Process::input('Hello World')->run('cat');

타임아웃

기본적으로 프로세스는 60초가 지나면 Illuminate\Process\Exceptions\ProcessTimedOutException 예외를 던집니다. timeout 메서드로 이 시간을 조정할 수 있습니다.

$result = Process::timeout(120)->run('bash import.sh');

타임아웃을 완전히 비활성화하려면 forever 메서드를 사용합니다.

$result = Process::forever()->run('bash import.sh');

idleTimeout 메서드는 출력이 전혀 없는 상태로 대기할 수 있는 최대 시간을 초 단위로 지정합니다.

$result = Process::timeout(60)->idleTimeout(30)->run('bash import.sh');

환경 변수

env 메서드로 프로세스에 환경 변수를 전달할 수 있습니다. 시스템에 이미 설정된 환경 변수도 자동으로 상속됩니다.

$result = Process::forever() ->env(['IMPORT_PATH' => __DIR__]) ->run('bash import.sh');

상속된 환경 변수를 제거하고 싶다면 해당 변수의 값을 false로 지정합니다.

$result = Process::forever() ->env(['LOAD_PATH' => false]) ->run('bash import.sh');

TTY 모드

tty 메서드를 사용하면 프로세스의 입출력을 현재 터미널과 직접 연결합니다. Vim, Nano 같은 대화형 프로그램을 실행할 때 유용합니다.

Process::forever()->tty()->run('vim');

프로세스 출력

앞서 살펴본 것처럼, 프로세스 결과에서 output(stdout)과 errorOutput(stderr) 메서드로 출력을 읽을 수 있습니다.

use Illuminate\Support\Facades\Process; $result = Process::run('ls -la'); echo $result->output(); echo $result->errorOutput();

run 메서드의 두 번째 인자로 클로저를 전달하면 실시간으로 출력을 받을 수도 있습니다. 클로저는 출력 유형(stdout 또는 stderr)과 출력 내용을 인자로 받습니다.

$result = Process::run('ls -la', function (string $type, string $output) { echo $output; });

특정 문자열이 출력에 포함되어 있는지 확인할 때는 seeInOutputseeInErrorOutput 메서드를 사용합니다.

if (Process::run('ls -la')->seeInOutput('laravel')) { // ... }

출력 비활성화

출력이 매우 많고 내용을 확인할 필요가 없다면 quietly 메서드로 출력 수집을 비활성화해 메모리를 절약할 수 있습니다.

use Illuminate\Support\Facades\Process; $result = Process::quietly()->run('bash import.sh');

파이프라인

한 프로세스의 출력을 다른 프로세스의 입력으로 연결하고 싶을 때(Unix 파이프와 동일한 개념) pipe 메서드를 사용합니다. 파이프라인의 프로세스는 순차적으로 동기 실행되며, 마지막 프로세스의 결과가 반환됩니다.

use Illuminate\Process\Pipe; use Illuminate\Support\Facades\Process; $result = Process::pipe(function (Pipe $pipe) { $pipe->command('cat example.txt'); $pipe->command('grep -i "laravel"'); }); if ($result->successful()) { // ... }

각 명령어를 커스터마이즈할 필요가 없다면 명령어 배열을 직접 전달해도 됩니다.

$result = Process::pipe([ 'cat example.txt', 'grep -i "laravel"', ]);

파이프라인에서도 두 번째 인자로 클로저를 전달해 실시간 출력을 처리할 수 있습니다.

$result = Process::pipe(function (Pipe $pipe) { $pipe->command('cat example.txt'); $pipe->command('grep -i "laravel"'); }, function (string $type, string $output) { echo $output; });

as 메서드로 파이프라인 내 각 프로세스에 문자열 키를 부여하면, 출력 클로저에서 어느 프로세스의 출력인지 구분할 수 있습니다.

$result = Process::pipe(function (Pipe $pipe) { $pipe->as('first')->command('cat example.txt'); $pipe->as('second')->command('grep -i "laravel"'); })->start(function (string $type, string $output, string $key) { // $key로 어느 프로세스의 출력인지 확인 가능 });

비동기 프로세스

run 메서드가 동기 방식이라면, start 메서드는 프로세스를 백그라운드에서 실행합니다. 프로세스가 실행되는 동안 애플리케이션은 다른 작업을 계속 수행할 수 있습니다. running 메서드로 프로세스가 아직 실행 중인지 확인할 수 있습니다.

$process = Process::timeout(120)->start('bash import.sh'); while ($process->running()) { // 프로세스가 실행되는 동안 다른 작업 수행... } $result = $process->wait();

wait 메서드는 프로세스가 완전히 종료될 때까지 기다린 뒤 결과 인스턴스를 반환합니다.

$process = Process::timeout(120)->start('bash import.sh'); // 다른 작업 수행... $result = $process->wait();

프로세스 ID와 시그널

id 메서드로 운영체제에서 할당한 프로세스 ID를 가져올 수 있습니다.

$process = Process::start('bash import.sh'); return $process->id();

signal 메서드로 실행 중인 프로세스에 시그널을 보낼 수 있습니다. 사용 가능한 시그널 상수 목록은 PHP 공식 문서에서 확인할 수 있습니다.

$process->signal(SIGUSR2);

비동기 프로세스 출력

비동기 프로세스가 실행되는 동안 outputerrorOutput으로 지금까지의 전체 출력을 읽을 수 있습니다. 마지막으로 읽은 이후의 새로운 출력만 가져오려면 latestOutputlatestErrorOutput을 사용합니다.

$process = Process::timeout(120)->start('bash import.sh'); while ($process->running()) { echo $process->latestOutput(); echo $process->latestErrorOutput(); sleep(1); }

start 메서드의 두 번째 인자로 클로저를 전달하면 비동기 프로세스에서도 실시간 출력을 처리할 수 있습니다.

$process = Process::start('bash import.sh', function (string $type, string $output) { echo $output; }); $result = $process->wait();

동시 프로세스

여러 비동기 프로세스를 동시에 실행하고 싶을 때는 프로세스 풀(pool)을 사용합니다. pool 메서드에 클로저를 전달하고, 그 안에서 실행할 프로세스를 정의합니다. start로 풀을 시작한 뒤 running 메서드로 실행 중인 프로세스 컬렉션을 확인할 수 있습니다.

use Illuminate\Process\Pool; use Illuminate\Support\Facades\Process; $pool = Process::pool(function (Pool $pool) { $pool->path(__DIR__)->command('bash import-1.sh'); $pool->path(__DIR__)->command('bash import-2.sh'); $pool->path(__DIR__)->command('bash import-3.sh'); })->start(function (string $type, string $output, int $key) { // 실시간 출력 처리 }); while ($pool->running()->isNotEmpty()) { // 모든 프로세스가 완료될 때까지 대기... } $results = $pool->wait();

wait 메서드는 모든 프로세스가 완료되면 결과 배열을 반환합니다. 각 프로세스의 결과는 키(숫자 인덱스)로 접근합니다.

$results = $pool->wait(); echo $results[0]->output();

편의를 위해 concurrently 메서드를 사용하면 풀을 시작하고 바로 결과를 기다릴 수 있습니다. PHP의 배열 구조 분해와 조합하면 더욱 간결하게 작성할 수 있습니다.

[$first, $second, $third] = Process::concurrently(function (Pool $pool) { $pool->path(__DIR__)->command('ls -la'); $pool->path(app_path())->command('ls -la'); $pool->path(storage_path())->command('ls -la'); }); echo $first->output();

풀 프로세스에 이름 지정

숫자 인덱스로 결과에 접근하는 것은 가독성이 떨어집니다. as 메서드로 각 프로세스에 문자열 키를 부여하면 이름으로 결과를 조회할 수 있으며, 출력 클로저에서도 어느 프로세스의 출력인지 구분할 수 있습니다.

$pool = Process::pool(function (Pool $pool) { $pool->as('first')->command('bash import-1.sh'); $pool->as('second')->command('bash import-2.sh'); $pool->as('third')->command('bash import-3.sh'); })->start(function (string $type, string $output, string $key) { // $key로 어느 프로세스의 출력인지 확인 가능 }); $results = $pool->wait(); return $results['first']->output();

풀 프로세스 ID와 시그널

running 메서드는 풀에서 실행 중인 모든 프로세스의 컬렉션을 반환하므로, 각 프로세스 ID를 손쉽게 조회할 수 있습니다.

$processIds = $pool->running()->each->id();

풀의 모든 프로세스에 동시에 시그널을 보내려면 풀 인스턴스에서 직접 signal 메서드를 호출합니다.

$pool->signal(SIGUSR2);

테스트

Laravel의 프로세스 서비스는 테스트를 위한 다양한 도구를 제공합니다. Process 파사드의 fake 메서드를 사용하면 실제 프로세스를 실행하지 않고 가짜(stub) 결과를 반환하도록 설정할 수 있습니다.

프로세스 페이킹

프로세스를 실행하는 라우트가 있다고 가정해봅니다.

use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\Route; Route::get('/import', function () { Process::run('bash import.sh'); return 'Import complete!'; });

이 라우트를 테스트할 때 Process::fake()를 인자 없이 호출하면, 모든 프로세스 실행이 성공한 것으로 처리됩니다. 이후 실제로 해당 프로세스가 실행됐는지 어서션으로 확인할 수 있습니다.

<?php namespace Tests\Feature; use Illuminate\Process\PendingProcess; use Illuminate\Contracts\Process\ProcessResult; use Illuminate\Support\Facades\Process; use Tests\TestCase; class ExampleTest extends TestCase { public function test_process_is_invoked(): void { Process::fake(); $response = $this->get('/import'); // 명령어 문자열로 간단하게 확인 Process::assertRan('bash import.sh'); // 또는 프로세스 설정값까지 세부적으로 확인 Process::assertRan(function (PendingProcess $process, ProcessResult $result) { return $process->command === 'bash import.sh' && $process->timeout === 60; }); } }

가짜 프로세스의 출력 내용이나 종료 코드를 직접 지정하려면 result 메서드를 활용합니다.

Process::fake([ '*' => Process::result( output: '테스트 출력', errorOutput: '테스트 에러 출력', exitCode: 1, ), ]);

특정 프로세스 페이킹

fake 메서드에 배열을 전달하면 명령어 패턴별로 다른 가짜 결과를 반환하도록 설정할 수 있습니다. 키는 명령어 패턴(와일드카드 * 사용 가능)이며, 페이킹 설정이 없는 명령어는 실제로 실행됩니다.

Process::fake([ 'cat *' => Process::result( output: '"cat" 명령어 테스트 출력', ), 'ls *' => Process::result( output: '"ls" 명령어 테스트 출력', ), ]);

종료 코드나 에러 출력을 별도로 지정할 필요가 없다면, 결과를 단순 문자열로 지정할 수도 있습니다.

Process::fake([ 'cat *' => '"cat" 명령어 테스트 출력', 'ls *' => '"ls" 명령어 테스트 출력', ]);

프로세스 시퀀스 페이킹

동일한 명령어를 여러 번 호출할 때 각 호출마다 다른 결과를 반환하고 싶다면 sequence 메서드를 사용합니다.

Process::fake([ 'ls *' => Process::sequence() ->push(Process::result('첫 번째 호출 결과')) ->push(Process::result('두 번째 호출 결과')), ]);

비동기 프로세스 라이프사이클 페이킹

start로 실행하는 비동기 프로세스를 테스트할 때는 running이 몇 번 true를 반환해야 하는지, 출력이 어떤 순서로 나와야 하는지 등 더 세밀한 설정이 필요합니다.

예를 들어 다음과 같은 라우트를 테스트한다고 가정합니다.

use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Route; Route::get('/import', function () { $process = Process::start('bash import.sh'); while ($process->running()) { Log::info($process->latestOutput()); Log::info($process->latestErrorOutput()); } return 'Done'; });

이런 경우 describe 메서드로 비동기 프로세스의 동작을 상세하게 정의할 수 있습니다.

Process::fake([ 'bash import.sh' => Process::describe() ->output('표준 출력 첫 번째 줄') ->errorOutput('에러 출력 첫 번째 줄') ->output('표준 출력 두 번째 줄') ->exitCode(0) ->iterations(3), ]);
  • output / errorOutput: 순서대로 반환될 출력 줄을 지정합니다.
  • exitCode: 프로세스 종료 시 반환할 종료 코드를 지정합니다.
  • iterations: running 메서드가 true를 반환할 횟수를 지정합니다.

사용 가능한 어서션

앞서 설명했듯이 Laravel은 기능 테스트에서 프로세스 실행을 검증하는 여러 어서션 메서드를 제공합니다.

assertRan

특정 프로세스가 실행됐는지 확인합니다.

use Illuminate\Support\Facades\Process; Process::assertRan('ls -la');

클로저를 전달하면 프로세스 설정값을 세부적으로 검사할 수 있습니다. 클로저가 true를 반환하면 어서션이 통과됩니다.

Process::assertRan(fn ($process, $result) => $process->command === 'ls -la' && $process->path === __DIR__ && $process->timeout === 60 );

클로저의 $processIlluminate\Process\PendingProcess, $resultIlluminate\Contracts\Process\ProcessResult 인스턴스입니다.

assertDidntRun

특정 프로세스가 실행되지 않았는지 확인합니다.

use Illuminate\Support\Facades\Process; Process::assertDidntRun('ls -la');

클로저를 전달할 수도 있으며, 이 경우 클로저가 true를 반환하면 어서션이 실패합니다.

Process::assertDidntRun(fn (PendingProcess $process, ProcessResult $result) => $process->command === 'ls -la' );

assertRanTimes

특정 프로세스가 지정한 횟수만큼 실행됐는지 확인합니다.

use Illuminate\Support\Facades\Process; Process::assertRanTimes('ls -la', times: 3);

클로저와 함께 사용하면 프로세스 설정값도 함께 검사할 수 있습니다. 클로저가 true를 반환하고 실행 횟수가 일치하면 어서션이 통과됩니다.

Process::assertRanTimes(function (PendingProcess $process, ProcessResult $result) { return $process->command === 'ls -la'; }, times: 3);

의도치 않은 프로세스 실행 방지

테스트 중 페이킹되지 않은 프로세스가 실제로 실행되는 상황을 막고 싶다면 preventStrayProcesses 메서드를 호출합니다. 이후 가짜 결과가 등록되지 않은 프로세스가 실행되면 실제 프로세스를 시작하는 대신 예외를 던집니다.

use Illuminate\Support\Facades\Process; Process::preventStrayProcesses(); Process::fake([ 'ls *' => '테스트 출력...', ]); // 가짜 결과가 반환됨 Process::run('ls -la'); // 예외 발생! Process::run('bash import.sh');

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

번역일: 2026년 6월 25일