Blazor 레이아웃 컴포넌트: MainLayout과 중첩 레이아웃

레이아웃 컴포넌트
📚 이 글에서 다루는 것

레이아웃 컴포넌트

MainLayout과 중첩 레이아웃을 구성해 공통 UI 구조를 재사용하는 방법을 배웁니다.

📌 학습 목표

  • 레이아웃 컴포넌트가 무엇이며 왜 필요한지 설명할 수 있습니다.
  • LayoutComponentBase를 상속하고 @Body를 올바르게 배치할 수 있습니다.
  • MainLayout.razor를 직접 작성하고 프로젝트에 적용할 수 있습니다.
  • 개별 페이지에 다른 레이아웃을 지정하는 세 가지 방법과 우선순위를 이해합니다.
  • 레이아웃 안에 레이아웃을 중첩하여 계층적 UI 구조를 구성할 수 있습니다.

 

 

🔄 Blazor 레이아웃 렌더링 흐름

 

App.razor
RouteView + DefaultLayout 지정
MainLayout.razor
공통 UI (헤더·사이드바·푸터)
@Body 위치
현재 라우팅된 페이지 컴포넌트

📝 개념 설명

1. 레이아웃 컴포넌트란?

웹 애플리케이션의 모든 페이지에는 헤더, 내비게이션 메뉴, 사이드바, 푸터 같은 공통 UI 요소가 반복됩니다. 이를 각 페이지마다 복사·붙여넣기로 관리한다면 수정 한 번에 수십 개의 파일을 변경해야 하는 문제가 생깁니다.

 

Blazor는 이 문제를 레이아웃 컴포넌트(Layout Component)로 해결합니다. 레이아웃 컴포넌트는 공통 UI 구조를 한 곳에 정의해 두고, 실제 페이지 내용이 표시될 위치만 @Body로 지정하는 특수 컴포넌트입니다.

💡 핵심 비유
레이아웃 컴포넌트는 액자 틀이고, 각 페이지 컴포넌트는 액자 안에 끼워지는 그림입니다. 틀(레이아웃)은 공통이고, 그림(페이지 내용)만 페이지마다 바뀝니다.

2. LayoutComponentBase와 @Body

레이아웃 컴포넌트가 일반 컴포넌트와 다른 핵심 차이점은 LayoutComponentBase를 상속한다는 것입니다. 이 클래스는 Microsoft.AspNetCore.Components 네임스페이스에 포함되어 있으며, @Body 속성(RenderFragment 타입)을 제공합니다.

@* Shared/MainLayout.razor — 가장 기본적인 구조 *@
@inherits LayoutComponentBase

<div class="layout-wrapper">
    <header>
        <h1>내 Blazor 앱</h1>
    </header>

    <main>
        @Body
    </main>

    <footer>
        <p>&copy; 2024 My App</p>
    </footer>
</div>
  • @inherits LayoutComponentBase : 이 컴포넌트가 레이아웃임을 Blazor에 알립니다. 반드시 파일 최상단에 선언해야 합니다.
  • @Body : 현재 라우팅된 페이지의 내용이 이 위치에 삽입됩니다. 레이아웃 파일 안에 정확히 한 번만 배치합니다.

3. 레이아웃 적용 방법 세 가지

방법 ① App.razor — DefaultLayout 지정 (전역 기본값)

가장 광범위한 방법으로, 별도 레이아웃을 지정하지 않은 모든 페이지에 기본 적용됩니다.

@* App.razor *@
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData"
                   DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>페이지를 찾을 수 없습니다.</p>
        </LayoutView>
    </NotFound>
</Router>

방법 ② _Imports.razor — 폴더 단위 레이아웃 지정

_Imports.razor@layout을 추가하면 해당 폴더(및 하위 폴더)의 모든 페이지에 레이아웃이 적용됩니다.

@* _Imports.razor *@
@using MyApp.Shared
@layout MainLayout

방법 ③ 개별 페이지 — @layout 지시어 직접 선언 (최우선)

특정 페이지만 다른 레이아웃을 사용할 때, 해당 페이지 최상단에 @layout을 직접 선언합니다. 세 가지 방법 중 가장 높은 우선순위를 가집니다.

@* Pages/Login.razor — 로그인 페이지는 사이드바 없는 심플 레이아웃 사용 *@
@page "/login"
@layout LoginLayout

<h2>로그인</h2>
<input type="text" placeholder="아이디" />

 

 

⚖️ 레이아웃 적용 우선순위 비교

 

방법선언 위치적용 범위우선순위
개별 페이지 @layout각 .razor 파일해당 페이지만🥇 가장 높음
_Imports.razor @layout폴더 내 _Imports.razor해당 폴더 전체🥈 중간
App.razor DefaultLayoutApp.razor RouteView앱 전체 기본값🥉 가장 낮음

4. 중첩 레이아웃 (Nested Layouts)

레이아웃 안에 또 다른 레이아웃을 중첩할 수 있습니다. 예를 들어, 관리자 섹션은 기본 MainLayout의 공통 헤더·푸터를 유지하면서 추가로 관리자 전용 사이드 메뉴를 가진 AdminLayout을 중첩해 사용할 수 있습니다.

 

자식 레이아웃 파일에 @layout 부모레이아웃을 선언하는 것만으로 중첩이 완성됩니다.

@* Shared/AdminLayout.razor — MainLayout을 부모로 사용하는 중첩 레이아웃 *@
@inherits LayoutComponentBase
@layout MainLayout

<div class="admin-container">
    <nav class="admin-sidebar">
        <ul>
            <li><a href="/admin/users">사용자 관리</a></li>
            <li><a href="/admin/posts">게시글 관리</a></li>
            <li><a href="/admin/settings">설정</a></li>
        </ul>
    </nav>

    <div class="admin-content">
        @Body
    </div>
</div>

관리자 페이지에서 AdminLayout을 지정하면, Blazor가 자동으로 MainLayout → AdminLayout → 페이지 순서로 중첩 렌더링합니다.

@* Pages/Admin/Users.razor *@
@page "/admin/users"
@layout AdminLayout

<h2>사용자 관리</h2>
<p>전체 사용자 목록입니다.</p>

💡 예제 & 실습 — 실전 레이아웃 구조 완성하기

아래 예제는 실제 프로젝트에 적용할 수 있는 완전한 레이아웃 구조입니다. 단계별로 따라 해보세요.

단계 1: Shared/MainLayout.razor 작성

@* Shared/MainLayout.razor *@
@inherits LayoutComponentBase

<div class="page">
    <header class="site-header">
        <nav>
            <a href="/">🏠 홈</a>
            <a href="/about">소개</a>
            <a href="/contact">문의</a>
        </nav>
    </header>

    <div class="content-wrapper">
        <aside class="sidebar">
            <h4>카테고리</h4>
            <ul>
                <li><a href="/posts/csharp">C#</a></li>
                <li><a href="/posts/blazor">Blazor</a></li>
            </ul>
        </aside>

        <main class="main-content">
            @Body
        </main>
    </div>

    <footer class="site-footer">
        <p>&copy; 2024 Blazor 학습 블로그</p>
    </footer>
</div>

@code {
    // @code 블록으로 의존성 주입(DI)·상태·이벤트 핸들러를 레이아웃 수준에서 정의할 수 있습니다
}

단계 2: App.razor에서 기본 레이아웃 지정

@* App.razor *@
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData"
                   DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">페이지를 찾을 수 없습니다.</p>
        </LayoutView>
    </NotFound>
</Router>

단계 3: 일반 페이지 — 레이아웃 자동 적용 확인

@* Pages/Index.razor — 별도 @layout 없이 DefaultLayout(MainLayout) 자동 적용 *@
@page "/"

<h1>안녕하세요!</h1>
<p>이 내용은 MainLayout의 @Body 위치에 렌더링됩니다.</p>

단계 4: EmptyLayout — 레이아웃 없는 전체 화면 페이지

레이아웃을 완전히 제거하고 싶다면, 내용만 통과시키는 최소 레이아웃(EmptyLayout)을 만들어 사용합니다. Blazor에서 레이아웃을 null로 지정하는 것은 불가능하므로 이 방법이 표준적인 패턴입니다.

@* Shared/EmptyLayout.razor *@
@inherits LayoutComponentBase

@Body
@* Pages/FullscreenMap.razor *@
@page "/map"
@layout EmptyLayout

<div style="width:100vw; height:100vh;">
    <!-- 전체 화면 지도 또는 전용 UI -->
</div>

⚠️ 자주 틀리는 것 / 주의사항

실수 1: @inherits LayoutComponentBase 누락

증상: 레이아웃 파일을 만들어도 적용되지 않거나 컴파일 오류가 발생합니다.
원인: @inherits LayoutComponentBase가 없으면 Blazor는 이 파일을 일반 컴포넌트로 취급합니다.
해결: 레이아웃 .razor 파일 최상단에 반드시 @inherits LayoutComponentBase를 선언하세요.

실수 2: @Body 누락 또는 잘못된 위치 배치

증상: 레이아웃(헤더, 푸터 등)은 표시되지만 페이지 내용이 전혀 보이지 않습니다.
원인: @Body가 없으면 현재 페이지 컴포넌트가 삽입될 위치가 없습니다.
해결: 콘텐츠 메인 영역 안에 반드시 @Body를 배치하세요. <main> 또는 내용 컨테이너 안이 적절합니다.

실수 3: 레이아웃 파일에 @page 지시어 사용

증상: 레이아웃이 독립 페이지처럼 URL로 직접 접근됩니다.
원인: 레이아웃 컴포넌트는 URL 라우팅 대상이 아닙니다. @page를 선언하면 라우터가 이를 독립 페이지로 인식합니다.
해결: MainLayout.razor 등 레이아웃 파일에서 @page를 제거하세요.

실수 4: 하나의 레이아웃에 @Body를 두 번 이상 배치

증상: 컴파일 오류, 또는 페이지 내용이 두 군데에 중복 렌더링됩니다.
원인: 하나의 레이아웃 파일 안에 @Body는 정확히 한 번만 허용됩니다.
해결: @Body를 단 하나만 배치하고, 나머지는 모두 제거하세요.

실수 5: 네임스페이스 누락으로 레이아웃 참조 실패

증상: App.razor에서 typeof(MainLayout) 참조 시 “이름을 찾을 수 없습니다” 오류 발생.
원인: MainLayout이 위치한 네임스페이스(일반적으로 프로젝트명.Shared)가 _Imports.razor에 열려있지 않습니다.
해결: _Imports.razor@using 프로젝트명.Shared를 추가하세요.

🎯 마무리

레이아웃 컴포넌트는 Blazor 애플리케이션의 UI 구조를 단 한 곳에서 통합 관리하게 해주는 핵심 기능입니다. @inherits LayoutComponentBase@Body 두 가지만 이해하면 기본 레이아웃은 바로 구현할 수 있으며, 적용 우선순위 규칙을 숙지하면 섹션마다 다른 레이아웃을 유연하게 설계할 수 있습니다.

 

중첩 레이아웃은 관리자 화면, 결제 플로우, 로그인 페이지처럼 서로 다른 구조가 필요한 영역을 깔끔하게 분리하는 강력한 도구입니다. 자식 레이아웃에 @layout 부모레이아웃 한 줄만 추가하면 계층적 UI 구조가 완성됩니다. 이 패턴을 잘 활용하면 전체 앱의 유지보수성이 크게 향상됩니다.

✅ 핵심 정리
  • 레이아웃 컴포넌트는 @inherits LayoutComponentBase를 선언하고, 페이지 내용 삽입 위치에 @Body를 정확히 한 번 배치합니다.
  • 레이아웃 적용 우선순위: 개별 페이지 @layout > _Imports.razor @layout > App.razor DefaultLayout
  • 중첩 레이아웃은 자식 레이아웃 파일에 @layout 부모레이아웃을 선언하는 것으로 구성됩니다.
  • 레이아웃 파일에는 @page 지시어를 절대 사용하지 않습니다.
  • 레이아웃에도 @code 블록을 사용해 의존성 주입, 상태 관리, 이벤트 처리를 구현할 수 있습니다.
  • 레이아웃을 완전히 제거하려면 EmptyLayout(@Body만 있는 최소 레이아웃)을 만들어 사용합니다.

댓글 남기기

Wordpress Social Share Plugin powered by Ultimatelysocial
Copy link
URL has been copied successfully!
THREADS
RSS
error: 저작권 콘텐츠보호를 부탁드립니다.