Artisan 콘솔
업데이트됨번역일: 2026년 10월 2일
이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.
- 원문 수정
- 2026년 10월 2일
- 번역 갱신
- 2026년 10월 2일
Artisan 콘솔
소개
Artisan은 Laravel에 기본 내장된 커맨드라인 인터페이스(CLI)입니다. 프로젝트 루트에 있는 artisan 스크립트를 통해 실행하며, 애플리케이션을 개발하는 동안 유용하게 사용할 수 있는 다양한 명령어를 제공합니다. 사용 가능한 전체 명령어 목록을 확인하려면 list 명령어를 실행하면 됩니다.
php artisan list모든 명령어에는 해당 명령어가 받아들이는 인자와 옵션을 안내하는 "도움말" 화면이 포함되어 있습니다. 도움말 화면을 보려면 명령어 이름 앞에 help를 붙여서 실행하세요.
php artisan help migrateNOTE
로컬 개발 환경으로 Laravel Sail을 사용하고 있다면, artisan 명령어를 실행할 때 반드시 sail 명령줄을 통해 호출해야 한다는 점을 기억하세요. Sail은 애플리케이션의 Docker 컨테이너 내부에서 Artisan 명령어를 실행합니다.
./vendor/bin/sail artisan listTinker (REPL)
Laravel Tinker는 PsySH 패키지를 기반으로 만들어진 Laravel 프레임워크용 강력한 REPL(Read-Eval-Print Loop)입니다.
설치
모든 Laravel 애플리케이션에는 기본적으로 Tinker가 포함되어 있습니다. 하지만 만약 이전에 애플리케이션에서 Tinker를 제거한 적이 있다면, Composer를 통해 다시 설치할 수 있습니다.
composer require laravel/tinkerNOTE
Laravel 애플리케이션과 상호작용할 때 핫 리로딩, 멀티라인 코드 편집, 자동완성 기능이 필요하신가요? Tinkerwell을 확인해보세요!
사용법
Tinker를 사용하면 Eloquent 모델, Job, 이벤트 등 애플리케이션 전체를 커맨드라인에서 직접 다루며 상호작용할 수 있습니다. Tinker 환경에 진입하려면 tinker Artisan 명령어를 실행하세요.
php artisan tinkervendor:publish 명령어로 Tinker의 설정 파일을 퍼블리시할 수 있습니다.
php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"WARNING
Vite 매니페스트를 다루는 dispatch 헬퍼 함수와 Dispatchable 클래스의 dispatch 메서드는 Tinker가 명령어를 큐에 담는 과정에서 서로 의존 관계가 있으므로, 이 둘을 반드시 함께 유지해야 합니다.
명령어 허용 목록
Tinker는 어떤 Artisan 명령어를 셸(shell) 안에서 실행할 수 있는지를 결정하는 "허용 목록(allow list)"을 사용합니다. 기본적으로 clear-compiled, down, env, inspire, migrate, optimize, up 명령어를 실행할 수 있습니다. 더 많은 명령어를 허용하고 싶다면, tinker.php 설정 파일의 commands 배열에 원하는 명령어 클래스를 추가하면 됩니다.
'commands' => [
// App\Console\Commands\ExampleCommand::class,
],별칭(alias)을 생성하지 않아야 할 클래스
일반적으로 Tinker는 상호작용하는 클래스에 대해 자동으로 별칭을 만들어줍니다. 하지만 일부 클래스는 별칭 생성 대상에서 제외하고 싶을 수 있습니다. tinker.php 설정 파일 안의 dont_alias 배열에 클래스를 나열하면 해당 클래스는 별칭 생성에서 제외됩니다.
'dont_alias' => [
App\Models\User::class,
],Artisan CLI
소개
Artisan은 Laravel에 내장된 명령줄 인터페이스(CLI)입니다. 애플리케이션 루트 디렉터리에 artisan 스크립트 형태로 존재하며, 애플리케이션을 개발하는 동안 유용하게 사용할 수 있는 다양한 명령어를 제공합니다. 사용 가능한 모든 Artisan 명령어 목록을 보려면 list 명령어를 실행하면 됩니다.
php artisan list모든 명령어에는 사용 가능한 인자(argument)와 옵션(option)을 설명해주는 "도움말" 화면이 포함되어 있습니다. 도움말 화면을 보려면 명령어 이름 앞에 help를 붙여 실행하세요.
php artisan help migrateLaravel Sail을 사용하는 경우
로컬 개발 환경으로 Laravel Sail을 사용하고 있다면, Artisan 명령어는 sail 명령줄 도구를 통해 실행해야 한다는 점을 기억하세요. Sail은 애플리케이션의 Docker 컨테이너 내부에서 Artisan 명령어를 실행해줍니다.
./vendor/bin/sail artisan listTinker (REPL)
Laravel Tinker는 PsySH 패키지를 기반으로 만들어진, Laravel을 위한 강력한 REPL(대화형 실행 환경) 도구입니다.
설치
Tinker는 모든 Laravel 애플리케이션에 기본으로 포함되어 있습니다. 다만 이전에 애플리케이션에서 제거한 적이 있다면, Composer를 통해 다시 설치할 수 있습니다.
composer require laravel/tinkerNOTE
Laravel 애플리케이션과 상호작용할 때 핫 리로딩, 여러 줄 코드 편집, 자동완성 기능이 필요하신가요? Tinkerwell을 확인해보세요!
사용법
Tinker를 이용하면 Eloquent 모델, Job, 이벤트 등을 포함한 애플리케이션 전체를 명령줄에서 자유롭게 다룰 수 있습니다. Tinker 환경에 진입하려면 tinker Artisan 명령어를 실행하세요.
php artisan tinkerTinker의 설정 파일은 vendor:publish 명령어로 퍼블리시할 수 있습니다.
php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"WARNING
dispatch 헬퍼 함수와 Dispatchable 클래스의 dispatch 메서드는 Job을 큐에 등록할 때 가비지 컬렉션(garbage collection)에 의존합니다. 따라서 Tinker를 사용할 때는 Job을 디스패치할 때 Bus::dispatch나 Queue::push를 사용해야 합니다. (일반적인 dispatch 호출은 Tinker 세션이 끝나기 전에 가비지 컬렉션이 일어나지 않아 Job이 실제로 큐에 쌓이지 않을 수 있습니다.)
명령어 허용 목록(Allow List)
Tinker는 자신의 셸 안에서 실행 가능한 Artisan 명령어를 "허용 목록(allow list)"으로 관리합니다. 기본적으로 clear-compiled, down, env, inspire, migrate, migrate:install, up, optimize 명령어만 실행할 수 있습니다. 더 많은 명령어를 허용하고 싶다면 tinker.php 설정 파일의 commands 배열에 추가하면 됩니다.
'commands' => [
// App\Console\Commands\ExampleCommand::class,
],별칭(alias)을 적용하지 않을 클래스
Tinker는 기본적으로 사용자가 상호작용하는 클래스에 자동으로 별칭(alias)을 부여합니다. 하지만 특정 클래스에는 별칭을 적용하고 싶지 않을 수도 있습니다. 이럴 때는 tinker.php 설정 파일의 dont_alias 배열에 해당 클래스를 추가하면 됩니다.
'dont_alias' => [
App\Models\User::class,
],Artisan CLI
커맨드 작성하기
Artisan이 기본으로 제공하는 커맨드 외에도, 직접 커스텀 커맨드를 만들어 사용할 수 있습니다. 커맨드 클래스는 보통 app/Console/Commands 디렉터리에 저장하지만, Laravel에게 다른 디렉터리도 스캔하도록 알려주기만 하면 원하는 위치에 자유롭게 저장할 수 있습니다.
커맨드 생성하기
새 커맨드를 만들려면 make:command Artisan 커맨드를 사용하면 됩니다. 이 명령은 app/Console/Commands 디렉터리에 새 커맨드 클래스를 생성합니다. 애플리케이션에 아직 이 디렉터리가 없더라도 걱정할 필요는 없습니다 - make:command를 처음 실행할 때 자동으로 생성됩니다:
php artisan make:command SendEmails커맨드 구조
커맨드를 생성한 뒤에는 Signature와 Description 속성(attribute)을 사용해 커맨드의 시그니처와 설명을 정의해야 합니다. Signature 속성을 통해 커맨드가 받을 입력값도 함께 정의할 수 있습니다. 커맨드가 실행되면 handle 메서드가 호출되므로, 실제 커맨드 로직은 이 메서드 안에 작성하면 됩니다.
예제 커맨드를 하나 살펴보겠습니다. handle 메서드를 통해 필요한 의존성을 자유롭게 요청할 수 있다는 점에 주목하세요. Laravel의 서비스 컨테이너가 이 메서드 시그니처에 타입힌트로 지정된 모든 의존성을 자동으로 주입해줍니다:
<?php
namespace App\Console\Commands;
use App\Models\User;
use App\Support\DripEmailer;
use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;
#[Signature('mail:send {user}')]
#[Description('Send a marketing email to a user')]
class SendEmails extends Command
{
/**
* 콘솔 커맨드를 실행합니다.
*/
public function handle(DripEmailer $drip): void
{
$drip->send(User::find($this->argument('user')));
}
}NOTE
코드 재사용성을 높이려면, 콘솔 커맨드 자체는 가볍게 유지하고 실제 작업은 애플리케이션 서비스에 위임하는 것이 좋은 습관입니다. 위 예제에서도 이메일 발송이라는 "본 작업"은 별도의 서비스 클래스에 위임하고 있는 것을 확인할 수 있습니다.
종료 코드(Exit Code)
handle 메서드가 아무 값도 반환하지 않고 정상적으로 실행을 마치면, 커맨드는 성공을 의미하는 0 종료 코드로 종료됩니다. 하지만 필요하다면 handle 메서드에서 정수값을 반환하여 종료 코드를 직접 지정할 수도 있습니다:
$this->error('문제가 발생했습니다.');
return 1;커맨드 내 어떤 메서드에서든 즉시 커맨드를 "실패" 처리하고 싶다면 fail 메서드를 사용할 수 있습니다. fail 메서드는 커맨드 실행을 즉시 종료시키고 종료 코드 1을 반환합니다:
$this->fail('문제가 발생했습니다.');클로저 기반 커맨드
클로저 기반 커맨드는 콘솔 커맨드를 클래스 대신 클로저로 정의하는 방법입니다. 라우트를 클로저로 정의하는 것이 컨트롤러를 대체하는 방법인 것처럼, 커맨드 클로저도 커맨드 클래스를 대체하는 방법이라고 생각하면 됩니다.
routes/console.php 파일은 HTTP 라우트를 정의하는 곳은 아니지만, 애플리케이션으로 들어오는 콘솔 기반의 진입점(라우트)을 정의하는 파일입니다. 이 파일 안에서 Artisan::command 메서드를 사용해 모든 클로저 기반 콘솔 커맨드를 정의할 수 있습니다. command 메서드는 커맨드 시그니처와, 커맨드의 인자와 옵션을 전달받는 클로저, 이렇게 두 개의 인자를 받습니다:
Artisan::command('mail:send {user}', function (string $user) {
$this->info("이메일을 보냅니다: {$user}!");
});이 클로저는 내부적으로 커맨드 인스턴스에 바인딩되므로, 일반적인 커맨드 클래스에서 사용할 수 있는 모든 헬퍼 메서드에 동일하게 접근할 수 있습니다.
의존성 타입힌트하기
커맨드 클로저는 커맨드의 인자와 옵션뿐만 아니라, 서비스 컨테이너에서 resolve하고 싶은 추가 의존성도 타입힌트로 지정할 수 있습니다:
use App\Models\User;
use App\Support\DripEmailer;
use Illuminate\Support\Facades\Artisan;
Artisan::command('mail:send {user}', function (DripEmailer $drip, string $user) {
$drip->send(User::find($user));
});클로저 커맨드 설명 추가하기
클로저 기반 커맨드를 정의할 때 purpose 메서드를 사용하면 커맨드에 대한 설명을 추가할 수 있습니다. 이 설명은 php artisan list나 php artisan help 명령을 실행했을 때 표시됩니다:
Artisan::command('mail:send {user}', function (string $user) {
// ...
})->purpose('사용자에게 마케팅 이메일을 보냅니다');격리 가능한(Isolatable) 커맨드
WARNING
이 기능을 사용하려면 애플리케이션의 기본 캐시 드라이버로 memcached, redis, dynamodb, database, file, array 중 하나를 사용하고 있어야 합니다. 또한 모든 서버가 동일한 중앙 캐시 서버와 통신하고 있어야 합니다.
경우에 따라 특정 커맨드가 동시에 단 하나의 인스턴스로만 실행되도록 보장하고 싶을 수 있습니다. 이럴 때는 커맨드 클래스에 Illuminate\Contracts\Console\Isolatable 인터페이스를 구현하면 됩니다:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Isolatable;
class SendEmails extends Command implements Isolatable
{
// ...
}커맨드를 Isolatable로 지정하면, Laravel은 별도로 옵션을 정의하지 않아도 자동으로 --isolated 옵션을 사용할 수 있게 해줍니다. 이 옵션과 함께 커맨드를 실행하면, Laravel은 해당 커맨드의 다른 인스턴스가 이미 실행 중이지 않은지 확인합니다. 이는 애플리케이션의 기본 캐시 드라이버를 이용해 원자적(atomic) 락을 획득하는 방식으로 동작합니다. 만약 동일한 커맨드의 다른 인스턴스가 이미 실행 중이라면 커맨드는 실행되지 않지만, 종료 코드는 여전히 성공을 나타내는 값으로 반환됩니다:
php artisan mail:send 1 --isolated커맨드가 실행되지 못했을 때 반환할 종료 코드를 직접 지정하고 싶다면, isolated 옵션에 원하는 상태 코드를 값으로 전달하면 됩니다:
php artisan mail:send 1 --isolated=12락 ID
기본적으로 Laravel은 커맨드의 이름을 이용해 애플리케이션 캐시에서 원자적 락을 획득하는 데 사용할 문자열 키를 생성합니다. 하지만 Artisan 커맨드 클래스에 isolatableId 메서드를 정의하면 이 키를 직접 커스터마이징할 수 있으며, 커맨드의 인자나 옵션 값을 키에 포함시키는 것도 가능합니다:
/**
* 커맨드의 isolatable ID를 가져옵니다.
*/
public function isolatableId(): string
{
return $this->argument('user');
}락 만료 시간
기본적으로 격리 락(isolation lock)은 커맨드 실행이 끝나면 즉시 만료됩니다. 혹은 커맨드 실행이 중간에 중단되어 정상적으로 끝나지 못한 경우에는 1시간 뒤에 자동으로 만료됩니다. 하지만 커맨드에 isolationLockExpiresAt 메서드를 정의하면 락 만료 시간을 원하는 대로 조정할 수 있습니다:
use DateTimeInterface;
use DateInterval;
/**
* 커맨드의 격리 락이 언제 만료될지 결정합니다.
*/
public function isolationLockExpiresAt(): DateTimeInterface|DateInterval
{
return now()->plus(minutes: 5);
}입력값 정의하기
콘솔 명령어를 작성하다 보면 인자(argument)나 옵션(option)을 통해 사용자로부터 입력을 받아야 하는 경우가 흔합니다. Laravel에서는 명령어 클래스의 signature 속성을 이용해 사용자에게 받을 입력을 매우 편리하게 정의할 수 있습니다. signature 속성을 사용하면 명령어의 이름, 인자, 옵션을 라우트 정의와 비슷한 표현력 있는 문법으로 한 번에 정의할 수 있습니다.
인자(Arguments)
사용자가 입력하는 인자와 옵션은 모두 중괄호({})로 감싸서 표현합니다. 아래 예시에서는 user라는 필수 인자 하나를 정의하고 있습니다.
/**
* 콘솔 명령어의 이름과 시그니처
*
* @var string
*/
protected $signature = 'mail:send {user}';인자를 선택적으로 만들거나 기본값을 지정할 수도 있습니다.
// 선택적 인자...
'mail:send {user?}'
// 기본값이 있는 선택적 인자...
'mail:send {user=foo}'옵션(Options)
옵션도 인자와 마찬가지로 사용자 입력을 받는 또 다른 방법입니다. 커맨드 라인에서 옵션을 지정할 때는 하이픈 두 개(--)를 접두사로 붙입니다. 옵션에는 값을 받는 옵션과 값을 받지 않는 옵션 두 가지 종류가 있습니다. 값을 받지 않는 옵션은 참/거짓을 나타내는 "스위치" 역할을 합니다. 먼저 이 스위치 형태의 옵션 예시를 살펴보겠습니다.
/**
* 콘솔 명령어의 이름과 시그니처
*
* @var string
*/
protected $signature = 'mail:send {user} {--queue}';이 예시에서 --queue 스위치는 Artisan 명령어를 호출할 때 함께 지정할 수 있습니다. --queue 스위치를 전달하면 해당 옵션의 값은 true가 되고, 전달하지 않으면 false가 됩니다.
php artisan mail:send 1 --queue값을 받는 옵션
이번에는 값을 받는 옵션을 살펴보겠습니다. 사용자가 옵션에 값을 반드시 지정해야 한다면 옵션 이름 뒤에 = 기호를 붙여서 정의합니다.
/**
* 콘솔 명령어의 이름과 시그니처
*
* @var string
*/
protected $signature = 'mail:send {user} {--queue=}';이렇게 정의하면 사용자는 다음과 같이 옵션 값을 전달할 수 있습니다. 명령어를 실행할 때 옵션을 지정하지 않으면 값은 null이 됩니다.
php artisan mail:send 1 --queue=default옵션 이름 뒤에 기본값을 지정해 두면, 사용자가 값을 전달하지 않았을 때 해당 기본값이 사용됩니다.
'mail:send {user} {--queue=default}'옵션 단축키(Shortcut)
옵션에 단축키를 지정하려면 옵션 이름 앞에 단축키를 적고 | 문자로 구분해주면 됩니다.
'mail:send {user} {--Q|queue=}'터미널에서 명령어를 실행할 때 단축키는 하이픈 하나만 붙이며, 값을 지정할 때는 = 기호 없이 바로 이어서 작성합니다.
php artisan mail:send 1 -Qdefault배열 형태의 입력값
인자나 옵션이 여러 개의 값을 받도록 정의하고 싶다면 * 문자를 사용하면 됩니다. 먼저 인자에 적용하는 예시를 살펴보겠습니다.
'mail:send {user*}'이 명령어를 실행할 때 user 인자는 순서대로 여러 개를 전달할 수 있습니다. 예를 들어 아래 명령어는 user의 값을 1과 2를 원소로 갖는 배열로 설정합니다.
php artisan mail:send 1 2* 문자는 선택적 인자 정의와 함께 사용해서, 값이 하나도 없거나 여러 개 있을 수 있도록 허용할 수도 있습니다.
'mail:send {user?*}'배열 형태의 옵션
여러 개의 입력값을 받는 옵션을 정의할 때는, 전달하는 각 옵션 값마다 옵션 이름을 반복해서 붙여줘야 합니다.
'mail:send {--id=*}'이런 명령어는 아래처럼 --id 인자를 여러 번 전달해서 호출할 수 있습니다.
php artisan mail:send --id=1 --id=2입력값 설명 추가하기
인자나 옵션 이름 뒤에 콜론(:)을 붙이고 설명을 작성하면 입력값에 대한 설명을 추가할 수 있습니다. 명령어 정의가 길어져서 공간이 더 필요하다면, 여러 줄에 걸쳐서 자유롭게 작성해도 됩니다.
/**
* 콘솔 명령어의 이름과 시그니처
*
* @var string
*/
protected $signature = 'mail:send
{user : 메일을 받을 사용자의 ID}
{--queue : 작업을 큐에 등록할지 여부}';누락된 입력값에 대해 되묻기
명령어에 필수 인자가 정의되어 있는데 사용자가 이를 입력하지 않으면 에러 메시지가 출력됩니다. 하지만 PromptsForMissingInput 인터페이스를 구현하면, 필수 인자가 누락되었을 때 자동으로 사용자에게 값을 입력하도록 되물어보게 만들 수 있습니다.
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
class SendEmails extends Command implements PromptsForMissingInput
{
/**
* 콘솔 명령어의 이름과 시그니처
*
* @var string
*/
protected $signature = 'mail:send {user}';
// ...
}필수 인자 값을 사용자로부터 받아야 하는 경우, Laravel은 인자 이름이나 설명을 바탕으로 적절한 질문 문구를 자동으로 만들어 사용자에게 물어봅니다. 필수 인자를 입력받기 위한 질문을 직접 커스터마이징하고 싶다면, promptForMissingArgumentsUsing 메서드를 구현하고 인자 이름을 키로 갖는 질문 배열을 반환하면 됩니다.
/**
* 누락된 입력 인자에 대해 사용할 질문들을 반환합니다.
*
* @return array<string, string>
*/
protected function promptForMissingArgumentsUsing(): array
{
return [
'user' => '메일을 받을 사용자의 ID는 무엇인가요?',
];
}질문과 함께 플레이스홀더 텍스트도 함께 제공하고 싶다면, 질문과 플레이스홀더로 구성된 튜플(배열)을 사용하면 됩니다.
return [
'user' => ['메일을 받을 사용자의 ID는 무엇인가요?', '예: 123'],
];프롬프트 동작을 완전히 직접 제어하고 싶다면, 사용자에게 값을 입력받아 반환하는 클로저를 제공할 수도 있습니다.
use App\Models\User;
use function Laravel\Prompts\search;
// ...
return [
'user' => fn () => search(
label: '사용자를 검색하세요:',
placeholder: '예: 홍길동',
options: fn ($value) => strlen($value) > 0
? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
: []
),
];NOTE
사용 가능한 프롬프트 종류와 사용법에 대한 자세한 내용은 Laravel Prompts 문서를 참고하세요.
옵션 값을 선택하거나 입력하도록 사용자에게 되묻고 싶다면, 명령어의 handle 메서드 안에 프롬프트 코드를 직접 작성하면 됩니다. 다만 "누락된 인자에 대해 자동으로 되묻는 과정이 실제로 실행되었을 때만" 추가로 프롬프트를 띄우고 싶다면, afterPromptingForMissingArguments 메서드를 구현하면 됩니다.
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use function Laravel\Prompts\confirm;
// ...
/**
* 누락된 인자에 대해 사용자에게 되물은 뒤 추가 작업을 수행합니다.
*/
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void
{
$input->setOption('queue', confirm(
label: '메일을 큐에 등록하시겠습니까?',
default: $this->option('queue')
));
}명령어 입출력
입력값 조회하기
명령어가 실행되는 동안 명령어가 받은 인수(argument)와 옵션(option) 값에 접근해야 하는 경우가 많습니다. 이때는 argument와 option 메서드를 사용하면 됩니다. 존재하지 않는 인수나 옵션을 조회하면 null이 반환됩니다.
/**
* 콘솔 명령어를 실행합니다.
*/
public function handle(): void
{
$userId = $this->argument('user');
}모든 인수를 array 형태로 한 번에 받고 싶다면 arguments 메서드를 호출하세요.
$arguments = $this->arguments();옵션도 인수와 마찬가지로 option 메서드로 쉽게 조회할 수 있습니다. 모든 옵션을 배열로 받으려면 options 메서드를 호출하면 됩니다.
// 특정 옵션 하나만 조회...
$queueName = $this->option('queue');
// 모든 옵션을 배열로 조회...
$options = $this->options();input 메서드를 사용하면 명령어의 인수와 옵션을 Illuminate\Console\CommandInput 인스턴스로 받아올 수 있습니다. 이 인스턴스는 HTTP 요청 객체나 다른 데이터 컨테이너에서 제공하는 것과 동일한 타입 지정 접근자(typed accessor)를 제공합니다.
use App\Enums\ReportType;
/**
* 콘솔 명령어를 실행합니다.
*/
public function handle(): void
{
$input = $this->input()->date('from');
// ...
}input 메서드는 인수나 옵션 중 하나의 값을 단일 값으로 조회할 때도 사용할 수 있습니다.
$queue = $this->input('queue', 'default');사용자에게 입력 요청하기
NOTE
Laravel Prompts는 플레이스홀더 텍스트, 유효성 검증 등 브라우저 폼과 유사한 기능을 제공하면서도 아름답고 사용하기 편한 CLI 입력 폼을 만들 수 있는 PHP 패키지입니다.
명령어는 출력뿐 아니라 실행 중에 사용자로부터 입력을 받을 수도 있습니다. ask 메서드는 주어진 질문을 사용자에게 보여주고, 입력값을 받아 명령어 코드로 그대로 반환해줍니다.
/**
* 콘솔 명령어를 실행합니다.
*/
public function handle(): void
{
$name = $this->ask('이름이 무엇인가요?');
// ...
}ask 메서드는 사용자가 아무 값도 입력하지 않았을 때 반환할 기본값을 두 번째 인수로 받을 수도 있습니다.
$name = $this->ask('이름이 무엇인가요?', 'Taylor');secret 메서드는 ask와 비슷하지만, 사용자가 입력하는 내용이 콘솔 화면에 표시되지 않습니다. 비밀번호와 같이 민감한 정보를 입력받을 때 유용합니다.
$password = $this->secret('비밀번호를 입력하세요');확인(예/아니오) 요청하기
사용자에게 단순한 "예/아니오" 확인을 받아야 한다면 confirm 메서드를 사용할 수 있습니다. 기본적으로 이 메서드는 false를 반환하지만, 사용자가 y 또는 yes를 입력하면 true를 반환합니다.
if ($this->confirm('계속 진행하시겠습니까?')) {
// ...
}필요하다면 confirm 메서드의 두 번째 인수에 true를 전달하여, 사용자가 아무 입력도 하지 않았을 때 기본값이 true가 되도록 지정할 수도 있습니다.
if ($this->confirm('계속 진행하시겠습니까?', true)) {
// ...
}자동 완성
anticipate 메서드는 선택 가능한 항목에 대한 자동 완성 기능을 제공합니다. 자동 완성 힌트가 있더라도 사용자는 원하는 값을 자유롭게 입력할 수 있습니다.
$name = $this->anticipate('이름이 무엇인가요?', ['Taylor', 'Dayle']);또는 anticipate 메서드의 두 번째 인수로 클로저를 전달할 수도 있습니다. 이 클로저는 사용자가 문자를 입력할 때마다 호출되며, 그때까지 사용자가 입력한 문자열을 매개변수로 받아 자동 완성에 사용할 선택지 배열을 반환해야 합니다.
use App\Models\Address;
$name = $this->anticipate('주소가 어떻게 되나요?', function (string $input) {
return Address::whereLike('name', "{$input}%")
->limit(5)
->pluck('name')
->all();
});객관식 질문
사용자에게 미리 정의된 선택지 중 하나를 고르게 하려면 choice 메서드를 사용하세요. 세 번째 인수로 기본으로 선택될 항목의 배열 인덱스를 지정할 수 있으며, 아무것도 선택하지 않았을 때 이 값이 반환됩니다.
$name = $this->choice(
'이름이 무엇인가요?',
['Taylor', 'Dayle'],
$defaultIndex
);또한 choice 메서드는 네 번째와 다섯 번째 인수를 통해 유효한 응답을 받기 위한 최대 시도 횟수와 다중 선택 허용 여부를 지정할 수 있습니다.
$name = $this->choice(
'이름이 무엇인가요?',
['Taylor', 'Dayle'],
$defaultIndex,
$maxAttempts = null,
$allowMultipleSelections = false
);출력하기
콘솔에 출력을 보낼 때는 line, newLine, info, comment, question, warn, alert, error 메서드를 사용할 수 있습니다. 각 메서드는 용도에 맞는 적절한 ANSI 색상을 사용해 출력합니다. 예를 들어 사용자에게 일반적인 안내 메시지를 보여줄 때는 info 메서드를 사용하며, 보통 콘솔에서 초록색 텍스트로 표시됩니다.
/**
* 콘솔 명령어를 실행합니다.
*/
public function handle(): void
{
// ...
$this->info('명령어가 성공적으로 실행되었습니다!');
}오류 메시지를 표시하려면 error 메서드를 사용하세요. 오류 메시지는 보통 빨간색으로 표시됩니다.
$this->error('문제가 발생했습니다!');별도의 색상 없이 일반 텍스트를 출력하려면 line 메서드를 사용하면 됩니다.
$this->line('화면에 이 내용을 출력합니다');빈 줄을 출력하려면 newLine 메서드를 사용하세요.
// 빈 줄 하나 출력...
$this->newLine();
// 빈 줄 세 개 출력...
$this->newLine(3);테이블
table 메서드를 사용하면 여러 행과 열로 이루어진 데이터를 깔끔하게 정렬된 형태로 쉽게 출력할 수 있습니다. 컬럼명과 데이터만 전달하면 Laravel이 테이블의 적절한 너비와 높이를 알아서 계산해줍니다.
use App\Models\User;
$this->table(
['Name', 'Email'],
User::all(['name', 'email'])->toArray()
);진행률 표시줄
오래 걸리는 작업을 실행할 때는 진행률 표시줄(progress bar)을 보여주면 사용자가 작업 진행 상황을 파악하는 데 도움이 됩니다. withProgressBar 메서드를 사용하면 주어진 반복 가능한(iterable) 값을 순회하면서 각 반복마다 진행률 표시줄을 자동으로 갱신해줍니다.
use App\Models\User;
$users = $this->withProgressBar(User::all(), function (User $user) {
$this->performTask($user);
});경우에 따라 진행률 표시줄을 좀 더 세밀하게 직접 제어해야 할 수도 있습니다. 이럴 때는 먼저 전체 처리 단계 수를 정의한 다음, 각 항목을 처리한 후 진행률을 수동으로 갱신하면 됩니다.
$users = App\Models\User::all();
$bar = $this->output->createProgressBar(count($users));
$bar->start();
foreach ($users as $user) {
$this->performTask($user);
$bar->advance();
}
$bar->finish();NOTE
더 다양한 옵션이 궁금하다면 Symfony Progress Bar 컴포넌트 문서를 참고하세요.
Artisan CLI
커맨드 등록하기
라라벨은 기본적으로 app/Console/Commands 디렉터리 안에 있는 모든 커맨드를 자동으로 등록합니다. 하지만 필요하다면 bootstrap/app.php 파일에서 withCommands 메서드를 사용해 다른 디렉터리도 Artisan 커맨드 검색 대상에 추가할 수 있습니다:
->withCommands([
__DIR__.'/../app/Domain/Orders/Commands',
])예를 들어 주문(Order) 도메인처럼 기능 단위로 디렉터리 구조를 나누는 프로젝트라면, 위와 같이 도메인별 커맨드 디렉터리를 추가로 등록해 사용할 수 있습니다.
필요한 경우 커맨드의 클래스명을 직접 withCommands 메서드에 전달해서 수동으로 등록할 수도 있습니다:
use App\Domain\Orders\Commands\SendEmails;
->withCommands([
SendEmails::class,
])Artisan이 부팅될 때, 애플리케이션에 등록된 모든 커맨드는 서비스 컨테이너를 통해 резол브(resolve)되어 Artisan에 등록됩니다.
프로그래밍 방식으로 명령어 실행하기
CLI를 거치지 않고 Artisan 명령어를 실행해야 할 때도 있습니다. 예를 들어 라우트나 컨트롤러 안에서 Artisan 명령어를 실행하고 싶은 경우입니다. 이럴 때는 Artisan 파사드의 call 메서드를 사용하면 됩니다. call 메서드는 첫 번째 인수로 명령어의 시그니처 이름 또는 클래스 이름을, 두 번째 인수로 명령어 파라미터 배열을 받습니다. 실행 후에는 종료 코드(exit code)가 반환됩니다.
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;
Route::post('/user/{user}/mail', function (string $user) {
$exitCode = Artisan::call('mail:send', [
'user' => $user, '--queue' => 'default'
]);
// ...
});또는 전체 Artisan 명령어를 문자열 형태로 call 메서드에 그대로 전달할 수도 있습니다.
Artisan::call('mail:send 1 --queue=default');배열 값 전달하기
명령어에 배열 값을 받는 옵션이 정의되어 있다면, 해당 옵션에 배열을 전달할 수 있습니다.
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;
Route::post('/mail', function () {
$exitCode = Artisan::call('mail:send', [
'--id' => [5, 13]
]);
});불리언(Boolean) 값 전달하기
migrate:refresh 명령어의 --force 플래그처럼 문자열 값을 받지 않는 옵션의 값을 지정해야 한다면, 해당 옵션의 값으로 true 또는 false를 전달하면 됩니다.
$exitCode = Artisan::call('migrate:refresh', [
'--force' => true,
]);Artisan 명령어 큐에 담기
Artisan 파사드의 queue 메서드를 사용하면 Artisan 명령어를 큐 워커가 백그라운드에서 처리하도록 큐에 넣을 수도 있습니다. 이 메서드를 사용하기 전에 큐 설정을 완료하고 큐 리스너가 실행 중인지 반드시 확인하세요.
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;
Route::post('/user/{user}/mail', function (string $user) {
Artisan::queue('mail:send', [
'user' => $user, '--queue' => 'default'
]);
// ...
});onConnection, onQueue 메서드를 사용하면 Artisan 명령어를 어떤 커넥션과 큐로 디스패치할지 지정할 수 있습니다.
Artisan::queue('mail:send', [
'user' => 1, '--queue' => 'default'
])->onConnection('redis')->onQueue('commands');다른 명령어에서 명령어 호출하기
기존 Artisan 명령어 안에서 또 다른 명령어를 호출해야 할 때가 있습니다. 이럴 때는 call 메서드를 사용하면 됩니다. 이 call 메서드는 명령어 이름과 인수 / 옵션 배열을 받습니다.
/**
* 콘솔 명령어를 실행합니다.
*/
public function handle(): void
{
$this->call('mail:send', [
'user' => 1, '--queue' => 'default'
]);
// ...
}다른 콘솔 명령어를 호출하면서 그 출력을 전부 숨기고 싶다면 callSilently 메서드를 사용할 수 있습니다. callSilently 메서드는 call 메서드와 동일한 시그니처를 가집니다.
$this->callSilently('mail:send', [
'user' => 1, '--queue' => 'default'
]);시그널 처리
운영체제는 실행 중인 프로세스에 시그널(signal)을 보낼 수 있습니다. 예를 들어 SIGTERM 시그널은 운영체제가 프로그램에게 "정상적으로 종료해달라"고 요청하는 수단입니다. Artisan 콘솔 명령어에서 이러한 시그널을 감지해서 특정 코드를 실행하고 싶다면 trap 메서드를 사용하면 됩니다:
/**
* 콘솔 명령어를 실행합니다.
*/
public function handle(): void
{
$this->trap(SIGTERM, fn () => $this->shouldKeepRunning = false);
while ($this->shouldKeepRunning) {
// ...
}
}NOTE
예를 들어 큐 워커나 장시간 실행되는 배치 작업 명령어에서 SIGTERM을 감지해 현재 작업을 마친 뒤 안전하게 루프를 종료하도록 구현하면, 배포 시 서버가 프로세스를 강제 종료하기 전에 깔끔하게 마무리할 수 있습니다.
여러 시그널을 한 번에 감지하고 싶다면, trap 메서드에 시그널 배열을 전달하면 됩니다:
$this->trap([SIGTERM, SIGQUIT], function (int $signal) {
$this->shouldKeepRunning = false;
dump($signal); // SIGTERM / SIGQUIT
});Dev 명령어
dev Artisan 명령어는 로컬 개발에 필요한 모든 프로세스를 터미널 창 하나에서 한꺼번에 실행해줍니다. 기본적으로 PHP 개발 서버, 큐 워커, Pail을 이용한 로그 모니터링, Vite 에셋 컴파일을 동시에 실행합니다:
php artisan dev내부적으로 dev 명령어는 @laravel/multiplex라는 npm 패키지를 사용해 이 프로세스들을 관리합니다. 덕분에 각 프로세스마다 별도의 탭이 생기고, 탭 안에서 출력 내용을 검색하거나 스크롤할 수 있습니다. 각 프로세스는 이름과 색상으로 구분되어 어떤 출력이 어느 프로세스에서 나온 것인지 한눈에 알 수 있습니다. 프로세스가 중간에 죽더라도 자동으로 재시작되며, 종료 시에는 모든 출력 내용이 터미널에 다시 기록되므로 로그를 놓칠 걱정이 없습니다.
NOTE
dev 명령어를 사용하려면 Node 22.13 이상이 필요합니다. Windows 환경에서는 concurrently npm 패키지로 대체 실행되며, 탭 인터페이스는 사용할 수 없습니다.
기본으로 실행되는 프로세스는 다음과 같습니다:
| 이름 | 명령어 |
|---|---|
server | php artisan serve |
queue | php artisan queue:listen --tries=1 --timeout=0 |
logs | php artisan pail --timeout=0 |
vite | npm run dev |
NOTE
vite 프로세스는 npm, pnpm, Yarn, Bun 중 현재 프로젝트에서 사용 중인 Node 패키지 매니저를 자동으로 감지하여 알맞은 실행 명령어를 사용합니다.
Dev 프로세스 커스터마이징
DevCommands 클래스를 이용하면 dev 명령어가 실행하는 프로세스 목록을 직접 구성할 수 있습니다. 보통 애플리케이션의 AppServiceProvider에 있는 boot 메서드 안에서 설정합니다. register 메서드는 실행할 명령어 문자열과 (선택적으로) 프로세스 이름을 인자로 받습니다:
use Illuminate\Foundation\DevCommands;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
DevCommands::register('some-command --flag', 'my-process');
}Artisan 명령어를 등록할 때는 artisan 메서드를 사용하면 됩니다. 이 메서드는 명령어 앞에 자동으로 php artisan을 붙여줍니다:
DevCommands::artisan('horizon', 'horizon');마찬가지로 node 메서드는 감지된 패키지 매니저의 실행 명령어(예: npm run)를 앞에 붙이고, nodeExec 메서드는 패키지 매니저의 exec 명령어(예: npx)를 앞에 붙입니다:
DevCommands::node('storybook', 'storybook');
DevCommands::nodeExec('tailwindcss -i resources/css/app.css -o public/css/app.css --watch', 'tailwind');기본 프로세스와 동일한 이름으로 프로세스를 등록하면 기본 프로세스를 대체합니다. 예를 들어 서버 프로세스가 다른 포트를 사용하도록 바꿀 수 있습니다:
DevCommands::artisan('serve --host=localhost --port=9000', 'server');터미널에 표시되는 프로세스 라벨의 색상도 지정할 수 있습니다. 사용 가능한 색상 메서드는 blue, purple, pink, orange, green, yellow이며, color 메서드에 직접 16진수 색상 코드를 전달할 수도 있습니다:
DevCommands::register('my-command', 'my-process')->green();
DevCommands::register('my-command', 'my-process')->color('#ff6347');등록된 dev 프로세스 목록을 실제로 실행하지 않고 확인하려면 dev:list 명령어를 사용하세요:
php artisan dev:list실패한 프로세스 재시작
프로세스가 중간에 멈추면, Laravel은 잠시 대기한 후 자동으로 재시작을 시도합니다. 최대 5번까지 재시도하며, 그래도 실패하면 해당 프로세스를 "실패" 상태로 표시합니다. 단, 시작 후 1초 이내에 종료된 프로세스는 애초에 정상적으로 시작되지 못한 것으로 간주하여 재시작하지 않습니다. r 키를 눌러 수동으로 프로세스를 재시작하면 재시도 횟수 카운터가 초기화됩니다.
이번 실행 한 번에 한해 자동 재시작을 끄려면 --no-restart 옵션을 사용하세요:
php artisan dev --no-restart또는 애플리케이션 전체에 대해 이 동작을 비활성화하려면 disableAutoRestart 메서드를 사용합니다:
DevCommands::disableAutoRestart();Dev 프로세스 필터링
only 메서드를 사용하면 dev 명령어 실행 시 지정한 프로세스만 실행하도록 할 수 있습니다. 반대로 except 메서드를 사용하면 특정 프로세스를 제외할 수 있습니다:
// server와 vite 프로세스만 실행...
DevCommands::only('server', 'vite');
// 큐 워커를 제외한 모든 프로세스 실행...
DevCommands::except('queue');패키지에서 등록한 명령어나 Laravel의 기본 명령어를 제외하려면 withoutVendorCommands, withoutDefaultCommands 메서드를 사용하면 됩니다:
DevCommands::withoutVendorCommands();
DevCommands::withoutDefaultCommands();스텁(Stub) 커스터마이징
Artisan 콘솔의 make 계열 명령어는 컨트롤러, Job, 마이그레이션, 테스트 등 다양한 클래스를 생성할 때 사용됩니다. 이 클래스들은 입력받은 값을 바탕으로 채워지는 "스텁(stub)" 파일을 기반으로 생성됩니다. 그런데 Artisan이 생성하는 파일을 약간씩 수정하고 싶을 때가 있습니다. 이럴 때는 stub:publish 명령어를 사용해서 자주 쓰이는 스텁 파일들을 애플리케이션에 퍼블리시한 뒤 자유롭게 수정할 수 있습니다:
php artisan stub:publish퍼블리시된 스텁 파일은 애플리케이션 루트의 stubs 디렉터리에 위치합니다. 이 스텁 파일들을 수정하면, 이후 Artisan의 make 명령어로 해당 종류의 클래스를 생성할 때마다 변경된 내용이 그대로 반영됩니다.
이벤트
Artisan은 명령어를 실행할 때 세 가지 이벤트를 발생시킵니다: Illuminate\Console\Events\ArtisanStarting, Illuminate\Console\Events\CommandStarting, Illuminate\Console\Events\CommandFinished입니다.
ArtisanStarting이벤트는 Artisan이 실행을 시작하는 즉시 발생합니다.CommandStarting이벤트는 각 명령어가 실행되기 직전에 발생합니다.CommandFinished이벤트는 명령어 실행이 끝난 직후에 발생합니다.
NOTE
예를 들어 모든 Artisan 명령어의 실행을 로깅하거나, 특정 명령어 실행 전후에 공통 로직을 끼워 넣고 싶을 때 이 이벤트들을 활용할 수 있습니다. 서비스 프로바이더의 boot 메서드에서 리스너를 등록해 사용하면 됩니다.