ASP.NET Core MVC에서 보기

작성자: Steve SmithDave Brock

이 항목에서는 ASP.NET Core MVC 애플리케이션에서 사용되는 보기에 대해 설명합니다. Razor Pages에 대한 자세한 내용은 ASP.NET Core의 Razor Pages 소개를 참조하세요.

MVC(Model-View-Controller) 패턴에서 보기는 앱의 데이터 프레젠테이션과 사용자 상호 작용을 처리합니다. 보기는 Razor 태그가 포함된 HTML 템플릿입니다. Razor 태그는 클라이언트로 전송되는 웹 페이지를 생성하기 위해 HTML과 상호 작용하는 코드입니다.

ASP.NET Core MVC의 보기는 Razor 태그에서 C# 프로그래밍 언어를 사용하는 .cshtml 파일입니다. 일반적으로 보기 파일은 앱의 컨트롤러 각각에 대해 명명된 폴더로 그룹화됩니다. 이 폴더는 앱 루트의 Views 폴더에 저장됩니다.

Views folder in Solution Explorer of Visual Studio is open with the Home folder open to show About.cshtml, Contact.cshtml, and Index.cshtml files

Home 컨트롤러는 Views 폴더 내에 Home 폴더로 표시됩니다. Home 폴더에는 About, ContactIndex(홈페이지) 웹 페이지에 대한 보기가 포함되어 있습니다. 사용자가 이러한 세 웹 페이지 중 하나를 요청하면 Home 컨트롤러의 컨트롤러 작업이 세 가지 보기 중 어떤 보기를 사용하여 웹 페이지를 만들고 사용자에게 반환할지 결정합니다.

일관된 웹 페이지 섹션을 제공하고 코드 반복을 줄이려면 레이아웃을 사용하세요. 레이아웃에는 보통 머리글, 탐색 및 메뉴 요소 및 바닥글이 포함됩니다. 머리글 및 바닥글에는 일반적으로 다양한 메타데이터 요소에 대한 상용구 태그와 스크립트 및 스타일 자산에 대한 링크가 포함됩니다. 레이아웃을 사용하면 이런 상용구 태그가 보기에 포함되는 것을 피할 수 있습니다.

부분 보기는 재사용 가능한 보기 부분을 관리하여 코드 중복을 줄입니다. 예를 들어, 부분 보기는 여러 보기에 나타나는 블로그 웹 사이트의 작성자 약력에 유용합니다. 작성자 약력은 평범한 보기 콘텐츠이고 웹 페이지의 콘텐츠를 생성하기 위해 코드를 실행하지 않아도 됩니다. 작성자 약력 콘텐츠는 모델 바인딩만으로 보기에서 사용할 수 있으므로 이러한 콘텐츠 유형에는 부분 보기를 사용하는 것이 좋습니다.

보기 구성 요소는 반복 코드를 줄일 수 있다는 점에서 부분 보기와 유사하지만 웹 페이지를 렌더링하기 위해 서버에서 코드를 실행해야 하는 보기 콘텐츠에 적합합니다. 보기 구성 요소는 장바구니와 같이 렌더링된 콘텐츠가 웹 사이트 데이터베이스 상호 작용을 필요로 할 때 유용합니다. 보기 구성 요소는 웹 페이지 출력을 생성하기 위해 모델 바인딩에만 제한되지 않습니다.

보기 사용 시의 이점

보기를 통해 사용자 인터페이스 태그를 앱의 다른 부분과 분리하여 MVC 앱 내에서 문제의 분리를 확립할 수 있습니다. SoC 디자인을 따르면 앱을 모듈화할 수 있으며 다음과 같은 여러 이점이 제공됩니다.

  • 앱이 더 잘 구성되어 있기 때문에 유지 관리가 더 쉽습니다. 보기는 일반적으로 앱 기능별로 그룹화됩니다. 이렇게 하면 기능을 사용할 때 관련된 보기를 보다 쉽게 찾을 수 있습니다.
  • 앱의 부분들이 느슨하게 결합됩니다. 비즈니스 논리 및 데이터 액세스 구성 요소와 별도로 앱의 보기를 작성하고 수정할 수 있습니다. 앱의 다른 부분을 수정하지 않고도 앱의 보기를 수정할 수 있습니다.
  • 뷰는 별도의 단위이므로 앱의 사용자 인터페이스 부분을 보다 쉽게 테스트할 수 있습니다.
  • 구성이 개선되어 사용자 인터페이스 섹션을 실수로 반복하는 일이 줄어듭니다.

보기 만들기

컨트롤러에 관한 보기는 Views/[ControllerName] 폴더에 생성됩니다. 컨트롤러 간에 공유되는 보기는 Views/Shared 폴더에 배치됩니다. 보기를 만들려면 새 파일을 추가하고 연결된 컨트롤러 작업과 동일한 이름을 .cshtml 파일 확장명으로 지정합니다. Home 컨트롤러에서 About 작업에 해당하는 보기를 만들려면 Views/Home 폴더에 About.cshtml 파일을 만듭니다.

@{
    ViewData["Title"] = "About";
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<p>Use this area to provide additional information.</p>

Razor 태그@ 기호로 시작됩니다. C# 코드를 중괄호({ ... })로 설정된 Razor 코드 블록 내에 배치하여 C# 문을 실행합니다. 예를 들어 위에 표시된 ViewData["Title"]에 "About"을 할당하는 부분을 참조하세요. @ 기호로 값을 참조하기만 하면 HTML 내에서 이 값을 표시할 수 있습니다. 위의 <h2><h3> 요소의 내용을 참조하세요.

위에 표시된 보기 콘텐츠는 사용자에게 렌더링되는 전체 웹 페이지의 일부분일 뿐입니다. 페이지 레이아웃의 나머지 부분 및 보기의 다른 일반적인 측면은 다른 보기 파일에 지정됩니다. 자세한 내용은 레이아웃 항목을 참조하세요.

컨트롤러에서 보기를 지정하는 방법

일반적으로 보기는 작업에서 ActionResult 형식인 ViewResult로 반환됩니다. 작업 메서드에서 직접 ViewResult를 만들고 반환할 수도 있지만 일반적으로 그렇게 하지는 않습니다. 대부분의 컨트롤러는 Controller에서 상속하므로 View 도우미 메서드를 사용하여 ViewResult를 반환합니다.

HomeController.cs:

public IActionResult About()
{
    ViewData["Message"] = "Your application description page.";

    return View();
}

이 작업이 반환되면 마지막 섹션에 표시된 About.cshtml 보기가 다음 웹 페이지와 같이 렌더링됩니다.

About page rendered in the Edge browser

View 도우미 메서드는 몇 가지 오버로드를 갖고 있습니다. 필요에 따라 다음을 지정할 수 있습니다.

  • 반환할 명시적 보기:

    return View("Orders");
    
  • 보기에 전달할 모델:

    return View(Orders);
    
  • 보기 및 모델 모두:

    return View("Orders", Orders);
    

보기 검색

작업이 보기를 반환하면 보기 검색이라는 프로세스가 수행됩니다. 이 프로세스는 보기 이름을 기반으로 어떤 보기 파일을 사용할지 결정합니다.

View 메서드(return View();)의 기본 동작은 호출된 작업 메서드와 같은 이름의 보기를 반환하는 것입니다. 예를 들어 컨트롤러의 AboutActionResult 메서드 이름은 About.cshtml이라는 이름의 보기 파일을 검색하는 데 사용됩니다. 먼저 런타임은 Views/[ControllerName] 폴더에서 보기를 찾습니다. 이 위치에서 일치하는 보기를 찾지 못하면 Shared 폴더에서 보기를 검색합니다.

return View();;`를 사용하여 암시적으로 ViewResult를 반환하거나 return View("<ViewName>");를 사용하여 명시적으로 보기 이름을 View 메서드에 전달하는 것은 문제가 되지 않습니다. 두 경우 모두, 보기 검색 시 일치하는 보기 파일을 다음 순서로 검색합니다.

  1. Views/\[ControllerName]/\[ViewName].cshtml
  2. Views/Shared/\[ViewName].cshtml

보기 이름 대신 보기 파일의 경로를 제공할 수 있습니다. 앱 루트에서 시작하는 절대 경로를 사용하는 경우(필요에 따라 "/" 또는 "~ /"로 시작하는) .cshtml 확장명을 지정해야 합니다.

return View("Views/Home/About.cshtml");

상재 경로를 사용하여 .cshtml 확장명 없이 다른 디렉터리에 뷰를 지정할 수도 있습니다. HomeController 내에서 상대 경로로 Manage 뷰의 Index 뷰를 반환할 수 있습니다.

return View("../Manage/Index");

마찬가지로, "./" 접두사로 현재 컨트롤러 관련 디렉터리를 나타낼 수 있습니다.

return View("./About");

부분 보기보기 구성 요소도 완전히 동일하지는 않지만 비슷한 검색 메커니즘을 사용합니다.

사용자 지정 IViewLocationExpander를 사용하여 보기가 앱 내부에 위치하는 방식에 대한 기본 규칙을 사용자 지정할 수 있습니다.

보기 검색은 파일 이름으로 보기 파일을 찾는 방식을 사용합니다. 기본 파일 시스템이 대/소문자를 구분하는 경우 보기 이름도 대/소문자를 구분합니다. 운영 체제 간 호환성을 위해, 컨트롤러 및 작업 이름, 관련 보기 폴더 및 파일 이름 간에 대/소문자를 일치시키세요. 대/소문자 구분 파일 시스템으로 작업하는 동안 파일 보기를 찾지 못하는 오류가 발생하면, 요청된 보기 파일과 실제 보기 파일 이름 간에 대/소문자가 일치하는지 확인합니다.

유지 관리 및 명확성을 위해 컨트롤러, 작업 및 보기 간의 관계를 반영하도록 보기에 대한 파일 구조를 구성하는 것이 가장 좋습니다.

보기에 데이터 전달

여러 가지 방법으로 보기에 데이터를 전달합니다.

  • 강력한 형식의 데이터: viewmodel
  • 약한 형식의 데이터
    • ViewData (ViewDataAttribute)
    • ViewBag

강력한 형식의 데이터(viewmodel)

가장 강력한 방법은 보기에서 모델 형식을 지정하는 것입니다. 이 모델은 일반적으로 viewmodel이라고 합니다. 작업에서 viewmodel 형식의 인스턴스를 보기에 전달합니다.

Viewmodel을 사용하여 보기에 데이터를 전달하면 보기에서 강력한 형식 검사를 활용할 수 있습니다. 강력한 형식화(또는 강력한 형식의)는 모든 변수 및 상수가 명시적으로 정의된 형식(예: string, int 또는 DateTime)을 포함함을 의미합니다. 뷰에 사용된 형식의 유효성 검사는 컴파일 시간에 검사됩니다.

Visual StudioVisual Studio CodeIntelliSense라는 기능을 사용하여 강력한 형식의 클래스 멤버를 나열합니다. viewmodel 속성을 보려는 경우 viewmodel에 대한 변수 이름과 마침표(.)를 입력합니다. 이렇게 하면 오류를 줄이면서 보다 빠르게 코드를 작성할 수 있습니다.

@model 지시문을 사용하여 모델을 지정합니다. @Model로 모델을 사용합니다.

@model WebApplication1.ViewModels.Address

<h2>Contact</h2>
<address>
    @Model.Street<br>
    @Model.City, @Model.State @Model.PostalCode<br>
    <abbr title="Phone">P:</abbr> 425.555.0100
</address>

모델을 보기에 제공하기 위해 컨트롤러는 이를 매개 변수로 전달합니다.

public IActionResult Contact()
{
    ViewData["Message"] = "Your contact page.";

    var viewModel = new Address()
    {
        Name = "Microsoft",
        Street = "One Microsoft Way",
        City = "Redmond",
        State = "WA",
        PostalCode = "98052-6399"
    };

    return View(viewModel);
}

뷰에 제공할 수 있는 모델 유형에 대한 제한은 없습니다. 동작(메서드)이 거의 또는 전혀 정의되지 않은 POCO(Plain Old CLR Object) viewmodel을 사용하는 것이 좋습니다. 일반적으로 viewmodel 클래스는 앱의 루트에서 Models 폴더 또는 별도의 ViewModels 폴더에 저장됩니다. 위의 예제에 사용된 Address viewmodel은 Address.cs라는 파일에 저장된 POCO viewmodel입니다.

namespace WebApplication1.ViewModels
{
    public class Address
    {
        public string Name { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string PostalCode { get; set; }
    }
}

Viewmodel 형식 및 비즈니스 모델 형식 모두에 같은 클래스를 사용할 수 있습니다. 그러나 별도의 모델을 사용하면 보기를 통해 앱의 비즈니스 논리 및 데이터 액세스 부분을 독립적으로 구분할 수 있습니다. 모델과 viewmodel을 분리하면 모델에서 사용자에 의해 앱으로 전송된 데이터에 대해 모델 바인딩유효성 검사를 사용할 때 보안상 이점도 제공합니다.

약하게 형식화된 데이터(ViewData, [ViewData] 특성 및 ViewBag)

ViewBag는 기본적으로 PagesPageModel클래스에서 Razor 사용할 수 없습니다.

강력한 형식의 뷰 외에도, 뷰는 약한 형식(느슨한 형식이라고도 함) 데이터 컬렉션에 액세스할 수 있습니다. 강력한 형식과 달리, 약한 형식(또는 느슨한 형식)은 사용 중인 데이터 형식을 명시적으로 선언하지 않음을 의미합니다. 컨트롤러 및 뷰 간에 적은 양의 데이터를 전달하기 위해 약한 형식의 데이터 컬렉션을 사용할 수 있습니다.

다음 사이에 데이터 전달 ... 예시
컨트롤러 및 뷰 드롭다운 목록을 데이터로 채웁니다.
보기 및 레이아웃 보기 보기 파일에서 레이아웃 보기의 <title> 요소 콘텐츠를 설정합니다.
부분 보기 및 보기 사용자가 요청한 웹 페이지에 따라 데이터를 표시하는 위젯입니다.

이 컬렉션은 컨트롤러 및 보기의 ViewData 또는 ViewBag 속성을 통해 참조할 수 있습니다. ViewData 속성은 약한 형식 개체의 사전입니다. ViewBag 속성은 기본 ViewData 컬렉션에 대해 동적 속성을 제공하는 ViewData 주변의 래퍼입니다. 참고: 키 조회는 ViewDataViewBag에 대해 둘 다 대/소문자를 구분하지 않습니다.

ViewDataViewBag은 런타임에 동적으로 확인됩니다. 컴파일 시간 형식 검사를 제공하지 않으므로 일반적으로 둘 다 viewmodel을 사용하는 것보다 오류가 발생하기 더 쉽습니다. 이러한 이유로, 일부 개발자는 ViewDataViewBag을 최소한으로 사용하거나 아예 사용하지 않습니다.

ViewData

ViewDatastring 키를 통해 액세스되는 ViewDataDictionary 개체입니다. 문자열 데이터는 캐스트할 필요 없이 직접 저장 및 사용할 수 있지만 다른 ViewData 개체 값을 추출할 때는 특정 형식으로 캐스트해야 합니다. ViewData를 사용하여 컨트롤러에서 보기로, 부분 보기레이아웃을 포함한 보기 내에서 데이터를 전달할 수 있습니다.

다음은 작업에서 ViewData를 사용하여 인사말 및 주소에 대한 값을 설정하는 예제입니다.

public IActionResult SomeAction()
{
    ViewData["Greeting"] = "Hello";
    ViewData["Address"]  = new Address()
    {
        Name = "Steve",
        Street = "123 Main St",
        City = "Hudson",
        State = "OH",
        PostalCode = "44236"
    };

    return View();
}

뷰에서 데이터 사용:

@{
    // Since Address isn't a string, it requires a cast.
    var address = ViewData["Address"] as Address;
}

@ViewData["Greeting"] World!

<address>
    @address.Name<br>
    @address.Street<br>
    @address.City, @address.State @address.PostalCode
</address>

[ViewData] 특성

ViewDataDictionary를 사용하는 다른 방법은 ViewDataAttribute입니다. [ViewData] 특성으로 표시된 컨트롤러 또는 Razor Page 모델의 속성은 사전에서 값이 저장되고 로드됩니다.

다음 예제에서 Home 컨트롤러에는 [ViewData]로 표시된 Title 속성이 있습니다. About 메서드는 정보 보기에 대한 제목을 설정합니다.

public class HomeController : Controller
{
    [ViewData]
    public string Title { get; set; }

    public IActionResult About()
    {
        Title = "About Us";
        ViewData["Message"] = "Your application description page.";

        return View();
    }
}

레이아웃에서 제목은 ViewData 사전에서 읽습니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"] - WebApplication</title>
    ...

ViewBag

ViewBag는 기본적으로 PagesPageModel클래스에서 Razor 사용할 수 없습니다.

ViewBagViewData에 저장된 개체에 대한 동적 액세스를 제공하는 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData 개체입니다. 캐스팅이 필요하지 않으므로 ViewBag이 작업하기 더 편리할 수 있습니다. 다음 예제에서는 ViewData를 사용할 때와 동일한 결과를 보여주는 ViewBag을 사용하는 방법을 보여 줍니다.

public IActionResult SomeAction()
{
    ViewBag.Greeting = "Hello";
    ViewBag.Address  = new Address()
    {
        Name = "Steve",
        Street = "123 Main St",
        City = "Hudson",
        State = "OH",
        PostalCode = "44236"
    };

    return View();
}
@ViewBag.Greeting World!

<address>
    @ViewBag.Address.Name<br>
    @ViewBag.Address.Street<br>
    @ViewBag.Address.City, @ViewBag.Address.State @ViewBag.Address.PostalCode
</address>

ViewDataViewBag 동시 사용

ViewBag는 기본적으로 PagesPageModel클래스에서 Razor 사용할 수 없습니다.

ViewDataViewBag은 동일한 기본 ViewData 컬렉션을 사용하므로 ViewDataViewBag을 모두 사용하고 값을 읽고 쓸 때 이들을 혼합 및 일치시킬 수 있습니다.

About.cshtml 보기의 맨 위에서 ViewBag을 사용하여 제목을, ViewData를 사용하여 설명을 설정합니다.

@{
    Layout = "/Views/Shared/_Layout.cshtml";
    ViewBag.Title = "About Contoso";
    ViewData["Description"] = "Let us tell you about Contoso's philosophy and mission.";
}

속성을 읽지만 ViewDataViewBag을 반대로 사용합니다. _Layout.cshtml 파일에서는 ViewData를 사용하여 제목을 가져오고 ViewBag을 사용하여 설명을 가져옵니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"]</title>
    <meta name="description" content="@ViewBag.Description">
    ...

ViewData의 문자열은 캐스트가 필요하지 않습니다. 캐스팅없이 @ViewData["Title"]를 사용할 수 있습니다.

ViewDataViewBag을 동시에 사용하면 속성을 읽고 쓰는 작업을 혼합 및 일치시킬 수 있습니다. 다음 태그가 렌더링됩니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>About Contoso</title>
    <meta name="description" content="Let us tell you about Contoso's philosophy and mission.">
    ...

ViewDataViewBag 간의 차이점에 대한 요약

ViewBag는 기본적으로 PagesPageModel클래스에서 Razor 사용할 수 없습니다.

  • ViewData
    • ViewDataDictionary에서 파생되므로 유용한 ContainsKey, Add, RemoveClear와 같은 사전 속성이 있습니다.
    • 사전의 키는 문자열이므로 공백을 사용할 수 있습니다. 예: ViewData["Some Key With Whitespace"]
    • ViewData를 사용하려면 string 이외의 모든 형식을 보기에서 캐스트해야 합니다.
  • ViewBag
    • Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData에서 파생되므로 점 표기법(@ViewBag.SomeKey = <value or object>)을 사용하여 동적 속성을 생성할 수 있으며 캐스팅이 필요하지 않습니다. ViewBag 구문을 사용하면 신속하게 컨트롤러와 보기에 추가할 수 있습니다.
    • 간단하게 Null 값을 확인합니다. 예: @ViewBag.Person?.Name

ViewData 또는 ViewBag을 사용하는 경우

ViewDataViewBag 둘 다 컨트롤러 및 보기 간에 적은 양의 데이터를 전달하는 데 유효한 방법입니다. 어떤 것을 사용할지는 선호도에 따라서 선택하면 됩니다. ViewDataViewBag 개체를 혼합 및 일치시키실 수는 있지만, 일관된 한 가지 방법을 사용하는 편이 코드를 보다 쉽게 읽고 유지 관리할 수 있습니다. 두 가지 방법 모두 런타임에 동적으로 확인되므로 런타임 오류가 발생하기 쉽습니다. 일부 개발 팀은 이 방법을 사용하지 않습니다.

동적 보기

@model을 사용하여 모델 형식을 선언하지는 않지만 전달된 모델 인스턴스가 있는 보기(예: return View(Address);)는 인스턴스의 속성을 동적으로 참조할 수 있습니다.

<address>
    @Model.Street<br>
    @Model.City, @Model.State @Model.PostalCode<br>
    <abbr title="Phone">P:</abbr> 425.555.0100
</address>

이 기능은 유연성을 제공하지만 컴파일 보호 또는 IntelliSense는 제공하지 않습니다. 속성이 존재하지 않으면 런타임 시 웹 페이지 생성에 실패합니다.

더 많은 보기 기능

태그 도우미를 통해 기존 HTML 태그에 서버 쪽 동작을 쉽게 추가할 수 있습니다. 태그 도우미를 사용하면 보기 내에서 사용자 지정 코드 또는 도우미를 작성할 필요가 없습니다. 태그 도우미는 HTML 요소에 특성으로 적용되고 처리할 수 없는 편집기에 의해 무시됩니다. 따라서 다양한 도구로 보기 태그를 편집 및 렌더링할 수 있습니다.

많은 기본 제공 HTML 도우미를 통해 사용자 지정 HTML 태그를 생성할 수 있습니다. 더 복잡한 사용자 인터페이스 논리는 보기 구성 요소로 처리할 수 있습니다. 보기 구성 요소는 컨트롤러 및 보기에서 제공하는 것과 동일한 SoC를 제공합니다. 공통적인 사용자 인터페이스 요소에서 사용되는 데이터를 처리하는 작업 및 보기에 대한 필요성을 제거할 수 있습니다.

ASP.NET Core의 다른 많은 요소와 마찬가지로 보기는 종속성 주입을 지원하며, 보기에 서비스를 주입할 수 있습니다.

CSS 격리

CSS 스타일을 개별 페이지, 뷰 및 구성 요소로 격리하여 다음을 줄이거나 방지합니다.

  • 유지 관리하기 어려울 수 있는 전역 스타일에 대한 종속성.
  • 중첩된 콘텐츠의 스타일 충돌.

페이지 또는 보기에 대한 범위가 지정되는 CSS 파일을 추가하려면 .cshtml 파일 이름과 일치하는 도우미 .cshtml.css 파일에 CSS 스타일을 배치합니다. 다음 예제에서 Index.cshtml.css 파일은 Index.cshtml 페이지 또는 보기에만 적용되는 CSS 스타일을 제공합니다.

Pages/Index.cshtml.css(Razor 페이지) 또는 Views/Index.cshtml.css(MVC):

h1 {
    color: red;
}

CSS 격리는 빌드 시간에 발생합니다. 프레임워크는 앱의 페이지 또는 보기에서 렌더링된 태그와 일치하도록 CSS 선택기를 다시 작성합니다. 다시 작성된 CSS 스타일은 번들로 묶여 정적 자산, {APP ASSEMBLY}.styles.css로 생성됩니다. 자리 표시자 {APP ASSEMBLY}는 프로젝트의 어셈블리 이름입니다. 번들로 묶인 CSS 스타일에 대한 링크는 앱의 레이아웃에 배치됩니다.

앱의 Pages/Shared/_Layout.cshtml(Razor Pages) 또는 Views/Shared/_Layout.cshtml(MVC)의 <head> 콘텐츠에서 번들로 묶인 CSS 스타일에 대한 링크의 존재를 추가하거나 확인합니다.

<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />

다음 예제에서 앱의 어셈블리 이름은 WebApp입니다.

<link rel="stylesheet" href="WebApp.styles.css" />

범위가 지정된 CSS 파일에 정의된 스타일은 일치하는 파일의 렌더링된 출력에만 적용됩니다. 위의 예제에서 앱의 다른 위치에 정의된 모든 h1 CSS 선언이 Index의 제목 스타일과 충돌하지 않습니다. CSS 스타일 계단식 배열 및 상속 규칙은 범위가 지정된 CSS 파일에 계속 적용됩니다. 예를 들어 Index.cshtml 파일의 <h1> 요소에 직접 적용된 스타일은 Index.cshtml.css에서 범위가 지정된 CSS 파일의 스타일을 재정의합니다.

참고 항목

묶음이 발생할 때 CSS 스타일 격리를 보장하기 위해 Razor 코드 블록에서 CSS 가져오기는 지원되지 않습니다.

CSS 격리는 HTML 요소에만 적용됩니다. 태그 도우미에는 CSS 격리가 지원되지 않습니다.

번들로 묶인 CSS 파일 내에서 각 페이지, 보기 또는 Razor 구성 요소는 b-{STRING} 형식의 범위 식별자와 연결됩니다. 여기서 {STRING} 자리 표시자는 프레임워크에서 생성된 10자 문자열입니다. 다음 예제에서는 Razor Pages 앱의 Index 페이지에서 위의 <h1> 요소에 대한 스타일을 제공합니다.

/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
    color: red;
}

번들로 묶인 파일의 CSS 스타일이 적용되는 Index 페이지에서 범위 식별자는 HTML 특성으로 추가됩니다.

<h1 b-3xxtam6d07>

식별자는 앱에 고유합니다. 빌드 시간에 {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css 규칙을 사용하여 프로젝트 번들을 만듭니다. 여기서 {STATIC WEB ASSETS BASE PATH} 자리 표시자는 정적 웹 자산 기본 경로입니다.

NuGet 패키지 또는 Razor 클래스 라이브러리 같은 다른 프로젝트를 활용하는 경우 묶은 파일은 다음과 같습니다.

  • CSS 가져오기를 사용하여 스타일을 참조합니다.
  • 스타일을 사용하는 앱의 정적 웹 자산으로 게시되지 않습니다.

CSS 전처리기 지원

CSS 전처리기는 변수, 중첩, 모듈, 믹스인 및 상속과 같은 기능을 활용하여 CSS 개발을 개선하는 데 유용합니다. CSS 격리는 Sass 또는 Less 같은 CSS 전처리기를 기본적으로 지원하지 않지만 프레임워크가 빌드 프로세스 중 CSS 선택기를 다시 작성하기 전에 전처리기 컴파일이 수행되는 한 CSS 전처리기를 원활하게 통합할 수 있습니다. 예를 들어 Visual Studio를 사용하여 Visual Studio 작업 실행기 탐색기에서 빌드 전 작업으로 기존 전처리기 컴파일을 구성합니다.

AspNetCore.SassCompiler와 같은 많은 타사 NuGet 패키지는 CSS 격리가 발생하기 전 빌드 프로세스 시작 부분에서 SASS/SCSS 파일을 컴파일할 수 있으며, 추가 구성이 필요하지 않습니다.

CSS 격리 구성

CSS 격리는 기존 도구 또는 워크플로에 대한 의존성이 있는 경우와 같은 일부 고급 시나리오에 대한 구성을 허용합니다.

범위 식별자 형식 사용자 지정

이 섹션에서 {Pages|Views} 자리 표시자는 Razor Pages 앱에 대해 Pages 또는 MVC 앱에 대해 Views입니다.

기본적으로 범위 식별자는 b-{STRING} 형식을 사용합니다. 여기서 {STRING} 자리 표시자는 프레임워크에서 생성된 10자 문자열입니다. 범위 식별자 형식을 사용자 지정하려면 프로젝트 파일을 원하는 패턴으로 업데이트합니다.

<ItemGroup>
  <None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

위의 예제에서 Index.cshtml.css에 대해 생성된 CSS는 범위 식별자를 b-{STRING}에서 custom-scope-identifier로 변경합니다.

범위가 지정된 CSS 파일을 사용하여 상속을 수행하려면 범위 식별자를 사용하세요. 다음 프로젝트 파일 예제에서 BaseView.cshtml.css 파일은 보기에 걸쳐 일반 스타일을 포함합니다. DerivedView.cshtml.css 파일은 이러한 스타일을 상속합니다.

<ItemGroup>
  <None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
  <None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

여러 파일에 걸쳐 범위 식별자를 공유하려면 와일드카드(*) 연산자를 사용합니다.

<ItemGroup>
  <None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

정적 웹 자산에 대한 기본 경로 변경

범위가 지정된 CSS 파일은 앱의 루트에 생성됩니다. 프로젝트 파일에서 StaticWebAssetBasePath 속성을 사용하여 기본 경로를 변경합니다. 다음 예제에서는 _content 경로에 범위가 지정된 CSS 파일과 앱의 나머지 자산을 배치합니다.

<PropertyGroup>
  <StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>

자동 묶음 사용 안 함

프레임워크가 런타임에 범위가 지정된 파일을 게시하고 로드하는 방법을 옵트아웃하려면 DisableScopedCssBundling 속성을 사용합니다. 이 속성을 사용하면 다른 도구나 프로세스가 obj 디렉터리에서 격리된 CSS 파일을 가져와 런타임에 게시하고 로드합니다.

<PropertyGroup>
  <DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>

RCL(Razor 클래스 라이브러리) 지원

RCL(Razor 클래스 라이브러리)이 격리된 스타일을 제공하는 경우 <link> 태그의 href 특성은 {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css를 가리키며, 여기서 자리 표시자는 다음과 같습니다.

  • {STATIC WEB ASSET BASE PATH}: 정적 웹 자산 기본 경로입니다.
  • {PACKAGE ID}: 라이브러리의 패키지 식별자입니다. 패키지 식별자가 프로젝트 파일에 지정되지 않은 경우 패키지 식별자는 기본적으로 프로젝트의 어셈블리 이름으로 설정됩니다.

다음 예제에서

  • 정적 웹 자산 기본 경로가 _content/ClassLib입니다.
  • 클래스 라이브러리의 어셈블리 이름이 ClassLib입니다.

Pages/Shared/_Layout.cshtml(Razor 페이지) 또는 Views/Shared/_Layout.cshtml(MVC):

<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">

RCL에 대한 자세한 내용은 다음 문서를 참조하세요.

Blazor CSS 격리에 대한 자세한 내용은 ASP.NET Core Blazor CSS 격리를 참조하세요.