프로세스
번역일: 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;
});특정 문자열이 출력에 포함되어 있는지 확인할 때는 seeInOutput과 seeInErrorOutput 메서드를 사용합니다.
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);비동기 프로세스 출력
비동기 프로세스가 실행되는 동안 output과 errorOutput으로 지금까지의 전체 출력을 읽을 수 있습니다. 마지막으로 읽은 이후의 새로운 출력만 가져오려면 latestOutput과 latestErrorOutput을 사용합니다.
$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
);클로저의 $process는 Illuminate\Process\PendingProcess, $result는 Illuminate\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');