블레이드 템플릿 (Blade Templates)
소개 (Introduction)
Blade는 Laravel에 포함된 간단하면서도 강력한 템플릿 엔진입니다. 일부 PHP 템플릿 엔진과 달리 Blade는 템플릿에서 일반 PHP 코드를 사용하는 것을 제한하지 않습니다. 실제로 모든 Blade 템플릿은 일반 PHP 코드로 컴파일되고 수정될 때까지 캐시됩니다. 즉, Blade는 기본적으로 애플리케이션에 오버헤드가 전혀 추가되지 않습니다. Blade 템플릿 파일은 .blade.php 파일 확장자를 사용하며 일반적으로 resources/views 디렉터리에 저장됩니다.
Blade 뷰는 전역 view 도우미를 사용하여 라우트 또는 컨트롤러에서 반환될 수 있습니다. 물론 뷰 문서에 언급된 대로 데이터는 view 도우미의 두 번째 인수를 사용하여 Blade 뷰에 전달될 수 있습니다.
Route::get('/', function () {
return view('greeting', ['name' => 'Finn']);
});
Livewire로 Blade 강화하기
Blade 템플릿을 한 단계 더 발전시키고 동적 인터페이스를 쉽게 구축하고 싶으십니까? Laravel Livewire를 확인하세요. Livewire를 사용하면 일반적으로 React, Svelte 또는 Vue와 같은 프론트엔드 프레임워크를 통해서만 가능한 동적 기능으로 강화된 Blade 컴포넌트를 작성할 수 있으며, 이는 복잡성, 클라이언트 측 렌더링 또는 많은 JavaScript 프레임워크의 구축 단계 없이 현대적이고 반응적인 프론트엔드를 구축하는 훌륭한 접근 방식을 제공합니다.
데이터 표시 (Displaying Data)
변수를 중괄호로 묶어 Blade 뷰에 전달된 데이터를 표시할 수 있습니다. 예를 들어, 다음 라우트가 주어지면:
Route::get('/', function () {
return view('welcome', ['name' => 'Samantha']);
});
다음과 같이 name 변수의 내용을 표시할 수 있습니다.
Hello, {{ $name }}.
[!NOTE] Blade의
{{ }}에코 문은 XSS 공격을 방지하기 위해 PHP의htmlspecialchars기능을 통해 자동으로 전송됩니다.
뷰에 전달된 변수의 내용을 표시하는 것으로 제한되지는 않습니다. PHP 함수의 결과를 에코할 수도 있습니다. 실제로 Blade echo 문 안에 원하는 PHP 코드를 넣을 수 있습니다.
The current UNIX timestamp is {{ time() }}.
HTML 엔터티 인코딩
기본적으로 Blade(및 Laravel e 함수)는 HTML 엔터티를 이중으로 인코딩합니다. 이중 인코딩을 비활성화하려면 AppServiceProvider의 boot 메서드에서 Blade::withoutDoubleEncoding 메서드를 호출하세요.
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Blade::withoutDoubleEncoding();
}
}
이스케이프되지 않은 데이터 표시
기본적으로 Blade {{ }} 문은 XSS 공격을 방지하기 위해 PHP의 htmlspecialchars 기능을 통해 자동으로 전송됩니다. 데이터가 이스케이프되는 것을 원하지 않으면 다음 구문을 사용할 수 있습니다.
Hello, {!! $name !!}.
[!WARNING] 애플리케이션 사용자가 제공한 콘텐츠를 에코할 때는 매우 주의하세요. 사용자 제공 데이터를 표시할 때 XSS 공격을 방지하려면 일반적으로 이스케이프된 이중 중괄호 구문을 사용해야 합니다.
Blade 및 JavaScript 프레임워크
많은 JavaScript 프레임워크는 주어진 표현식이 브라우저에 표시되어야 함을 나타내기 위해 "중괄호"를 사용하므로 @ 기호를 사용하여 표현식이 그대로 유지되어야 함을 Blade 렌더링 엔진에 알릴 수 있습니다. 예를 들어:
<h1>Laravel</h1>
Hello, @{{ name }}.
이 예에서 @ 기호는 Blade에 의해 제거됩니다. 그러나 {{ name }} 표현식은 Blade 엔진의 영향을 받지 않고 그대로 유지되므로 JavaScript 프레임워크에서 렌더링할 수 있습니다.
@ 기호는 Blade 지시문을 이스케이프하는 데 사용될 수도 있습니다.
{{-- Blade template --}}
@@if()
<!-- HTML output -->
@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 인용문에 포함되도록 올바르게 이스케이프되었는지 확인합니다. from 메소드는 주어진 개체 또는 배열을 유효한 JavaScript 개체로 변환하는 문자열 JSON.parse JavaScript 문을 반환합니다.
<script>
var app = {{ Illuminate\Support\Js::from($array) }};
</script>
Laravel 애플리케이션 스켈레톤의 최신 버전에는 Js 외관이 포함되어 있어 Blade 템플릿 내에서 이 기능에 편리하게 액세스할 수 있습니다.
<script>
var app = {{ Js::from($array) }};
</script>
[!WARNING] 기존 변수를 JSON로 렌더링하려면
Js::from메서드만 사용해야 합니다. Blade 템플릿은 정규식을 기반으로 하며 복잡한 표현식을 지시문에 전달하려고 하면 예기치 않은 오류가 발생할 수 있습니다.
@verbatim 지시어
템플릿의 많은 부분에 JavaScript 변수를 표시하는 경우 각 Blade 에코 문 앞에 @ 기호를 붙일 필요가 없도록 HTML을 @verbatim 지시어로 래핑할 수 있습니다.
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
Blade 지시어 (Blade Directives)
템플릿 상속 및 데이터 표시 외에도 Blade는 조건문 및 루프와 같은 일반적인 PHP 제어 구조에 대한 편리한 바로 가기를 제공합니다. 이러한 단축키는 PHP 제어 구조에 대한 친숙함을 유지하면서 PHP 제어 구조로 작업하는 매우 깔끔하고 간결한 방법을 제공합니다.
If 문
@if, @elseif, @else 및 @endif 지시어를 사용하여 if 문을 생성할 수 있습니다. 이러한 지시어는 해당 PHP 지시어와 동일하게 작동합니다.
@if (count($records) === 1)
I have one record!
@elseif (count($records) > 1)
I have multiple records!
@else
I don't have any records!
@endif
편의를 위해 Blade는 @unless 지시문도 제공합니다.
@unless (Auth::check())
You are not signed in.
@endunless
이미 설명한 조건부 지시문 외에도 @isset 및 @empty 지시문을 해당 PHP 기능에 대한 편리한 바로 가기로 사용할 수 있습니다.
@isset($records)
// $records is defined and is not null...
@endisset
@empty($records)
// $records is "empty"...
@endempty
인증 지시어
@auth 및 @guest 지시어를 사용하면 현재 사용자가 인증되었는지 아니면 게스트인지 빠르게 확인할 수 있습니다.
@auth
// The user is authenticated...
@endauth
@guest
// The user is not authenticated...
@endguest
필요한 경우 @auth 및 @guest 지시문을 사용할 때 확인해야 하는 인증 가드를 지정할 수 있습니다.
@auth('admin')
// The user is authenticated...
@endauth
@guest('admin')
// The user is not authenticated...
@endguest
환경 지침
@production 지시문을 사용하여 애플리케이션이 프로덕션 환경에서 실행되고 있는지 확인할 수 있습니다.
@production
// Production specific content...
@endproduction
또는 @env 지시어를 사용하여 애플리케이션이 특정 환경에서 실행되고 있는지 확인할 수 있습니다.
@env('staging')
// The application is running in "staging"...
@endenv
@env(['staging', 'production'])
// The application is running in "staging" or "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 지시문 내의 템플릿 내용이 평가됩니다. @session 지시문의 내용 내에서 $value 변수를 에코하여 세션 값을 표시할 수 있습니다.
@session('status')
<div class="p-4 bg-green-100">
{{ $value }}
</div>
@endsession
컨텍스트 지시어
@context 지시어는 context 값이 존재하는지 확인하는 데 사용될 수 있습니다. 컨텍스트 값이 존재하는 경우 @context 및 @endcontext 지시문 내의 템플릿 내용이 평가됩니다. @context 지시문의 내용 내에서 $value 변수를 에코하여 컨텍스트 값을 표시할 수 있습니다.
@context('canonical')
<link href="{{ $value }}" rel="canonical">
@endcontext
스위치 문
Switch 문은 @switch, @case, @break, @default 및 @endswitch 지시문을 사용하여 구성할 수 있습니다.
@switch($i)
@case(1)
First case...
@break
@case(2)
Second case...
@break
@default
Default case...
@endswitch
루프
조건문 외에도 Blade는 PHP의 루프 구조 작업을 위한 간단한 지시문을 제공합니다. 다시 말하지만, 이러한 각 지시문은 PHP 대응 항목과 동일하게 작동합니다.
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
@foreach ($users as $user)
<p>This is user {{ $user->id }}</p>
@endforeach
@forelse ($users as $user)
<li>{{ $user->name }}</li>
@empty
<p>No users</p>
@endforelse
@while (true)
<p>I'm looping forever.</p>
@endwhile
[!NOTE]
foreach루프를 반복하는 동안 루프 변수를 사용하여 루프를 통해 첫 번째 또는 마지막 반복에 있는지와 같은 루프에 대한 귀중한 정보를 얻을 수 있습니다.
루프를 사용할 때 현재 반복을 건너뛰거나 @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
루프 변수
foreach 루프를 반복하는 동안 루프 내에서 $loop 변수를 사용할 수 있습니다. 이 변수는 현재 루프 인덱스 및 이것이 루프를 통한 첫 번째 또는 마지막 반복인지 여부와 같은 몇 가지 유용한 정보에 대한 액세스를 제공합니다.
@foreach ($users as $user)
@if ($loop->first)
This is the first iteration.
@endif
@if ($loop->last)
This is the last iteration.
@endif
<p>This is user {{ $user->id }}</p>
@endforeach
중첩 루프에 있는 경우 parent 속성을 통해 상위 루프의 $loop 변수에 액세스할 수 있습니다.
@foreach ($users as $user)
@foreach ($user->posts as $post)
@if ($loop->parent->first)
This is the first iteration of the parent loop.
@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 | 중첩 루프에 있는 경우 상위 루프 변수입니다. |
조건부 클래스 및 스타일
@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 스타일을 조건부로 추가할 수 있습니다.
@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 확인란 입력이 "선택"되었는지 쉽게 나타낼 수 있습니다. 제공된 조건이 true로 평가되면 이 지시어는 checked를 표시합니다.
<input
type="checkbox"
name="active"
value="active"
@checked(old('active', $user->active))
/>
마찬가지로 @selected 지시문을 사용하여 주어진 선택 옵션을 "선택"해야 하는지 여부를 나타낼 수 있습니다.
<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())>Submit</button>
또한 @readonly 지시문을 사용하여 특정 요소가 "읽기 전용"인지 여부를 나타낼 수 있습니다.
<input
type="email"
name="email"
value="[email protected]"
@readonly($user->isNotAdmin())
/>
또한 @required 지시문을 사용하여 특정 요소가 "필수"인지 여부를 나타낼 수 있습니다.
<input
type="text"
name="title"
value="title"
@required($user->isAdmin())
/>
하위 뷰 포함
[!NOTE]
@include지시문을 자유롭게 사용할 수 있지만 Blade 컴포넌트는 유사한 기능을 제공하고 데이터 및 속성 바인딩과 같은@include지시문에 비해 여러 가지 이점을 제공합니다.
Blade의 @include 지시어를 사용하면 다른 뷰 내에 Blade 뷰를 포함할 수 있습니다. 상위 뷰에서 사용할 수 있는 모든 변수는 포함된 뷰에서도 사용할 수 있습니다.
<div>
@include('shared.errors')
<form>
<!-- Form Contents -->
</form>
</div>
포함된 뷰가 상위 뷰에서 사용 가능한 모든 데이터를 상속하더라도 포함된 뷰에서 사용 가능해야 하는 추가 데이터 배열을 전달할 수도 있습니다.
@include('view.name', ['status' => 'complete'])
존재하지 않는 뷰를 @include하려고 시도하면 Laravel에서 오류가 발생합니다. 존재하거나 존재하지 않을 수 있는 뷰를 포함하려면 @includeIf 지시문을 사용해야 합니다.
@includeIf('view.name', ['status' => 'complete'])
주어진 부울 표현식이 true 또는 false로 평가되는 경우 @include 및 뷰를 수행하려면 @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__상수는 캐시되고 컴파일된 뷰의 위치를 참조하므로 사용을 피해야 합니다.
컬렉션을 위한 뷰 렌더링
Blade의 @each 지시문을 사용하여 루프와 포함을 한 줄로 결합할 수 있습니다.
@each('view.name', $jobs, 'job')
@each 지시문의 첫 번째 인수는 배열 또는 컬렉션의 각 요소에 대해 렌더링하는 뷰입니다. 두 번째 인수는 반복하려는 배열 또는 컬렉션이고, 세 번째 인수는 뷰 내에서 현재 반복에 할당될 변수 이름입니다. 따라서 예를 들어 jobs 배열을 반복하는 경우 일반적으로 각 작업을 뷰 내의 job 변수로 액세스하려고 합니다. 현재 반복의 배열 키는 뷰 내에서 key 변수로 사용할 수 있습니다.
@each 지시문에 네 번째 인수를 전달할 수도 있습니다. 이 인수는 주어진 배열이 비어 있는 경우 렌더링될 뷰를 결정합니다.
@each('view.name', $jobs, 'job', 'view.empty')
[!WARNING]
@each를 통해 렌더링된 뷰는 상위 뷰로부터 변수를 상속받지 않습니다. 하위 뷰에 이러한 변수가 필요한 경우 대신@foreach및@include지시어를 사용해야 합니다.
@once 지시어
@once 지시문을 사용하면 렌더링 주기당 한 번만 평가되는 템플릿 부분을 정의할 수 있습니다. 이는 스택을 사용하여 JavaScript의 특정 부분을 페이지 헤더에 푸시하는 데 유용할 수 있습니다. 예를 들어, 루프 내에서 특정 컴포넌트를 렌더링하는 경우 컴포넌트가 처음 렌더링될 때 JavaScript만 헤더에 푸시할 수 있습니다.
@once
@push('scripts')
<script>
// Your custom JavaScript...
</script>
@endpush
@endonce
@once 지시어는 @push 또는 @prepend 지시어와 함께 사용되는 경우가 많으므로 편의를 위해 @pushOnce 및 @prependOnce 지시어를 사용할 수 있습니다.
@pushOnce('scripts')
<script>
// Your custom JavaScript...
</script>
@endPushOnce
두 개의 개별 Blade 템플릿에서 중복 콘텐츠를 푸시하는 경우 콘텐츠가 한 번만 렌더링되도록 @pushOnce 지시어에 대한 두 번째 인수로 고유 식별자를 제공해야 합니다.
<!-- 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 코드를 뷰에 삽입하는 것이 유용합니다. Blade @php 지시문을 사용하여 템플릿 내에서 일반 PHP 블록을 실행할 수 있습니다.
@php
$counter = 1;
@endphp
또는 클래스를 가져오기 위해 PHP만 사용해야 하는 경우 @use 지시문을 사용할 수 있습니다.
@use('App\Models\Flight')
가져온 클래스의 별칭을 지정하기 위해 @use 지시문에 두 번째 인수를 제공할 수 있습니다.
@use('App\Models\Flight', 'FlightModel')
동일한 네임스페이스 내에 여러 클래스가 있는 경우 해당 클래스의 가져오기를 그룹화할 수 있습니다.
@use('App\Models\{Flight, Airport}')
@use 지시문은 가져오기 경로 앞에 function 또는 const 수정자를 추가하여 PHP 함수 및 상수 가져오기를 지원합니다.
@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')
그룹화된 가져오기는 function 및 const 한정자 모두에서 지원되므로 단일 지시어로 동일한 네임스페이스에서 여러 기호를 가져올 수 있습니다.
@use(function App\Helpers\{format_currency, format_date})
@use(const App\Constants\{MAX_ATTEMPTS, DEFAULT_TIMEOUT})
댓글
Blade를 사용하면 뷰에 주석을 정의할 수도 있습니다. 그러나 HTML 주석과 달리 Blade 주석은 애플리케이션에서 반환된 HTML에 포함되지 않습니다.
{{-- This comment will not be present in the rendered HTML --}}
컴포넌트 (Components)
컴포넌트와 슬롯은 섹션, 레이아웃 및 포함과 유사한 이점을 제공합니다. 그러나 일부는 컴포넌트와 슬롯의 정신적 모델을 이해하기가 더 쉽다고 생각할 수도 있습니다. 컴포넌트를 작성하는 방법에는 클래스 기반 컴포넌트와 익명 컴포넌트라는 두 가지 접근 방식이 있습니다.
클래스 기반 컴포넌트를 생성하려면 make:component Artisan 명령을 사용할 수 있습니다. 컴포넌트 사용 방법을 설명하기 위해 간단한 Alert 컴포넌트를 만들어 보겠습니다. make:component 명령은 컴포넌트를 app/View/Components 디렉터리에 배치합니다.
php artisan make:component Alert
make:component 명령은 컴포넌트에 대한 뷰 템플릿도 생성합니다. 뷰는 resources/views/components 디렉토리에 배치됩니다. 자신의 애플리케이션에 대한 컴포넌트를 작성할 때 컴포넌트는 app/View/Components 디렉터리 및 resources/views/components 디렉터리 내에서 자동으로 검색되므로 일반적으로 추가 컴포넌트 등록이 필요하지 않습니다.
하위 디렉터리 내에 컴포넌트를 생성할 수도 있습니다.
php artisan make:component Forms/Input
위 명령은 app/View/Components/Forms 디렉터리에 Input 컴포넌트를 생성하고 뷰는 resources/views/components/forms 디렉터리에 배치됩니다.
패키지 컴포넌트 수동 등록
자신의 애플리케이션에 대한 컴포넌트를 작성할 때 컴포넌트는 app/View/Components 디렉터리 및 resources/views/components 디렉터리 내에서 자동으로 검색됩니다.
그러나 Blade 컴포넌트를 활용하는 패키지를 구축하는 경우 컴포넌트 클래스와 해당 HTML 태그 별칭을 수동으로 등록해야 합니다. 일반적으로 패키지 서비스 프로바이더의 boot 메서드에 컴포넌트를 등록해야 합니다.
use Illuminate\Support\Facades\Blade;
/**
* Bootstrap your package's services.
*/
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;
/**
* Bootstrap your package's services.
*/
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}
이렇게 하면 package-name:: 구문을 사용하여 공급업체 네임스페이스에서 패키지 컴포넌트를 사용할 수 있습니다.
<x-nightshade::calendar />
<x-nightshade::color-picker />
Blade는 컴포넌트 이름을 파스칼 대소문자로 구분하여 이 컴포넌트에 연결된 클래스를 자동으로 감지합니다. 하위 디렉터리도 "점" 표기법을 사용하여 지원됩니다.
컴포넌트 렌더링
컴포넌트를 표시하려면 Blade 템플릿 중 하나에서 Blade 컴포넌트 태그를 사용할 수 있습니다. Blade 컴포넌트 태그는 x- 문자열로 시작하고 그 뒤에 컴포넌트 클래스의 케밥 케이스 이름이 옵니다.
<x-alert/>
<x-user-profile/>
컴포넌트 클래스가 app/View/Components 디렉터리 내에서 더 깊게 중첩된 경우 . 문자를 사용하여 디렉터리 중첩을 나타낼 수 있습니다. 예를 들어 컴포넌트가 app/View/Components/Inputs/Button.php에 있다고 가정하면 다음과 같이 렌더링할 수 있습니다.
<x-inputs.button/>
컴포넌트를 조건부로 렌더링하려면 컴포넌트 클래스에 shouldRender 메소드를 정의하면 됩니다. shouldRender 메소드가 false를 반환하면 컴포넌트가 렌더링되지 않습니다.
use Illuminate\Support\Str;
/**
* Whether the component should be rendered
*/
public function shouldRender(): bool
{
return Str::length($this->message) > 0;
}
인덱스 컴포넌트
때로는 컴포넌트가 컴포넌트 그룹의 일부이고 단일 디렉터리 내에서 관련 컴포넌트를 그룹화할 수도 있습니다. 예를 들어, 다음과 같은 클래스 구조를 가진 "카드" 컴포넌트를 상상해 보세요.
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
{
/**
* Create the component instance.
*/
public function __construct(
public string $type,
public string $message,
) {}
/**
* Get the view / contents that represent the component.
*/
public function render(): View
{
return view('components.alert');
}
}
컴포넌트가 렌더링되면 변수를 이름으로 에코하여 컴포넌트의 공용 변수 내용을 표시할 수 있습니다.
<div class="alert alert-{{ $type }}">
{{ $message }}
</div>
대소문자 규칙
컴포넌트 생성자 인수는 camelCase를 사용하여 지정해야 하며, kebab-case는 HTML 속성에서 인수 이름을 참조할 때 사용해야 합니다. 예를 들어 다음과 같은 컴포넌트 생성자가 있다고 가정합니다.
/**
* Create the component instance.
*/
public function __construct(
public string $alertType,
) {}
$alertType 인수는 다음과 같이 컴포넌트에 제공될 수 있습니다.
<x-alert alert-type="danger" />
짧은 속성 구문
속성을 컴포넌트에 전달할 때 "짧은 속성" 구문을 사용할 수도 있습니다. 속성 이름은 해당 속성 이름과 자주 일치하므로 이는 편리한 경우가 많습니다.
{{-- Short attribute syntax... --}}
<x-profile :$userId :$name />
{{-- Is equivalent to... --}}
<x-profile :user-id="$userId" :name="$name" />
이스케이프 속성 렌더링
Alpine.js와 같은 일부 JavaScript 프레임워크도 콜론 접두사 속성을 사용하므로 이중 콜론(::) 접두사를 사용하여 해당 속성이 PHP 표현식이 아님을 Blade에 알릴 수 있습니다. 예를 들어 다음 컴포넌트가 있다고 가정해 보겠습니다.
<x-button ::class="{ danger: isDeleting }">
Submit
</x-button>
다음 HTML은 Blade에 의해 렌더링됩니다.
<button :class="{ danger: isDeleting }">
Submit
</button>
컴포넌트 방법
컴포넌트 템플릿에 사용할 수 있는 공용 변수 외에도 컴포넌트의 모든 공용 메서드를 호출할 수 있습니다. 예를 들어 isSelected 메서드가 있는 컴포넌트를 상상해 보세요.
/**
* Determine if the given option is the currently selected option.
*/
public function isSelected(string $option): bool
{
return $option === $this->selected;
}
메소드 이름과 일치하는 변수를 호출하여 컴포넌트 템플릿에서 이 메소드를 실행할 수 있습니다.
<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">
{{ $label }}
</option>
컴포넌트 클래스 내의 속성 및 슬롯에 액세스
Blade 컴포넌트를 사용하면 클래스의 렌더링 메서드 내 컴포넌트 이름, 속성 및 슬롯에 액세스할 수도 있습니다. 그러나 이 데이터에 액세스하려면 컴포넌트의 render 메서드에서 클로저를 반환해야 합니다.
use Closure;
/**
* Get the view / contents that represent the component.
*/
public function render(): Closure
{
return function () {
return '<div {{ $attributes }}>Components content</div>';
};
}
컴포넌트의 render 메서드에서 반환된 클로저는 $data 배열을 유일한 인수로 받을 수도 있습니다. 이 배열에는 컴포넌트에 대한 정보를 제공하는 여러 요소가 포함됩니다.
return function (array $data) {
// $data['componentName'];
// $data['attributes'];
// $data['slot'];
return '<div {{ $attributes }}>Components content</div>';
}
[!WARNING]
$data배열의 요소는render메서드에서 반환된 Blade 문자열에 직접 포함되어서는 안 됩니다. 그렇게 하면 악성 특성 콘텐츠를 통해 원격 코드가 실행될 수 있습니다.
componentName는 HTML 태그에서 x- 접두사 뒤에 사용되는 이름과 같습니다. 따라서 <x-alert />의 componentName는 alert가 됩니다. attributes 요소에는 HTML 태그에 있던 모든 속성이 포함됩니다. slot 요소는 컴포넌트 슬롯의 콘텐츠가 포함된 Illuminate\Support\HtmlString 인스턴스입니다.
클로저는 문자열을 반환해야 합니다. 반환된 문자열이 기존 뷰에 해당하는 경우 해당 뷰가 렌더링됩니다. 그렇지 않으면 반환된 문자열은 인라인 Blade 뷰로 평가됩니다.
추가 종속성
컴포넌트에 Laravel의 서비스 컨테이너의 종속성이 필요한 경우 컴포넌트의 데이터 속성 앞에 이를 나열할 수 있으며 컨테이너에 의해 자동으로 주입됩니다.
use App\Services\AlertCreator;
/**
* Create the component instance.
*/
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
{
/**
* The properties / methods that should not be exposed to the component template.
*
* @var array
*/
protected $except = ['type'];
/**
* Create the component instance.
*/
public function __construct(
public string $type,
) {}
}
컴포넌트 속성
우리는 이미 데이터 속성을 컴포넌트에 전달하는 방법을 살펴보았습니다. 그러나 때로는 컴포넌트가 작동하는 데 필요한 데이터의 일부가 아닌 class와 같은 추가 HTML 속성을 지정해야 할 수도 있습니다. 일반적으로 이러한 추가 특성을 컴포넌트 템플릿의 루트 요소에 전달하려고 합니다. 예를 들어 다음과 같이 alert 컴포넌트를 렌더링한다고 가정해 보겠습니다.
<x-alert type="error" :message="$message" class="mt-4"/>
컴포넌트 생성자의 일부가 아닌 모든 속성은 컴포넌트의 "속성 모음"에 자동으로 추가됩니다. 이 속성 백은 $attributes 변수를 통해 컴포넌트에 자동으로 사용 가능해집니다. 모든 속성은 다음 변수를 에코하여 컴포넌트 내에서 렌더링될 수 있습니다.
<div {{ $attributes }}>
<!-- Component content -->
</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">
<!-- Contents of the $message variable -->
</div>
조건부 병합 클래스
때로는 주어진 조건이 true인 경우 클래스를 병합하고 싶을 수도 있습니다. 이 작업은 배열 키에 추가하려는 클래스가 포함되어 있고 값은 부울 표현식인 클래스 배열을 허용하는 class 메서드를 통해 수행할 수 있습니다. 배열 요소에 숫자 키가 있으면 렌더링된 클래스 목록에 항상 포함됩니다.
<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}>
{{ $message }}
</div>
다른 속성을 컴포넌트에 병합해야 하는 경우 merge 메서드를 class 메서드에 연결할 수 있습니다.
<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
[!NOTE] 병합된 속성을 수신하면 안 되는 다른 HTML 요소에 대한 클래스를 조건부로 컴파일해야 하는 경우 @class 지시어를 사용할 수 있습니다.
비클래스 속성 병합
class 속성이 아닌 속성을 병합하는 경우 merge 메소드에 제공된 값은 속성의 "기본" 값으로 간주됩니다. 그러나 class 속성과 달리 이러한 속성은 삽입된 속성 값과 병합되지 않습니다. 대신 덮어쓰게 됩니다. 예를 들어 button 컴포넌트의 구현은 다음과 같을 수 있습니다.
<button {{ $attributes->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
사용자 지정 type를 사용하여 버튼 컴포넌트를 렌더링하려면 컴포넌트를 사용할 때 지정할 수 있습니다. 유형이 지정되지 않으면 button 유형이 사용됩니다.
<x-button type="submit">
Submit
</x-button>
이 예에서 button 컴포넌트의 렌더링된 HTML은 다음과 같습니다.
<button type="submit">
Submit
</button>
class 이외의 속성에 기본값과 주입된 값을 함께 결합하려면 prepends 방법을 사용할 수 있습니다. 이 예에서 data-controller 속성은 항상 profile-controller로 시작하고 추가로 주입된 data-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 attribute is present</div>
@endif
배열이 has 메소드에 전달되면 해당 메소드는 지정된 속성이 모두 컴포넌트에 있는지 확인합니다.
@if ($attributes->has(['name', 'class']))
<div>All of the attributes are present</div>
@endif
hasAny 메소드는 주어진 속성 중 하나라도 컴포넌트에 존재하는지 확인하는 데 사용될 수 있습니다.
@if ($attributes->hasAny(['href', ':href', 'v-bind:href']))
<div>One of the attributes is present</div>
@endif
get 메소드를 사용하여 특정 속성의 값을 검색할 수 있습니다:
{{ $attributes->get('class') }}
only 메소드는 주어진 키를 가진 속성만을 검색하는데 사용될 수 있습니다:
{{ $attributes->only(['class']) }}
except 메소드는 주어진 키를 가진 속성을 제외한 모든 속성을 검색하는 데 사용될 수 있습니다:
{{ $attributes->except(['class']) }}
예약된 키워드
기본적으로 일부 키워드는 컴포넌트를 렌더링하기 위해 Blade의 내부 사용을 위해 예약되어 있습니다. 다음 키워드는 컴포넌트 내에서 공용 속성이나 메서드 이름으로 정의할 수 없습니다.
datarenderresolveresolveViewshouldRenderviewwithAttributeswithName
슬롯
"슬롯"을 통해 컴포넌트에 추가 콘텐츠를 전달해야 하는 경우가 종종 있습니다. 컴포넌트 슬롯은 $slot 변수를 반영하여 렌더링됩니다. 이 개념을 살펴보기 위해 alert 컴포넌트에 다음 마크업이 있다고 가정해 보겠습니다.
<!-- /resources/views/components/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
컴포넌트에 콘텐츠를 주입하여 slot에 콘텐츠를 전달할 수 있습니다.
<x-alert>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
때로는 컴포넌트가 컴포넌트 내 서로 다른 위치에 여러 개의 서로 다른 슬롯을 렌더링해야 할 수도 있습니다. "제목" 슬롯 삽입을 허용하도록 경고 컴포넌트를 수정해 보겠습니다.
<!-- /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>
Server Error
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
슬롯의 isEmpty 메소드를 호출하여 슬롯에 콘텐츠가 포함되어 있는지 확인할 수 있습니다.
<span class="alert-title">{{ $title }}</span>
<div class="alert alert-danger">
@if ($slot->isEmpty())
This is default content if the slot is empty.
@else
{{ $slot }}
@endif
</div>
또한 hasActualContent 메소드를 사용하여 슬롯에 HTML 주석이 아닌 "실제" 콘텐츠가 포함되어 있는지 확인할 수 있습니다.
@if ($slot->hasActualContent())
The scope has non-comment content.
@endif
범위가 지정된 슬롯
Vue와 같은 JavaScript 프레임워크를 사용한 적이 있다면 슬롯 내 컴포넌트의 데이터나 메서드에 액세스할 수 있는 "범위 지정 슬롯"에 익숙할 수 있습니다. 컴포넌트에 공용 메서드나 속성을 정의하고 $component 변수를 통해 슬롯 내의 컴포넌트에 액세스하면 Laravel에서 유사한 동작을 얻을 수 있습니다. 이 예에서는 x-alert 컴포넌트의 컴포넌트 클래스에 공개 formatAlert 메서드가 정의되어 있다고 가정합니다.
<x-alert>
<x-slot:title>
{{ $component->formatAlert('Server Error') }}
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
슬롯 속성
Blade 컴포넌트와 마찬가지로 CSS 클래스 이름과 같은 슬롯에 추가 속성을 할당할 수 있습니다.
<x-card class="shadow-sm">
<x-slot:heading class="font-bold">
Heading
</x-slot>
Content
<x-slot:footer class="text-sm">
Footer
</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 메소드에서 직접 컴포넌트의 마크업을 반환할 수 있습니다.
/**
* Get the view / contents that represent the component.
*/
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/Components 디렉터리 및 resources/views/components 디렉터리 내에서 자동으로 검색됩니다.
그러나 Blade 컴포넌트를 활용하는 패키지를 구축하거나 기존 디렉토리가 아닌 디렉토리에 컴포넌트를 배치하는 경우 Laravel가 컴포넌트를 찾을 수 있는 위치를 알 수 있도록 컴포넌트 클래스와 해당 HTML 태그 별칭을 수동으로 등록해야 합니다. 일반적으로 패키지 서비스 프로바이더의 boot 메서드에 컴포넌트를 등록해야 합니다.
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;
/**
* Bootstrap your package's services.
*/
public function boot(): void
{
Blade::component('package-alert', AlertComponent::class);
}
컴포넌트가 등록되면 태그 별칭을 사용하여 렌더링될 수 있습니다.
<x-package-alert/>
패키지 컴포넌트 자동 로드
또는 componentNamespace 메서드를 사용하여 규칙에 따라 컴포넌트 클래스를 자동 로드할 수도 있습니다. 예를 들어, Nightshade 패키지에는 Package\Views\Components 네임스페이스 내에 상주하는 Calendar 및 ColorPicker 컴포넌트가 있을 수 있습니다.
use Illuminate\Support\Facades\Blade;
/**
* Bootstrap your package's services.
*/
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}
이렇게 하면 package-name:: 구문을 사용하여 공급업체 네임스페이스에서 패키지 컴포넌트를 사용할 수 있습니다.
<x-nightshade::calendar />
<x-nightshade::color-picker />
Blade는 컴포넌트 이름을 파스칼 대소문자로 구분하여 이 컴포넌트에 연결된 클래스를 자동으로 감지합니다. 하위 디렉터리도 "점" 표기법을 사용하여 지원됩니다.
익명 컴포넌트 (Anonymous Components)
인라인 컴포넌트와 유사하게 익명 컴포넌트는 단일 파일을 통해 컴포넌트를 관리하는 메커니즘을 제공합니다. 그러나 익명 컴포넌트는 단일 뷰 파일을 활용하며 연결된 클래스가 없습니다. 익명 컴포넌트를 정의하려면 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 파일을 생성합니다.
익명 인덱스 컴포넌트
때로는 컴포넌트가 많은 Blade 템플릿으로 구성된 경우 단일 디렉터리 내에서 지정된 컴포넌트의 템플릿을 그룹화할 수 있습니다. 예를 들어, 다음 디렉터리 구조를 가진 "아코디언" 컴포넌트를 상상해 보세요.
/resources/views/components/accordion.blade.php
/resources/views/components/accordion/item.blade.php
이 디렉터리 구조를 사용하면 아코디언 컴포넌트와 해당 항목을 다음과 같이 렌더링할 수 있습니다.
<x-accordion>
<x-accordion.item>
...
</x-accordion.item>
</x-accordion>
그러나 x-accordion를 통해 아코디언 컴포넌트를 렌더링하기 위해 "인덱스" 아코디언 컴포넌트 템플릿을 다른 아코디언 관련 템플릿과 함께 accordion 디렉터리 내에 중첩하는 대신 resources/views/components 디렉터리에 배치해야 했습니다.
다행히도 Blade를 사용하면 컴포넌트의 디렉터리 이름과 일치하는 파일을 컴포넌트의 디렉터리 자체에 배치할 수 있습니다. 이 템플릿이 존재하면 디렉터리 내에 중첩되어 있더라도 컴포넌트의 "루트" 요소로 렌더링될 수 있습니다. 따라서 위의 예에 제공된 것과 동일한 Blade 구문을 계속 사용할 수 있습니다. 그러나 디렉토리 구조를 다음과 같이 조정하겠습니다.
/resources/views/components/accordion/accordion.blade.php
/resources/views/components/accordion/item.blade.php
데이터 속성/속성
익명 컴포넌트에는 연결된 클래스가 없으므로 컴포넌트에 변수로 전달되어야 하는 데이터와 컴포넌트의 속성 모음에 배치되어야 하는 속성을 어떻게 구별할 수 있는지 궁금할 수 있습니다.
컴포넌트의 Blade 템플릿 상단에 있는 @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 소품은 상위(<x-menu>)에만 전달되었으므로 <x-menu.item> 내에서는 사용할 수 없습니다. 그러나 @aware 지시어를 사용하면 <x-menu.item> 내부에서도 사용할 수 있습니다.
<!-- /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 템플릿을 배치하여 정의됩니다. 그러나 기본 경로 외에 Laravel을 사용하여 다른 익명 컴포넌트 경로를 등록하려는 경우도 있습니다.
anonymousComponentPath 메소드는 익명 컴포넌트 위치에 대한 "경로"를 첫 번째 인수로 받아들이고 컴포넌트가 배치되어야 하는 선택적 "네임스페이스"를 두 번째 인수로 받아들입니다. 일반적으로 이 메소드는 애플리케이션의 서비스 프로바이더 중 하나의 boot 메소드에서 호출되어야 합니다.
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Blade::anonymousComponentPath(__DIR__.'/../components');
}
위의 예와 같이 지정된 접두사 없이 컴포넌트 경로가 등록되면 해당 접두사 없이 Blade 컴포넌트에서도 렌더링될 수 있습니다. 예를 들어 위에서 등록한 경로에 panel.blade.php 컴포넌트가 존재하는 경우 다음과 같이 렌더링될 수 있습니다.
<x-panel />
접두사 "네임스페이스"는 anonymousComponentPath 메소드의 두 번째 인수로 제공될 수 있습니다.
Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');
접두사가 제공되면 해당 "네임스페이스" 내의 컴포넌트는 컴포넌트가 렌더링될 때 컴포넌트 이름에 컴포넌트의 네임스페이스 접두사를 추가하여 렌더링될 수 있습니다.
<x-dashboard::panel />
레이아웃 구성하기 (Building Layouts)
컴포넌트를 사용한 레이아웃
대부분의 웹 애플리케이션은 다양한 페이지에서 동일한 일반 레이아웃을 유지합니다. 우리가 생성하는 모든 뷰에서 전체 레이아웃 HTML을 반복해야 한다면 애플리케이션을 유지 관리하는 것이 엄청나게 번거롭고 어려울 것입니다. 다행히도 이 레이아웃을 단일 Blade 컴포넌트로 정의한 다음 애플리케이션 전체에서 사용하는 것이 편리합니다.
레이아웃 컴포넌트 정의
예를 들어, "todo" 목록 애플리케이션을 구축한다고 가정해 보겠습니다. 다음과 같은 layout 컴포넌트를 정의할 수 있습니다.
<!-- resources/views/components/layout.blade.php -->
<html>
<head>
<title>{{ $title ?? 'Todo Manager' }}</title>
</head>
<body>
<h1>Todos</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>
Custom Title
</x-slot>
@foreach ($tasks as $task)
<div>{{ $task }}</div>
@endforeach
</x-layout>
이제 레이아웃과 작업 목록 뷰를 정의했으므로 라우트에서 task 뷰를 반환하면 됩니다.
use App\Models\Task;
Route::get('/tasks', function () {
return view('tasks', ['tasks' => Task::all()]);
});
템플릿 상속을 사용한 레이아웃
레이아웃 정의
레이아웃은 "템플릿 상속"을 통해 생성될 수도 있습니다. 이는 컴포넌트가 도입되기 전에 애플리케이션을 구축하는 기본 방법이었습니다.
시작하려면 간단한 예를 살펴보겠습니다. 먼저 페이지 레이아웃을 살펴보겠습니다. 대부분의 웹 애플리케이션은 다양한 페이지에서 동일한 일반 레이아웃을 유지하므로 이 레이아웃을 단일 Blade 뷰로 정의하는 것이 편리합니다.
<!-- resources/views/layouts/app.blade.php -->
<html>
<head>
<title>App Name - @yield('title')</title>
</head>
<body>
@section('sidebar')
This is the master sidebar.
@show
<div class="container">
@yield('content')
</div>
</body>
</html>
보시다시피 이 파일에는 일반적인 HTML 마크업이 포함되어 있습니다. 그러나 @section 및 @yield 지시어에 유의하세요. @section 지시문은 이름에서 알 수 있듯이 콘텐츠 섹션을 정의하는 반면 @yield 지시문은 지정된 섹션의 콘텐츠를 표시하는 데 사용됩니다.
이제 애플리케이션의 레이아웃을 정의했으므로 레이아웃을 상속하는 하위 페이지를 정의해 보겠습니다.
레이아웃 확장
하위 뷰를 정의할 때 @extends Blade 지시문을 사용하여 하위 뷰가 "상속"해야 하는 레이아웃을 지정합니다. Blade 레이아웃을 확장하는 뷰는 @section 지시문을 사용하여 레이아웃 섹션에 콘텐츠를 삽입할 수 있습니다. 위의 예에서 볼 수 있듯이 이러한 섹션의 내용은 @yield를 사용하여 레이아웃에 표시됩니다.
<!-- resources/views/child.blade.php -->
@extends('layouts.app')
@section('title', 'Page Title')
@section('sidebar')
@@parent
<p>This is appended to the master sidebar.</p>
@endsection
@section('content')
<p>This is my body content.</p>
@endsection
이 예에서 sidebar 섹션은 @@parent 지시문을 활용하여 레이아웃의 사이드바에 콘텐츠를 덮어쓰는 대신 추가합니다. @@parent 지시어는 뷰가 렌더링될 때 레이아웃의 내용으로 대체됩니다.
[!NOTE] 이전 예와 달리 이
sidebar섹션은@show대신@endsection로 끝납니다.@endsection지시문은 섹션만 정의하는 반면,@show는 섹션을 정의하고 즉시 생성합니다.
@yield 지시문은 두 번째 매개변수로 기본값을 허용합니다. 생성되는 섹션이 정의되지 않은 경우 이 값이 렌더링됩니다.
@yield('content', 'Default content')
양식 (Forms)
CSRF 필드
애플리케이션에서 HTML 양식을 정의할 때마다 CSRF 보호 미들웨어가 요청을 확인할 수 있도록 양식에 숨겨진 CSRF 토큰 필드를 포함해야 합니다. @csrf Blade 지시문을 사용하여 토큰 필드를 생성할 수 있습니다.
<form method="POST" action="/profile">
@csrf
...
</form>
메소드 필드
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">Post 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">Email address</label>
<input
id="email"
type="email"
class="@error('email') is-invalid @else is-valid @enderror"
/>
특정 오류 백의 이름을 @error 지시문의 두 번째 매개변수로 전달하여 여러 양식이 포함된 페이지에서 유효성 검사 오류 메시지를 검색할 수 있습니다.
<!-- /resources/views/auth.blade.php -->
<label for="email">Email address</label>
<input
id="email"
type="email"
class="@error('email', 'login') is-invalid @enderror"
/>
@error('email', 'login')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
스택 (Stacks)
Blade를 사용하면 다른 뷰 또는 레이아웃의 다른 위치에 렌더링될 수 있는 명명된 스택으로 푸시할 수 있습니다. 이는 자녀 뷰에 필요한 JavaScript 라이브러리를 지정하는 데 특히 유용할 수 있습니다.
@push('scripts')
<script src="/example.js"></script>
@endpush
주어진 부울 표현식이 true로 평가되는 경우 @push 콘텐츠를 원하는 경우 @pushIf 지시문을 사용할 수 있습니다.
@pushIf($shouldPush, 'scripts')
<script src="/example.js"></script>
@endPushIf
필요한 만큼 여러 번 스택에 푸시할 수 있습니다. 전체 스택 내용을 렌더링하려면 스택 이름을 @stack 지시어에 전달하세요.
<head>
<!-- Head Contents -->
@stack('scripts')
</head>
스택 시작 부분에 콘텐츠를 추가하려면 @prepend 지시문을 사용해야 합니다.
@push('scripts')
This will be second...
@endpush
// Later...
@prepend('scripts')
This will be first...
@endprepend
@hasstack 지시어는 스택이 비어 있는지 확인하는 데 사용될 수 있습니다.
@hasstack('list')
<ul>
@stack('list')
</ul>
@endif
서비스 주입 (Service Injection)
@inject 지시어는 Laravel 서비스 컨테이너에서 서비스를 검색하는 데 사용될 수 있습니다. @inject에 전달된 첫 번째 인수는 서비스가 배치될 변수의 이름이고, 두 번째 인수는 해결하려는 서비스의 클래스 또는 인터페이스 이름입니다.
@inject('metrics', 'App\Services\MetricsService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
인라인 Blade 템플릿 렌더링 (Rendering Inline Blade Templates)
때로는 원시 Blade 템플릿 문자열을 유효한 HTML로 변환해야 할 수도 있습니다. Blade 파사드에서 제공하는 render 메소드를 사용하여 이를 수행할 수 있습니다. render 메서드는 Blade 템플릿 문자열과 템플릿에 제공할 선택적 데이터 배열을 허용합니다.
use Illuminate\Support\Facades\Blade;
return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
Laravel는 인라인 Blade 템플릿을 storage/framework/views 디렉터리에 작성하여 렌더링합니다. Blade 템플릿을 렌더링한 후 Laravel가 이러한 임시 파일을 제거하도록 하려면 메서드에 deleteCachedView 인수를 제공할 수 있습니다.
return Blade::render(
'Hello, {{ $name }}',
['name' => 'Julian Bashir'],
deleteCachedView: true
);
Blade 조각 렌더링 (Rendering Blade Fragments)
Turbo 및 htmx와 같은 프론트엔드 프레임워크를 사용할 때 때때로 HTTP 응답 내에서 Blade 템플릿의 일부만 반환해야 할 수도 있습니다. Blade "조각"을 사용하면 바로 이러한 작업을 수행할 수 있습니다. 시작하려면 @fragment 및 @endfragment 지시문 내에 Blade 템플릿의 일부를 배치하세요.
@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 메서드를 사용하면 주어진 조건에 따라 뷰의 조각을 조건부로 반환할 수 있습니다. 그렇지 않으면 전체 뷰가 반환됩니다.
return view('dashboard', ['users' => $users])
->fragmentIf($request->hasHeader('HX-Request'), 'user-list');
fragments 및 fragmentsIf 메서드를 사용하면 응답에서 여러 뷰 조각을 반환할 수 있습니다. 조각은 서로 연결됩니다.
view('dashboard', ['users' => $users])
->fragments(['user-list', 'comment-list']);
view('dashboard', ['users' => $users])
->fragmentsIf(
$request->hasHeader('HX-Request'),
['user-list', 'comment-list']
);
Blade 확장 (Extending Blade)
Blade를 사용하면 directive 메서드를 사용하여 사용자 지정 지시문을 정의할 수 있습니다. Blade 컴파일러는 사용자 지정 지시문을 발견하면 지시문에 포함된 표현식을 사용하여 제공된 콜백을 호출합니다.
다음 예에서는 DateTime의 인스턴스여야 하는 지정된 $var의 형식을 지정하는 @datetime($var) 지시어를 생성합니다.
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Blade::directive('datetime', function (string $expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
}
}
보시다시피 지시문에 전달되는 표현식에 format 메서드를 연결합니다. 따라서 이 예에서 이 지시어에 의해 생성된 최종 PHP는 다음과 같습니다.
<?php echo ($var)->format('m/d/Y H:i'); ?>
[!WARNING] Blade 지시문의 논리를 업데이트한 후에는 캐시된 Blade 뷰를 모두 삭제해야 합니다. 캐시된 Blade 뷰는
view:clearArtisan 명령을 사용하여 제거할 수 있습니다.
맞춤형 에코 핸들러
Blade를 사용하여 개체를 "에코"하려고 하면 개체의 __toString 메서드가 호출됩니다. __toString 메서드는 PHP에 내장된 "마법 메서드" 중 하나입니다. 그러나 상호 작용하는 클래스가 타사 라이브러리에 속하는 경우와 같이 특정 클래스의 __toString 메서드를 제어할 수 없는 경우도 있습니다.
이러한 경우 Blade를 사용하면 특정 유형의 개체에 대한 사용자 지정 에코 처리기를 등록할 수 있습니다. 이를 수행하려면 Blade의 stringable 메서드를 호출해야 합니다. stringable 메소드는 클로저를 허용합니다. 이 클로저는 렌더링을 담당하는 객체의 유형을 유형 힌트해야 합니다. 일반적으로 stringable 메서드는 애플리케이션 AppServiceProvider 클래스의 boot 메서드 내에서 호출되어야 합니다.
use Illuminate\Support\Facades\Blade;
use Money\Money;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Blade::stringable(function (Money $money) {
return $money->formatTo('en_GB');
});
}
사용자 지정 에코 핸들러가 정의되면 Blade 템플릿에 객체를 간단히 에코할 수 있습니다.
Cost: {{ $money }}
사용자 지정 If 문
사용자 지정 지시어 프로그래밍은 간단한 사용자 지정 조건문을 정의할 때 필요한 것보다 더 복잡한 경우가 있습니다. 이러한 이유로 Blade는 클로저를 사용하여 사용자 지정 조건부 지시문을 빠르게 정의할 수 있는 Blade::if 메서드를 제공합니다. 예를 들어, 애플리케이션에 대해 구성된 기본 "디스크"를 확인하는 사용자 지정 조건을 정의해 보겠습니다. AppServiceProvider의 boot 메서드에서 이 작업을 수행할 수 있습니다.
use Illuminate\Support\Facades\Blade;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Blade::if('disk', function (string $value) {
return config('filesystems.default') === $value;
});
}
사용자 지정 조건이 정의되면 템플릿 내에서 사용할 수 있습니다.
@disk('local')
<!-- The application is using the local disk... -->
@elsedisk('s3')
<!-- The application is using the s3 disk... -->
@else
<!-- The application is using some other disk... -->
@enddisk
@unlessdisk('local')
<!-- The application is not using the local disk... -->
@enddisk