Livewire란
Laravel에서 동적인 UI를 만들려고 하면, 이전에는 Vue.js나 React 등의 JavaScript 프레임워크를 조합하거나, 스스로 AJAX 요청을 작성할 수밖에 없었습니다.
Livewire 는 그 문제를 해결하는 Laravel용 패키지입니다. PHP와 Blade만으로 리액티브한 UI를 구축할 수 있습니다. 폼의 실시간 유효성 검증, 라이브 검색, 카운터 등, 기존이라면 JavaScript가 필요했던 기능을 모두 PHP로 구현할 수 있습니다.
Livewire 4는 Laravel 10 이후, PHP 8.1 이후에서 동작합니다. 현재의 최신 버전은 Livewire 4입니다.
구조 개요
Livewire 컴포넌트는 서버 상의 PHP 클래스와 Blade 템플릿의 조합입니다. 사용자가 버튼을 클릭하거나 폼에 입력하면, Livewire는 뒤에서 AJAX 요청을 보내 PHP를 실행하고, 변경된 부분만을 페이지에 반영합니다. 개발자는 이 구조를 의식하지 않고 PHP만으로 기술할 수 있습니다.
Laravel 애플리케이션의 루트 디렉터리에서 다음 명령을 실행합니다.
composer require livewire/livewire
Laravel의 패키지 자동 검출이 유효하므로 추가 설정은 불필요합니다.
레이아웃 파일 생성
풀 페이지 컴포넌트로 사용할 경우 레이아웃 파일이 필요합니다. 다음 Artisan 명령으로 생성할 수 있습니다.
php artisan livewire:layout
resources/views/layouts/app.blade.php가 생성됩니다.
<! DOCTYPE html >
< html lang = " {{ str_replace ('_', '-', app () -> getLocale ()) }} " >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > {{ $title ?? config ( 'app.name' ) }} </ title >
@vite ([ 'resources/css/app.css' , 'resources/js/app.js' ])
@livewireStyles
</ head >
< body >
{{ $slot }}
@livewireScripts
</ body >
</ html >
@livewireStyles와 @livewireScripts가 Livewire와 Alpine.js의 애셋을 자동으로 로드합니다.
첫 컴포넌트
Livewire 컴포넌트를 생성하는 Artisan 명령이 준비되어 있습니다. 심플한 카운터를 만들어 봅시다.
php artisan make:livewire Counter
이 명령으로 resources/views/components/⚡counter.blade.php라는 단일 파일 컴포넌트가 생성됩니다.
파일명의 ⚡는 Livewire 컴포넌트를 한눈에 식별하기 위한 것입니다. 에디터에서의 시인성이 오릅니다. 불필요하면 설정으로 무효화할 수 있습니다.
생성된 파일을 다음과 같이 편집합니다.
<?php
use Livewire\ Component ;
new class extends Component {
public int $count = 0 ;
public function increment () : void
{
$this -> count ++ ;
}
public function decrement () : void
{
$this -> count -- ;
}
};
?>
< div >
< h1 > 카운트: {{ $count }} </ h1 >
< button wire:click = "increment" > +1 </ button >
< button wire:click = "decrement" > -1 </ button >
</ div >
Blade 템플릿에 이 컴포넌트를 삽입하려면 일반적인 Blade 컴포넌트 구문을 사용합니다.
버튼을 클릭할 때마다 페이지 리로드 없이 카운트가 갱신됩니다. wire:click이 JavaScript 대신 PHP 메서드를 호출하는 구조입니다.
프로퍼티와 액션
프로퍼티 — wire:model
wire:model 디렉티브로 입력 요소와 컴포넌트의 프로퍼티를 양방향 바인드할 수 있습니다.
<?php
use Livewire\ Component ;
new class extends Component {
public string $name = '' ;
public string $email = '' ;
};
?>
< div >
< input type = "text" wire:model = "name" placeholder = "이름" >
< input type = "email" wire:model = "email" placeholder = "이메일" >
< p > 안녕하세요, {{ $name }} 님 ( {{ $email }} ) </ p >
</ div >
기본으로는 wire:model은 액션(폼 송신 등)이 실행되었을 때만 서버와 동기화합니다. 입력할 때마다 동기화시키고 싶은 경우는 .live 수식자를 추가합니다.
표기법 동작 wire:model액션 실행 시(폼 송신 등)에만 동기화 (기본) wire:model.live입력할 때마다 요청 전송 wire:model.blur포커스를 잃었을 때 동기화 (요청은 발생하지 않음) wire:model.live.blur포커스를 잃었을 때 요청 전송
액션 — wire:click, wire:submit
wire:click은 클릭 이벤트, wire:submit은 폼의 송신 이벤트에 메서드를 연결합니다.
<?php
use Livewire\ Component ;
use App\Models\ Task ;
new class extends Component {
public string $taskName = '' ;
public function addTask () : void
{
Task :: create ([ 'name' => $this -> taskName ]);
$this -> taskName = '' ;
}
public function render ()
{
return $this -> view ([
'tasks' => Task :: latest () -> get (),
]);
}
};
?>
< div >
< form wire:submit = "addTask" >
< input type = "text" wire:model = "taskName" placeholder = "태스크명" >
< button type = "submit" > 추가 </ button >
</ form >
< ul >
@foreach ( $tasks as $task )
< li > {{ $task -> name }} </ li >
@endforeach
</ ul >
</ div >
실시간 유효성 검증
Livewire 4에서는 #[Validate] 어트리뷰트로 프로퍼티에 유효성 검증 룰을 직접 정의할 수 있습니다.
<?php
use Livewire\Attributes\ Validate ;
use Livewire\ Component ;
use App\Models\ Post ;
new class extends Component {
#[Validate('required|min:3')]
public string $title = '' ;
#[Validate('required|min:10')]
public string $content = '' ;
public function save () : void
{
$this -> validate ();
Post :: create ([
'title' => $this -> title ,
'content' => $this -> content ,
]);
$this -> reset ([ 'title' , 'content' ]);
session () -> flash ( 'message' , '게시물을 저장했습니다.' );
}
};
?>
< div >
@if ( session ( 'message' ))
< div > {{ session ( 'message' ) }} </ div >
@endif
< form wire:submit = "save" >
< div >
< input type = "text" wire:model.live.blur = "title" placeholder = "제목" >
@error ( 'title' ) < span style = "color: red;" > {{ $message }} </ span > @enderror
</ div >
< div >
< textarea wire:model.live.blur = "content" placeholder = "본문" ></ textarea >
@error ( 'content' ) < span style = "color: red;" > {{ $message }} </ span > @enderror
</ div >
< button type = "submit" > 저장 </ button >
</ form >
</ div >
#[Validate]를 붙인 프로퍼티는 갱신할 때마다 자동 유효성 검증이 실행됩니다. wire:model.live.blur와 조합함으로써, 포커스를 잃은 순간에 오류 메시지가 표시되는 실시간 유효성 검증 경험을 실현할 수 있습니다.
$this->validate()는 폼 송신 시 전 프로퍼티를 일괄 검증합니다. #[Validate]에 의한 자동 유효성 검증과 2단계로 사용하는 것이 권장 패턴입니다.
라이프사이클 훅
Livewire 컴포넌트에는 몇 가지 라이프사이클 훅이 있습니다.
훅 타이밍 mount()컴포넌트가 처음 생성될 때 (한 번만) boot()매 요청의 시작 시 (초기 · 후속 요청 양쪽) updating($property, $value)프로퍼티가 갱신되기 직전 updated($property)프로퍼티가 갱신된 직후 rendering()뷰의 렌더링 전 rendered()뷰의 렌더링 후 dehydrate()매 요청의 종료 시
mount() — 초기화
mount()는 컴포넌트의 초기화에 사용합니다. 생성자의 대신입니다.
<?php
use Illuminate\Support\Facades\ Auth ;
use Livewire\ Component ;
new class extends Component {
public string $name = '' ;
public string $email = '' ;
public function mount () : void
{
$this -> name = Auth :: user () -> name ;
$this -> email = Auth :: user () -> email ;
}
};
?>
< div >
< p > 이름: {{ $name }} </ p >
< p > 이메일: {{ $email }} </ p >
</ div >
updated() — 프로퍼티 변경 후 처리
프로퍼티가 갱신된 후에 처리를 끼워 넣고 싶은 경우는 updated()를 사용합니다. 특정 프로퍼티로 좁히는 경우는 메서드 이름에 포함시킵니다.
<?php
use Livewire\ Component ;
new class extends Component {
public string $username = '' ;
public function updatedUsername () : void
{
$this -> username = strtolower ( $this -> username );
}
};
?>
< div >
< input type = "text" wire:model.live = "username" >
< p > 사용자명: {{ $username }} </ p >
</ div >
입력할 때마다 자동으로 소문자로 변환됩니다.
Laravel과의 통합
Eloquent 모델 활용
Livewire는 프로퍼티에 직접 Eloquent 모델을 유지할 수 있습니다.
<?php
use Livewire\Attributes\ Validate ;
use Livewire\ Component ;
use App\Models\ User ;
new class extends Component {
public User $user ;
public function mount ( User $user ) : void
{
$this -> user = $user ;
}
#[Validate('required|min:2')]
public string $name = '' ;
public function save () : void
{
$this -> validate ();
$this -> user -> update ([ 'name' => $this -> name ]);
session () -> flash ( 'message' , '프로필을 갱신했습니다.' );
}
public function render ()
{
return $this -> view ();
}
};
?>
< div >
@if ( session ( 'message' ))
< div > {{ session ( 'message' ) }} </ div >
@endif
< form wire:submit = "save" >
< input type = "text" wire:model = "name" placeholder = "이름" >
@error ( 'name' ) < span style = "color: red;" > {{ $message }} </ span > @enderror
< button type = "submit" > 갱신 </ button >
</ form >
</ div >
폼 오브젝트
복잡한 폼은 폼 오브젝트로 분리함으로써 컴포넌트를 심플하게 유지할 수 있습니다.
php artisan livewire:form PostForm
namespace App\Livewire\Forms ;
use Livewire\Attributes\ Validate ;
use Livewire\ Form ;
use App\Models\ Post ;
class PostForm extends Form
{
#[ Validate ( 'required|min:3' )]
public string $title = '' ;
#[ Validate ( 'required|min:10' )]
public string $content = '' ;
public function store () : void
{
Post :: create ( $this -> only ([ 'title' , 'content' ]));
}
}
폼 오브젝트를 컴포넌트에서 사용합니다.
<?php
use App\Livewire\Forms\ PostForm ;
use Livewire\ Component ;
new class extends Component {
public PostForm $form ;
public function save () : void
{
$this -> form -> validate ();
$this -> form -> store ();
$this -> form -> reset ();
session () -> flash ( 'message' , '게시물을 작성했습니다.' );
}
};
?>
< div >
< form wire:submit = "save" >
< input type = "text" wire:model = "form.title" placeholder = "제목" >
@error ( 'form.title' ) < span style = "color: red;" > {{ $message }} </ span > @enderror
< textarea wire:model = "form.content" placeholder = "본문" ></ textarea >
@error ( 'form.content' ) < span style = "color: red;" > {{ $message }} </ span > @enderror
< button type = "submit" > 게시 </ button >
</ form >
</ div >
Livewire가 특히 적합한 유스케이스를 정리합니다.
유스케이스 Livewire로 실현할 수 있는 것 폼 처리 실시간 유효성 검증, 오류 표시 데이터 일람 라이브 검색, 정렬, 페이지네이션 카운터나 토글 페이지 리로드 없는 UI 갱신 관리 화면 Eloquent와의 직접 연동으로 CRUD 조작 위저드형 폼 스텝 관리를 PHP로 구현
Livewire는 “Blade를 사용해 본 적이 있다”라는 Laravel 엔지니어라면 오늘부터 사용을 시작할 수 있습니다. JavaScript 프레임워크의 학습 비용 없이 인터랙티브한 UI를 실현할 수 있는 것이 최대의 강점입니다.
SPA 급의 고도의 인터랙션이 필요한 장면에서는 Inertia.js가 적합하지만, 폼이나 관리 화면, 데이터 테이블 같은 용도라면 Livewire 쪽이 더 심플하게 구현할 수 있는 경우가 많습니다. 우선 작은 컴포넌트 하나부터 시험해 보세요.
Livewire 공식 문서 Livewire의 전체 기능(파일 업로드, 페이지네이션, 테스트 등)에 대해서는 공식 문서를 참고하세요.