Blade 템플릿

업데이트됨

번역일: 2026년 7월 2일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 6월 20일
번역 갱신
2026년 7월 2일

Blade 템플릿

소개

Blade는 Laravel에 내장된 심플하면서도 강력한 템플릿 엔진입니다. 일부 PHP 템플릿 엔진과 달리, Blade는 뷰 안에서 순수 PHP 코드를 자유롭게 사용할 수 있습니다. 실제로 모든 Blade 뷰는 PHP 코드로 컴파일된 뒤 캐시되므로, 뷰가 변경되지 않는 한 재컴파일이 발생하지 않아 성능 부담이 거의 없습니다.

Blade 뷰 파일의 확장자는 .blade.php이며, 기본적으로 resources/views 디렉터리에 위치합니다. 컴파일된 뷰는 storage/framework/views 디렉터리에 캐시됩니다. 라우트나 컨트롤러에서 전역 view 헬퍼 함수를 사용해 Blade 뷰를 반환할 수 있습니다.

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

Livewire로 Blade 확장하기

Blade 템플릿을 한 단계 더 발전시키고 싶다면 Laravel Livewire를 살펴보세요. Livewire를 사용하면 복잡한 JavaScript 프레임워크 없이도 Blade 컴포넌트에 동적인 기능을 추가할 수 있습니다. SPA에 버금가는 현대적인 UI를 순수 서버 사이드 중심으로 구현할 수 있어, 많은 개발자들이 선호하는 방식입니다.

데이터 출력

뷰에 전달된 변수를 출력할 때는 중괄호 두 개({{ }})로 변수를 감싸면 됩니다. 예를 들어 아래와 같은 라우트가 있다면:

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

뷰에서 다음과 같이 name 변수를 출력할 수 있습니다:

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

NOTE

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

변수뿐만 아니라 PHP 함수의 반환값도 출력할 수 있습니다. Blade 에코 구문 안에서는 어떤 PHP 표현식도 사용할 수 있습니다:

현재 시각: {{ now()->format('Y년 m월 d일') }}.

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가 해당 표현식을 처리하지 않도록 할 수 있습니다. @ 기호는 최종 출력에서 자동으로 제거됩니다:

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

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

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

{{-- Blade가 처리하는 경우 --}} @@if() {{-- 렌더링 결과 --}} @if()

JSON 렌더링

배열을 JavaScript 변수로 초기화하기 위해 JSON으로 출력해야 할 때가 있습니다. 예를 들어:

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

이렇게 직접 json_encode를 호출하는 대신, Illuminate\Support\Js::from 메서드 디렉티브를 사용할 수 있습니다. from 메서드는 PHP의 json_encode와 동일한 인수를 받으며, 반환되는 JSON은 HTML 속성 안에서도 안전하게 사용될 수 있도록 이스케이프 처리됩니다. 반환값은 주어진 객체나 배열을 JavaScript 객체로 변환하는 JSON.parse JavaScript 구문 문자열입니다:

<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 변수를 출력해야 한다면, 해당 HTML을 @verbatim 디렉티브로 감싸면 각 Blade 에코 구문마다 @를 붙이지 않아도 됩니다:

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

Blade 디렉티브

Blade는 템플릿 상속과 데이터 출력 외에도, 조건문이나 반복문과 같이 자주 사용하는 PHP 제어 구조를 간결하게 표현할 수 있는 디렉티브를 제공합니다. 이 디렉티브들은 친숙한 PHP 문법을 그대로 따르면서도 훨씬 깔끔하게 작성할 수 있게 해줍니다.

If 문

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

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

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

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

앞서 소개한 디렉티브 외에도, @isset@empty 디렉티브는 각각 PHP의 isset()empty() 함수에 대응하는 편리한 단축 표현입니다:

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

인증 디렉티브

@auth@guest 디렉티브를 사용하면 현재 사용자가 인증된 상태인지 게스트 상태인지를 빠르게 확인할 수 있습니다:

@auth // 인증된 사용자만 볼 수 있습니다. @endauth @guest // 비인증 사용자(게스트)만 볼 수 있습니다. @endguest

필요에 따라 특정 인증 가드를 지정할 수도 있습니다:

@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 디렉티브로 세션 값이 존재하는지 확인할 수 있습니다. 세션 값이 있으면 @session@endsession 사이의 내용이 출력됩니다. 블록 안에서는 $value 변수로 세션 값에 접근할 수 있습니다:

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

Switch 문

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

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

반복문

조건문 외에도 Blade는 PHP의 반복 구조를 위한 다양한 디렉티브를 제공합니다. 이 디렉티브들은 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 클래스를 조건부로 적용할 때 사용합니다. 배열의 키에 클래스명을, 값에 조건(불리언 표현식)을 넣으면 됩니다. 배열 키가 숫자인 경우(정수 키)에는 조건 없이 항상 클래스 목록에 포함됩니다:

@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 디렉티브를 사용하면 HTML 요소에 인라인 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" 상태인지를 쉽게 표현할 수 있습니다. 조건이 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 디렉티브는 요소가 비활성화(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보다 더 많은 기능을 제공합니다. 새로운 프로젝트에서는 컴포넌트 사용을 우선 고려해보세요.

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

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

서브뷰는 부모 뷰의 모든 변수를 상속하지만, 추가로 데이터를 배열로 전달할 수도 있습니다:

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

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

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

불리언 표현식이 true 또는 false일 때 조건부로 뷰를 포함하려면 @includeWhen@includeUnless를 사용하세요:

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

주어진 뷰 배열에서 가장 먼저 존재하는 뷰를 포함하려면 @includeFirst 디렉티브를 사용합니다:

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

WARNING

Blade 뷰 안에서 __DIR____FILE__ 상수는 사용하지 마세요. 이 상수들은 캐시된 컴파일 뷰의 경로를 가리키기 때문에 예기치 않은 결과가 발생할 수 있습니다.

컬렉션을 위한 뷰 렌더링

반복문과 뷰 포함을 하나의 디렉티브로 합칠 때는 @each 디렉티브를 사용하면 됩니다:

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

첫 번째 인수는 개별 항목을 렌더링할 뷰이고, 두 번째는 반복할 배열이나 컬렉션, 세 번째는 뷰 내부에서 현재 항목에 접근할 변수명입니다. 예를 들어 jobs 배열을 반복한다면 각 job은 뷰 안에서 job 변수로 접근할 수 있습니다. 현재 항목의 배열 키는 key 변수로 사용할 수 있습니다.

네 번째 인수로 배열이 비어 있을 때 사용할 뷰를 지정할 수 있습니다:

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

WARNING

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

`@once` 디렉티브

@once 디렉티브를 사용하면 해당 템플릿 블록이 렌더링 사이클당 한 번만 실행됩니다. 예를 들어 반복문 안에서 특정 JavaScript를 페이지 헤더에 한 번만 추가하고 싶을 때 유용합니다:

@foreach ($products as $product) @once @push('scripts') <script> // 커스텀 JavaScript 초기화 코드 </script> @endpush @endonce @endforeach

@once@push@prepend와 함께 자주 사용하므로, @pushOnce@prependOnce 디렉티브도 제공합니다:

@pushOnce('scripts') <script> // 커스텀 JavaScript 초기화 코드 </script> @endPushOnce

순수 PHP

뷰 안에서 PHP 코드를 직접 실행해야 할 때는 @php 디렉티브를 사용하세요:

@php $counter = 1; @endphp

클래스 하나만 import하면 된다면 @use 디렉티브를 활용할 수 있습니다:

@use('App\Models\Flight')

@use 디렉티브의 두 번째 인수로 별칭(alias)을 지정할 수 있습니다:

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

폰트

Volt 컴포넌트를 인라인으로 작성할 때는 @volt 디렉티브를 사용합니다. 이 디렉티브에 대한 자세한 내용은 Volt 문서를 참고하세요.

주석

Blade 뷰에 주석을 추가할 때는 다음과 같이 작성합니다. HTML 주석과 달리 Blade 주석은 최종 HTML에 포함되지 않습니다:

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

컴포넌트

컴포넌트와 슬롯은 섹션, 레이아웃, 포함(include)과 비슷한 기능을 제공하지만, 개념적으로 더 이해하기 쉬운 경우도 있습니다. 컴포넌트를 만드는 방법에는 클래스 기반 컴포넌트와 익명 컴포넌트 두 가지가 있습니다.

클래스 기반 컴포넌트를 만들려면 make:component Artisan 명령어를 사용합니다. 사용 방법을 설명하기 위해 간단한 Alert 컴포넌트를 만들어 보겠습니다:

php artisan make:component Alert

make:component 명령어는 컴포넌트 클래스를 app/View/Components 디렉터리에 생성하고, 이에 대응하는 뷰를 resources/views/components 디렉터리에 생성합니다.

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

php artisan make:component Forms/Input

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

Blade 파일만 있는 익명 컴포넌트(PHP 클래스 없이)를 만들려면 make:component 명령어에 --view 플래그를 추가하세요:

php artisan make:component forms.input --view

이 명령어는 resources/views/components/forms/input.blade.php 파일만 생성하며, <x-forms.input />으로 렌더링할 수 있습니다.

패키지 컴포넌트 수동 등록

직접 만드는 애플리케이션 컴포넌트는 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 네임스페이스 아래 있다면:

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는 컴포넌트 이름을 파스칼 케이스로 변환해 연결된 클래스를 자동으로 찾습니다. 서브디렉터리도 "점(.)" 표기법으로 지원됩니다.

컴포넌트 렌더링

컴포넌트를 사용하려면 Blade 템플릿 안에서 x- 접두사를 붙인 태그로 작성합니다:

<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; }

인덱스 컴포넌트

여러 Blade 템플릿으로 구성된 컴포넌트 그룹을 하나의 디렉터리로 묶고 싶을 때가 있습니다. 예를 들어 다음과 같은 구조의 accordion 컴포넌트가 있다고 가정해보겠습니다:

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

이 구조에서는 아코디언 컴포넌트와 아코디언 아이템 컴포넌트를 각각 이렇게 렌더링합니다:

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

그런데 accordion.blade.php를 디렉터리로 바꿔 index.blade.php로 만들면 더 직관적으로 구성할 수 있습니다:

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

이렇게 하면 <x-accordion />는 자동으로 인덱스 파일을 렌더링합니다:

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

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

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이 있다면:

<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'] — 컴포넌트 이름 (예: "package-alert") // $data['attributes'] — 컴포넌트 속성 (Illuminate\View\ComponentAttributeBag 인스턴스) // $data['slot'] — 컴포넌트 슬롯 내용 (Illuminate\Support\HtmlString 인스턴스) return '<div {{ $attributes }}>컴포넌트 내용</div>'; };

componentNamex- 이후의 이름과 동일합니다. 예를 들어 <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, ) {}

속성 및 메서드 숨기기

일부 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 컴포넌트에 class를 추가하고 싶다면:

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

생성자에 포함되지 않은 속성들은 컴포넌트의 "속성 백(attribute bag)"에 자동으로 담깁니다. 이 속성 백은 $attributes 변수로 뷰에서 접근할 수 있습니다. {{ $attributes }}로 모든 속성을 렌더링할 수 있습니다:

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

WARNING

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

기본값 및 속성 병합

일부 속성에 기본값을 지정하거나 컴포넌트에 전달된 값과 병합해야 할 때는 merge 메서드를 사용합니다. class 속성을 예로 들면:

<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>

조건부 클래스 병합

특정 조건에서만 클래스를 병합하고 싶다면 mergeUnless 메서드나 addClass 메서드를 활용하거나, class 메서드를 사용할 수 있습니다. 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로 병합할 때, 제공한 기본값은 해당 속성의 "기본값"으로 동작합니다. 단, class와 달리 이런 속성들은 전달된 값으로 덮어써지며 결합되지 않습니다. 예를 들어 button 컴포넌트에서:

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

type을 지정해서 사용하면 해당 값으로 교체됩니다:

<x-button type="submit"> 제출 </x-button>

렌더링 결과:

<button type="submit"> 제출 </button>

type 외에 기본값과 전달값을 합치고 싶다면 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

배열을 전달하면 모든 속성이 존재할 때만 true를 반환합니다:

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

hasAny 메서드는 배열에 있는 속성 중 하나라도 존재하면 true를 반환합니다:

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

get 메서드로 특정 속성 값을 가져올 수 있습니다:

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

예약 키워드

기본적으로 일부 키워드는 Blade 내부에서 컴포넌트 렌더링을 위해 예약되어 있습니다. 다음 키워드들은 컴포넌트 내에서 public 프로퍼티 또는 메서드명으로 사용할 수 없습니다:

  • 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>

때로는 컴포넌트 내의 여러 위치에 각각 다른 내용을 삽입해야 할 수 있습니다. 이때 명명된 슬롯(named slot)을 사용합니다. 아래는 title 슬롯을 사용하는 예시입니다:

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

<x-slot> 태그를 사용해 명명된 슬롯에 콘텐츠를 전달할 수 있습니다. name 속성으로 슬롯명을 지정합니다. <x-slot> 태그 밖의 내용은 기본 $slot 변수로 전달됩니다:

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

스코프 슬롯

Vue 같은 JavaScript 프레임워크에 익숙하다면 "스코프 슬롯" 개념을 알고 있을 것입니다. 스코프 슬롯은 슬롯 안에서 컴포넌트의 데이터나 메서드에 접근할 수 있게 해줍니다. Laravel에서도 컴포넌트에 public 메서드나 프로퍼티를 정의하고 슬롯 안에서 $component 변수로 컴포넌트에 접근하면 비슷한 효과를 낼 수 있습니다:

<x-alert> <x-slot:title> {{ $component->formatTitle($errors) }} </x-slot> </x-alert>

슬롯 콘텐츠 여부 확인

Blade 템플릿

소개

Blade는 Laravel에 기본 내장된 간결하면서도 강력한 템플릿 엔진입니다. 일부 PHP 템플릿 엔진과 달리, Blade는 템플릿 안에서 순수 PHP 코드를 자유롭게 사용할 수 있습니다. 실제로 모든 Blade 템플릿은 순수 PHP 코드로 컴파일된 뒤 파일이 변경되기 전까지 캐시로 보관됩니다. 즉, Blade 자체가 애플리케이션 성능에 미치는 부하는 사실상 없다고 봐도 됩니다. Blade 템플릿 파일은 .blade.php 확장자를 사용하며, 일반적으로 resources/views 디렉터리에 저장합니다.

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

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

Livewire로 Blade 확장하기

Blade 템플릿을 한 단계 더 발전시켜 동적인 인터페이스를 손쉽게 구축하고 싶다면 Laravel Livewire를 살펴보세요. Livewire를 사용하면 React, Svelte, Vue 같은 프론트엔드 프레임워크에서나 가능했던 동적 기능을 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 엔티티를 이중 인코딩(double encode)합니다. 이중 인코딩을 비활성화하려면 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.js나 Alpine.js 같은 많은 JavaScript 프레임워크도 중괄호({{ }})를 표현식 출력에 사용합니다. Blade 엔진이 해당 표현식을 처리하지 않도록 하려면 @ 기호를 앞에 붙이세요:

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

이 예시에서 @ 기호는 Blade가 제거하지만, {{ name }} 표현식 자체는 그대로 HTML에 남아 JavaScript 프레임워크가 처리하게 됩니다.

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

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

JSON 렌더링

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

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

하지만 Illuminate\Support\Js::from 메서드를 사용하면 더 안전하게 처리할 수 있습니다. from 메서드는 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 디렉티브로 해당 블록 전체를 감쌀 수 있습니다:

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

@verbatim 블록 내부에서는 Blade가 어떠한 표현식도 처리하지 않으므로, JavaScript 프레임워크의 템플릿 구문을 그대로 작성할 수 있습니다.

Blade 디렉티브

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

If 문

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

@if (count($records) === 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') // 관리자로 인증된 경우... @endauth @guest('admin') // 관리자로 인증되지 않은 경우... @endguest

환경 디렉티브

프로덕션 환경에서만 표시할 내용이 있다면 @production 디렉티브를 사용합니다.

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

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

@env('staging') // "staging" 환경에서 실행 중... @endenv @env(['staging', 'production']) // "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

반복문

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

중첩 반복문에서는 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 클래스 문자열을 동적으로 생성합니다. 배열 키에 클래스명을, 배열 값에 조건(불리언)을 지정합니다. 숫자 키를 가진 항목은 조건 없이 항상 포함됩니다.

@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@example.com" @readonly($user->isNotAdmin()) />

@required 디렉티브는 요소를 필수 입력으로 지정할 때 사용합니다.

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

서브뷰 포함

NOTE

@include 디렉티브도 충분히 사용할 수 있지만, Blade 컴포넌트는 데이터 및 속성 바인딩 등 @include보다 더 많은 기능을 제공합니다. 복잡한 재사용 UI라면 컴포넌트를 먼저 고려하세요.

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

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

포함된 뷰에 추가 데이터를 전달하려면 배열을 두 번째 인수로 넘깁니다.

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

존재하지 않는 뷰를 @include하면 Laravel이 오류를 발생시킵니다. 뷰가 있을 수도 없을 수도 있는 상황이라면 @includeIf를 사용하세요.

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

조건에 따라 포함 여부를 결정하려면 @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')

첫 번째 인수는 각 항목에 대해 렌더링할 뷰, 두 번째 인수는 순회할 배열이나 컬렉션, 세 번째 인수는 뷰 안에서 사용할 변수명입니다. 예를 들어 jobs 배열을 순회한다면 뷰 안에서 각 항목을 $job 변수로 사용할 수 있습니다. 현재 항목의 배열 키는 $key 변수로 접근할 수 있습니다.

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

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

WARNING

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

`@once` 디렉티브

@once 디렉티브는 렌더링 사이클 당 한 번만 실행될 템플릿 블록을 정의합니다. 반복문 안에서 컴포넌트를 여러 번 렌더링할 때, JavaScript를 페이지 헤더에 단 한 번만 추가하고 싶을 때 유용합니다.

@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

순수 PHP

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

@php $counter = 1; @endphp

클래스를 임포트하는 용도라면 @use 디렉티브를 사용하는 것이 더 간결합니다.

@use('App\Models\Flight')

두 번째 인수로 별칭을 지정할 수도 있습니다.

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

같은 네임스페이스 안의 여러 클래스를 한 번에 임포트하려면 그룹 임포트를 사용합니다.

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

@use 디렉티브는 함수와 상수 임포트도 지원합니다. 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')

함수와 상수도 그룹 임포트를 지원합니다.

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

폰트

Laravel Vite 폰트 최적화 기능을 사용할 때, @fonts 디렉티브를 통해 설정된 폰트의 preload 링크와 인라인 폰트 CSS를 레이아웃에 렌더링할 수 있습니다.

<!doctype html> <head> {{-- ... --}} @fonts @vite('resources/js/app.js') </head>

@fonts 디렉티브는 vite.config.js에서 설정된 모든 폰트 패밀리를 렌더링합니다. 폰트를 사용하는 콘텐츠보다 앞서 애플리케이션 루트 레이아웃의 <head> 안에 배치하는 것을 권장합니다.

특정 페이지에서 일부 폰트만 필요하다면 폰트 별칭을 인수로 전달합니다.

{{-- 단일 폰트 별칭 로드... --}} @fonts('sans') {{-- 여러 폰트 별칭 로드... --}} @fonts(['sans', 'mono'])

폰트 별칭은 Vite 설정에서 alias 옵션으로 정의합니다. @fonts 디렉티브는 내부적으로 Vite 파사드의 fonts 메서드를 호출하며, 이 메서드를 직접 사용하는 것도 가능합니다.

{{ Vite::fonts(['sans', 'mono']) }}

주석

Blade 뷰 안에서 주석을 작성할 때는 {{-- --}} 문법을 사용합니다. HTML 주석(<!-- -->)과 달리 Blade 주석은 렌더링된 HTML 응답에 포함되지 않습니다.

{{-- 이 주석은 렌더링된 HTML에 포함되지 않습니다 --}}

Blade 템플릿

컴포넌트

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

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

php artisan make:component Alert

make:component 명령은 컴포넌트 클래스 외에 뷰 템플릿 파일도 함께 생성합니다. 뷰 파일은 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 패키지에 CalendarColorPicker 컴포넌트가 Package\Views\Components 네임스페이스에 있다면 다음과 같이 등록합니다.

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 템플릿에서 컴포넌트를 표시하려면 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"/>

컴포넌트에 필요한 데이터는 클래스 생성자(constructor)에 정의합니다. 생성자의 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이 있다면 다음과 같이 전달합니다.

/** * 컴포넌트 인스턴스를 생성합니다. */ 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>

위 코드는 다음과 같이 렌더링됩니다.

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

컴포넌트 메서드

컴포넌트의 public 메서드도 뷰 템플릿에서 변수처럼 호출할 수 있습니다. 예를 들어 isSelected 메서드가 있다면,

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

뷰에서 다음과 같이 사용합니다.

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

클래스에서 어트리뷰트와 슬롯 접근하기

컴포넌트 클래스의 render 메서드에서 컴포넌트 이름, 어트리뷰트, 슬롯에 접근하려면 클로저(closure)를 반환하면 됩니다.

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 뷰로 평가됩니다.

추가 의존성 주입

컴포넌트가 서비스 컨테이너에서 의존성을 필요로 한다면, 생성자에서 데이터 어트리뷰트보다 앞에 선언하면 컨테이너가 자동으로 주입해 줍니다.

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"/>

생성자에 정의되지 않은 어트리뷰트는 자동으로 컴포넌트의 "어트리뷰트 백(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"/>

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

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

조건부 클래스 병합

조건에 따라 클래스를 추가하고 싶다면 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 메서드는 주어진 어트리뷰트 중 하나라도 존재하면 true를 반환합니다.

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

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

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

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

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

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

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

예약 키워드

다음 키워드들은 Blade 컴포넌트 내부적으로 사용되므로 컴포넌트의 public 프로퍼티나 메서드 이름으로 사용할 수 없습니다.

  • 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>

때로는 컴포넌트 안의 여러 위치에 서로 다른 슬롯을 배치해야 할 수도 있습니다. title 슬롯을 추가하도록 alert 컴포넌트를 수정해 보겠습니다.

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

명명된 슬롯(named slot)의 내용은 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.js의 "스코프 슬롯"처럼, 슬롯 안에서 컴포넌트의 데이터나 메서드에 접근하고 싶다면 $component 변수를 사용하세요. 아래 예시에서는 x-alert 컴포넌트에 formatAlert public 메서드가 정의되어 있다고 가정합니다.

<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-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

동적 컴포넌트

어떤 컴포넌트를 렌더링할지 런타임에 결정해야 한다면 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 패키지의 Calendar, ColorPicker 컴포넌트가 Package\Views\Components 네임스페이스에 있다면,

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 템플릿으로 구성될 경우, 관련 파일들을 한 디렉터리 안에 모아 관리하고 싶을 수 있습니다. 예를 들어 "아코디언" 컴포넌트를 다음과 같은 구조로 만들었다고 가정해 봅시다:

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

이 구조에서는 다음처럼 아코디언 컴포넌트와 하위 항목을 렌더링할 수 있습니다:

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

그런데 위 구조에서는 루트 컴포넌트인 accordion.blade.phpaccordion/ 디렉터리 밖(components/ 바로 아래)에 두어야 하는 불편함이 있습니다. 관련 파일들을 하나의 디렉터리에 모아두기 어렵다는 뜻입니다.

Blade는 이 문제를 해결하기 위해 컴포넌트 디렉터리 안에 디렉터리 이름과 동일한 파일을 두면, 해당 파일을 루트 컴포넌트로 인식하는 기능을 제공합니다. 위 예시라면 다음과 같이 구조를 변경할 수 있습니다:

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

이렇게 하면 관련 파일들을 accordion/ 디렉터리 하나로 묶으면서도, 렌더링 문법은 기존과 동일하게 유지됩니다:

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

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

익명 컴포넌트는 클래스가 없기 때문에, 외부에서 전달된 값 중 무엇이 컴포넌트 변수(props)이고 무엇이 HTML 어트리뷰트인지 구분하는 방법이 필요합니다.

@props 디렉티브를 Blade 템플릿 상단에 선언하면 해당 값들은 컴포넌트 변수로 취급되고, 나머지 값들은 자동으로 어트리뷰트 백에 담깁니다. 기본값을 지정하려면 배열의 키-값 형태로 작성합니다:

<!-- /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"/>

typemessage@props에 선언했으므로 컴포넌트 변수로 사용되고, class는 어트리뷰트 백을 통해 <div>에 병합됩니다.

부모 컴포넌트 데이터 접근

자식 컴포넌트 안에서 부모 컴포넌트의 데이터를 참조해야 할 때는 @aware 디렉티브를 사용합니다. 예를 들어 부모 <x-menu>와 자식 <x-menu.item>으로 구성된 메뉴 컴포넌트를 생각해 봅시다:

<x-menu color="purple"> <x-menu.item>...</x-menu.item> <x-menu.item>...</x-menu.item> </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 디렉터리에서 불러옵니다. 그러나 패키지 개발이나 도메인 분리 구조에서는 추가 경로를 등록해야 할 수도 있습니다.

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 ?? '할 일 관리' }}</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

앞서 레이아웃 예시에서 sidebar 섹션은 @show로 끝났지만, 자식 뷰에서는 @endsection으로 닫습니다. @endsection은 섹션을 정의만 하는 반면, @show는 섹션을 정의하면서 즉시 출력까지 수행합니다. 레이아웃 파일에서 기본값을 바로 렌더링해야 할 때 @show를 사용합니다.

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

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

폼(Forms)

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

NOTE

로그인 폼과 회원가입 폼처럼 하나의 페이지에 여러 폼이 공존할 때, 각 폼에 별도의 오류 백 이름을 지정하면 오류 메시지가 뒤섞이는 문제를 방지할 수 있습니다.

스택(Stacks)

Blade는 스택이라는 이름 있는 컨텐츠 저장소를 제공합니다. 자식 뷰나 컴포넌트에서 @push로 내용을 쌓아두고, 레이아웃의 원하는 위치에서 @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> <!-- Head Contents --> @stack('scripts') </head>

기본적으로 @push는 내용을 스택의 에 추가합니다. 스택의 에 내용을 삽입하려면 @prepend 디렉티브를 사용하세요:

@push('scripts') 두 번째로 출력됩니다... @endpush {{-- 이후 어딘가에서... --}} @prepend('scripts') 첫 번째로 출력됩니다... @endprepend

NOTE

@prepend는 이미 쌓인 내용의 앞에 삽입하므로, 의존성 순서가 중요한 스크립트를 먼저 로드해야 할 때 활용하면 편리합니다.

스택에 내용이 있는지 확인하려면 @hasstack 디렉티브를 사용하세요. 스택이 비어 있으면 해당 블록은 렌더링되지 않습니다:

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

서비스 주입

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

@inject('metrics', 'App\Services\MetricsService') <div> 이번 달 매출: {{ $metrics->monthlyRevenue() }}</div>

인라인 Blade 템플릿 렌더링

인라인 Blade 템플릿 렌더링

Blade 템플릿 문자열을 직접 HTML로 변환해야 할 때가 있습니다. 이럴 때는 Blade 파사드의 render 메서드를 사용할 수 있습니다. 이 메서드는 Blade 템플릿 문자열과 템플릿에 전달할 데이터 배열(선택 사항)을 인자로 받습니다:

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

Laravel은 인라인 Blade 템플릿을 렌더링할 때 storage/framework/views 디렉터리에 임시 파일을 생성합니다. 렌더링 후 이 임시 파일을 자동으로 삭제하려면 deleteCachedView 인자를 전달하면 됩니다:

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

NOTE

deleteCachedView: true를 사용하면 임시 캐시 파일이 매번 삭제되므로, 동일한 템플릿을 반복적으로 렌더링하는 경우에는 성능에 영향을 줄 수 있습니다. 일회성 렌더링이나 동적으로 생성되는 템플릿에 적합합니다.

Blade 프래그먼트 렌더링

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

NOTE

htmx나 Turbo는 페이지 전체를 다시 로드하지 않고 특정 영역만 서버에서 받아 업데이트하는 방식으로 동작합니다. 이때 서버는 전체 HTML이 아닌 해당 영역에 해당하는 HTML 조각(프래그먼트)만 반환하면 충분합니다.

사용 방법은 간단합니다. 템플릿에서 반환할 부분을 @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에서 객체를 {{ }} 로 출력하려 하면, PHP의 매직 메서드인 __toString이 호출됩니다. 그런데 서드파티 라이브러리에서 제공하는 클래스처럼 __toString을 직접 제어할 수 없는 경우가 있습니다.

이런 상황을 위해 Blade는 특정 타입의 객체에 대한 커스텀 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

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

번역일: 2026년 7월 2일