프로세스
업데이트됨번역일: 2026년 9월 30일
이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.
- 원문 수정
- 2026년 9월 30일
- 번역 갱신
- 2026년 9월 30일
프로세스
소개
Laravel은 Symfony Process 컴포넌트를 기반으로, 표현력 있고 간결한 API를 제공합니다. 이를 통해 애플리케이션에서 외부 프로세스(셸 명령어, 스크립트 등)를 편리하게 실행할 수 있습니다. Laravel의 프로세스 기능은 가장 흔히 사용되는 시나리오에 초점을 맞춰, 개발자 경험을 최우선으로 설계되었습니다.
프로세스 실행하기
프로세스를 실행하려면 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->command();
$result->successful();
$result->failed();
$result->output();
$result->errorOutput();
$result->exitCode();예외 던지기
프로세스 결과에서 종료 코드가 0보다 큰 경우(즉, 실패를 의미하는 경우) Illuminate\Process\Exceptions\ProcessFailedException 예외를 던지고 싶다면 throw, throwIf 메서드를 사용할 수 있습니다. 프로세스가 실패하지 않았다면 ProcessResult 인스턴스가 그대로 반환됩니다:
$result = Process::run('ls -la')->throw();
$result = Process::run('ls -la')->throwIf($condition);프로세스 옵션
프로세스를 실행하기 전에 다양한 옵션을 조정해야 할 때가 있습니다. Laravel은 작업 디렉터리, 타임아웃, 환경 변수 등 여러 프로세스 동작을 손쉽게 커스터마이징할 수 있도록 지원합니다.
작업 디렉터리 경로
path 메서드를 사용하면 프로세스의 작업 디렉터리를 지정할 수 있습니다. 이 메서드를 호출하지 않으면 프로세스는 현재 실행 중인 PHP 스크립트의 작업 디렉터리를 그대로 상속합니다:
$result = Process::path(__DIR__)->run('ls -la');입력값(Input)
input 메서드를 사용하면 프로세스의 "표준 입력(standard input)"으로 값을 전달할 수 있습니다:
$result = Process::input('Hello World')->run('cat');타임아웃
기본적으로 프로세스는 60초 이상 실행되면 Illuminate\Process\Exceptions\ProcessTimedOutException 예외를 던집니다. 이 동작은 timeout 메서드로 원하는 값으로 변경할 수 있습니다:
$result = Process::timeout(120)->run('bash import.sh');timeout과 idleTimeout 메서드는 CarbonInterval 인스턴스도 인자로 받을 수 있습니다:
use function Illuminate\Support\minutes;
$result = Process::timeout(minutes(2))->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 메서드를 사용하면 프로세스에 TTY 모드를 활성화할 수 있습니다. TTY 모드는 프로세스의 입출력을 프로그램의 입출력과 직접 연결해주므로, Vim이나 Nano 같은 에디터를 프로세스로 실행할 수 있게 해줍니다:
Process::forever()->tty()->run('vim');WARNING
TTY 모드는 Windows에서는 지원되지 않습니다.
프로세스 출력
앞서 살펴본 것처럼, 프로세스 결과 인스턴스의 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;
});Laravel은 seeInOutput, seeInErrorOutput 메서드도 제공하는데, 이를 사용하면 프로세스 출력에 특정 문자열이 포함되어 있는지 손쉽게 확인할 수 있습니다:
if (Process::run('ls -la')->seeInOutput('laravel')) {
// ...
}프로세스 출력 비활성화하기
관심 없는 대량의 출력을 프로세스가 생성하는 경우, 출력 수집 자체를 비활성화하면 메모리를 절약할 수 있습니다. 프로세스를 구성할 때 quietly 메서드를 호출하면 됩니다:
use Illuminate\Support\Facades\Process;
$result = Process::quietly()->run('bash import.sh');파이프라인
때로는 한 프로세스의 출력을 다른 프로세스의 입력으로 전달하고 싶을 때가 있습니다. 이를 흔히 프로세스 출력을 다음 프로세스로 "파이핑(piping)"한다고 부릅니다. Process 파사드가 제공하는 pipe 메서드를 사용하면 이 작업을 쉽게 처리할 수 있습니다. 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()) {
// ...
}파이프라인을 구성하는 개별 프로세스를 세부적으로 커스터마이징할 필요가 없다면, 명령어 문자열 배열을 pipe 메서드에 바로 전달할 수도 있습니다:
$result = Process::pipe([
'cat example.txt',
'grep -i "laravel"',
]);pipe 메서드의 두 번째 인자로 클로저를 전달하면 파이프라인의 출력을 실시간으로 받아볼 수 있습니다. 이 클로저는 출력 "타입"(stdout 또는 stderr)과 출력 문자열을 인자로 전달받습니다:
$result = Process::pipe(function (Pipe $pipe) {
$pipe->command('cat example.txt');
$pipe->command('grep -i "laravel"');
}, function (string $type, string $output) {
echo $output;
});또한, as 메서드를 사용해 파이프라인 내의 각 프로세스에 문자열 키를 부여할 수 있습니다. 이 키는 pipe 메서드에 전달한 출력 클로저에도 함께 전달되므로, 출력이 어느 프로세스에서 발생한 것인지 구분할 수 있습니다:
$result = Process::pipe(function (Pipe $pipe) {
$pipe->as('first')->command('cat example.txt');
$pipe->as('second')->command('grep -i "laravel"');
}, function (string $type, string $output, string $key) {
// ...
});비동기 프로세스
run 메서드는 프로세스를 동기적으로 실행하지만, start 메서드를 사용하면 프로세스를 비동기로 실행할 수 있습니다. 이렇게 하면 프로세스가 백그라운드에서 실행되는 동안 애플리케이션은 다른 작업을 계속 처리할 수 있습니다. 프로세스를 시작한 뒤에는 running 메서드로 프로세스가 아직 실행 중인지 확인할 수 있습니다:
$process = Process::timeout(120)->start('bash import.sh');
while ($process->running()) {
// ...
}
$result = $process->wait();위 예제에서 볼 수 있듯, wait 메서드를 호출하면 프로세스가 종료될 때까지 대기한 뒤 ProcessResult 인스턴스를 얻을 수 있습니다:
$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);
}run 메서드와 마찬가지로, start 메서드의 두 번째 인자로 클로저를 전달하면 비동기 프로세스의 출력을 실시간으로 받아볼 수 있습니다. 이 클로저는 출력 "타입"(stdout 또는 stderr)과 출력 문자열을 인자로 전달받습니다:
$process = Process::start('bash import.sh', function (string $type, string $output) {
echo $output;
});
$result = $process->wait();프로세스가 완전히 종료될 때까지 무조건 기다리는 대신, 프로세스 출력을 기준으로 대기를 멈추고 싶다면 waitUntil 메서드를 사용할 수 있습니다. waitUntil에 전달한 클로저가 true를 반환하면 Laravel은 그 즉시 대기를 종료합니다:
$process = Process::start('bash import.sh');
$process->waitUntil(function (string $type, string $output) {
return $output === 'Ready...';
});비동기 프로세스 타임아웃
비동기 프로세스가 실행 중인 동안, ensureNotTimedOut 메서드를 사용해 프로세스가 타임아웃되지 않았는지 확인할 수 있습니다. 이미 타임아웃이 발생했다면 이 메서드는 타임아웃 예외를 던집니다:
$process = Process::timeout(120)->start('bash import.sh');
while ($process->running()) {
$process->ensureNotTimedOut();
// ...
sleep(1);
}동시 실행 프로세스
Laravel은 여러 개의 비동기 프로세스를 동시에 관리하는 "풀(pool)" 기능도 제공하므로, 여러 작업을 한 번에 병렬로 실행하기가 매우 쉽습니다. pool 메서드를 사용해보겠습니다. 이 메서드는 Illuminate\Process\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 메서드를 사용하면 풀 안의 모든 프로세스가 종료될 때까지 기다린 뒤 각 프로세스의 결과를 받아올 수 있습니다. wait 메서드가 반환하는 값은 배열처럼 인덱스로 접근할 수 있는 객체로, 각 프로세스의 ProcessResult 인스턴스를 키를 통해 조회할 수 있습니다:
$results = $pool->wait();
echo $results[0]->output();또는, 편의를 위해 concurrently 메서드를 사용하면 비동기 프로세스 풀을 시작하고 즉시 결과를 기다리는 작업을 한 번에 처리할 수 있습니다. PHP의 배열 구조 분해(array destructuring) 문법과 함께 사용하면 코드가 한층 간결해집니다:
[$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();풀 프로세스 이름 지정하기
숫자 키로 풀의 결과에 접근하는 방식은 코드의 가독성이 떨어질 수 있습니다. 따라서 Laravel은 as 메서드를 통해 풀 내의 각 프로세스에 문자열 키를 지정할 수 있도록 지원합니다. 이 키는 start 메서드에 전달한 클로저에도 함께 전달되므로, 출력이 어느 프로세스에서 발생한 것인지 쉽게 구분할 수 있습니다:
$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) {
// ...
});
$results = $pool->wait();
return $results['first']->output();풀 프로세스 ID와 시그널
프로세스 풀의 running 메서드는 풀 내에서 실행 중인 모든 프로세스의 컬렉션을 반환하므로, 각 프로세스의 프로세스 ID를 쉽게 조회할 수 있습니다:
$processIds = $pool->running()->map->id();편의를 위해, 프로세스 풀에서 바로 signal 메서드를 호출하면 풀 안의 모든 프로세스에 동시에 시그널을 보낼 수도 있습니다:
$pool->signal(SIGUSR2);테스트
Laravel의 많은 서비스가 표현력 있는 테스트 작성을 도와주는 기능을 제공하는데, 프로세스 서비스도 예외는 아닙니다. Process 파사드의 fake 메서드를 사용하면 실제 프로세스를 실행하는 대신 미리 정해둔(스텁/가짜) 결과를 반환하도록 지시할 수 있습니다.
프로세스 페이크(가짜 처리)
Laravel의 프로세스 페이크 기능을 살펴보기 위해, 프로세스를 실행하는 라우트를 하나 예로 들어보겠습니다:
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Route;
Route::get('/import', function () {
Process::run('bash import.sh');
return 'Import complete!';
});이 라우트를 테스트할 때, Process 파사드의 fake 메서드를 아무 인자 없이 호출하면 모든 프로세스 실행에 대해 성공적인 가짜 결과를 반환하도록 지시할 수 있습니다. 더 나아가, 특정 프로세스가 실제로 "실행"되었는지 검증(assert)할 수도 있습니다:
Pest
<?php
use Illuminate\Contracts\Process\ProcessResult;
use Illuminate\Process\PendingProcess;
use Illuminate\Support\Facades\Process;
test('process is invoked', function () {
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;
});
});PHPUnit
<?php
namespace Tests\Feature;
use Illuminate\Contracts\Process\ProcessResult;
use Illuminate\Process\PendingProcess;
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;
});
}
}앞서 살펴본 대로, Process 파사드에서 fake 메서드를 호출하면 항상 출력 없이 성공하는 가짜 프로세스 결과가 반환됩니다. 하지만 Process 파사드의 result 메서드를 사용하면 가짜 프로세스의 출력값과 종료 코드를 원하는 값으로 지정할 수도 있습니다:
Process::fake([
'*' => Process::result(
output: 'Test output',
errorOutput: 'Test error output',
exitCode: 1,
),
]);특정 프로세스만 페이크하기
앞의 예제에서 보았듯, Process 파사드의 fake 메서드에 배열을 전달하면 프로세스마다 서로 다른 가짜 결과를 지정할 수 있습니다.
배열의 키에는 페이크 처리할 명령어 패턴을, 값에는 그에 해당하는 결과를 지정합니다. * 문자는 와일드카드로 사용할 수 있습니다. 페이크로 지정되지 않은 명령어는 실제로 실행됩니다. 이런 명령어들의 스텁/가짜 결과를 만들 때도 Process 파사드의 result 메서드를 사용할 수 있습니다:
Process::fake([
'cat *' => Process::result(
output: 'Test "cat" output',
),
'ls *' => Process::result(
output: 'Test "ls" output',
),
]);가짜 프로세스의 종료 코드나 에러 출력을 별도로 지정할 필요가 없다면, 결과값을 단순 문자열로 지정하는 것이 더 편리할 수 있습니다:
Process::fake([
'cat *' => 'Test "cat" output',
'ls *' => 'Test "ls" output',
]);프로세스 시퀀스 페이크하기
테스트 대상 코드가 동일한 명령어로 프로세스를 여러 번 실행한다면, 각 호출마다 서로 다른 가짜 결과를 반환하도록 지정하고 싶을 수 있습니다. 이럴 때는 Process 파사드의 sequence 메서드를 사용하세요:
Process::fake([
'ls *' => Process::sequence()
->push(Process::result('First invocation'))
->push(Process::result('Second invocation')),
]);비동기 프로세스 생명주기 페이크하기
지금까지는 주로 run 메서드로 동기적으로 실행되는 프로세스를 페이크하는 방법을 살펴봤습니다. 하지만 start로 실행되는 비동기 프로세스와 상호작용하는 코드를 테스트하려면, 가짜 프로세스를 좀 더 정교하게 정의해야 할 수 있습니다.
예를 들어, 비동기 프로세스와 상호작용하는 다음 라우트를 살펴보겠습니다:
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';
});이 프로세스를 제대로 페이크하려면, running 메서드가 true를 몇 번 반환할지 지정할 수 있어야 합니다. 또한 순서대로 여러 줄의 출력을 반환하도록 지정하고 싶을 수도 있습니다. 이럴 때는 Process 파사드의 describe 메서드를 사용하면 됩니다:
Process::fake([
'bash import.sh' => Process::describe()
->output('First line of standard output')
->errorOutput('First line of error output')
->output('Second line of standard output')
->exitCode(0)
->iterations(3),
]);위 예제를 좀 더 자세히 살펴보겠습니다. output, errorOutput 메서드를 사용하면 순서대로 반환될 여러 줄의 출력을 지정할 수 있습니다. exitCode 메서드는 가짜 프로세스의 최종 종료 코드를 지정합니다. 그리고 iterations 메서드는 running 메서드가 몇 번 true를 반환할지를 지정합니다.
사용 가능한 어서션
앞서 살펴본 것처럼, Laravel은 기능 테스트에서 사용할 수 있는 다양한 프로세스 관련 어서션을 제공합니다. 아래에서 각각을 살펴보겠습니다.
assertRan
특정 프로세스가 실행되었는지 검증합니다:
use Illuminate\Support\Facades\Process;
Process::assertRan('ls -la');프로세스가 배열 형태의 인자로 실행되었다면, 동일한 배열을 어서션에 전달할 수 있습니다:
Process::assertRan(['php', 'artisan', 'migrate']);assertRanTimes, assertDidntRun 메서드도 마찬가지로 배열 형태의 명령어를 받을 수 있습니다.
assertRan 메서드는 클로저도 인자로 받을 수 있습니다. 이 클로저는 프로세스 인스턴스와 프로세스 결과 인스턴스를 전달받아, 프로세스에 설정된 옵션들을 검사할 수 있게 해줍니다. 클로저가 true를 반환하면 어서션은 통과합니다:
Process::assertRan(fn ($process, $result) =>
$process->command === 'ls -la' &&
$process->path === __DIR__ &&
$process->timeout === 60
);assertRan의 클로저에 전달되는 $process는 Illuminate\Process\PendingProcess 인스턴스이며, $result는 Illuminate\Contracts\Process\ProcessResult 인스턴스입니다.
assertDidntRun
특정 프로세스가 실행되지 않았는지 검증합니다:
use Illuminate\Support\Facades\Process;
Process::assertDidntRun('ls -la');assertRan과 마찬가지로 assertDidntRun 메서드도 클로저를 인자로 받을 수 있습니다. 이 클로저는 프로세스 인스턴스와 프로세스 결과 인스턴스를 전달받으며, 클로저가 true를 반환하면 이번엔 어서션이 "실패"합니다:
Process::assertDidntRun(fn (PendingProcess $process, ProcessResult $result) =>
$process->command === 'ls -la'
);assertRanTimes
특정 프로세스가 지정한 횟수만큼 실행되었는지 검증합니다:
use Illuminate\Support\Facades\Process;
Process::assertRanTimes('ls -la', times: 3);assertRanTimes 메서드도 클로저를 받을 수 있으며, 이 클로저는 PendingProcess와 ProcessResult 인스턴스를 전달받아 프로세스 설정을 검사할 수 있게 해줍니다. 클로저가 true를 반환하고, 프로세스가 지정된 횟수만큼 실행되었다면 어서션은 통과합니다:
Process::assertRanTimes(function (PendingProcess $process, ProcessResult $result) {
return $process->command === 'ls -la';
}, times: 3);assertRanInOrder
프로세스들이 지정한 순서대로 실행되었는지 검증합니다:
Process::assertRanInOrder([
'git fetch',
'composer install',
]);assertRanInOrder 메서드 역시 다른 프로세스 어서션들과 마찬가지로 명령어 문자열, 명령어 인자 배열, 클로저를 모두 인자로 받을 수 있습니다.
의도치 않은 프로세스 실행 방지하기
개별 테스트나 전체 테스트 스위트에서 실행되는 모든 프로세스가 반드시 페이크 처리되었는지 보장하고 싶다면 preventStrayProcesses 메서드를 호출하면 됩니다. 이 메서드를 호출한 이후에는, 대응하는 가짜 결과가 지정되지 않은 프로세스가 실행될 경우 실제 프로세스를 실행하는 대신 예외를 던집니다:
use Illuminate\Support\Facades\Process;
Process::preventStrayProcesses();
Process::fake([
'ls *' => 'Test output...',
]);
// 가짜 응답이 반환됨...
Process::run('ls -la');
// 예외가 발생함...
Process::run('bash import.sh');