다음을 통해 공유


ASP.NET Core에서 정적 파일 매핑

작성자: Rick Anderson

HTML, CSS, 이미지 및 JavaScript와 같은 정적 파일은 기본적으로 ASP.NET Core 앱이 클라이언트에 직접 제공하는 자산입니다.

Blazor 이 문서의 지침을 추가하거나 대체하는 정적 파일 지침은 ASP.NET Core Blazor 정적 파일을 참조하세요.

정적 파일 제공

정적 파일은 프로젝트의 웹 루트 디렉터리 내에 저장됩니다. 기본 디렉터리는 {content root}/wwwroot이지만, UseWebRoot 메서드를 통해 변경할 수 있습니다. 자세한 내용은 콘텐츠 루트웹 루트를 참조하세요.

CreateBuilder 메서드는 콘텐츠 루트를 현재 디렉터리로 설정합니다.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.MapStaticAssets();

app.UseAuthorization();

app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();

app.Run();

정적 파일은 웹 루트에 상대적인 경로를 통해 액세스할 수 있습니다. 예를 들어 웹 애플리케이션 프로젝트 템플릿에는 wwwroot 폴더 내에 여러 폴더가 포함되어 있습니다.

  • wwwroot
    • css
    • js
    • lib

wwwroot/images/MyImage.jpg 파일이 포함된 앱을 고려합니다. images 폴더의 파일에 액세스하기 위한 URI 형식은 https://<hostname>/images/<image_file_name>입니다. 예를 들어 https://localhost:5001/images/MyImage.jpg

정적 자산 라우팅 엔드포인트 규칙 매핑(MapStaticAssets)

성능이 좋은 웹앱을 만들려면 브라우저에 대한 자산 배달을 최적화해야 합니다. MapStaticAssets의 가능한 최적화는 다음과 같습니다.

MapStaticAssets:

  • 빌드 및 게시 프로세스 중에 정적 웹 자산에 대해 수집된 정보를 브라우저에 제공하는 파일을 최적화하기 위해 이 정보를 처리하는 런타임 라이브러리와 통합합니다.
  • 앱에서 정적 자산의 배달을 최적화하는 라우팅 엔드포인트 규칙입니다. 페이지 및 MVC를 비롯한 BlazorRazor 모든 UI 프레임워크에서 작동하도록 설계되었습니다.

MapStaticAssetsUseStaticFiles

MapStaticAssets 는 .NET 9 이상의 ASP.NET Core에서 사용할 수 있습니다. UseStaticFiles 는 .NET 9 이전 버전에서 사용해야 합니다.

UseStaticFiles 정적 파일을 제공하지만 MapStaticAssets동일한 수준의 최적화를 제공하지는 않습니다. MapStaticAssets는 앱이 런타임 시 알고 있는 자산을 제공하는 데 최적화되어 있습니다. 앱이 디스크 또는 포함된 리소스와 같은 다른 위치에서 자산을 제공하는 경우, UseStaticFiles를 사용해야 합니다.

Map Static Assets는 UseStaticFiles호출할 때 사용할 수 없는 다음과 같은 이점을 제공합니다.

  • JavaScript(JS) 및 스타일시트를 포함하여 앱의 모든 자산에 대한 빌드 시간 압축이지만 이미 압축된 이미지 및 글꼴 자산은 제외됩니다. Gzip(Content-Encoding: gz) 압축은 개발 중에 사용됩니다. Brotli(Content-Encoding: br) 압축이 있는 Gzip은 게시 중에 사용됩니다.
  • 빌드 시 각 파일 콘텐츠의 SHA-256 해시를 Base64로 인코딩된 문자열로 변환하여 모든 자산에 대한 지문을 생성합니다. 이렇게 하면 이전 파일이 캐시된 경우에도 이전 버전의 파일을 다시 사용할 수 없습니다. 지문 자산은 immutable 지시문사용하여 캐시되므로 브라우저에서 변경될 때까지 자산을 다시 요청하지 않습니다. immutable 지시문을 지원하지 않는 브라우저의 경우 max-age 지시문 추가됩니다.
    • 자산이 지문이 아닌 경우에도 파일의 지문 해시를 ETags 값으로 사용하여 각 정적 자산에 대해 콘텐츠 기반 ETag 생성됩니다. 이렇게 하면 콘텐츠가 변경되거나 파일이 처음으로 다운로드되는 경우에만 브라우저에서 파일을 다운로드할 수 있습니다.
    • 내부적으로 프레임워크는 물리적 자산을 지문에 매핑하므로 앱에서 다음을 수행할 수 있습니다.
      • 자동으로 생성된 자산을 찾습니다. 예를 들어, Razor의 Blazor을 위한 구성 요소 범위 CSS와 JS에 정의된 JS 자산 등이 있습니다.
      • 페이지의 <head> 콘텐츠에 링크 태그를 생성하여 자산을 미리 로드합니다.
  • Visual Studio 핫 리로드 개발 테스트 중:
    • 앱이 실행되는 동안 파일이 변경되는 문제를 방지하기 위해 자산에서 무결성 정보가 제거됩니다.
    • 정적 자산은 브라우저가 항상 현재 콘텐츠를 검색하도록 캐시되지 않습니다.

맵 정적 자산은 축소 또는 기타 파일 변환을 위한 기능을 제공하지 않습니다. 축소는 일반적으로 사용자 지정 코드 또는 타사 도구의해 처리됩니다.

다음 기능은 UseStaticFiles 지원되지만 MapStaticAssets지원되지는 않습니다.

웹 루트의 파일 제공

기본 웹앱 템플릿은 MapStaticAssets에서 Program.cs 메서드를 호출하여 정적 파일을 제공할 수 있습니다.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.MapStaticAssets();

app.UseAuthorization();

app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();

app.Run();

매개 변수가 없는 MapStaticAssets 메서드 오버로드는 웹 루트에 있는 파일을 제공 가능으로 표시합니다. 다음 마크업 참조는 wwwroot/images/MyImage.jpg입니다.

<img src="~/images/MyImage.jpg" class="img" alt="My image" />

위의 태그에서 물결표 문자(~)는 웹 루트를 가리킵니다.

웹 루트 외부의 파일 제공

제공할 정적 파일이 웹 루트 외부에 있는 디렉터리 계층 구조를 고려해보세요.

  • wwwroot
    • css
    • images
    • js
  • MyStaticFiles
    • images
      • red-rose.jpg

요청은 정적 파일 미들웨어를 다음과 같이 구성하여 red-rose.jpg 파일에 액세스할 수 있습니다.

using Microsoft.Extensions.FileProviders;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();    //Serve files from wwwroot
app.UseStaticFiles(new StaticFileOptions
 {
     FileProvider = new PhysicalFileProvider(
            Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
     RequestPath = "/StaticFiles"
 });

app.UseAuthorization();

app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();

app.Run();

위의 코드에서 MyStaticFiles 디렉터리 계층 구조는 StaticFiles URI 세그먼트를 통해 공개적으로 노출됩니다. https://<hostname>/StaticFiles/images/red-rose.jpg에 대한 요청은 red-rose.jpg 파일을 제공합니다.

다음 마크업 참조는 MyStaticFiles/images/red-rose.jpg입니다.

<img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose" />

여러 위치에서 파일을 제공하려면 여러 위치에서 파일 제공을 참조하세요.

HTTP 응답 헤더 설정

StaticFileOptions 개체를 사용하여 HTTP 응답 헤더를 설정할 수 있습니다. 다음 코드는 웹 루트에서 제공되는 정적 파일을 구성하는 것 외에도 Cache-Control 헤더를 설정합니다.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();

 var cacheMaxAgeOneWeek = (60 * 60 * 24 * 7).ToString();
 app.UseStaticFiles(new StaticFileOptions
 {
     OnPrepareResponse = ctx =>
     {
         ctx.Context.Response.Headers.Append(
              "Cache-Control", $"public, max-age={cacheMaxAgeOneWeek}");
     }
 });

app.UseAuthorization();

app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();

app.Run();

위의 코드는 정적 파일을 1주일 동안 로컬 캐시에서 공개적으로 사용할 수 있도록 합니다.

정적 파일 권한 부여

ASP.NET Core 템플릿은 MapStaticAssets을 호출하기 전에 UseAuthorization를 호출합니다. 대부분의 앱은 이 패턴을 따릅니다. 권한 부여 미들웨어 전에 MapStaticAssets 호출되는 경우:

  • 정적 파일에서 권한 부여 검사가 수행되지 않습니다.
  • wwwroot 아래에 있는 정적 파일과 같이 정적 파일 미들웨어가 제공하는 정적 파일은 공개적으로 액세스할 수 있습니다.

권한 부여에 따라 정적 파일을 제공하려면 정적 파일 권한 부여참조하세요.

여러 위치에서 파일 제공

Razor 파일을 표시하는 다음 /MyStaticFiles/image3.png 페이지를 고려하세요.

@page

<p> Test /MyStaticFiles/image3.png</p>

<img src="~/image3.png" class="img" asp-append-version="true" alt="Test">

UseStaticFilesUseFileServer는 기본적으로 wwwroot를 가리키는 파일 공급자로 설정됩니다. 다른 위치에서 파일을 제공하기 위해 다른 파일 공급자와 UseStaticFilesUseFileServer의 추가 인스턴스를 제공할 수 있습니다. 다음 예제에서는 UseStaticFiles를 두 번 호출하여 wwwrootMyStaticFiles 모두에서 파일을 제공합니다.

app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"))
});

위의 코드를 사용하여 다음을 수행합니다.

  • /MyStaticFiles/image3.png 파일이 표시됩니다.
  • 이미지 태그 도우미에 의존하기 때문에 적용되지 않습니다. WebRootFileProvider 폴더를 포함하도록 MyStaticFiles가 업데이트되지 않았습니다.

다음 코드는 WebRootFileProvider를 업데이트하여 이미지 태그 도우미가 버전을 제공할 수 있도록 합니다.

var webRootProvider = new PhysicalFileProvider(builder.Environment.WebRootPath);
var newPathProvider = new PhysicalFileProvider(
  Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"));

var compositeProvider = new CompositeFileProvider(webRootProvider,
                                                  newPathProvider);

// Update the default provider.
app.Environment.WebRootFileProvider = compositeProvider;

app.MapStaticAssets();

비고

이전 방법은 Pages 및 MVC 앱에 Razor 적용됩니다. Blazor Web App에 적용되는 지침은 ASP.NET Core Blazor 정적 파일을 참조하세요.

IWebHostEnvironment.WebRootPath를 업데이트하여 wwwroot 외부의 파일 제공

IWebHostEnvironment.WebRootPathwwwroot 이외의 폴더로 설정된 경우:

  • 개발 환경에서는 wwwroot 및 업데이트된 IWebHostEnvironment.WebRootPath 모두에서 찾은 정적 자산이 wwwroot에서 제공됩니다.
  • 개발 환경 이외의 환경에서는 중복된 정적 자산이 업데이트된 IWebHostEnvironment.WebRootPath 폴더에서 제공됩니다.

빈 웹 템플릿으로 만든 웹앱을 고려합니다.

  • Index.htmlwwwrootwwwroot-custom 파일 포함

  • 업데이트된 Program.cs 파일과 함께 WebRootPath = "wwwroot-custom"을 설정합니다.

    var builder = WebApplication.CreateBuilder(new WebApplicationOptions
    {
        Args = args,
        // Look for static files in "wwwroot-custom"
        WebRootPath = "wwwroot-custom"
    });
    
    var app = builder.Build();
    
    app.UseDefaultFiles();
    app.MapStaticAssets();
    
    app.Run();
    

위의 코드에서 /에 대한 요청은

  • 개발 환경에서는 wwwroot/Index.html을 반환합니다.
  • 개발 환경 이외의 환경에서는 wwwroot-custom/Index.html을 반환합니다.

wwwroot-custom의 자산이 반환되도록 하려면 다음 방법 중 하나를 사용합니다.

  • wwwroot에서 중복 명명된 자산을 삭제합니다.

  • "ASPNETCORE_ENVIRONMENT"에서 Properties/launchSettings.json"Development" 이외의 값으로 설정합니다.

  • 프로젝트 파일에서 <StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>를 설정하여 정적 웹 자산을 완전히 사용하지 않도록 설정합니다. 경고, 정적 웹 자산을 사용하지 않도록 설정하면 Razor 클래스 라이브러리를 사용할 수 없습니다.

  • 프로젝트 파일에 다음 XML을 추가합니다.

    <ItemGroup>
        <Content Remove="wwwroot\**" />
    </ItemGroup>
    

다음 코드에서는 IWebHostEnvironment.WebRootPath를 개발이 아닌 값으로 업데이트하여 중복 콘텐츠가 wwwroot-custom가 아니라 wwwroot에서 반환되도록 합니다.

var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    Args = args,
    // Examine Hosting environment: logging value
    EnvironmentName = Environments.Staging,
    WebRootPath = "wwwroot-custom"
});

var app = builder.Build();

app.Logger.LogInformation("ASPNETCORE_ENVIRONMENT: {env}",
      Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));

app.Logger.LogInformation("app.Environment.IsDevelopment(): {env}",
      app.Environment.IsDevelopment().ToString());

app.UseDefaultFiles();
app.MapStaticAssets();

app.Run();

추가 리소스