Blade 템플릿

번역일: 2026년 7월 2일

Blade 템플릿

소개

Blade는 Laravel에 기본 내장된 템플릿 엔진입니다. 일부 PHP 템플릿 엔진과 달리, Blade는 뷰 파일 안에서 순수 PHP 코드를 자유롭게 사용할 수 있습니다. 실제로 모든 Blade 뷰는 PHP 코드로 컴파일된 후 캐시되므로, 별도의 오버헤드 없이 효율적으로 동작합니다. 캐시된 파일은 뷰가 변경될 때만 다시 컴파일됩니다.

Blade 뷰 파일은 .blade.php 확장자를 사용하며, 일반적으로 resources/views 디렉터리에 저장합니다. 라우트나 컨트롤러에서는 전역 view 헬퍼 함수를 통해 Blade 뷰를 반환할 수 있습니다.

Route::get('/', function () { return view('greeting', ['name' => '철수']); });

Livewire로 Blade 확장하기

Blade 템플릿을 한 단계 더 발전시키고 싶다면 Laravel Livewire를 살펴보세요. Livewire를 사용하면 Vue나 React 같은 프론트엔드 프레임워크 없이도 동적이고 반응형인 UI를 Blade 컴포넌트로 구현할 수 있습니다. 복잡한 JavaScript 빌드 과정 없이 현대적인 인터랙티브 UI를 만들 수 있는 강력한 방법입니다.

데이터 출력

뷰에 전달된 변수는 이중 중괄호({{ }})로 감싸서 출력합니다. 예를 들어, 다음과 같은 라우트가 있다면:

Route::get('/', function () { return view('welcome', ['name' => '철수']); });

뷰에서는 아래와 같이 name 변수를 출력할 수 있습니다.

안녕하세요, {{ $name }}.

NOTE

Blade의 {{ }} 구문은 XSS 공격을 방지하기 위해 PHP의 htmlspecialchars 함수를 자동으로 적용합니다.

Blade의 출력 구문에는 변수뿐만 아니라 어떤 PHP 표현식도 사용할 수 있습니다.

현재 UNIX 타임스탬프: {{ time() }}.

HTML 엔티티 인코딩

Blade(그리고 Laravel의 e 헬퍼)는 기본적으로 HTML 엔티티를 이중으로 인코딩합니다. 이중 인코딩을 비활성화하려면 AppServiceProviderboot 메서드에서 Blade::withoutDoubleEncoding을 호출하면 됩니다.

<?php namespace App\Providers; use Illuminate\Support\Facades\Blade; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * 애플리케이션 서비스를 초기화합니다. */ public function boot(): void { Blade::withoutDoubleEncoding(); } }

이스케이프 없이 데이터 출력하기

기본적으로 {{ }} 구문은 HTML 이스케이프가 적용됩니다. 이스케이프 처리 없이 원시 HTML을 출력하려면 {!! !!} 구문을 사용하세요.

안녕하세요, {!! $name !!}.

WARNING

{!! !!}를 사용할 때는 사용자가 입력한 데이터를 그대로 출력하지 않도록 주의해야 합니다. XSS 공격을 방지하려면 반드시 신뢰할 수 있는 데이터에만 사용하세요.

Blade와 JavaScript 프레임워크

Vue나 Alpine.js 같은 JavaScript 프레임워크도 이중 중괄호를 사용하는 경우가 많습니다. 이 경우 @ 기호를 앞에 붙이면 Blade가 해당 표현식을 처리하지 않고 그대로 남겨둡니다. 이렇게 하면 JavaScript 프레임워크가 브라우저에서 직접 처리하게 됩니다.

<h1>Laravel</h1> 안녕하세요, @{{ name }}.

위 예시에서 @ 기호는 Blade가 제거하고, {{ name }} 표현식은 그대로 HTML에 남아 JavaScript 프레임워크가 처리합니다.

@ 기호는 Blade 디렉티브를 이스케이프하는 데도 사용할 수 있습니다.

{{-- Blade 템플릿 --}} @@if() <!-- HTML 출력 결과 --> @if()

JSON 렌더링

JavaScript 변수를 초기화하기 위해 뷰에 배열을 전달하고 JSON으로 렌더링해야 할 때가 있습니다. 예를 들면:

<script> var app = <?php echo json_encode($array); ?>; </script>

이 대신 Illuminate\Support\Js::from 메서드를 사용할 수 있습니다. 이 메서드는 PHP의 json_encode와 동일한 인자를 받으며, 결과 JSON이 HTML 속성 안에 안전하게 포함될 수 있도록 적절히 이스케이프 처리합니다. from 메서드는 주어진 객체나 배열을 JavaScript 객체로 변환하는 JSON.parse 구문 문자열을 반환합니다.

<script> var app = {{ Illuminate\Support\Js::from($array) }}; </script>

최신 버전의 Laravel 애플리케이션 스켈레톤에는 Js 파사드가 포함되어 있어 더 간결하게 작성할 수 있습니다.

<script> var app = {{ Js::from($array) }}; </script>

WARNING

Js::from은 기존 변수를 JSON으로 렌더링할 때만 사용하세요. Blade 템플릿 엔진은 정규식 기반으로 동작하므로, 복잡한 표현식을 이 구문에 전달하면 예상치 못한 오류가 발생할 수 있습니다.

`@verbatim` 디렉티브

뷰의 넓은 영역에서 JavaScript 변수를 출력해야 할 때, 모든 표현식 앞에 @를 붙이는 대신 @verbatim 디렉티브로 해당 영역을 감쌀 수 있습니다. 감싼 영역 내의 이중 중괄호는 Blade가 처리하지 않습니다.

@verbatim <div class="container"> 안녕하세요, {{ name }}. </div> @endverbatim

Blade 디렉티브

Blade는 이중 중괄호를 이용한 변수 출력 외에도, 조건문·반복문 등 일반적인 PHP 제어 구조를 더 간결하게 표현할 수 있는 다양한 디렉티브를 제공합니다. 이 디렉티브들은 PHP 제어 구조와 완전히 동일하게 동작하면서도 코드를 훨씬 읽기 쉽게 만들어 줍니다.

조건문

조건문은 @if, @elseif, @else, @endif 디렉티브를 사용합니다. 이 디렉티브들은 PHP의 if, elseif, else와 완전히 동일하게 동작합니다.

@if (count($records) === 1) 레코드가 하나 있습니다! @elseif (count($records) > 1) 레코드가 여러 개 있습니다! @else 레코드가 없습니다. @endif

편의를 위해 @unless 디렉티브도 제공합니다.

@unless (Auth::check()) 로그인이 필요합니다. @endunless

앞서 소개한 디렉티브 외에도 @isset@empty 디렉티브를 각각 PHP의 issetempty 함수의 단축 표현으로 사용할 수 있습니다.

@isset($records) // $records가 정의되어 있고 null이 아닌 경우 @endisset @empty($records) // $records가 "비어 있는" 경우 @endempty

인증 디렉티브

@auth@guest 디렉티브를 사용하면 현재 사용자가 인증된 상태인지, 비인증(게스트) 상태인지를 쉽게 확인할 수 있습니다.

@auth // 인증된 사용자에게만 표시 @endauth @guest // 비인증 사용자(게스트)에게만 표시 @endguest

필요한 경우 @auth@guest 디렉티브에 특정 인증 가드를 지정할 수도 있습니다.

@auth('admin') // 관리자로 인증된 경우 @endauth @guest('admin') // 관리자로 인증되지 않은 경우 @endguest

환경 디렉티브

@production 디렉티브로 애플리케이션이 프로덕션 환경에서 실행 중인지 확인할 수 있습니다.

@production // 프로덕션 전용 콘텐츠 @endproduction

특정 환경을 직접 지정하려면 @env 디렉티브를 사용합니다.

@env('staging') // 스테이징 환경에서 실행 중인 경우 @endenv @env(['staging', 'production']) // 스테이징 또는 프로덕션 환경에서 실행 중인 경우 @endenv

섹션 디렉티브

@hasSection 디렉티브로 특정 템플릿 상속 섹션에 콘텐츠가 있는지 확인할 수 있습니다.

@hasSection('navigation') <div class="pull-right"> @yield('navigation') </div> <div class="clearfix"></div> @endif

@sectionMissing 디렉티브를 사용하면 섹션에 콘텐츠가 없는 경우를 확인할 수 있습니다.

@sectionMissing('navigation') <div class="pull-right"> @include('default-navigation') </div> @endif

세션 디렉티브

@session 디렉티브를 사용하면 세션 값이 존재하는지 확인할 수 있습니다. 세션 값이 존재하면 $value 변수를 통해 해당 값에 접근할 수 있습니다.

@session('status') <div class="p-4 bg-green-100"> {{ $value }} </div> @endsession

Switch 문

Switch 문은 @switch, @case, @break, @default, @endswitch 디렉티브로 작성합니다.

@switch($i) @case(1) 첫 번째 케이스 @break @case(2) 두 번째 케이스 @break @default 기본 케이스 @endswitch

반복문

Blade는 PHP의 반복문에 대응하는 디렉티브를 제공합니다.

@for ($i = 0; $i < 10; $i++) 현재 값: {{ $i }} @endfor @foreach ($users as $user) <p>사용자: {{ $user->id }}</p> @endforeach @forelse ($users as $user) <li>{{ $user->name }}</li> @empty <p>사용자가 없습니다.</p> @endforelse @while (true) <p>무한 반복 중입니다.</p> @endwhile

NOTE

@foreach 반복문 안에서는 loop 변수를 활용해 첫 번째 반복인지, 마지막 반복인지 등 유용한 정보를 손쉽게 확인할 수 있습니다.

반복문 안에서 @continue@break 디렉티브를 사용해 현재 반복을 건너뛰거나 반복문을 종료할 수 있습니다.

@foreach ($users as $user) @if ($user->type == 1) @continue @endif <li>{{ $user->name }}</li> @if ($user->number == 5) @break @endif @endforeach

조건을 디렉티브에 직접 포함할 수도 있습니다.

@foreach ($users as $user) @continue($user->type == 1) <li>{{ $user->name }}</li> @break($user->number == 5) @endforeach

Loop 변수

@foreach 반복문 안에서는 $loop 변수를 사용할 수 있습니다. 이 변수를 통해 현재 반복 인덱스, 첫 번째/마지막 반복 여부 등 다양한 정보에 접근할 수 있습니다.

@foreach ($users as $user) @if ($loop->first) 첫 번째 항목입니다. @endif @if ($loop->last) 마지막 항목입니다. @endif <p>사용자: {{ $user->id }}</p> @endforeach

중첩 반복문에서는 $loop->parent를 통해 부모 반복문의 $loop 변수에 접근할 수 있습니다.

@foreach ($users as $user) @foreach ($user->posts as $post) @if ($loop->parent->first) 첫 번째 사용자의 첫 번째 게시글입니다. @endif @endforeach @endforeach

$loop 변수가 제공하는 전체 프로퍼티 목록은 다음과 같습니다.

프로퍼티설명
$loop->index현재 반복의 인덱스 (0부터 시작)
$loop->iteration현재 반복 횟수 (1부터 시작)
$loop->remaining남은 반복 횟수
$loop->count반복 중인 배열의 전체 항목 수
$loop->first첫 번째 반복인지 여부
$loop->last마지막 반복인지 여부
$loop->even현재 반복이 짝수 번째인지 여부
$loop->odd현재 반복이 홀수 번째인지 여부
$loop->depth중첩 반복문의 깊이
$loop->parent중첩 반복문에서 부모 loop 변수

조건부 클래스

@class 디렉티브를 사용하면 특정 조건에 따라 CSS 클래스를 동적으로 추가할 수 있습니다. 배열 키에 클래스명을, 배열 값에 조건(Boolean 표현식)을 지정합니다. 배열 키가 숫자인 경우에는 조건과 무관하게 항상 클래스 목록에 포함됩니다.

@php $isActive = false; $hasError = true; @endphp <span @class([ 'p-4', 'font-bold' => $isActive, 'text-gray-500' => ! $isActive, 'bg-red' => $hasError, ])></span> <!-- 출력 결과 --> <span class="p-4 text-gray-500 bg-red"></span>

추가 속성

@style 디렉티브를 사용하면 CSS 인라인 스타일을 조건에 따라 동적으로 추가할 수 있습니다. @class와 마찬가지로 배열 키에 스타일을, 값에 조건을 지정합니다.

@php $isActive = true; @endphp <span @style([ 'background-color: red', 'font-weight: bold' => $isActive, ])></span> <!-- 출력 결과 --> <span style="background-color: red; font-weight: bold;"></span>

@checked 디렉티브를 사용하면 HTML 체크박스의 checked 속성을 조건에 따라 쉽게 설정할 수 있습니다.

<input type="checkbox" name="active" value="active" @checked(old('active', $user->active)) />

@selected 디렉티브는 select 옵션의 selected 속성을 조건에 따라 설정할 때 사용합니다.

<select name="version"> @foreach ($product->versions as $version) <option value="{{ $version }}" @selected(old('version') == $version)> {{ $version }} </option> @endforeach </select>

@disabled 디렉티브는 요소의 disabled 속성을 조건에 따라 설정합니다.

<button type="submit" @disabled($errors->isNotEmpty())>제출</button>

@readonly 디렉티브는 입력 필드의 readonly 속성을 조건에 따라 설정합니다.

<input type="email" name="email" value="이메일@예시.com" @readonly($user->isNotAdmin()) />

@required 디렉티브는 입력 필드의 required 속성을 조건에 따라 설정합니다.

<input type="text" name="title" value="제목" @required($user->isAdmin()) />

서브뷰 포함하기

NOTE

@include 디렉티브를 사용하는 것도 좋지만, Blade 컴포넌트를 사용하면 데이터와 속성 바인딩 등 더 강력한 기능을 활용할 수 있습니다.

@include 디렉티브를 사용하면 다른 Blade 뷰를 현재 뷰 안에 포함시킬 수 있습니다. 부모 뷰에서 사용 가능한 모든 변수는 포함된 뷰에서도 그대로 사용할 수 있습니다.

<div> @include('shared.errors') <form> <!-- 폼 내용 --> </form> </div>

포함된 뷰는 부모 뷰의 모든 변수를 상속받으며, 추가 데이터를 배열로 전달할 수도 있습니다.

@include('view.name', ['status' => '완료'])

존재하지 않는 뷰를 @include하면 오류가 발생합니다. 뷰가 없을 수도 있는 경우에는 @includeIf 디렉티브를 사용하세요.

@includeIf('view.name', ['status' => '완료'])

특정 Boolean 표현식이 참이거나 거짓일 때만 뷰를 포함하려면 @includeWhen@includeUnless 디렉티브를 사용합니다.

@includeWhen($boolean, 'view.name', ['status' => '완료']) @includeUnless($boolean, 'view.name', ['status' => '완료'])

주어진 뷰 배열 중 존재하는 첫 번째 뷰를 포함하려면 @includeFirst 디렉티브를 사용합니다.

@includeFirst(['custom.admin', 'admin'], ['status' => '완료'])

WARNING

Blade 뷰에서는 __DIR____FILE__ 상수 사용을 피하세요. 이 상수들은 컴파일된 캐시 파일의 경로를 반환하므로 의도한 결과가 나오지 않을 수 있습니다.

컬렉션을 뷰로 렌더링하기

반복문과 @include를 하나의 디렉티브로 합칠 수 있습니다. @each 디렉티브는 배열이나 컬렉션의 각 항목에 대해 뷰를 렌더링합니다.

@each('view.name', $jobs, 'job')

첫 번째 인자는 렌더링할 뷰, 두 번째 인자는 반복할 배열 또는 컬렉션, 세 번째 인자는 뷰 안에서 현재 항목에 접근할 변수명입니다. 예를 들어 jobs 배열을 반복하는 경우, 각 항목은 뷰 안에서 job 변수로 접근할 수 있습니다.

네 번째 인자로 배열이 비어 있을 때 렌더링할 뷰를 지정할 수도 있습니다.

@each('view.name', $jobs, 'job', 'view.empty')

WARNING

@each로 렌더링되는 뷰는 부모 뷰의 변수를 상속받지 않습니다. 자식 뷰에서 부모 변수가 필요하다면 @foreach@include를 조합해 사용하세요.

`@once` 디렉티브

@once 디렉티브는 해당 블록의 내용이 렌더링 사이클당 단 한 번만 출력되도록 합니다. 예를 들어 반복문 안에서 특정 JavaScript를 페이지 <head>에 한 번만 삽입하고 싶을 때 유용합니다.

@foreach ($products as $product) @once @push('scripts') <script> // ... </script> @endpush @endonce @endforeach

@once@push는 자주 함께 사용되므로, @pushOnce 디렉티브로 간결하게 작성할 수 있습니다.

@pushOnce('scripts') <script> // ... </script> @endPushOnce

Raw PHP

뷰 안에서 순수 PHP 코드를 실행해야 할 경우 @php 디렉티브를 사용합니다.

@php $counter = 1; @endphp

PHP 클래스를 임포트할 때만 PHP가 필요한 경우에는 @use 디렉티브를 사용할 수 있습니다.

@use('App\Models\Flight')

@use 디렉티브의 두 번째 인자로 별칭을 지정할 수 있습니다.

@use('App\Models\Flight', 'FlightModel')

주석

Blade에서 주석을 작성할 때는 다음 구문을 사용합니다. HTML 주석과 달리, Blade 주석은 렌더링된 HTML에 포함되지 않습니다.

{{-- 이 주석은 렌더링된 HTML에 표시되지 않습니다. --}}

컴포넌트

컴포넌트와 슬롯은 섹션(section), 레이아웃(layout), 인클루드(include)와 유사한 기능을 제공하지만, 컴포넌트의 개념 모델이 더 직관적으로 느껴질 수 있습니다. 컴포넌트를 정의하는 방법은 크게 두 가지로, 클래스 기반 컴포넌트와 익명 컴포넌트가 있습니다.

클래스 기반 컴포넌트를 생성하려면 make:component Artisan 명령어를 사용합니다. 컴포넌트 사용법을 익히기 위해 간단한 Alert 컴포넌트를 만들어 보겠습니다. make:component 명령어를 실행하면 컴포넌트 클래스가 app/View/Components 디렉터리에, 뷰 파일이 resources/views/components 디렉터리에 각각 생성됩니다.

php artisan make:component Alert

서브디렉터리 안에 컴포넌트를 생성하려면 다음과 같이 입력합니다.

php artisan make:component Forms/Input

이 명령을 실행하면 app/View/Components/Forms/Input.php 클래스와 resources/views/components/forms/input.blade.php 뷰 파일이 생성됩니다.

NOTE

익명 컴포넌트(Blade 파일만 있고 클래스가 없는 컴포넌트)를 생성하려면 make:component 명령에 --view 옵션을 추가합니다.

php artisan make:component forms.input --view

패키지 컴포넌트 수동 등록

자체 애플리케이션에서 컴포넌트를 만들면 Laravel이 app/View/Componentsresources/views/components 디렉터리를 자동으로 탐색하여 컴포넌트를 찾습니다.

하지만 Blade 컴포넌트를 사용하는 패키지를 개발하는 경우에는 컴포넌트 클래스와 HTML 태그 별칭을 직접 등록해야 합니다. 서비스 프로바이더의 boot 메서드에서 컴포넌트를 등록합니다.

use Illuminate\Support\Facades\Blade; /** * 패키지 서비스를 초기화합니다. */ public function boot(): void { Blade::component('package-alert', Alert::class); }

등록 후에는 태그 별칭으로 컴포넌트를 렌더링할 수 있습니다.

<x-package-alert/>

또는 componentNamespace 메서드를 사용해 컴포넌트 클래스를 네임스페이스 규칙에 따라 자동 로드할 수 있습니다. 예를 들어 Nightshade 패키지에 Package\Views\Components 네임스페이스의 Calendar, ColorPicker 컴포넌트가 있다면:

use Illuminate\Support\Facades\Blade; /** * 패키지 서비스를 초기화합니다. */ public function boot(): void { Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); }

이렇게 하면 패키지명::컴포넌트명 형식으로 컴포넌트를 사용할 수 있습니다.

<x-nightshade::calendar /> <x-nightshade::color-picker />

Blade는 컴포넌트 이름을 파스칼 케이스(PascalCase)로 변환하여 해당 클래스를 자동으로 찾아냅니다.

컴포넌트 렌더링

Blade 템플릿에서 컴포넌트를 렌더링하려면 x- 접두사를 붙인 태그를 사용합니다.

<x-alert/> <x-user-profile/>

컴포넌트 클래스가 app/View/Components 디렉터리 하위에 중첩된 경우, .으로 디렉터리 구조를 표현합니다.

<x-inputs.button/>

컴포넌트를 조건에 따라 렌더링하고 싶다면 컴포넌트 클래스에 shouldRender 메서드를 정의하면 됩니다. 이 메서드가 false를 반환하면 컴포넌트가 렌더링되지 않습니다.

use Illuminate\Support\Str; /** * 컴포넌트를 렌더링할지 여부를 반환합니다. */ public function shouldRender(): bool { return Str::length($this->message) > 0; }

인덱스 컴포넌트

컴포넌트가 여러 Blade 템플릿으로 구성되는 경우, 해당 컴포넌트의 뷰 파일들을 하나의 디렉터리에 모아 관리하고 싶을 수 있습니다. 예를 들어 다음과 같은 디렉터리 구조의 accordion 컴포넌트가 있다고 가정해 보겠습니다.

/resources/views/components/accordion.blade.php
/resources/views/components/accordion/item.blade.php

이 구조를 사용하면 accordion 컴포넌트와 그 하위 item 컴포넌트를 아래처럼 렌더링할 수 있습니다.

<x-accordion> <x-accordion.item> ... </x-accordion.item> </x-accordion>

그런데 accordion.blade.php 파일을 accordion 디렉터리 안으로 옮기고 싶다면, 파일 이름을 index.blade.php로 변경하면 됩니다.

/resources/views/components/accordion/index.blade.php
/resources/views/components/accordion/item.blade.php

디렉터리를 index.blade.php로 대표하는 방식은 다른 프레임워크나 JavaScript 생태계에서도 흔히 볼 수 있는 패턴입니다. 변경 후에도 동일한 방식으로 컴포넌트를 사용할 수 있습니다.

<x-accordion> <x-accordion.item> ... </x-accordion.item> </x-accordion>

컴포넌트에 데이터 전달하기

HTML 속성을 통해 Blade 컴포넌트에 데이터를 전달할 수 있습니다. 단순한 문자열과 같은 기본 타입은 일반 HTML 속성으로 전달하고, PHP 표현식이나 변수는 속성 이름 앞에 :을 붙여 전달합니다.

<x-alert type="error" :message="$message"/>

컴포넌트에서 사용할 데이터는 클래스 생성자에서 정의합니다. 생성자의 모든 public 프로퍼티는 컴포넌트 뷰에서 자동으로 사용할 수 있습니다. 데이터를 뷰에 전달할 때 render 메서드에서 별도로 처리하지 않아도 됩니다.

<?php namespace App\View\Components; use Illuminate\View\Component; use Illuminate\View\View; class Alert extends Component { /** * 컴포넌트 인스턴스를 생성합니다. */ public function __construct( public string $type, public string $message, ) {} /** * 컴포넌트를 나타내는 뷰를 반환합니다. */ public function render(): View { return view('components.alert'); } }

컴포넌트가 렌더링될 때, public 프로퍼티 값을 변수명으로 접근하여 뷰에서 출력할 수 있습니다.

<div class="alert alert-{{ $type }}"> {{ $message }} </div>

케이스 표기법

컴포넌트 생성자의 인자는 카멜 케이스(camelCase)로 작성하고, HTML 속성에서는 케밥 케이스(kebab-case)를 사용합니다. 예를 들어 생성자 인자가 $alertType이라면 HTML에서는 alert-type으로 전달합니다.

<x-alert alert-type="danger" />

축약 속성 문법

컴포넌트에 속성을 전달할 때, "축약 속성" 문법을 사용할 수 있습니다. 속성 이름과 변수명이 같을 때 특히 편리합니다.

{{-- 축약 속성 문법 --}} <x-profile :$userId :$name /> {{-- 아래와 동일합니다 --}} <x-profile :user-id="$userId" :name="$name" />

속성 렌더링 이스케이프

Alpine.js와 같은 일부 JavaScript 프레임워크에서도 콜론(:) 접두사 속성을 사용합니다. 이때 Blade에게 해당 속성이 PHP 표현식이 아님을 알리려면 이중 콜론(::)을 사용하세요.

<x-button ::class="{ danger: isDeleting }"> 삭제 </x-button>

Blade가 렌더링하면 다음과 같이 출력됩니다.

<button :class="{ danger: isDeleting }"> 삭제 </button>

컴포넌트 메서드

컴포넌트 클래스의 public 프로퍼티뿐만 아니라 public 메서드도 뷰에서 호출할 수 있습니다. 예를 들어 isSelected 메서드가 있다면:

/** * 주어진 옵션이 현재 선택되어 있는지 확인합니다. */ public function isSelected(string $option): bool { return $option === $this->selected; }

컴포넌트 뷰에서 메서드 이름과 동일한 변수처럼 호출할 수 있습니다.

<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}"> {{ $label }} </option>

컴포넌트 클래스 안에서 속성과 슬롯 접근하기

Blade 컴포넌트에서는 클래스의 render 메서드 안에서 컴포넌트 이름, 속성, 슬롯에 접근할 수 있습니다. 단, 이 데이터에 접근하려면 render 메서드가 클로저를 반환해야 합니다.

use Closure; /** * 컴포넌트를 나타내는 뷰 / 콘텐츠를 반환합니다. */ public function render(): Closure { return function () { return '<div {{ $attributes }}>컴포넌트 내용</div>'; }; }

반환하는 클로저는 $data 배열을 인자로 받을 수 있습니다. 이 배열에는 컴포넌트에 대한 다양한 정보가 포함됩니다.

return function (array $data) { // $data['componentName']; // $data['attributes']; // $data['slot']; return '<div {{ $attributes }}>컴포넌트 내용</div>'; }

WARNING

render 메서드에서 반환하는 클로저에 $data 파라미터를 정의하더라도, 이름이 바인딩되는 방식이 아닌 위치 기반으로 전달됩니다. 즉, $data 외에 다른 이름을 사용해도 동작하지만, 관례적으로 $data를 사용합니다.

componentName은 HTML 태그에서 x- 접두사 뒤에 오는 이름과 같습니다. 따라서 <x-alert />componentNamealert입니다. attributes는 HTML 태그에 지정된 모든 속성을, slot은 슬롯 내용을 담고 있습니다.

클로저는 문자열을 반환해야 합니다. 반환된 문자열이 존재하는 뷰 이름과 일치하면 해당 뷰가 렌더링되고, 일치하지 않으면 인라인 Blade 템플릿으로 처리됩니다.

추가 의존성 주입

컴포넌트가 Laravel의 서비스 컨테이너에서 의존성을 주입받아야 한다면, 생성자에서 데이터 속성보다 먼저 타입 힌트로 선언하면 됩니다. 컨테이너가 자동으로 해당 의존성을 주입해 줍니다.

use App\Services\AlertCreator; /** * 컴포넌트 인스턴스를 생성합니다. */ public function __construct( public AlertCreator $creator, public string $type, public string $message, ) {}

속성/메서드 노출 제한

컴포넌트 클래스의 특정 public 메서드나 프로퍼티가 뷰 변수로 노출되지 않게 하려면 $except 배열에 해당 이름을 추가합니다.

<?php namespace App\View\Components; use Illuminate\View\Component; class Alert extends Component { /** * 뷰 변수로 노출하지 않을 프로퍼티/메서드 목록 * * @var array */ protected $except = ['type']; /** * 컴포넌트 인스턴스를 생성합니다. */ public function __construct( public string $type, ) {} }

컴포넌트 속성

데이터 속성을 전달하는 방법은 이미 살펴봤습니다. 하지만 컴포넌트 클래스에서 정의하지 않은 추가 HTML 속성(예: class)을 전달해야 할 때도 있습니다. 이런 추가 속성은 컴포넌트의 루트 요소에 전달하는 것이 일반적입니다. 예를 들어 alert 컴포넌트를 다음과 같이 렌더링한다고 가정해 보겠습니다.

<x-alert type="error" :message="$message" class="mt-4"/>

typemessage는 컴포넌트 생성자에 정의된 속성이고, class는 컴포넌트 클래스에서 알지 못하는 추가 속성입니다. 이런 추가 속성들은 $attributes 변수로 컴포넌트 뷰에서 접근할 수 있습니다.

<div {{ $attributes }}> <!-- 컴포넌트 내용 --> </div>

WARNING

현재 컴포넌트 태그 안에서 @env와 같은 디렉티브를 사용하는 것은 지원되지 않습니다. 예를 들어 <x-alert :live="@env('production')"/>는 컴파일되지 않습니다.

기본값 및 속성 병합

속성에 기본값을 지정하거나 추가 값을 병합하려면 $attributes 변수의 merge 메서드를 사용합니다. 이 기능은 항상 특정 CSS 클래스를 포함해야 하는 컴포넌트에 특히 유용합니다.

<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}> {{ $message }} </div>

이 컴포넌트를 다음과 같이 사용하면:

<x-alert type="error" :message="$message" class="mb-4"/>

최종 렌더링 결과는 다음과 같습니다.

<div class="alert alert-error mb-4"> <!-- $message 변수 내용 --> </div>

조건부 클래스 병합

특정 조건이 참일 때만 클래스를 병합하려면 class 메서드를 사용합니다. 이 메서드는 클래스 배열을 받으며, 배열 키는 추가할 클래스, 배열 값은 Boolean 표현식입니다.

<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}> {{ $message }} </div>

다른 속성도 함께 병합하려면 class 메서드 뒤에 merge 메서드를 체이닝합니다.

<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}> {{ $slot }} </button>

NOTE

병합된 속성을 받지 않아야 하는 다른 HTML 요소에 조건부 클래스를 적용하려면 @class 디렉티브를 사용하세요.

class 외 속성 병합

class 이외의 속성은 merge 메서드로 기본값을 지정할 수 있습니다. 이 속성들은 전달된 값이 기본값을 완전히 대체합니다.

<button {{ $attributes->merge(['type' => 'button']) }}> {{ $slot }} </button>

버튼 컴포넌트를 렌더링할 때 type을 별도로 지정하지 않으면 button이 기본값으로 사용됩니다.

<x-button>로그인</x-button>

렌더링 결과:

<button type="button"> 로그인 </button>

type 외에 다른 값을 지정하면 기본값 대신 해당 값이 사용됩니다.

<x-button type="submit">로그인</x-button>

렌더링 결과:

<button type="submit"> 로그인 </button>

class 이외의 속성에서 기본값과 전달된 값을 모두 포함하고 싶다면 prepends 메서드를 사용합니다.

<div {{ $attributes->merge(['data-controller' => $attributes->prepends('profile-controller')]) }}> {{ $slot }} </div>

속성 조회와 필터링

filter 메서드를 사용하면 특정 조건에 맞는 속성만 필터링할 수 있습니다. 이 메서드는 클로저를 인자로 받으며, 클로저가 true를 반환하는 속성만 유지됩니다.

{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }}

편의를 위해 whereStartsWith 메서드를 사용해 특정 문자열로 시작하는 속성만 가져올 수 있습니다.

{{ $attributes->whereStartsWith('wire:model') }}

반대로, whereDoesntStartWith 메서드를 사용하면 특정 문자열로 시작하는 속성을 제외할 수 있습니다.

{{ $attributes->whereDoesntStartWith('wire:model') }}

first 메서드를 사용하면 주어진 속성 백에서 첫 번째 속성을 렌더링할 수 있습니다.

{{ $attributes->whereStartsWith('wire:model')->first() }}

컴포넌트에 특정 속성이 있는지 확인하려면 has 메서드를 사용합니다. 이 메서드는 속성 이름을 인자로 받으며, 해당 속성이 존재하면 true를 반환합니다.

@if ($attributes->has('class')) <div>class 속성이 있습니다.</div> @endif

배열을 전달하면 배열에 있는 모든 속성이 존재하는지 확인합니다.

@if ($attributes->has(['name', 'class'])) <div>name과 class 속성이 모두 있습니다.</div> @endif

hasAny 메서드를 사용하면 지정한 속성 중 하나라도 존재하는지 확인합니다.

@if ($attributes->hasAny(['href', ':href', 'v-bind:href'])) <div>href 속성이 있습니다.</div> @endif

get 메서드를 사용해 특정 속성값을 가져올 수 있습니다.

{{ $attributes->get('class') }}

예약 키워드

Blade가 내부적으로 사용하는 일부 키워드는 컴포넌트 생성자의 인자명으로 사용할 수 없습니다. 다음 키워드들은 컴포넌트 프로퍼티명으로 예약되어 있습니다.

  • data
  • render
  • resolveView
  • shouldRender
  • view
  • withAttributes
  • withName

슬롯

컴포넌트에 추가 콘텐츠를 전달하려면 "슬롯"을 활용합니다. 컴포넌트 슬롯은 $slot 변수를 출력하여 렌더링합니다. 예를 들어 alert 컴포넌트의 뷰가 아래와 같다면:

<!-- /resources/views/components/alert.blade.php --> <div class="alert alert-danger"> {{ $slot }} </div>

컴포넌트에 콘텐츠를 삽입하여 슬롯을 채울 수 있습니다.

<x-alert> <strong>이런!</strong> 문제가 발생했습니다. </x-alert>

컴포넌트 안에 여러 개의 슬롯을 배치해야 할 때도 있습니다. "title" 슬롯을 위한 플레이스홀더를 추가해 보겠습니다.

<!-- /resources/views/components/alert.blade.php --> <span class="alert-title">{{ $title }}</span> <div class="alert alert-danger"> {{ $slot }} </div>

<x-slot> 태그를 사용해 명명된 슬롯의 내용을 정의합니다. <x-slot> 태그 안에 없는 콘텐츠는 $slot 변수를 통해 기본 슬롯으로 전달됩니다.

<x-alert> <x-slot:title> 서버 오류 </x-slot> <strong>이런!</strong> 문제가 발생했습니다. </x-alert>

스코프 슬롯

Vue와 같은 JavaScript 프레임워크를 사용해 본 경험이 있다면 "스코프 슬롯"에 익숙할 것입니다. 스코프 슬롯을 사용하면 슬롯 내에서 컴포넌트의 데이터나 메서드에 접근할 수 있습니다.

컴포넌트 클래스에 public 메서드 또는 프로퍼티가 있고, 슬롯 안에서 이를 $component 변수를 통해 접근하는 방식으로 사용할 수 있습니다.

<x-alert> <x-slot:title> {{ $component->formatAlert('서버 오류') }} </x-slot> <strong>이런!</strong> 문제가 발생했습니다. </x-alert>

슬롯 속성

Blade 컴포넌트와 마찬가지로, 슬롯에도 CSS 클래스와 같은 추가 속성을 지정할 수 있습니다.

<x-card class="shadow-sm"> <x-slot:heading class="font-bold"> 제목 </x-slot> 내용 </x-card>

슬롯 속성에 접근하려면 슬롯 변

Blade 템플릿

목차


소개

Blade는 Laravel에 기본 내장된 간결하면서도 강력한 템플릿 엔진입니다. 일부 PHP 템플릿 엔진과 달리, Blade는 템플릿 안에서 순수 PHP 코드를 사용하는 것을 전혀 제한하지 않습니다. 실제로 모든 Blade 템플릿은 순수 PHP 코드로 컴파일된 뒤 파일이 변경될 때까지 캐시되므로, 애플리케이션 성능에 사실상 추가 부하가 없습니다.

Blade 템플릿 파일은 .blade.php 확장자를 사용하며, 일반적으로 resources/views 디렉터리에 저장합니다.

Blade 뷰는 전역 view 헬퍼를 통해 라우트나 컨트롤러에서 반환할 수 있습니다. 뷰 문서에서 설명하듯이, view 헬퍼의 두 번째 인자로 데이터를 전달할 수 있습니다.

Route::get('/', function () { return view('greeting', ['name' => '김철수']); });

Livewire로 Blade 확장하기

Blade 템플릿을 한 단계 더 발전시켜 동적인 인터페이스를 손쉽게 구축하고 싶다면 Laravel Livewire를 살펴보세요. Livewire를 사용하면 React, Svelte, Vue 같은 프론트엔드 프레임워크에서나 가능했던 동적 기능을 Blade 컴포넌트에 직접 추가할 수 있습니다. 복잡한 설정, 클라이언트 사이드 렌더링, 별도의 빌드 과정 없이도 현대적이고 반응형인 프론트엔드를 구축할 수 있는 실용적인 접근 방식입니다.

데이터 출력

Blade 뷰에 전달된 데이터는 중괄호({{ }})로 감싸서 출력할 수 있습니다. 예를 들어 다음과 같은 라우트가 있다면:

Route::get('/', function () { return view('welcome', ['name' => 'Samantha']); });

뷰에서 name 변수를 아래처럼 출력합니다:

Hello, {{ $name }}.

NOTE

Blade의 {{ }} 출력 구문은 XSS 공격을 방지하기 위해 PHP의 htmlspecialchars 함수를 자동으로 거칩니다.

변수뿐만 아니라 PHP 함수의 반환값도 자유롭게 출력할 수 있습니다. {{ }} 안에는 어떤 PHP 표현식이든 사용할 수 있습니다:

현재 UNIX 타임스탬프: {{ time() }}

HTML 엔티티 이중 인코딩

Blade(그리고 Laravel의 e 헬퍼 함수)는 기본적으로 HTML 엔티티를 이중 인코딩합니다. 이미 인코딩된 문자열이 한 번 더 인코딩되는 것을 막고 싶다면, AppServiceProviderboot 메서드에서 Blade::withoutDoubleEncoding을 호출하세요:

<?php namespace App\Providers; use Illuminate\Support\Facades\Blade; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Blade::withoutDoubleEncoding(); } }

이스케이프 없이 출력하기

{{ }} 구문은 기본적으로 htmlspecialchars를 통해 이스케이프됩니다. 이스케이프 처리 없이 원본 HTML을 그대로 출력하려면 {!! !!} 구문을 사용하세요:

Hello, {!! $name !!}.

WARNING

사용자 입력값을 출력할 때 {!! !!}를 사용하면 XSS 취약점이 생길 수 있습니다. 사용자로부터 받은 데이터는 반드시 {{ }}를 사용해 이스케이프 처리하세요.

Blade와 JavaScript 프레임워크 함께 사용하기

Vue, Alpine.js 등 많은 JavaScript 프레임워크도 중괄호({{ }})를 표현식 출력에 사용합니다. Blade가 해당 구문을 처리하지 않고 그대로 남겨두도록 하려면 @ 기호를 앞에 붙이세요:

<h1>Laravel</h1> Hello, @{{ name }}.

이 경우 @ 기호는 Blade가 제거하지만, {{ name }} 구문은 그대로 남아 JavaScript 프레임워크가 렌더링하게 됩니다.

@ 기호는 Blade 디렉티브 자체를 이스케이프할 때도 사용할 수 있습니다:

{{-- Blade 템플릿 --}} @@if() <!-- HTML 출력 결과 --> @if()

JSON 렌더링

PHP 배열을 JavaScript 변수로 초기화하기 위해 JSON으로 출력해야 할 때가 있습니다. 기존 방식은 다음과 같습니다:

<script> var app = <?php echo json_encode($array); ?>; </script>

하지만 Illuminate\Support\Js::from 메서드를 사용하면 더 안전하게 처리할 수 있습니다. 이 메서드는 PHP의 json_encode와 동일한 인수를 받으면서, HTML 속성 안에서 안전하게 사용할 수 있도록 결과를 적절히 이스케이프합니다. 또한 반환값은 JSON.parse(...) 형태의 JavaScript 구문으로, 주어진 배열이나 객체를 유효한 JavaScript 객체로 변환합니다:

<script> var app = {{ Illuminate\Support\Js::from($array) }}; </script>

최신 Laravel 애플리케이션 스켈레톤에는 Js 파사드가 포함되어 있어 Blade 템플릿에서 더 간결하게 사용할 수 있습니다:

<script> var app = {{ Js::from($array) }}; </script>

WARNING

Js::from이미 존재하는 변수를 JSON으로 출력할 때만 사용하세요. Blade 템플릿은 정규식 기반으로 동작하기 때문에, 복잡한 표현식을 직접 전달하면 예기치 않은 오류가 발생할 수 있습니다.

`@verbatim` 디렉티브

템플릿의 넓은 영역에서 JavaScript 변수를 출력해야 한다면, 매번 @를 붙이는 대신 해당 영역 전체를 @verbatim 디렉티브로 감싸세요. 그러면 그 안의 Blade 구문은 모두 처리되지 않고 그대로 출력됩니다:

@verbatim <div class="container"> Hello, {{ name }}. </div> @endverbatim

Blade 디렉티브

템플릿 상속과 데이터 출력 외에도, Blade는 조건문·반복문 같은 일반적인 PHP 제어 구조를 위한 편리한 단축 디렉티브를 제공합니다. PHP 문법과 거의 동일하게 동작하면서도, 훨씬 깔끔하고 간결하게 작성할 수 있습니다.

조건문 (If Statements)

@if, @elseif, @else, @endif 디렉티브로 조건문을 작성할 수 있습니다. PHP의 if 문과 완전히 동일하게 동작합니다.

@if (count($records) === 1) 레코드가 1개 있습니다! @elseif (count($records) > 1) 레코드가 여러 개 있습니다! @else 레코드가 없습니다! @endif

편의상, 조건을 반전시키는 @unless 디렉티브도 제공합니다.

@unless (Auth::check()) 로그인하지 않은 상태입니다. @endunless

PHP의 isset()empty()에 대응하는 @isset, @empty 디렉티브도 사용할 수 있습니다.

@isset($records) // $records가 정의되어 있고 null이 아닌 경우... @endisset @empty($records) // $records가 비어 있는 경우... @endempty

인증 디렉티브

@auth@guest 디렉티브를 사용하면 현재 사용자가 인증된 사용자인지, 비로그인 게스트인지 간편하게 확인할 수 있습니다.

@auth // 로그인된 사용자에게만 표시... @endauth @guest // 비로그인 사용자에게만 표시... @endguest

필요하다면, 확인할 인증 가드를 명시적으로 지정할 수 있습니다.

@auth('admin') // admin 가드로 인증된 경우... @endauth @guest('admin') // admin 가드로 인증되지 않은 경우... @endguest

환경 디렉티브

@production 디렉티브로 현재 애플리케이션이 프로덕션 환경에서 실행 중인지 확인할 수 있습니다.

@production // 프로덕션 환경에서만 표시되는 내용... @endproduction

특정 환경을 직접 지정하고 싶다면 @env 디렉티브를 사용하세요.

@env('staging') // 스테이징 환경에서 실행 중... @endenv @env(['staging', 'production']) // 스테이징 또는 프로덕션 환경에서 실행 중... @endenv

섹션 디렉티브

@hasSection 디렉티브로 템플릿 상속의 특정 섹션에 내용이 있는지 확인할 수 있습니다.

@hasSection('navigation') <div class="pull-right"> @yield('navigation') </div> <div class="clearfix"></div> @endif

반대로 섹션에 내용이 없는지 확인하려면 @sectionMissing 디렉티브를 사용합니다.

@sectionMissing('navigation') <div class="pull-right"> @include('default-navigation') </div> @endif

세션 디렉티브

@session 디렉티브를 사용하면 특정 세션 값이 존재하는지 확인할 수 있습니다. 세션 값이 존재하면 블록 내부가 실행되며, $value 변수로 해당 세션 값을 출력할 수 있습니다.

@session('status') <div class="p-4 bg-green-100"> {{ $value }} </div> @endsession

컨텍스트 디렉티브

@context 디렉티브를 사용하면 특정 컨텍스트 값이 존재하는지 확인할 수 있습니다. 값이 존재하면 블록 내부가 실행되며, $value 변수로 해당 값을 출력할 수 있습니다.

@context('canonical') <link href="{{ $value }}" rel="canonical"> @endcontext

Switch 문

@switch, @case, @break, @default, @endswitch 디렉티브로 switch 문을 작성할 수 있습니다.

@switch($i) @case(1) 첫 번째 케이스... @break @case(2) 두 번째 케이스... @break @default 기본 케이스... @endswitch

반복문 (Loops)

Blade는 PHP의 반복문에 대응하는 디렉티브도 제공합니다. 동작 방식은 PHP와 동일합니다.

@for ($i = 0; $i < 10; $i++) 현재 값: {{ $i }} @endfor @foreach ($users as $user) <p>사용자 ID: {{ $user->id }}</p> @endforeach @forelse ($users as $user) <li>{{ $user->name }}</li> @empty <p>사용자가 없습니다.</p> @endforelse @while (true) <p>무한 반복 중...</p> @endwhile

NOTE

@foreach 반복문 안에서는 $loop 변수를 활용할 수 있습니다. 첫 번째·마지막 반복 여부 등 유용한 정보를 제공합니다.

@continue@break 디렉티브로 현재 반복을 건너뛰거나 반복문을 종료할 수 있습니다.

@foreach ($users as $user) @if ($user->type == 1) @continue @endif <li>{{ $user->name }}</li> @if ($user->number == 5) @break @endif @endforeach

조건식을 디렉티브에 직접 인라인으로 작성하는 것도 가능합니다.

@foreach ($users as $user) @continue($user->type == 1) <li>{{ $user->name }}</li> @break($user->number == 5) @endforeach

Loop 변수

@foreach 반복문 안에서는 $loop 변수를 자동으로 사용할 수 있습니다. 현재 인덱스, 첫/마지막 반복 여부 등 유용한 정보를 담고 있습니다.

@foreach ($users as $user) @if ($loop->first) 첫 번째 반복입니다. @endif @if ($loop->last) 마지막 반복입니다. @endif <p>사용자 ID: {{ $user->id }}</p> @endforeach

중첩 반복문에서는 $loop->parent를 통해 상위 반복문의 $loop 변수에 접근할 수 있습니다.

@foreach ($users as $user) @foreach ($user->posts as $post) @if ($loop->parent->first) 이것은 부모 반복문의 첫 번째 반복입니다. @endif @endforeach @endforeach

$loop 변수가 제공하는 전체 속성 목록입니다.

속성설명
$loop->index현재 반복 인덱스 (0부터 시작)
$loop->iteration현재 반복 횟수 (1부터 시작)
$loop->remaining남은 반복 횟수
$loop->count순회 중인 배열의 전체 항목 수
$loop->first첫 번째 반복인지 여부
$loop->last마지막 반복인지 여부
$loop->even짝수 번째 반복인지 여부
$loop->odd홀수 번째 반복인지 여부
$loop->depth현재 반복문의 중첩 깊이
$loop->parent중첩 반복문에서 부모 반복문의 $loop 변수

조건부 클래스 & 스타일

@class 디렉티브를 사용하면 조건에 따라 CSS 클래스를 동적으로 적용할 수 있습니다. 배열의 키에 클래스명을 지정하고, 값에 boolean 표현식을 씁니다. 숫자 키를 가진 항목은 조건 없이 항상 포함됩니다.

@php $isActive = false; $hasError = true; @endphp <span @class([ 'p-4', 'font-bold' => $isActive, 'text-gray-500' => ! $isActive, 'bg-red' => $hasError, ])></span> <span class="p-4 text-gray-500 bg-red"></span>

마찬가지로, @style 디렉티브를 사용하면 조건에 따라 인라인 CSS 스타일을 동적으로 적용할 수 있습니다.

@php $isActive = true; @endphp <span @style([ 'background-color: red', 'font-weight: bold' => $isActive, ])></span> <span style="background-color: red; font-weight: bold;"></span>

추가 HTML 속성 디렉티브

@checked 디렉티브를 사용하면 체크박스의 checked 상태를 간편하게 표현할 수 있습니다. 조건이 true이면 checked를 출력합니다.

<input type="checkbox" name="active" value="active" @checked(old('active', $user->active)) />

@selected 디렉티브는 select 옵션의 선택 상태를 처리할 때 사용합니다.

<select name="version"> @foreach ($product->versions as $version) <option value="{{ $version }}" @selected(old('version') == $version)> {{ $version }} </option> @endforeach </select>

@disabled 디렉티브는 요소를 비활성화 상태로 만들 때 사용합니다.

<button type="submit" @disabled($errors->isNotEmpty())>제출</button>

@readonly 디렉티브는 입력 요소를 읽기 전용으로 만들 때 사용합니다.

<input type="email" name="email" value="email@laravel.com" @readonly($user->isNotAdmin()) />

@required 디렉티브는 입력 요소를 필수 항목으로 만들 때 사용합니다.

<input type="text" name="title" value="title" @required($user->isAdmin()) />

서브뷰 포함 (Including Subviews)

NOTE

@include 디렉티브도 자유롭게 사용할 수 있지만, Blade 컴포넌트는 데이터 바인딩 및 속성 관리 등 더 강력한 기능을 제공합니다. 복잡한 재사용 UI라면 컴포넌트를 먼저 고려해보세요.

@include 디렉티브를 사용하면 다른 Blade 뷰를 현재 뷰에 포함시킬 수 있습니다. 부모 뷰의 모든 변수는 포함된 뷰에서도 그대로 사용할 수 있습니다.

<div> @include('shared.errors') <form> <!-- 폼 내용 --> </form> </div>

포함된 뷰에 추가 데이터를 전달하려면 배열로 넘길 수 있습니다.

@include('view.name', ['status' => 'complete'])

존재하지 않는 뷰를 @include하면 Laravel이 오류를 발생시킵니다. 뷰 파일이 없을 수도 있는 경우에는 @includeIf를 사용하세요.

@includeIf('view.name', ['status' => 'complete'])

boolean 조건에 따라 뷰를 포함하려면 @includeWhen@includeUnless를 사용할 수 있습니다.

@includeWhen($boolean, 'view.name', ['status' => 'complete']) @includeUnless($boolean, 'view.name', ['status' => 'complete'])

주어진 뷰 목록 중 가장 먼저 존재하는 뷰를 포함하려면 @includeFirst를 사용하세요.

@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])

부모 뷰의 변수를 상속받지 않고 명시적으로 전달한 변수만 사용하는 독립적인 포함이 필요하다면 @includeIsolated를 사용하세요.

@includeIsolated('view.name', ['user' => $user])

WARNING

Blade 뷰 파일 안에서 __DIR__이나 __FILE__ 상수를 사용하지 마세요. 컴파일된 캐시 파일의 경로를 참조하게 됩니다.

컬렉션 반복 렌더링

@each 디렉티브를 사용하면 반복문과 뷰 포함을 한 줄로 처리할 수 있습니다.

@each('view.name', $jobs, 'job')

첫 번째 인자는 각 항목에 렌더링할 뷰, 두 번째는 순회할 배열 또는 컬렉션, 세 번째는 뷰 안에서 현재 항목에 접근할 변수명입니다. 현재 항목의 배열 키는 뷰 내부에서 $key 변수로 접근할 수 있습니다.

네 번째 인자로 배열이 비어 있을 때 렌더링할 뷰를 지정할 수도 있습니다.

@each('view.name', $jobs, 'job', 'view.empty')

WARNING

@each로 렌더링된 뷰는 부모 뷰의 변수를 상속받지 않습니다. 자식 뷰에서 부모 변수가 필요하다면 @foreach@include를 조합해서 사용하세요.

`@once` 디렉티브

@once 디렉티브는 한 렌더링 사이클에서 딱 한 번만 실행되어야 하는 템플릿 블록을 정의할 때 사용합니다. 예를 들어, 반복문 안에서 컴포넌트를 여러 번 렌더링하더라도 JavaScript는 딱 한 번만 <head>에 추가하고 싶을 때 유용합니다.

@once @push('scripts') <script> // 커스텀 JavaScript... </script> @endpush @endonce

@once@push, @prepend와 함께 자주 사용되기 때문에, 편의를 위해 @pushOnce@prependOnce 디렉티브도 제공합니다.

@pushOnce('scripts') <script> // 커스텀 JavaScript... </script> @endPushOnce

서로 다른 두 Blade 템플릿에서 동일한 내용을 push할 경우, 두 번째 인자로 고유 식별자를 지정하면 중복 렌더링을 방지할 수 있습니다.

<!-- pie-chart.blade.php --> @pushOnce('scripts', 'chart.js') <script src="/chart.js"></script> @endPushOnce <!-- line-chart.blade.php --> @pushOnce('scripts', 'chart.js') <script src="/chart.js"></script> @endPushOnce

위 예시에서 두 파일 모두 같은 식별자 'chart.js'를 지정했으므로, chart.js 스크립트는 페이지에 단 한 번만 포함됩니다.

순수 PHP 코드

뷰 안에서 PHP 코드를 직접 실행해야 할 때는 @php 디렉티브를 사용합니다.

@php $counter = 1; @endphp

클래스를 import하는 용도라면 @use 디렉티브를 사용할 수 있습니다.

@use('App\Models\Flight')

두 번째 인자로 별칭(alias)을 지정하는 것도 가능합니다.

@use('App\Models\Flight', 'FlightModel')

같은 네임스페이스의 여러 클래스를 한 번에 import하려면 중괄호 그룹 문법을 사용하세요.

@use('App\Models\{Flight, Airport}')

@use 디렉티브는 함수와 상수도 import할 수 있습니다. function 또는 const 한정자를 앞에 붙이면 됩니다.

@use(function App\Helpers\format_currency) @use(const App\Constants\MAX_ATTEMPTS)

함수와 상수에도 별칭을 지정할 수 있습니다.

@use(function App\Helpers\format_currency, 'formatMoney') @use(const App\Constants\MAX_ATTEMPTS, 'MAX_TRIES')

함수와 상수도 그룹 import를 지원합니다.

@use(function App\Helpers\{format_currency, format_date}) @use(const App\Constants\{MAX_ATTEMPTS, DEFAULT_TIMEOUT})

주석 (Comments)

Blade는 뷰 안에 주석을 작성하는 문법도 제공합니다. HTML 주석과 달리, Blade 주석은 렌더링된 HTML에 포함되지 않습니다.

{{-- 이 주석은 렌더링된 HTML에 나타나지 않습니다 --}}

컴포넌트

컴포넌트와 슬롯은 섹션, 레이아웃, @include와 비슷한 역할을 하지만, 많은 개발자들이 컴포넌트와 슬롯 모델을 더 직관적으로 느낍니다. 컴포넌트를 작성하는 방법은 두 가지입니다: 클래스 기반 컴포넌트익명 컴포넌트.

클래스 기반 컴포넌트를 만들려면 make:component Artisan 명령어를 사용합니다. 예시로 간단한 Alert 컴포넌트를 만들어 보겠습니다. 생성된 컴포넌트 클래스는 app/View/Components 디렉터리에 위치합니다:

php artisan make:component Alert

이 명령어는 컴포넌트 클래스와 함께 뷰 템플릿도 자동으로 생성합니다. 뷰는 resources/views/components 디렉터리에 저장됩니다. 애플리케이션에서 직접 작성하는 컴포넌트는 이 두 디렉터리에서 자동으로 검색되므로, 별도의 등록 작업은 필요하지 않습니다.

서브디렉터리 안에 컴포넌트를 생성할 수도 있습니다:

php artisan make:component Forms/Input

위 명령어는 app/View/Components/Forms 디렉터리에 Input 컴포넌트 클래스를, resources/views/components/forms 디렉터리에 뷰 파일을 생성합니다.

패키지 컴포넌트 수동 등록

애플리케이션 내에서 직접 작성하는 컴포넌트는 자동으로 검색되지만, Blade 컴포넌트를 포함하는 패키지를 개발하는 경우에는 컴포넌트 클래스와 HTML 태그 별칭을 수동으로 등록해야 합니다. 일반적으로 패키지의 서비스 프로바이더 boot 메서드에서 등록합니다:

use Illuminate\Support\Facades\Blade; /** * 패키지 서비스를 부트스트랩합니다. */ public function boot(): void { Blade::component('package-alert', Alert::class); }

등록이 완료되면 태그 별칭으로 렌더링할 수 있습니다:

<x-package-alert/>

또는 componentNamespace 메서드를 사용해 네임스페이스 규칙에 따라 컴포넌트 클래스를 자동으로 로드할 수도 있습니다. 예를 들어, Nightshade 패키지에 Package\Views\Components 네임스페이스 아래 CalendarColorPicker 컴포넌트가 있다면 다음과 같이 등록합니다:

use Illuminate\Support\Facades\Blade; /** * 패키지 서비스를 부트스트랩합니다. */ public function boot(): void { Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); }

이렇게 하면 package-name:: 구문으로 패키지 컴포넌트를 사용할 수 있습니다:

<x-nightshade::calendar /> <x-nightshade::color-picker />

Blade는 컴포넌트 이름을 PascalCase로 변환해 대응하는 클래스를 자동으로 찾습니다. 서브디렉터리는 "점(dot) 표기법"으로 지원됩니다.

컴포넌트 렌더링

컴포넌트를 표시하려면 Blade 템플릿 안에서 Blade 컴포넌트 태그를 사용합니다. Blade 컴포넌트 태그는 x-로 시작하고, 그 뒤에 컴포넌트 클래스 이름을 kebab-case로 붙입니다:

<x-alert/> <x-user-profile/>

컴포넌트 클래스가 app/View/Components 디렉터리 하위에 중첩되어 있다면 . 문자로 디렉터리 구조를 표현합니다. 예를 들어, 컴포넌트가 app/View/Components/Inputs/Button.php에 있다면 다음과 같이 렌더링합니다:

<x-inputs.button/>

컴포넌트를 조건부로 렌더링하고 싶다면 컴포넌트 클래스에 shouldRender 메서드를 정의합니다. 이 메서드가 false를 반환하면 컴포넌트가 렌더링되지 않습니다:

use Illuminate\Support\Str; /** * 컴포넌트를 렌더링할지 여부를 결정합니다. */ public function shouldRender(): bool { return Str::length($this->message) > 0; }

인덱스 컴포넌트

관련된 컴포넌트들을 하나의 디렉터리로 묶어 관리하고 싶을 때가 있습니다. 예를 들어, "card" 컴포넌트 그룹이 아래와 같은 구조를 갖는다고 가정해 봅시다:

App\Views\Components\Card\Card
App\Views\Components\Card\Header
App\Views\Components\Card\Body

루트 Card 컴포넌트가 Card 디렉터리 안에 중첩되어 있어서 <x-card.card>처럼 렌더링해야 할 것 같지만, Laravel은 컴포넌트 파일 이름이 디렉터리 이름과 같을 경우 해당 컴포넌트를 루트 컴포넌트로 자동 인식합니다. 따라서 디렉터리 이름을 반복하지 않고도 렌더링할 수 있습니다:

<x-card> <x-card.header>...</x-card.header> <x-card.body>...</x-card.body> </x-card>

컴포넌트에 데이터 전달

HTML 어트리뷰트를 통해 Blade 컴포넌트에 데이터를 전달할 수 있습니다. 하드코딩된 기본값은 일반 HTML 어트리뷰트 문자열로 전달하고, PHP 표현식이나 변수는 : 접두사를 붙인 어트리뷰트로 전달합니다:

<x-alert type="error" :message="$message"/>

컴포넌트의 모든 데이터 어트리뷰트는 클래스 생성자에서 정의합니다. 컴포넌트의 퍼블릭 프로퍼티는 자동으로 뷰에서 사용 가능하므로, render 메서드에서 별도로 데이터를 뷰에 전달할 필요가 없습니다:

<?php namespace App\View\Components; use Illuminate\View\Component; use Illuminate\View\View; class Alert extends Component { /** * 컴포넌트 인스턴스를 생성합니다. */ public function __construct( public string $type, public string $message, ) {} /** * 컴포넌트를 나타내는 뷰를 반환합니다. */ public function render(): View { return view('components.alert'); } }

컴포넌트가 렌더링될 때 퍼블릭 변수를 그대로 출력할 수 있습니다:

<div class="alert alert-{{ $type }}"> {{ $message }} </div>

케이스 규칙

컴포넌트 생성자 인수는 camelCase로 작성하고, HTML 어트리뷰트에서는 kebab-case로 참조합니다. 예를 들어, 생성자가 다음과 같다면:

/** * 컴포넌트 인스턴스를 생성합니다. */ public function __construct( public string $alertType, ) {}

컴포넌트에 값을 전달할 때는 다음과 같이 사용합니다:

<x-alert alert-type="danger" />

단축 어트리뷰트 구문

어트리뷰트 이름과 변수 이름이 같을 경우, "단축 어트리뷰트" 구문을 사용하면 편리합니다:

{{-- 단축 어트리뷰트 구문 --}} <x-profile :$userId :$name /> {{-- 아래 코드와 동일합니다 --}} <x-profile :user-id="$userId" :name="$name" />

어트리뷰트 렌더링 이스케이프

Alpine.js와 같은 JavaScript 프레임워크도 콜론(:) 접두사 어트리뷰트를 사용하기 때문에, Blade에서 해당 어트리뷰트가 PHP 표현식이 아님을 알려야 할 때가 있습니다. 이 경우 이중 콜론(::) 접두사를 사용합니다:

<x-button ::class="{ danger: isDeleting }"> Submit </x-button>

Blade는 위 코드를 다음과 같이 렌더링합니다:

<button :class="{ danger: isDeleting }"> Submit </button>

컴포넌트 메서드

컴포넌트의 퍼블릭 프로퍼티뿐만 아니라 퍼블릭 메서드도 뷰 템플릿에서 호출할 수 있습니다. 예를 들어, isSelected 메서드가 있는 컴포넌트가 있다면:

/** * 주어진 옵션이 현재 선택된 옵션인지 확인합니다. */ public function isSelected(string $option): bool { return $option === $this->selected; }

템플릿에서 메서드 이름에 해당하는 변수를 호출하는 방식으로 실행할 수 있습니다:

<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}"> {{ $label }} </option>

컴포넌트 클래스 내에서 어트리뷰트와 슬롯 접근

컴포넌트 이름, 어트리뷰트, 슬롯에 render 메서드 안에서 접근해야 할 경우, render 메서드에서 클로저를 반환할 수 있습니다:

use Closure; /** * 컴포넌트를 나타내는 뷰를 반환합니다. */ public function render(): Closure { return function () { return '<div {{ $attributes }}>컴포넌트 내용</div>'; }; }

클로저는 $data 배열을 인수로 받을 수 있으며, 이 배열에는 컴포넌트에 대한 여러 정보가 담겨 있습니다:

return function (array $data) { // $data['componentName']; // $data['attributes']; // $data['slot']; return '<div {{ $attributes }}>컴포넌트 내용</div>'; }

WARNING

$data 배열의 요소를 render 메서드에서 반환하는 Blade 문자열에 직접 삽입하면, 악의적인 어트리뷰트 내용을 통해 원격 코드 실행(RCE)이 발생할 수 있습니다. 직접 삽입하지 마세요.

componentName은 HTML 태그의 x- 접두사 이후 이름과 동일합니다. <x-alert />componentNamealert입니다. attributes 요소에는 HTML 태그에 있는 모든 어트리뷰트가 포함되고, slot 요소는 슬롯 내용이 담긴 Illuminate\Support\HtmlString 인스턴스입니다.

클로저는 문자열을 반환해야 합니다. 반환된 문자열이 기존 뷰와 일치하면 해당 뷰를 렌더링하고, 일치하지 않으면 인라인 Blade 뷰로 평가합니다.

추가 의존성 주입

컴포넌트가 Laravel 서비스 컨테이너에서 의존성을 주입받아야 하는 경우, 생성자에서 데이터 어트리뷰트 앞에 의존성을 선언하면 컨테이너가 자동으로 주입합니다:

use App\Services\AlertCreator; /** * 컴포넌트 인스턴스를 생성합니다. */ public function __construct( public AlertCreator $creator, public string $type, public string $message, ) {}

어트리뷰트 / 메서드 노출 제한

일부 퍼블릭 메서드나 프로퍼티를 컴포넌트 템플릿에 노출하지 않으려면 컴포넌트의 $except 배열 프로퍼티에 추가합니다:

<?php namespace App\View\Components; use Illuminate\View\Component; class Alert extends Component { /** * 컴포넌트 템플릿에 노출하지 않을 프로퍼티 / 메서드 목록. * * @var array */ protected $except = ['type']; /** * 컴포넌트 인스턴스를 생성합니다. */ public function __construct( public string $type, ) {} }

컴포넌트 어트리뷰트

앞서 컴포넌트에 데이터를 전달하는 방법을 살펴봤지만, 컴포넌트 동작에 필요한 데이터가 아닌 추가 HTML 어트리뷰트(예: class)를 전달해야 할 때도 있습니다. 이런 어트리뷰트는 보통 컴포넌트 템플릿의 루트 엘리먼트로 전달하고 싶을 것입니다. 예를 들어, alert 컴포넌트를 다음과 같이 렌더링한다고 가정해 봅시다:

<x-alert type="error" :message="$message" class="mt-4"/>

컴포넌트 생성자에 포함되지 않은 모든 어트리뷰트는 자동으로 컴포넌트의 "어트리뷰트 백(attribute bag)"에 추가됩니다. 이 어트리뷰트 백은 $attributes 변수를 통해 컴포넌트 뷰에서 사용할 수 있습니다:

<div {{ $attributes }}> <!-- 컴포넌트 내용 --> </div>

WARNING

컴포넌트 태그 안에서 @env와 같은 디렉티브를 사용하는 것은 현재 지원되지 않습니다. 예를 들어, <x-alert :live="@env('production')"/>는 정상적으로 컴파일되지 않습니다.

기본값 / 어트리뷰트 병합

어트리뷰트에 기본값을 지정하거나 추가 값을 병합해야 할 때는 어트리뷰트 백의 merge 메서드를 사용합니다. 항상 적용되어야 하는 기본 CSS 클래스를 정의하는 데 특히 유용합니다:

<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}> {{ $message }} </div>

이 컴포넌트를 다음과 같이 사용한다면:

<x-alert type="error" :message="$message" class="mb-4"/>

최종적으로 렌더링되는 HTML은 다음과 같습니다:

<div class="alert alert-error mb-4"> <!-- $message 변수의 내용 --> </div>

조건부 클래스 병합

특정 조건이 true일 때만 클래스를 병합하고 싶다면 class 메서드를 사용합니다. 배열 키에 추가할 클래스명을, 배열 값에 불리언 표현식을 지정합니다. 배열 키가 숫자인 경우 항상 렌더링됩니다:

<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}> {{ $message }} </div>

다른 어트리뷰트도 함께 병합해야 한다면 class 메서드 뒤에 merge 메서드를 체이닝합니다:

<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}> {{ $slot }} </button>

NOTE

병합된 어트리뷰트를 받지 않는 다른 HTML 엘리먼트에서 조건부로 클래스를 적용하려면 @class 디렉티브를 사용할 수 있습니다.

class 외 어트리뷰트 병합

class가 아닌 어트리뷰트를 merge로 병합하면, merge에 전달된 값이 해당 어트리뷰트의 "기본값"이 됩니다. 단, class 어트리뷰트와 달리 외부에서 주입된 값과 병합되지 않고 덮어씌워집니다. 예를 들어, button 컴포넌트가 다음과 같이 구현되어 있다면:

<button {{ $attributes->merge(['type' => 'button']) }}> {{ $slot }} </button>

사용 시 type을 직접 지정하면 그 값이 사용되고, 지정하지 않으면 기본값인 button이 적용됩니다:

<x-button type="submit"> Submit </x-button>

위 코드의 렌더링 결과:

<button type="submit"> Submit </button>

class 이외의 어트리뷰트에서도 기본값과 주입된 값을 합치고 싶다면 prepends 메서드를 사용합니다. 아래 예시에서 data-controller 어트리뷰트는 항상 profile-controller로 시작하고, 외부에서 추가로 주입된 값이 그 뒤에 붙습니다:

<div {{ $attributes->merge(['data-controller' => $attributes->prepends('profile-controller')]) }}> {{ $slot }} </div>

어트리뷰트 조회 및 필터링

filter 메서드를 사용해 어트리뷰트를 필터링할 수 있습니다. 클로저가 true를 반환하는 어트리뷰트만 어트리뷰트 백에 남습니다:

{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }}

키가 특정 문자열로 시작하는 어트리뷰트만 가져오려면 whereStartsWith 메서드를 사용합니다:

{{ $attributes->whereStartsWith('wire:model') }}

반대로, 키가 특정 문자열로 시작하는 어트리뷰트를 제외하려면 whereDoesntStartWith 메서드를 사용합니다:

{{ $attributes->whereDoesntStartWith('wire:model') }}

first 메서드로 어트리뷰트 백의 첫 번째 어트리뷰트를 렌더링할 수 있습니다:

{{ $attributes->whereStartsWith('wire:model')->first() }}

has 메서드로 특정 어트리뷰트가 컴포넌트에 존재하는지 확인할 수 있습니다:

@if ($attributes->has('class')) <div>class 어트리뷰트가 있습니다</div> @endif

배열을 전달하면 모든 어트리뷰트가 존재하는지 확인합니다:

@if ($attributes->has(['name', 'class'])) <div>모든 어트리뷰트가 있습니다</div> @endif

hasAny 메서드는 주어진 어트리뷰트 중 하나라도 존재하는지 확인합니다:

@if ($attributes->hasAny(['href', ':href', 'v-bind:href'])) <div>어트리뷰트 중 하나가 있습니다</div> @endif

특정 어트리뷰트의 값을 가져오려면 get 메서드를 사용합니다:

{{ $attributes->get('class') }}

only 메서드로 지정한 키의 어트리뷰트만 가져올 수 있습니다:

{{ $attributes->only(['class']) }}

except 메서드로 지정한 키의 어트리뷰트를 제외한 나머지를 가져올 수 있습니다:

{{ $attributes->except(['class']) }}

예약어

다음 키워드들은 Blade가 컴포넌트를 렌더링할 때 내부적으로 사용하므로, 컴포넌트 클래스의 퍼블릭 프로퍼티나 메서드 이름으로 사용할 수 없습니다:

  • data
  • render
  • resolve
  • resolveView
  • shouldRender
  • view
  • withAttributes
  • withName

슬롯

컴포넌트에 추가 콘텐츠를 전달할 때 "슬롯"을 사용합니다. 슬롯의 내용은 $slot 변수를 출력해서 렌더링합니다. 예를 들어, alert 컴포넌트의 마크업이 다음과 같다면:

<!-- /resources/views/components/alert.blade.php --> <div class="alert alert-danger"> {{ $slot }} </div>

컴포넌트 태그 안에 내용을 넣어 슬롯에 전달할 수 있습니다:

<x-alert> <strong>이런!</strong> 문제가 발생했습니다! </x-alert>

컴포넌트에서 여러 위치에 각각 다른 슬롯을 렌더링해야 할 경우, "이름 있는 슬롯(named slot)"을 사용합니다. alert 컴포넌트에 "title" 슬롯을 추가해 봅시다:

<!-- /resources/views/components/alert.blade.php --> <span class="alert-title">{{ $title }}</span> <div class="alert alert-danger"> {{ $slot }} </div>

이름 있는 슬롯의 내용은 x-slot 태그로 전달합니다. x-slot 태그 밖의 내용은 기본 $slot 변수로 전달됩니다:

<x-alert> <x-slot:title> 서버 오류 </x-slot> <strong>이런!</strong> 문제가 발생했습니다! </x-alert>

슬롯이 비어 있는지 확인하려면 isEmpty 메서드를 사용합니다:

<span class="alert-title">{{ $title }}</span> <div class="alert alert-danger"> @if ($slot->isEmpty()) 슬롯이 비어 있을 때 표시되는 기본 내용입니다. @else {{ $slot }} @endif </div>

HTML 주석을 제외한 실제 콘텐츠가 있는지 확인하려면 hasActualContent 메서드를 사용합니다:

@if ($slot->hasActualContent()) 슬롯에 주석이 아닌 실제 내용이 있습니다. @endif

스코프 슬롯

Vue와 같은 JavaScript 프레임워크의 "스코프 슬롯"처럼, 슬롯 안에서 컴포넌트의 데이터나 메서드에 접근하고 싶을 때가 있습니다. Laravel에서는 컴포넌트 클래스에 퍼블릭 메서드나 프로퍼티를 정의하고, 슬롯 안에서 $component 변수를 통해 컴포넌트에 접근하면 됩니다. 아래 예시에서 x-alert 컴포넌트 클래스에는 formatAlert 퍼블릭 메서드가 정의되어 있다고 가정합니다:

<x-alert> <x-slot:title> {{ $component->formatAlert('Server Error') }} </x-slot> <strong>이런!</strong> 문제가 발생했습니다! </x-alert>

슬롯 어트리뷰트

Blade 컴포넌트와 마찬가지로, 슬롯에도 CSS 클래스 등 추가 어트리뷰트를 지정할 수 있습니다:

<x-card class="shadow-sm"> <x-slot:heading class="font-bold"> 제목 </x-slot> 본문 내용 <x-slot:footer class="text-sm"> 푸터 </x-slot> </x-card>

슬롯의 어트리뷰트는 해당 슬롯 변수의 attributes 프로퍼티로 접근합니다. 어트리뷰트 조작에 대한 자세한 내용은 컴포넌트 어트리뷰트 문서를 참고하세요:

@props([ 'heading', 'footer', ]) <div {{ $attributes->class(['border']) }}> <h1 {{ $heading->attributes->class(['text-lg']) }}> {{ $heading }} </h1> {{ $slot }} <footer {{ $footer->attributes->class(['text-gray-700']) }}> {{ $footer }} </footer> </div>

인라인 컴포넌트 뷰

매우 작은 컴포넌트의 경우, 컴포넌트 클래스와 별도의 뷰 파일을 함께 관리하는 것이 번거로울 수 있습니다. 이럴 때는 render 메서드에서 마크업을 직접 반환할 수 있습니다:

/** * 컴포넌트를 나타내는 뷰를 반환합니다. */ public function render(): string { return <<<'blade' <div class="alert alert-danger"> {{ $slot }} </div> blade; }

인라인 뷰 컴포넌트 생성

인라인 뷰를 렌더링하는 컴포넌트를 생성하려면 make:component 명령어에 --inline 옵션을 추가합니다:

php artisan make:component Alert --inline

동적 컴포넌트

어떤 컴포넌트를 렌더링할지 런타임 전까지 알 수 없는 경우, Laravel 내장 dynamic-component 컴포넌트를 사용해 변수 값에 따라 동적으로 렌더링할 수 있습니다:

// $componentName = "secondary-button"; <x-dynamic-component :component="$componentName" class="mt-4" />

컴포넌트 수동 등록

WARNING

아래 수동 등록 내용은 주로 뷰 컴포넌트를 포함하는 Laravel 패키지를 개발하는 경우에 해당합니다. 패키지를 개발하지 않는다면 이 내용은 해당되지 않을 수 있습니다.

일반 애플리케이션에서 직접 작성하는 컴포넌트는 app/View/Componentsresources/views/components 디렉터리에서 자동으로 검색됩니다.

그러나 Blade 컴포넌트를 포함하는 패키지를 개발하거나, 컴포넌트를 비표준 디렉터리에 배치하는 경우에는 컴포넌트 클래스와 HTML 태그 별칭을 수동으로 등록해야 합니다. 일반적으로 패키지의 서비스 프로바이더 boot 메서드에서 등록합니다:

use Illuminate\Support\Facades\Blade; use VendorPackage\View\Components\AlertComponent; /** * 패키지 서비스를 부트스트랩합니다. */ public function boot(): void { Blade::component('package-alert', AlertComponent::class); }

등록 후에는 태그 별칭으로 렌더링할 수 있습니다:

<x-package-alert/>

패키지 컴포넌트 자동 로드

componentNamespace 메서드를 사용하면 네임스페이스 규칙에 따라 컴포넌트 클래스를 자동으로 로드할 수 있습니다. 예를 들어, Nightshade 패키지에 Package\Views\Components 네임스페이스 아래 CalendarColorPicker 컴포넌트가 있다면:

use Illuminate\Support\Facades\Blade; /** * 패키지 서비스를 부트스트랩합니다. */ public function boot(): void { Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); }

이렇게 하면 package-name:: 구문으로 패키지 컴포넌트를 사용할 수 있습니다:

<x-nightshade::calendar /> <x-nightshade::color-picker />

Blade는 컴포넌트 이름을 PascalCase로 변환해 대응하는 클래스를 자동으로 찾으며, 서브디렉터리는 점(dot) 표기법으로 지원됩니다.

익명 컴포넌트

익명 컴포넌트는 인라인 컴포넌트와 유사하게 단일 파일로 컴포넌트를 관리하는 방법입니다. 단, 익명 컴포넌트는 별도의 클래스 없이 Blade 뷰 파일 하나만으로 동작합니다. 익명 컴포넌트를 정의하려면 resources/views/components 디렉터리에 Blade 템플릿 파일을 추가하기만 하면 됩니다.

예를 들어, resources/views/components/alert.blade.php 파일을 만들었다면 아래처럼 렌더링할 수 있습니다:

<x-alert/>

컴포넌트가 components 디렉터리 하위에 중첩되어 있는 경우 . 문자로 경로를 표현합니다. 예를 들어, resources/views/components/inputs/button.blade.php에 정의된 컴포넌트는 다음과 같이 사용합니다:

<x-inputs.button/>

Artisan 명령어로 익명 컴포넌트를 생성하려면 make:component 명령에 --view 플래그를 사용하세요:

php artisan make:component forms.input --view

위 명령을 실행하면 resources/views/components/forms/input.blade.php 파일이 생성되며, <x-forms.input />으로 렌더링할 수 있습니다.

익명 인덱스 컴포넌트

하나의 컴포넌트가 여러 Blade 템플릿으로 구성될 경우, 관련 파일들을 하나의 디렉터리로 묶고 싶을 때가 있습니다. 예를 들어, "accordion" 컴포넌트를 아래와 같은 구조로 만들 수 있습니다:

/resources/views/components/accordion.blade.php
/resources/views/components/accordion/item.blade.php

이 구조에서는 accordion 컴포넌트와 하위 item 컴포넌트를 다음처럼 사용할 수 있습니다:

<x-accordion> <x-accordion.item> ... </x-accordion.item> </x-accordion>

그런데 위 구조의 문제점은, x-accordion으로 렌더링하려면 accordion의 루트 템플릿 파일을 accordion 디렉터리 안이 아닌 components 디렉터리에 직접 두어야 한다는 점입니다. 관련 파일을 한 디렉터리로 모으지 못해 구조가 다소 어색해집니다.

다행히 Blade는 컴포넌트 디렉터리 안에 디렉터리와 같은 이름의 파일이 있으면, 이를 해당 컴포넌트의 "루트" 템플릿으로 인식합니다. 따라서 디렉터리 구조를 다음처럼 변경해도 동일하게 동작합니다:

/resources/views/components/accordion/accordion.blade.php
/resources/views/components/accordion/item.blade.php

이제 accordion 관련 파일을 모두 accordion 디렉터리 안에 모아둘 수 있으며, 사용 방법은 기존과 동일합니다.

데이터 프로퍼티 / 어트리뷰트

익명 컴포넌트는 클래스가 없기 때문에, 어떤 값을 변수로 받고 어떤 값을 어트리뷰트 백에 담을지 구분하는 방법이 필요합니다.

컴포넌트 Blade 템플릿 상단에서 @props 디렉티브로 변수로 받을 어트리뷰트를 지정할 수 있습니다. @props에 선언되지 않은 나머지 어트리뷰트는 자동으로 어트리뷰트 백에 담깁니다. 기본값이 필요한 경우 배열의 키-값 쌍으로 지정하면 됩니다:

<!-- /resources/views/components/alert.blade.php --> @props(['type' => 'info', 'message']) <div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}> {{ $message }} </div>

위 컴포넌트는 다음과 같이 렌더링합니다:

<x-alert type="error" :message="$message" class="mb-4"/>

부모 컴포넌트 데이터 접근

자식 컴포넌트에서 부모 컴포넌트의 데이터에 접근해야 할 때는 @aware 디렉티브를 사용할 수 있습니다. 예를 들어, <x-menu><x-menu.item>으로 구성된 메뉴 컴포넌트를 생각해 보겠습니다:

<x-menu color="purple"> <x-menu.item>...</x-menu.item> <x-menu.item>...</x-menu.item> </x-menu>

부모인 <x-menu> 컴포넌트는 다음과 같이 정의됩니다:

<!-- /resources/views/components/menu/index.blade.php --> @props(['color' => 'gray']) <ul {{ $attributes->merge(['class' => 'bg-'.$color.'-200']) }}> {{ $slot }} </ul>

color prop은 부모(<x-menu>)에만 전달되었기 때문에, 기본적으로 <x-menu.item> 안에서는 사용할 수 없습니다. @aware 디렉티브를 사용하면 자식 컴포넌트에서도 해당 값에 접근할 수 있습니다:

<!-- /resources/views/components/menu/item.blade.php --> @aware(['color' => 'gray']) <li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}> {{ $slot }} </li>

WARNING

@aware 디렉티브는 부모 컴포넌트에 HTML 어트리뷰트로 명시적으로 전달된 데이터만 접근할 수 있습니다. 부모 컴포넌트의 @props에 정의된 기본값 중 실제로 전달되지 않은 값은 @aware로 접근할 수 없습니다.

익명 컴포넌트 경로 등록

앞서 설명했듯이 익명 컴포넌트는 기본적으로 resources/views/components 디렉터리에 정의합니다. 하지만 필요에 따라 추가적인 익명 컴포넌트 경로를 Laravel에 등록할 수도 있습니다.

Blade::anonymousComponentPath() 메서드의 첫 번째 인자로 컴포넌트가 위치한 경로를, 두 번째 인자로 선택적으로 네임스페이스(접두사)를 전달합니다. 이 메서드는 보통 서비스 프로바이더boot 메서드 안에서 호출합니다:

/** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Blade::anonymousComponentPath(__DIR__.'/../components'); }

접두사 없이 경로를 등록한 경우, 해당 경로의 컴포넌트도 접두사 없이 사용할 수 있습니다. 예를 들어, 위에서 등록한 경로에 panel.blade.php 파일이 있다면 다음처럼 렌더링합니다:

<x-panel />

두 번째 인자로 네임스페이스 접두사를 지정할 수도 있습니다:

Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');

접두사를 지정한 경우, 해당 네임스페이스의 컴포넌트는 컴포넌트 이름 앞에 네임스페이스를 붙여 렌더링합니다:

<x-dashboard::panel />

Blade 템플릿

레이아웃 구성

컴포넌트를 활용한 레이아웃

대부분의 웹 애플리케이션은 여러 페이지에 걸쳐 동일한 전체 레이아웃을 공유합니다. 매 뷰마다 레이아웃 HTML 전체를 반복해서 작성해야 한다면 유지보수가 몹시 번거로울 것입니다. 다행히 레이아웃을 하나의 Blade 컴포넌트로 정의해 두면 애플리케이션 전반에서 편리하게 재사용할 수 있습니다.

레이아웃 컴포넌트 정의하기

예를 들어 할 일 목록(todo) 애플리케이션을 만든다고 가정해 보겠습니다. 다음과 같이 layout 컴포넌트를 정의할 수 있습니다.

<!-- resources/views/components/layout.blade.php --> <html> <head> <title>{{ $title ?? 'Todo 관리자' }}</title> </head> <body> <h1>할 일 목록</h1> <hr/> {{ $slot }} </body> </html>

레이아웃 컴포넌트 적용하기

layout 컴포넌트를 정의했으면, 이 컴포넌트를 사용하는 Blade 뷰를 만들 수 있습니다. 아래는 할 일 목록을 출력하는 간단한 뷰 예시입니다.

<!-- resources/views/tasks.blade.php --> <x-layout> @foreach ($tasks as $task) <div>{{ $task }}</div> @endforeach </x-layout>

컴포넌트 태그 사이에 작성한 내용은 layout 컴포넌트 내부의 기본 $slot 변수로 전달됩니다. 앞서 정의한 layout 컴포넌트를 보면 $title 슬롯도 지원하는 것을 알 수 있는데, 값이 전달되지 않으면 기본 제목이 표시됩니다. 컴포넌트 문서에서 설명하는 슬롯 문법을 사용해 커스텀 제목을 주입할 수 있습니다.

<!-- resources/views/tasks.blade.php --> <x-layout> <x-slot:title> 나의 할 일 목록 </x-slot> @foreach ($tasks as $task) <div>{{ $task }}</div> @endforeach </x-layout>

레이아웃과 태스크 목록 뷰를 모두 정의했으면, 라우트에서 해당 뷰를 반환하면 됩니다.

use App\Models\Task; Route::get('/tasks', function () { return view('tasks', ['tasks' => Task::all()]); });

템플릿 상속을 활용한 레이아웃

레이아웃 정의하기

레이아웃은 "템플릿 상속" 방식으로도 만들 수 있습니다. 이 방식은 컴포넌트가 도입되기 전부터 사용되어 온 전통적인 방법입니다.

먼저 간단한 예시를 살펴보겠습니다. 여러 페이지가 동일한 레이아웃을 공유하므로, 이 레이아웃을 하나의 Blade 뷰 파일로 정의합니다.

<!-- resources/views/layouts/app.blade.php --> <html> <head> <title>앱 이름 - @yield('title')</title> </head> <body> @section('sidebar') 기본 사이드바입니다. @show <div class="container"> @yield('content') </div> </body> </html>

파일 내용은 일반적인 HTML 마크업과 거의 동일하지만, @section@yield 디렉티브가 눈에 띕니다. @section은 콘텐츠 영역을 정의하고, @yield는 해당 영역의 내용을 실제로 출력하는 역할을 합니다.

레이아웃을 정의했으면, 이 레이아웃을 상속하는 자식 페이지를 만들어 보겠습니다.

레이아웃 상속하기

자식 뷰에서는 @extends 디렉티브로 어떤 레이아웃을 상속할지 지정합니다. 그리고 @section 디렉티브를 사용해 레이아웃의 각 영역에 콘텐츠를 주입합니다. 주입된 내용은 레이아웃의 @yield 위치에 출력됩니다.

<!-- resources/views/child.blade.php --> @extends('layouts.app') @section('title', '페이지 제목') @section('sidebar') @@parent <p>기본 사이드바에 내용을 추가합니다.</p> @endsection @section('content') <p>본문 내용입니다.</p> @endsection

여기서 sidebar 섹션은 @@parent 디렉티브를 사용해 레이아웃의 기본 사이드바 내용을 덮어쓰지 않고 그 뒤에 추가합니다. @@parent는 뷰가 렌더링될 때 레이아웃에 정의된 해당 섹션의 내용으로 교체됩니다.

NOTE

레이아웃 파일의 @section@show로 끝나지만, 자식 뷰의 @section@endsection으로 끝납니다. @endsection은 섹션을 정의만 하고, @show는 정의함과 동시에 즉시 출력합니다. 이 차이를 혼동하지 않도록 주의하세요.

@yield 디렉티브는 두 번째 파라미터로 기본값을 받을 수 있습니다. 해당 섹션이 자식 뷰에서 정의되지 않은 경우 이 기본값이 출력됩니다.

@yield('content', '기본 콘텐츠')

CSRF 필드

애플리케이션에서 HTML 폼을 정의할 때는 반드시 숨겨진 CSRF 토큰 필드를 포함해야 합니다. 그래야 CSRF 보호 미들웨어가 요청을 정상적으로 검증할 수 있습니다. @csrf Blade 디렉티브를 사용하면 토큰 필드를 간단하게 생성할 수 있습니다.

<form method="POST" action="/profile"> @csrf ... </form>

HTTP 메서드 필드

HTML 폼은 기본적으로 PUT, PATCH, DELETE 요청을 직접 전송할 수 없습니다. 이런 HTTP 메서드를 사용하려면 숨겨진 _method 필드를 폼에 추가해야 합니다. @method Blade 디렉티브가 이 필드를 자동으로 생성해 줍니다.

<form action="/foo/bar" method="POST"> @method('PUT') ... </form>

유효성 검사 오류

@error 디렉티브를 사용하면 특정 필드에 유효성 검사 오류 메시지가 존재하는지 간편하게 확인할 수 있습니다. @error 블록 안에서는 $message 변수를 출력하여 오류 메시지를 표시할 수 있습니다.

<!-- /resources/views/post/create.blade.php --> <label for="title">게시글 제목</label> <input id="title" type="text" class="@error('title') is-invalid @enderror" /> @error('title') <div class="alert alert-danger">{{ $message }}</div> @enderror

@error 디렉티브는 내부적으로 if 문으로 컴파일되므로, @else 디렉티브를 함께 사용하여 오류가 없을 때 표시할 내용을 지정할 수도 있습니다.

<!-- /resources/views/auth.blade.php --> <label for="email">이메일 주소</label> <input id="email" type="email" class="@error('email') is-invalid @else is-valid @enderror" />

한 페이지에 여러 폼이 있는 경우, @error 디렉티브의 두 번째 인수로 특정 오류 백(error bag)의 이름을 전달하여 해당 폼의 유효성 검사 오류만 가져올 수 있습니다.

<!-- /resources/views/auth.blade.php --> <label for="email">이메일 주소</label> <input id="email" type="email" class="@error('email', 'login') is-invalid @enderror" /> @error('email', 'login') <div class="alert alert-danger">{{ $message }}</div> @enderror

스택

Blade는 스택(Stack) 이라는 기능을 제공합니다. 스택에 콘텐츠를 추가해 두면, 레이아웃이나 다른 뷰의 원하는 위치에서 한꺼번에 렌더링할 수 있습니다. 자식 뷰에서 필요한 JavaScript 파일을 레이아웃의 <head>에 모아서 출력할 때 특히 유용합니다.

@push('scripts') <script src="/example.js"></script> @endpush

특정 조건이 true일 때만 스택에 추가하고 싶다면 @pushIf 디렉티브를 사용하세요:

@pushIf($shouldPush, 'scripts') <script src="/example.js"></script> @endPushIf

@push는 필요한 만큼 여러 번 호출할 수 있습니다. 쌓인 내용을 실제로 출력하려면 @stack 디렉티브에 스택 이름을 전달하면 됩니다:

<head> <!-- 헤드 콘텐츠 --> @stack('scripts') </head>

스택의 맨 앞에 내용을 추가하고 싶다면 @prepend 디렉티브를 사용하세요. @push로 추가한 내용보다 먼저 출력됩니다:

@push('scripts') 두 번째로 출력됩니다... @endpush // 이후에... @prepend('scripts') 첫 번째로 출력됩니다... @endprepend

NOTE

@push는 호출 순서대로 스택 끝에 추가되고, @prepend는 항상 스택의 맨 앞에 삽입됩니다. 순서가 중요한 스크립트(예: 의존성이 있는 라이브러리)를 다룰 때 적절히 활용하세요.

스택이 비어 있는지 확인하려면 @hasstack 디렉티브를 사용하세요. 스택에 내용이 있을 때만 감싸는 HTML을 출력하는 데 활용할 수 있습니다:

@hasstack('list') <ul> @stack('list') </ul> @endif

서비스 주입 (Service Injection)

@inject 디렉티브를 사용하면 Laravel 서비스 컨테이너에서 서비스를 직접 가져올 수 있습니다. 첫 번째 인수는 서비스를 담을 변수 이름이고, 두 번째 인수는 resolve할 클래스 또는 인터페이스의 이름입니다.

@inject('metrics', 'App\Services\MetricsService') <div> 월간 매출: {{ $metrics->monthlyRevenue() }}. </div>

NOTE

@inject는 뷰 안에서 서비스 컨테이너를 바로 활용할 수 있어 간편하지만, 뷰에 비즈니스 로직이 섞이지 않도록 컨트롤러나 뷰 컴포저에서 데이터를 전달하는 방식을 우선적으로 고려하는 것이 좋습니다.

인라인 Blade 템플릿 렌더링

때로는 순수한 Blade 템플릿 문자열을 즉시 HTML로 변환해야 할 경우가 있습니다. 이럴 때 Blade 파사드의 render 메서드를 사용할 수 있습니다. render 메서드는 Blade 템플릿 문자열과, 해당 템플릿에 제공할 데이터 배열(선택)을 인수로 받습니다.

use Illuminate\Support\Facades\Blade; return Blade::render('안녕하세요, {{ $name }}님!', ['name' => '철수']);

Laravel은 인라인 Blade 템플릿을 storage/framework/views 디렉터리에 임시 파일로 저장합니다. 렌더링 후 이 임시 파일을 삭제하고 싶다면 deleteCachedView 인수를 true로 전달하세요.

return Blade::render( '안녕하세요, {{ $name }}님!', ['name' => '철수'], deleteCachedView: true );

인라인 Blade 템플릿 렌더링

Blade 템플릿 문자열을 직접 HTML로 변환해야 할 때가 있습니다. Blade 파사드의 render 메서드를 사용하면 됩니다. 첫 번째 인수로 Blade 템플릿 문자열을, 두 번째 인수로 템플릿에 전달할 데이터 배열을 넘겨줍니다.

use Illuminate\Support\Facades\Blade; return Blade::render('안녕하세요, {{ $name }}님!', ['name' => '홍길동']);

인라인 Blade 템플릿은 렌더링 시 storage/framework/views 디렉터리에 임시 파일로 저장됩니다. 렌더링 후 이 임시 파일을 자동으로 삭제하려면 deleteCachedView 인수를 true로 지정하세요.

return Blade::render( '안녕하세요, {{ $name }}님!', ['name' => '홍길동'], deleteCachedView: true );

NOTE

임시 파일 삭제 옵션을 사용하지 않으면 동일한 인라인 템플릿을 반복 호출할 때 캐시 파일이 누적될 수 있습니다. 빈번하게 호출하는 경우라면 deleteCachedView: true를 함께 지정하는 것이 좋습니다.

Blade 프래그먼트 렌더링

Turbohtmx 같은 프론트엔드 프레임워크를 사용할 때, HTTP 응답으로 Blade 템플릿의 일부분만 반환해야 하는 경우가 있습니다. Blade 프래그먼트(fragment) 기능이 바로 이런 상황을 위한 것입니다.

NOTE

htmx를 예로 들면, 사용자가 목록을 새로고침할 때 전체 페이지가 아니라 <ul> 영역만 교체하고 싶을 수 있습니다. 프래그먼트를 활용하면 별도의 뷰 파일을 만들지 않고도 이런 부분 렌더링이 가능합니다.

사용하려면 Blade 템플릿에서 반환할 영역을 @fragment@endfragment 디렉티브로 감싸세요:

@fragment('user-list') <ul> @foreach ($users as $user) <li>{{ $user->name }}</li> @endforeach </ul> @endfragment

그런 다음, 해당 뷰를 렌더링할 때 fragment 메서드를 호출하면 지정한 프래그먼트만 HTTP 응답에 포함됩니다:

return view('dashboard', ['users' => $users])->fragment('user-list');

fragmentIf 메서드를 사용하면 주어진 조건에 따라 프래그먼트 또는 전체 뷰를 선택적으로 반환할 수 있습니다. 조건이 false이면 뷰 전체가 반환됩니다:

return view('dashboard', ['users' => $users]) ->fragmentIf($request->hasHeader('HX-Request'), 'user-list');

여러 프래그먼트를 한 번에 반환하려면 fragmentsfragmentsIf 메서드를 사용하세요. 지정한 프래그먼트들은 순서대로 이어붙여져 반환됩니다:

view('dashboard', ['users' => $users]) ->fragments(['user-list', 'comment-list']); view('dashboard', ['users' => $users]) ->fragmentsIf( $request->hasHeader('HX-Request'), ['user-list', 'comment-list'] );

Blade 확장

directive 메서드를 사용하면 나만의 커스텀 디렉티브를 정의할 수 있습니다. Blade 컴파일러가 커스텀 디렉티브를 만나면, 디렉티브에 전달된 표현식을 인자로 하여 등록된 콜백을 호출합니다.

아래 예시는 DateTime 인스턴스인 $var를 포맷하는 @datetime($var) 디렉티브를 만드는 코드입니다:

<?php namespace App\Providers; use Illuminate\Support\Facades\Blade; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * 애플리케이션 서비스를 등록합니다. */ public function register(): void { // ... } /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Blade::directive('datetime', function (string $expression) { return "<?php echo ($expression)->format('Y-m-d H:i'); ?>"; }); } }

디렉티브에 전달된 표현식에 format 메서드를 체이닝하는 방식입니다. 위 예시에서 이 디렉티브가 최종적으로 생성하는 PHP 코드는 다음과 같습니다:

<?php echo ($var)->format('Y-m-d H:i'); ?>

WARNING

Blade 디렉티브의 로직을 수정한 뒤에는 반드시 캐시된 Blade 뷰를 삭제해야 합니다. view:clear Artisan 명령어로 캐시를 제거할 수 있습니다.

커스텀 Echo 핸들러

Blade에서 객체를 {{ }} 로 출력하면, 해당 객체의 __toString 매직 메서드가 호출됩니다. 그런데 서드파티 라이브러리의 클래스처럼 __toString을 직접 제어할 수 없는 경우도 있습니다.

이럴 때는 Blade의 stringable 메서드로 특정 타입의 객체에 대한 커스텀 Echo 핸들러를 등록할 수 있습니다. stringable은 클로저를 인자로 받으며, 클로저의 타입 힌트로 렌더링할 객체 타입을 지정합니다. 보통 AppServiceProviderboot 메서드 안에서 호출합니다:

use Illuminate\Support\Facades\Blade; use Money\Money; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Blade::stringable(function (Money $money) { return $money->formatTo('ko_KR'); }); }

핸들러를 등록하고 나면, Blade 템플릿에서 해당 객체를 그냥 출력하기만 하면 됩니다:

결제 금액: {{ $money }}

커스텀 조건문 디렉티브

단순한 조건 분기를 위해 커스텀 디렉티브를 직접 작성하면 코드가 불필요하게 복잡해질 수 있습니다. 이런 경우를 위해 Blade는 Blade::if 메서드를 제공합니다. 클로저만 넘기면 손쉽게 커스텀 조건 디렉티브를 만들 수 있습니다.

예를 들어, 애플리케이션의 기본 파일시스템 디스크를 확인하는 조건 디렉티브를 AppServiceProviderboot 메서드에서 정의해 보겠습니다:

use Illuminate\Support\Facades\Blade; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Blade::if('disk', function (string $value) { return config('filesystems.default') === $value; }); }

커스텀 조건 디렉티브를 등록하면, 템플릿에서 다음과 같이 사용할 수 있습니다:

@disk('local') {{-- 애플리케이션이 로컬 디스크를 사용 중입니다 --}} @elsedisk('s3') {{-- 애플리케이션이 S3 디스크를 사용 중입니다 --}} @else {{-- 애플리케이션이 다른 디스크를 사용 중입니다 --}} @enddisk @unlessdisk('local') {{-- 애플리케이션이 로컬 디스크를 사용하지 않습니다 --}} @enddisk

Blade::ifdisk를 등록하면 @disk, @elsedisk, @unlessdisk, @enddisk 디렉티브가 자동으로 생성됩니다. 별도로 각각을 정의할 필요가 없습니다.

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

번역일: 2026년 7월 2일