Blade 가이드
일반적인 서버 렌더링 앱을 위해 일곱 가지 Blade 지시문를 제공합니다. 보통은 그중 @seo 하나면 충분합니다.
통합 지시문
<!DOCTYPE html>
<html>
<head>
@seo($post)
</head>@seo는 우선순위 체인을 통해 모델 값을 결정하고 전체 head 블록을 렌더링합니다. 여기에는 <title>, 메타 설명, canonical 링크, robots, Open Graph 태그, Twitter Card 태그, 연결된 JSON-LD가 포함됩니다. robots 태그는 사이트 기본값과 다를 때만 출력합니다. 중복되는 index,follow는 생략하며, 태그가 없으면 이미 index,follow를 의미합니다. 항상 렌더링하려면 seo.robots.emit_default를 설정하세요. 자세한 내용은 렌더링 계약을 참고하세요.
호출 형식:
@seo($post) {{-- model page --}}
@seo($seoData) {{-- a hand-built SEOData (model-less page) --}}
@seo($post, 'blog.show') {{-- model + route defaults --}}
@seo($post, null, 'fr') {{-- model + locale --}}
@seo(null) {{-- current page, no model --}}@seo에는 Model, 직접 구성한 SEOData 또는 null을 전달할 수 있습니다. 라우트와 로캘 인수는 Model/null 경로에만 적용됩니다. 직접 구성한 SEOData는 자체 값을 사용합니다.
라우트 페이지(모델 없음)
정적 페이지, 아카이브 및 라우트를 기반으로 하는 다른 페이지에서는 다음과 같이 사용합니다.
@seoForRoute('pages.about')
@seoForRoute('contact', 'de') {{-- with locale --}}라우트 값은 해당 라우트 이름으로 범위가 지정된 seo_defaults 행에서 가져옵니다.
모델 없는 페이지: 직접 구성한 SEOData
목록, 검색 결과처럼 컨트롤러에서 구성하는 페이지에는 하나의 기반 모델이 없는 경우가 많습니다. SEOData를 구성해 @seo 또는 SEO 파사드에 바로 전달하세요. app(TagRenderer::class)->render(...)를 사용할 필요는 없습니다.
use Rankbeam\Seo\Data\SEOData;
return view('search.results', [
'seo' => new SEOData(
title: "Results for \"{$query}\"",
description: "Browse {$count} matches for {$query}.",
ogImage: '/images/search-share.jpg', // relative is fine — see below
),
]);<head>
@seo($seo)
</head>직접 구성한 SEOData는 명시적 의도로 취급합니다. 설정한 모든 값은 보존하고, 렌더링 시 비어 있는 다음 값만 채웁니다.
canonical/og:url값이 없으면 현재 URL에서 유도합니다. 명시적canonical은 쿼리 문자열까지 그대로 유지합니다.- 제목에
title_suffix가 없을 때만 이를 덧붙입니다. 제목에 이미 브랜드 토큰이 있으면 접미사를 아예 추가하지 않습니다.title_suffix_skip_when_contains를 참고하세요. - 상대 경로인
og:image/twitter:image값은url()로 절대 URL로 변환합니다. 현재 프로토콜을 따르며 HTTPS를 강제하지 않습니다. og:site_name및locale값은 설정 또는 앱 로캘에서 채웁니다.
데이터베이스의 우선순위 체인(전역 / 모델 유형 / 라우트 / seo_meta 기본값)은 직접 구성한 SEOData에 병합하지 않습니다. 전달한 값과 위에서 설명한 빈 값 보완만 렌더링에 반영됩니다.
같은 값을 파사드에서도 사용할 수 있습니다.
SEO::render($seoData); // HTML string
SEO::toArray($seoData); // Vue/React structure
SEO::forInertia($seoData); // Inertia Head structure확장 가능한 레이아웃 패턴
하나의 레이아웃으로 모델 페이지, 라우트 페이지, 그 밖의 페이지를 모두 처리할 수 있습니다.
<head>
@if(isset($seoModel))
@seo($seoModel)
@elseif(isset($seoRoute))
@seoForRoute($seoRoute)
@else
@seo(null)
@endif
</head>컨트롤러는 'seoModel' => $post 또는 'seoRoute' => 'blog.index'만 전달하며 마크업을 직접 다루지 않습니다.
개별 지시문
다른 패키지의 출력과 함께 사용하는 경우처럼 개별 태그를 제어해야 할 때 사용하세요.
| 지시문 | 출력 |
|---|---|
@seoTitle($post) | <title>만 출력 |
@seoMeta($post) | 메타 설명만 출력 |
@seoCanonical($post) | canonical 링크만 출력(값이 없으면 현재 URL 사용) |
@seoRobots($post) | robots 메타만 항상 출력. 명시적으로 출력을 요청한 것이므로 @seo와 달리 기본값과 같을 때 생략하는 규칙을 적용하지 않습니다. |
@seoSchema($post) | JSON-LD <script>만 출력. head와 body 어디에나 사용 가능 |
모두 @seo와 같은 ($model, $route, $locale) 표현식을 받으며, 인수를 생략하면 현재 페이지를 사용합니다.
Hreflang 대체 페이지
HasSEO를 사용하는 모델은 리졸버를 통해 hreflang 링크를 직접 제공할 수 있습니다.
public function getSEOAlternates(): ?array
{
return [
['hreflang' => 'en', 'href' => route('posts.show', ['locale' => 'en', 'post' => $this])],
['hreflang' => 'it', 'href' => route('posts.show', ['locale' => 'it', 'post' => $this])],
];
}절대 URL을 사용하세요. @seo($post)는 이 항목들의 값을 결정하고 각각 <link rel="alternate" hreflang="..." href="...">로 렌더링합니다. 코드는 먼저 BCP 47 형식으로 변환합니다(it_IT → it-IT). seo.hreflang 정책에 따라 페이지의 자기 참조와 x-default를 추가할 수 있습니다. 무료 감사는 잘못된 항목, 중복 항목, 자기 참조 누락을 표시합니다. 다국어 콘텐츠를 참고하세요.
이스케이프와 안전성
텍스트 값은 e()로 이스케이프합니다. JSON-LD는 JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP로 인코딩하므로 사용자 콘텐츠에 </script>가 있어도 script 요소 밖으로 빠져나올 수 없습니다.