ASP.NET Core의 레이아웃Layout in ASP.NET Core

작성자: Steve SmithDave BrockBy Steve Smith and Dave Brock

페이지 및 보기는 시각적 개체 및 프로그래밍 요소를 자주 공유합니다.Pages and views frequently share visual and programmatic elements. 이 문서에서는 다음을 수행하는 방법을 보여줍니다.This article demonstrates how to:

  • 일반적인 레이아웃 사용.Use common layouts.
  • 지시문 공유.Share directives.
  • 페이지 또는 보기를 렌더링하기 전에 일반적인 코드 실행.Run common code before rendering pages or views.

이 문서에서는 ASP.NET Core MVC: Razor Pages 및 보기를 사용하는 컨트롤러에 대한 두 가지 방식의 레이아웃을 설명합니다.This document discusses layouts for the two different approaches to ASP.NET Core MVC: Razor Pages and controllers with views. 이 항목에서는 차이점이 최소화되어 있습니다.For this topic, the differences are minimal:

  • Razor Pages는 Pages 폴더에 있습니다.Razor Pages are in the Pages folder.
  • 보기를 사용하는 컨트롤러는 Views 폴더의 보기를 사용합니다.Controllers with views uses a Views folder for views.

레이아웃이란What is a Layout

대부분의 웹앱에는 사용자가 페이지를 탐색하는 동안 일관된 환경을 제공하는 일반적인 레이아웃이 있습니다.Most web apps have a common layout that provides the user with a consistent experience as they navigate from page to page. 일반적으로 레이아웃에는 앱 헤더, 탐색 또는 메뉴 요소, 바닥글과 같은 공통 사용자 인터페이스 요소가 포함됩니다.The layout typically includes common user interface elements such as the app header, navigation or menu elements, and footer.

페이지 레이아웃 예제

스크립트 및 스타일시트와 같은 공통 HTML 구조는 앱 내에서 여러 페이지에서도 자주 사용됩니다.Common HTML structures such as scripts and stylesheets are also frequently used by many pages within an app. 이러한 모든 공유 요소를 레이아웃 파일에 정의한 후 앱 내에 사용된 모든 뷰에서 참조할 수 있습니다.All of these shared elements may be defined in a layout file, which can then be referenced by any view used within the app. 레이아웃은 보기에서 중복 코드를 줄입니다.Layouts reduce duplicate code in views.

규칙에 따라, ASP.NET Core 앱의 기본 레이아웃 이름을 _Layout.cshtml로 지정합니다.By convention, the default layout for an ASP.NET Core app is named _Layout.cshtml. 템플릿을 사용하여 생성된 새로운 ASP.NET Core 프로젝트의 레이아웃 파일:The layout files for new ASP.NET Core projects created with the templates are:

  • Razor Pages: Pages/Shared/_Layout.cshtmlRazor Pages: Pages/Shared/_Layout.cshtml

    솔루션 탐색기의 Pages 폴더

  • 보기를 사용하는 컨트롤러: Views/Shared/_Layout.cshtmlController with views: Views/Shared/_Layout.cshtml

    솔루션 탐색기의 Views 폴더

이 레이아웃은 앱의 뷰에 대한 최상위 수준 템플릿을 정의합니다.The layout defines a top level template for views in the app. 앱에는 레이아웃이 필요하지 않습니다.Apps don't require a layout. 앱은 다른 레이아웃을 지정하는 서로 다른 뷰를 포함하는 둘 이상의 레이아웃을 정의할 수 있습니다.Apps can define more than one layout, with different views specifying different layouts.

다음 코드는 컨트롤러 및 보기를 사용하여 만든 템플릿 프로젝트의 레이아웃 파일을 보여줍니다.The following code shows the layout file for a template created project with a controller and views:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebApplication1</title>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    </environment>
</head>
<body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a asp-page="/Index" class="navbar-brand">WebApplication1</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-page="/Index">Home</a></li>
                    <li><a asp-page="/About">About</a></li>
                    <li><a asp-page="/Contact">Contact</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <partial name="_CookieConsentPartial" />

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2018 - WebApplication1</p>
        </footer>
    </div>

    <environment include="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment exclude="Development">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>

    @RenderSection("Scripts", required: false)
</body>
</html>

레이아웃 지정Specifying a Layout

Razor 뷰는 Layout 속성을 포함합니다.Razor views have a Layout property. 이 속성을 설정하여 레이아웃을 지정하는 개별 뷰:Individual views specify a layout by setting this property:

@{
    Layout = "_Layout";
}

지정된 레이아웃은 전체 경로(예: /Pages/Shared/_Layout.cshtml 또는 /Views/Shared/_Layout.cshtml) 또는 부분적인 이름(예: _Layout)을 사용할 수 있습니다.The layout specified can use a full path (for example, /Pages/Shared/_Layout.cshtml or /Views/Shared/_Layout.cshtml) or a partial name (example: _Layout). 부분 이름이 제공되면 Razor 뷰 엔진이 표준 검색 프로세스를 사용하여 레이아웃 파일을 검색합니다.When a partial name is provided, the Razor view engine searches for the layout file using its standard discovery process. 처리기 메서드(또는 컨트롤러)가 있는 폴더가 먼저 검색된 후 공유 폴더가 검색됩니다.The folder where the handler method (or controller) exists is searched first, followed by the Shared folder. 이 검색 프로세스는 부분 뷰를 검색하는 데 사용된 프로세스와 동일합니다.This discovery process is identical to the process used to discover partial views.

기본적으로 모든 레이아웃에서 RenderBody를 호출해야 합니다.By default, every layout must call RenderBody. RenderBody 호출이 배치될 때마다 뷰의 내용이 렌더링됩니다.Wherever the call to RenderBody is placed, the contents of the view will be rendered.

섹션Sections

레이아웃은 RenderSection을 호출하여 필요에 따라 하나 이상의 섹션을 참조합니다.A layout can optionally reference one or more sections, by calling RenderSection. 섹션에서는 특정 페이지 요소를 배치할 위치를 구성하는 방법을 제공합니다.Sections provide a way to organize where certain page elements should be placed. RenderSection 호출 때마다 섹션이 필수 또는 옵션인지 여부를 지정할 수 있습니다.Each call to RenderSection can specify whether that section is required or optional:

@section Scripts {
    @RenderSection("Scripts", required: false)
}

필수 섹션이 없는 경우 예외가 throw됩니다.If a required section isn't found, an exception is thrown. 개별 뷰는 @section Razor 구문을 사용하여 섹션 내에 렌더링될 콘텐츠를 지정합니다.Individual views specify the content to be rendered within a section using the @section Razor syntax. 페이지 또는 보기에서 섹션을 정의하는 경우 렌더링되어야 합니다(그렇지 않은 경우 오류 발생).If a page or view defines a section, it must be rendered (or an error will occur).

Razor Pages 보기의 @section 정의 예:An example @section definition in Razor Pages view:

@section Scripts {
     <script type="text/javascript" src="/scripts/main.js"></script>
}

이전 코드에서는 scripts/main.js가 페이지 또는 보기의 scripts 섹션에 추가됩니다.In the preceding code, scripts/main.js is added to the scripts section on a page or view. 동일한 앱의 다른 페이지 또는 보기는 이 스크립트가 필요하지 않을 수 있으며, 스크립트 섹션을 정의하지 않습니다.Other pages or views in the same app might not require this script and wouldn't define a scripts section.

다음 태그는 부분 태그 도우미를 사용하여 _ValidationScriptsPartial.cshtml을 렌더링합니다.The following markup uses the Partial Tag Helper to render _ValidationScriptsPartial.cshtml:

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

이전의 태그는 ID를 스캐폴딩하여 생성되었습니다.The preceding markup was generated by scaffolding Identity.

페이지 또는 보기에 정의된 섹션은 즉시 레이아웃 페이지에서만 사용할 수 있습니다.Sections defined in a page or view are available only in its immediate layout page. 이들은 부분 뷰, 뷰 구성 요소 또는 뷰 시스템의 다른 부분에서 참조할 수 없습니다.They cannot be referenced from partials, view components, or other parts of the view system.

섹션 무시Ignoring sections

기본적으로 콘텐츠 페이지 본문 및 모든 섹션은 레이아웃 페이지에서 모두 렌더링되어야 합니다.By default, the body and all sections in a content page must all be rendered by the layout page. Razor 뷰 엔진은 본문과 각 섹션이 렌더링되었는지 여부를 추적하여 이를 적용합니다.The Razor view engine enforces this by tracking whether the body and each section have been rendered.

뷰 엔진이 본문 또는 섹션을 무시하도록 지시하려면 IgnoreBodyIgnoreSection 메서드를 호출합니다.To instruct the view engine to ignore the body or sections, call the IgnoreBody and IgnoreSection methods.

Razor 페이지의 본문 및 모든 섹션은 렌더링되거나 무시되어야 합니다.The body and every section in a Razor page must be either rendered or ignored.

공유 지시문 가져오기Importing Shared Directives

보기 및 페이지는 Razor 지시문을 사용하여 네임스페이스를 가져오고 종속성 주입을 사용합니다.Views and pages can use Razor directives to importing namespaces and use dependency injection. 여러 보기에서 공유하는 지시문을 공용 _ViewImports.cshtml 파일에 지정할 수 있습니다.Directives shared by many views may be specified in a common _ViewImports.cshtml file. _ViewImports 파일은 다음 지시문을 지원합니다.The _ViewImports file supports the following directives:

  • @addTagHelper
  • @removeTagHelper
  • @tagHelperPrefix
  • @using
  • @model
  • @inherits
  • @inject

이 파일은 다른 Razor 기능(예: 함수 및 섹션 정의)을 지원하지 않습니다.The file doesn't support other Razor features, such as functions and section definitions.

샘플 _ViewImports.cshtml 파일:A sample _ViewImports.cshtml file:

@using WebApplication1
@using WebApplication1.Models
@using WebApplication1.Models.AccountViewModels
@using WebApplication1.Models.ManageViewModels
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

ASP.NET Core MVC 앱에 대한 _ViewImports.cshtml 파일은 일반적으로 Pages(또는 Views) 폴더에 배치됩니다.The _ViewImports.cshtml file for an ASP.NET Core MVC app is typically placed in the Pages (or Views) folder. _ViewImports.cshtml 파일은 모든 폴더 내에 배치할 수 있으며, 이 경우 해당 폴더 및 해당 하위 폴더 내의 페이지 또는 보기에만 적용됩니다.A _ViewImports.cshtml file can be placed within any folder, in which case it will only be applied to pages or views within that folder and its subfolders. _ViewImports 파일은 루트 수준부터 처리된 후 각 폴더의 페이지 또는 보기 자체의 위치까지 처리됩니다._ViewImports files are processed starting at the root level and then for each folder leading up to the location of the page or view itself. 루트 수준에 지정된 _ViewImports 설정은 폴더 수준에서 재정의될 수 있습니다._ViewImports settings specified at the root level may be overridden at the folder level.

예를 들어 다음을 가정해 보세요.For example, suppose:

  • 루트 수준 _ViewImports.cshtml 파일에 @model MyModel1@addTagHelper *, MyTagHelper1이 포함되어 있습니다.The root level _ViewImports.cshtml file includes @model MyModel1 and @addTagHelper *, MyTagHelper1.
  • 하위 폴더 _ViewImports.cshtml 파일에 @model MyModel2@addTagHelper *, MyTagHelper2이 포함되어 있습니다.A subfolder _ViewImports.cshtml file includes @model MyModel2 and @addTagHelper *, MyTagHelper2.

하위 폴더의 페이지 및 보기는 태그 도우미와 MyModel2 모델에 액세스할 수 있습니다.Pages and views in the subfolder will have access to both Tag Helpers and the MyModel2 model.

파일 계층 구조에 _ViewImports.cshtml 파일이 여러 개 있을 경우 지시문의 결합된 동작은 다음과 같습니다.If multiple _ViewImports.cshtml files are found in the file hierarchy, the combined behavior of the directives are:

  • @addTagHelper, @removeTagHelper: 순서대로 모두 실행@addTagHelper, @removeTagHelper: all run, in order
  • @tagHelperPrefix: 뷰에 가장 가까운 것이 다른 것보다 우선함@tagHelperPrefix: the closest one to the view overrides any others
  • @model: 뷰에 가장 가까운 것이 다른 것보다 우선함@model: the closest one to the view overrides any others
  • @inherits: 뷰에 가장 가까운 것이 다른 것보다 우선함@inherits: the closest one to the view overrides any others
  • @using: 모두 포함됨. 중복 항목은 무시됨@using: all are included; duplicates are ignored
  • @inject: 각 속성에 대해 뷰에 가장 가까운 것이 같은 속성 이름의 다른 것보다 우선함@inject: for each property, the closest one to the view overrides any others with the same property name

각 뷰 이전에 코드 실행Running Code Before Each View

_ViewStart.cshtml 파일에 각 보기 또는 페이지를 배치하기 전에 실행되어야 하는 코드.Code that needs to run before each view or page should be placed in the _ViewStart.cshtml file. 규칙에 따라, _ViewStart.cshtml 파일은 Pages(또는 Views) 폴더에 있습니다.By convention, the _ViewStart.cshtml file is located in the Pages (or Views) folder. _ViewStart.cshtml에 나열된 문은 모든 전체 뷰(레이아웃 및 부분 뷰가 아님) 이전에 실행됩니다.The statements listed in _ViewStart.cshtml are run before every full view (not layouts, and not partial views). ViewImports.cshtml처럼 _ViewStart.cshtml은 계층적입니다.Like ViewImports.cshtml, _ViewStart.cshtml is hierarchical. _ViewStart.cshtml 파일이 보기 또는 페이지 폴더에 정의된 경우 Pages(또는 Views) 폴더의 루트에 정의된 항목 뒤에 실행됩니다(있는 경우).If a _ViewStart.cshtml file is defined in the view or pages folder, it will be run after the one defined in the root of the Pages (or Views) folder (if any).

샘플 _ViewStart.cshtml 파일:A sample _ViewStart.cshtml file:

@{
    Layout = "_Layout";
}

위의 파일은 모든 뷰가 _Layout.cshtml 레이아웃을 사용하도록 지정합니다.The file above specifies that all views will use the _Layout.cshtml layout.

_ViewStart.cshtml_ViewImports.cshtml은 일반적으로 /Pages/Shared(또는 /Views/Shared) 폴더에 배치되지 않습니다._ViewStart.cshtml and _ViewImports.cshtml are not typically placed in the /Pages/Shared (or /Views/Shared) folder. 이러한 파일의 앱 수준 버전은 /Pages(또는 /Views) 폴더에 직접 배치해야 합니다.The app-level versions of these files should be placed directly in the /Pages (or /Views) folder.