ASP.NET Core의 정적 파일

작성자: Rick AndersonKirk Larkin

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

예제 코드 살펴보기 및 다운로드 (다운로드 방법)

정적 파일 제공

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

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

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

위의 코드는 웹앱 템플릿을 사용하여 만들었습니다.

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

  • wwwroot
    • css
    • js
    • lib

wwwroot/images 폴더를 만들고 wwwroot/images/MyImage.jpg 파일을 추가하는 것이 좋습니다. images 폴더의 파일에 액세스하기 위한 URI 형식은 https://<hostname>/images/<image_file_name>입니다. 예를 들면 https://localhost:5001/images/MyImage.jpg과 같습니다.

웹 루트의 파일 제공

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

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

매개 변수가 없는 UseStaticFiles 메서드 오버로드는 웹 루트에 있는 파일을 제공 가능으로 표시합니다. 다음 태그는 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 파일에 액세스할 수 있습니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    // using Microsoft.Extensions.FileProviders;
    // using System.IO;
    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(env.ContentRootPath, "MyStaticFiles")),
        RequestPath = "/StaticFiles"
    });

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

위의 코드에서 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 헤더를 설정합니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    const string cacheMaxAge = "604800";
    app.UseStaticFiles(new StaticFileOptions
    {
        OnPrepareResponse = ctx =>
        {
            // using Microsoft.AspNetCore.Http;
            ctx.Context.Response.Headers.Append(
                 "Cache-Control", $"public, max-age={cacheMaxAge}");
        }
    });

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

정적 파일은 600초 동안 공개적으로 캐시할 수 있습니다.

추가된 캐시 제어 헤더를 보여 주는 응답 헤더

정적 파일 권한 부여

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

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

권한 부여를 기반으로 정적 파일을 제공하려면 다음을 수행합니다.

  • wwwroot 외부에 정적 파일을 저장합니다.
  • UseAuthorization을 호출한 후 경로를 지정하여 UseStaticFiles를 호출합니다.
  • 대체 권한 부여 정책을 설정합니다.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    // wwwroot css, JavaScript, and images don't require authentication.
    app.UseStaticFiles();   

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
                     Path.Combine(env.ContentRootPath, "MyStaticFiles")),
        RequestPath = "/StaticFiles"
    });

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
        services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddRazorPages();

        services.AddAuthorization(options =>
        {
            options.FallbackPolicy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
        });
    }

    // Remaining code ommitted for brevity.

위 코드에서 대체 권한 부여 정책은 모든 사용자를 인증하도록 요구합니다. 자체 권한 부여 요구 사항을 지정하는 컨트롤러, Razor Pages 등과 같은 엔드포인트는 대체 권한 부여 정책을 사용하지 않습니다. 예를 들어 [AllowAnonymous] 또는 [Authorize(PolicyName="MyPolicy")]가 있는 Razor Pages, 컨트롤러 또는 작업 메서드는 대체 권한 부여 정책이 아닌 적용된 권한 부여 특성을 사용합니다.

RequireAuthenticatedUserDenyAnonymousAuthorizationRequirement를 현재 인스턴스에 추가하여 현재 사용자가 인증될 것을 요구합니다.

기본 정적 파일 미들웨어(app.UseStaticFiles();)가 UseAuthentication 전에 호출되기 때문에 wwwroot 아래의 정적 자산은 공개적으로 액세스할 수 있습니다. MyStaticFiles 폴더의 정적 자산에는 인증이 필요합니다. 샘플 코드는 이를 보여 줍니다.

권한 부여를 기반으로 파일을 제공하는 대체 방법은 다음과 같습니다.

  • 파일을 wwwroot 외부의 정적 파일 미들웨어에 액세스할 수 있는 임의의 디렉터리에 저장합니다.
  • 권한 부여가 적용되는 작업 메서드를 통해 파일을 제공하고 FileResult 개체를 반환합니다.
[Authorize]
public IActionResult BannerImage()
{
    var filePath = Path.Combine(
        _env.ContentRootPath, "MyStaticFiles", "images", "red-rose.jpg");

    return PhysicalFile(filePath, "image/jpeg");
}

디렉터리 검색

디렉터리 검색을 통해 지정된 디렉터리 내에 디렉터리를 나열할 수 있습니다.

기본적으로 디렉터리 검색은 보안상의 이유로 사용하지 않도록 설정됩니다. 자세한 내용은 고려 사항을 참조하세요.

다음을 통해 디렉터리 검색을 사용하도록 설정합니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddDirectoryBrowser();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    // using Microsoft.Extensions.FileProviders;
    // using System.IO;
    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(env.WebRootPath, "images")),
        RequestPath = "/MyImages"
    });

    app.UseDirectoryBrowser(new DirectoryBrowserOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(env.WebRootPath, "images")),
        RequestPath = "/MyImages"
    });

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

위의 코드를 통해 URL https://<hostname>/MyImages를 사용하여 각 파일 및 폴더에 대한 링크가 제공되는 wwwroot/images 폴더의 디렉터리 검색을 사용할 수 있습니다.

디렉터리 검색

기본 문서 제공

기본 페이지를 설정하면 방문자에게 사이트의 시작 지점이 제공됩니다. 정규화된 URI 없이 wwwroot의 기본 페이지를 제공하려면 UseDefaultFiles 메서드를 호출합니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseDefaultFiles();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

기본 파일을 제공하려면 UseStaticFiles 전에 UseDefaultFiles를 먼저 호출해야 합니다. UseDefaultFiles는 파일을 제공하지 않는 URL 재작성기입니다.

UseDefaultFiles를 사용하면 wwwroot의 폴더에 대한 요청이 다음을 검색합니다.

  • default.htm
  • default.html
  • index.htm
  • index.html

목록에서 찾은 첫 번째 파일이 마치 요청이 정규화된 URI인 것처럼 제공됩니다. 브라우저 URL은 요청된 URI를 계속 반영합니다.

다음 코드는 기본 파일 이름을 mydefault.html 로 변경합니다.

var options = new DefaultFilesOptions();
options.DefaultFileNames.Clear();
options.DefaultFileNames.Add("mydefault.html");
app.UseDefaultFiles(options);
app.UseStaticFiles();

다음 코드는 위의 코드와 함께 Startup.Configure를 보여 줍니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    var options = new DefaultFilesOptions();
    options.DefaultFileNames.Clear();
    options.DefaultFileNames.Add("mydefault.html");
    app.UseDefaultFiles(options);
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

기본 문서의 UseFileServer

UseFileServerUseStaticFiles, UseDefaultFiles 및 선택적으로 UseDirectoryBrowser의 기능을 병행합니다.

app.UseFileServer를 호출하여 정적 파일 및 기본 파일을 제공할 수 있도록 합니다. 디렉터리 검색은 활성화되지 않습니다. 다음 코드는 UseFileServer와 함께 Startup.Configure를 보여 줍니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseFileServer();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

다음 코드를 사용하면 정적 파일, 기본 파일 및 디렉터리 검색을 제공할 수 있습니다.

app.UseFileServer(enableDirectoryBrowsing: true);

다음 코드는 위의 코드와 함께 Startup.Configure를 보여 줍니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseFileServer(enableDirectoryBrowsing: true);

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

다음 디렉터리 계층 구조를 고려해보세요.

  • wwwroot
    • css
    • images
    • js
  • MyStaticFiles
    • images
      • MyImage.jpg
    • default.html

다음 코드를 사용하면 MyStaticFiles의 정적 파일, 기본 파일 및 디렉터리 검색을 제공할 수 있습니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddDirectoryBrowser();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseStaticFiles(); // For the wwwroot folder.

    // using Microsoft.Extensions.FileProviders;
    // using System.IO;
    app.UseFileServer(new FileServerOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(env.ContentRootPath, "MyStaticFiles")),
        RequestPath = "/StaticFiles",
        EnableDirectoryBrowsing = true
    });

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

AddDirectoryBrowserEnableDirectoryBrowsing 속성 값이 true인 경우 호출해야 합니다.

URL은 파일 계층 구조 및 이전 코드를 사용하여 다음과 같이 확인합니다.

URI 응답
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles MyStaticFiles/default.html

MyStaticFiles 디렉터리에 기본 이름이 지정된 파일이 없는 경우 https://<hostname>/StaticFiles는 클릭 가능한 링크와 함께 디렉터리 목록을 반환합니다.

정적 파일 목록

UseDefaultFilesUseDirectoryBrowser는 후행 /가 없는 대상 URI에서 후행 /가 있는 대상 URI로 클라이언트 쪽 리디렉션을 수행합니다. 예를 들어 https://<hostname>/StaticFiles에서 https://<hostname>/StaticFiles/로 리디렉션합니다. StaticFiles 디렉터리 내의 상대 URL은 후행 슬래시(/)가 있어야 유효합니다.

FileExtensionContentTypeProvider

FileExtensionContentTypeProvider 클래스에는 MIME 콘텐츠 형식에 대한 파일 확장명 매핑 역할을 하는 Mappings 속성이 포함되어 있습니다. 다음 샘플에서는 여러 파일 확장명이 알려진 MIME 형식에 매핑됩니다. .rtf 확장명은 대체되며 .mp4 는 제거됩니다.

// using Microsoft.AspNetCore.StaticFiles;
// using Microsoft.Extensions.FileProviders;
// using System.IO;

// Set up custom content types - associating file extension to MIME type
var provider = new FileExtensionContentTypeProvider();
// Add new mappings
provider.Mappings[".myapp"] = "application/x-msdownload";
provider.Mappings[".htm3"] = "text/html";
provider.Mappings[".image"] = "image/png";
// Replace an existing mapping
provider.Mappings[".rtf"] = "application/x-msdownload";
// Remove MP4 videos.
provider.Mappings.Remove(".mp4");

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(env.WebRootPath, "images")),
    RequestPath = "/MyImages",
    ContentTypeProvider = provider
});

app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(env.WebRootPath, "images")),
    RequestPath = "/MyImages"
});

다음 코드는 위의 코드와 함께 Startup.Configure를 보여 줍니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    // using Microsoft.AspNetCore.StaticFiles;
    // using Microsoft.Extensions.FileProviders;
    // using System.IO;

    // Set up custom content types - associating file extension to MIME type
    var provider = new FileExtensionContentTypeProvider();
    // Add new mappings
    provider.Mappings[".myapp"] = "application/x-msdownload";
    provider.Mappings[".htm3"] = "text/html";
    provider.Mappings[".image"] = "image/png";
    // Replace an existing mapping
    provider.Mappings[".rtf"] = "application/x-msdownload";
    // Remove MP4 videos.
    provider.Mappings.Remove(".mp4");

    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(env.WebRootPath, "images")),
        RequestPath = "/MyImages",
        ContentTypeProvider = provider
    });

    app.UseDirectoryBrowser(new DirectoryBrowserOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(env.WebRootPath, "images")),
        RequestPath = "/MyImages"
    });

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

MIME 콘텐츠 형식을 참조하세요.

비표준 콘텐츠 형식

정적 파일 미들웨어는 거의 400가지의 알려진 파일 콘텐츠 형식을 이해합니다. 사용자가 알 수 없는 파일 형식의 파일을 요청하는 경우 정적 파일 미들웨어가 해당 요청을 파이프라인의 다음 미들웨어로 전달합니다. 요청을 처리한 미들웨어가 없으면 ‘404 찾을 수 없음’ 응답이 반환됩니다. 디렉터리 검색이 사용 가능한 경우 파일에 대한 링크가 디렉터리 목록에 표시됩니다.

다음 코드는 알 수 없는 형식 제공을 사용하도록 설정하고 알 수 없는 파일을 이미지로 렌더링합니다.

app.UseStaticFiles(new StaticFileOptions
{
    ServeUnknownFileTypes = true,
    DefaultContentType = "image/png"
});

다음 코드는 위의 코드와 함께 Startup.Configure를 보여 줍니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseStaticFiles(new StaticFileOptions
    {
        ServeUnknownFileTypes = true,
        DefaultContentType = "image/png"
    });

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

위의 코드를 사용하면 알 수 없는 콘텐츠 형식의 파일에 대한 요청은 이미지로 반환됩니다.

경고

ServeUnknownFileTypes를 사용하도록 설정하는 것은 보안상 위험합니다. 기본적으로 비활성화되어 있으며 사용은 권장되지 않습니다. FileExtensionContentTypeProvider는 비표준 확장명을 가진 파일을 제공하는 것보다 안전한 대체 방법을 제공합니다.

여러 위치에서 파일 제공

UseStaticFilesUseFileServer는 기본적으로 wwwroot를 가리키는 파일 공급자로 설정됩니다. 다른 위치에서 파일을 제공하기 위해 다른 파일 공급자와 UseStaticFilesUseFileServer의 추가 인스턴스를 제공할 수 있습니다. 자세한 내용은 이 GitHub 이슈를 참조하세요.

정적 파일의 보안 고려 사항

경고

UseDirectoryBrowserUseStaticFiles는 비밀 정보를 누출할 수 있습니다. 프로덕션 환경에서는 디렉터리 검색을 비활성화하는 것이 좋습니다. UseStaticFiles 또는 UseDirectoryBrowser를 통해 어떤 디렉터리가 활성화되었는지 주의 깊게 검토하세요. 전체 디렉터리와 해당 하위 디렉터리는 공개적으로 액세스할 수 있습니다. <content_root>/wwwroot와 같은 전용 디렉터리에 공개적으로 제공하는 데 적합한 파일을 저장합니다. MVC 뷰, Razor Pages, 구성 파일 등과 해당 파일을 분리합니다.

  • UseDirectoryBrowserUseStaticFiles로 노출된 콘텐츠에 대한 URL은 기본 파일 시스템의 대/소문자 구분 및 문자 제한이 적용됩니다. 예를 들어 Windows는 대/소문자를 구분하지 않지만 macOS 및 Linux는 대/소문자를 구분합니다.

  • IIS에서 호스팅되는 ASP.NET Core 앱은 ASP.NET Core 모듈을 사용하여 정적 파일 요청을 비롯한 모든 요청을 앱에 전달합니다. IIS 정적 파일 처리기는 사용되지 않으며 요청을 처리할 수 없습니다.

  • 서버 또는 웹 사이트 수준에서 IIS 정적 파일 처리기를 제거하려면 IIS 관리자에서 다음 단계를 완료합니다.

    1. 모듈 기능으로 이동합니다.
    2. 목록에서 StaticFileModule 을 선택합니다.
    3. 동작 사이드바에서 제거 를 클릭합니다.

경고

IIS 정적 파일 처리기를 사용하도록 설정되었으며 그리고 ASP.NET Core 모듈이 올바르게 구성되지 않은 경우, 정적 파일이 제공됩니다. 예를 들어 web.config 파일이 배포되지 않았을 수 있습니다.

  • .cs.cshtml 을 포함한 코드 파일을 앱 프로젝트의 웹 루트 외부에 배치합니다. 따라서 논리적 분리가 앱의 클라이언트 쪽 콘텐츠 및 서버 기반 코드 사이에 만들어집니다. 그러면 서버 쪽 코드가 유출되지 않습니다.

추가 자료

작성자: Rick AndersonScott Addie

HTML, CSS, 이미지 및 JavaScript와 같은 정적 파일은 ASP.NET Core 앱이 클라이언트에 직접 제공하는 자산입니다. 일부 구성은 이러한 파일을 제공하는 데 필수적입니다.

예제 코드 살펴보기 및 다운로드 (다운로드 방법)

정적 파일 제공

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

앱의 웹 호스트에서 콘텐츠 루트 디렉터리를 인식하도록 해야 합니다.

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

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

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

  • wwwroot
    • css
    • images
    • js

images 하위 폴더에 있는 파일에 액세스하기 위한 URI 형식은 http://<server_address>/images/<image_file_name> 입니다. 예를 들어 http://localhost:9189/images/banner3.svg 와 같습니다.

.NET Framework를 대상으로 하는 경우 Microsoft.AspNetCore.StaticFiles 패키지를 프로젝트에 추가합니다. .NET Core를 대상으로 하는 경우 Microsoft.AspNetCore.App 메타패키지에 이 패키지가 포함되어 있습니다.

정적 파일을 제공할 수 있도록 미들웨어를 구성합니다.

웹 루트 내부의 파일 제공

Startup.Configure 내에서 UseStaticFiles 메서드를 호출합니다.

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();
}

매개 변수가 없는 UseStaticFiles 메서드 오버로드는 웹 루트에 있는 파일을 제공 가능으로 표시합니다. 다음 표시는 wwwroot/images/banner1.svg 를 참조합니다.

<img src="~/images/banner1.svg" alt="ASP.NET" class="img-responsive" />

위의 코드에서 물결표 문자 ~/웹 루트를 가리킵니다.

웹 루트 외부의 파일 제공

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

  • wwwroot
    • css
    • images
    • js
  • MyStaticFiles
    • images
      • banner1.svg

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

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles(); // For the wwwroot folder

    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
        RequestPath = "/StaticFiles"
    });
}

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

다음 태그는 MyStaticFiles/images/banner1.svg 를 참조합니다.

<img src="~/StaticFiles/images/banner1.svg" alt="ASP.NET" class="img-responsive" />

HTTP 응답 헤더 설정

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

public void Configure(IApplicationBuilder app)
{
    var cachePeriod =  "604800";
    app.UseStaticFiles(new StaticFileOptions
    {
        OnPrepareResponse = ctx =>
        {
            // Requires the following import:
            // using Microsoft.AspNetCore.Http;
            ctx.Context.Response.Headers.Append("Cache-Control", $"public, max-age={cachePeriod}");
        }
    });
}

영어 이외의 언어로 번역된 코드 주석을 보려면 이 GitHub 토론 이슈에서 알려주세요.

HeaderDictionaryExtensions.Append 메서드는 Microsoft.AspNetCore.Http 패키지에 있습니다.

개발 환경에서 파일은 10분(600초) 동안 공개적으로 캐시할 수 있습니다.

추가된 캐시 제어 헤더를 보여 주는 응답 헤더

정적 파일 권한 부여

정적 파일 미들웨어는 권한 부여 검사를 제공하지 않습니다. wwwroot 아래의 항목을 비롯한 제공되는 모든 파일은 공개적으로 사용할 수 있습니다. 권한 부여를 기반으로 파일을 제공하려면 다음을 수행합니다.

  • 파일을 wwwroot 외부의 정적 파일 미들웨어에 액세스할 수 있는 임의의 디렉터리에 저장합니다.

  • 권한 부여가 적용되는 작업 메서드를 통해서 파일을 제공합니다. FileResult 개체를 반환합니다.

    [Authorize]
    public IActionResult BannerImage()
    {
        var file = Path.Combine(Directory.GetCurrentDirectory(), 
                                "MyStaticFiles", "images", "banner1.svg");
    
        return PhysicalFile(file, "image/svg+xml");
    }
    

디렉터리 검색 사용

디렉터리 검색을 사용하면 웹앱 사용자가 지정된 디렉터리 내의 디렉터리 목록 및 파일을 볼 수 있습니다. 기본적으로 디렉터리 검색은 보안상의 이유로 비활성화되어 있습니다(고려할 사항 참조). Startup.Configure에서 UseDirectoryBrowser 메서드를 호출하여 디렉터리 검색을 사용하도록 설정합니다.

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles(); // For the wwwroot folder

    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
        RequestPath = "/MyImages"
    });

    app.UseDirectoryBrowser(new DirectoryBrowserOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
        RequestPath = "/MyImages"
    });
}

Startup.ConfigureServices에서 AddDirectoryBrowser 메서드를 호출하여 필요한 서비스를 추가합니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDirectoryBrowser();
}

위의 코드를 통해 URL http://<server_address>/MyImages 를 사용하여 각 파일 및 폴더에 대한 링크가 제공되는 wwwroot/images 폴더의 디렉터리 검색을 사용할 수 있습니다.

디렉터리 검색

검색을 활성화하는 경우의 보안 위험에 대한 고려할 사항을 참조하세요.

다음 예제에서 두 UseStaticFiles 호출을 참고하세요. 첫 번째 호출은 wwwroot 폴더에서 정적 파일 제공을 사용하도록 설정합니다. 두 번째 호출은 URL http://<server_address>/MyImages 를 사용하여 wwwroot/images 폴더의 디렉터리 검색을 사용하도록 설정합니다.

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles(); // For the wwwroot folder

    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
        RequestPath = "/MyImages"
    });

    app.UseDirectoryBrowser(new DirectoryBrowserOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
        RequestPath = "/MyImages"
    });
}

기본 문서 제공

기본 홈페이지를 설정하면 방문자가 사이트를 방문할 때 논리적 시작점을 제공합니다. 사용자가 URI를 정규화하지 않더라도 기본 페이지를 제공하려면 Startup.Configure에서 UseDefaultFiles 메서드를 호출합니다.

public void Configure(IApplicationBuilder app)
{
    app.UseDefaultFiles();
    app.UseStaticFiles();
}

중요

기본 파일을 제공하려면 UseStaticFiles 전에 UseDefaultFiles를 먼저 호출해야 합니다. UseDefaultFiles는 실제로 파일을 제공하지 않는 URL 재작성기입니다. 파일을 제공하도록 UseStaticFiles를 통해 정적 파일 미들웨어를 활성화합니다.

UseDefaultFiles를 사용하면 폴더에 대한 요청이 다음 파일들을 검색합니다.

  • default.htm
  • default.html
  • index.htm
  • index.html

목록에서 찾은 첫 번째 파일이 마치 요청이 정규화된 URI인 것처럼 제공됩니다. 브라우저 URL은 요청된 URI를 계속 반영합니다.

다음 코드는 기본 파일 이름을 mydefault.html 로 변경합니다.

public void Configure(IApplicationBuilder app)
{
    // Serve my app-specific default file, if present.
    DefaultFilesOptions options = new DefaultFilesOptions();
    options.DefaultFileNames.Clear();
    options.DefaultFileNames.Add("mydefault.html");
    app.UseDefaultFiles(options);
    app.UseStaticFiles();
}

UseFileServer

UseFileServerUseStaticFiles, UseDefaultFiles 및 선택적으로 UseDirectoryBrowser의 기능을 병행합니다.

다음 코드는 정적 파일 및 기본 파일 제공을 활성화합니다. 디렉터리 검색은 활성화되지 않습니다.

app.UseFileServer();

다음 코드는 매개 변수가 없는 오버로드에 추가적으로 디렉터리 검색을 활성화하여 빌드합니다.

app.UseFileServer(enableDirectoryBrowsing: true);

다음 디렉터리 계층 구조를 고려해보세요.

  • wwwroot
    • css
    • images
    • js
  • MyStaticFiles
    • images
      • banner1.svg
    • default.html

다음 코드는 MyStaticFiles의 정적 파일, 기본 파일 및 디렉터리 검색을 활성화합니다.

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles(); // For the wwwroot folder

    app.UseFileServer(new FileServerOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
        RequestPath = "/StaticFiles",
        EnableDirectoryBrowsing = true
    });
}

EnableDirectoryBrowsing 속성 값이 true인 경우 AddDirectoryBrowser를 호출해야 합니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDirectoryBrowser();
}

URL은 파일 계층 구조 및 이전 코드를 사용하여 다음과 같이 확인합니다.

URI 응답
http://<server_address>/StaticFiles/images/banner1.svg MyStaticFiles/images/banner1.svg
http://<server_address>/StaticFiles MyStaticFiles/default.html

MyStaticFiles 디렉터리에 기본 이름이 지정된 파일이 없는 경우 http://<server_address>/StaticFiles 는 클릭 가능한 링크와 함께 디렉터리 목록을 반환합니다.

정적 파일 목록

참고

UseDefaultFilesUseDirectoryBrowserhttp://{SERVER ADDRESS}/StaticFiles(후행 슬래시 없음)에서 http://{SERVER ADDRESS}/StaticFiles/(후행 슬래시 있음)로 클라이언트 쪽 리디렉션을 수행합니다. StaticFiles 디렉터리 내의 상대 URL은 후행 슬래시가 있어야 유효합니다.

FileExtensionContentTypeProvider

FileExtensionContentTypeProvider 클래스에는 MIME 콘텐츠 형식에 대한 파일 확장명 매핑 역할을 하는 Mappings 속성이 포함되어 있습니다. 다음 샘플에서는 여러 파일 확장명이 알려진 MIME 형식에 등록됩니다. .rtf 확장은 대체되며 .mp4 는 제거됩니다.

public void Configure(IApplicationBuilder app)
{
    // Set up custom content types - associating file extension to MIME type
    var provider = new FileExtensionContentTypeProvider();
    // Add new mappings
    provider.Mappings[".myapp"] = "application/x-msdownload";
    provider.Mappings[".htm3"] = "text/html";
    provider.Mappings[".image"] = "image/png";
    // Replace an existing mapping
    provider.Mappings[".rtf"] = "application/x-msdownload";
    // Remove MP4 videos.
    provider.Mappings.Remove(".mp4");

    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
        RequestPath = "/MyImages",
        ContentTypeProvider = provider
    });

    app.UseDirectoryBrowser(new DirectoryBrowserOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
        RequestPath = "/MyImages"
    });
}

MIME 콘텐츠 형식을 참조하세요.

사용자 지정 FileExtensionContentTypeProvider를 사용하거나 Blazor Server 앱에서 다른 StaticFileOptions를 구성하는 방법에 대한 자세한 내용은 ASP.NET Core Blazor 정적 파일를 참조하세요.

비표준 콘텐츠 형식

정적 파일 미들웨어는 거의 400가지의 알려진 파일 콘텐츠 형식을 이해합니다. 사용자가 알 수 없는 파일 형식의 파일을 요청하는 경우 정적 파일 미들웨어가 해당 요청을 파이프라인의 다음 미들웨어로 전달합니다. 요청을 처리한 미들웨어가 없으면 ‘404 찾을 수 없음’ 응답이 반환됩니다. 디렉터리 검색이 사용 가능한 경우 파일에 대한 링크가 디렉터리 목록에 표시됩니다.

다음 코드는 알 수 없는 형식 제공을 사용하도록 설정하고 알 수 없는 파일을 이미지로 렌더링합니다.

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles(new StaticFileOptions
    {
        ServeUnknownFileTypes = true,
        DefaultContentType = "image/png"
    });
}

위의 코드를 사용하면 알 수 없는 콘텐츠 형식의 파일에 대한 요청은 이미지로 반환됩니다.

경고

ServeUnknownFileTypes를 사용하도록 설정하는 것은 보안상 위험합니다. 기본적으로 비활성화되어 있으며 사용은 권장되지 않습니다. FileExtensionContentTypeProvider는 비표준 확장명을 가진 파일을 제공하는 것보다 안전한 대체 방법을 제공합니다.

여러 위치에서 파일 제공

UseStaticFilesUseFileServer은(는) wwwroot 를 가리키는 파일 공급자를 기본값으로 설정합니다. 다른 위치에서 파일을 제공하기 위해 다른 파일 공급자와 UseStaticFilesUseFileServer의 추가 인스턴스를 제공할 수 있습니다. 자세한 내용은 이 GitHub 이슈를 참조하세요.

고려 사항

경고

UseDirectoryBrowserUseStaticFiles는 비밀 정보를 누출할 수 있습니다. 프로덕션 환경에서는 디렉터리 검색을 비활성화하는 것이 좋습니다. UseStaticFiles 또는 UseDirectoryBrowser를 통해 어떤 디렉터리가 활성화되었는지 주의 깊게 검토하세요. 전체 디렉터리와 해당 하위 디렉터리는 공개적으로 액세스할 수 있습니다. <content_root>/wwwroot 와 같은 전용 디렉터리에 공개적으로 제공하는 데 적합한 파일을 저장하세요. MVC 뷰, Razor Pages(2.x에만 해당), 구성 파일 등과 해당 파일을 분리하세요.

  • UseDirectoryBrowserUseStaticFiles로 노출된 콘텐츠에 대한 URL은 기본 파일 시스템의 대/소문자 구분 및 문자 제한이 적용됩니다. 예를 들어 Windows는 대/소문자를 구분하지 않는 반면 macOS 및 Linux는 그렇지 않습니다.

  • IIS에서 호스팅되는 ASP.NET Core 앱은 ASP.NET Core 모듈을 사용하여 정적 파일 요청을 비롯한 모든 요청을 앱에 전달합니다. IIS 정적 파일 처리기는 사용되지 않습니다. 모듈에서 처리하기 전에는 요청을 처리할 수 없습니다.

  • 서버 또는 웹 사이트 수준에서 IIS 정적 파일 처리기를 제거하려면 IIS 관리자에서 다음 단계를 완료합니다.

    1. 모듈 기능으로 이동합니다.
    2. 목록에서 StaticFileModule 을 선택합니다.
    3. 동작 사이드바에서 제거 를 클릭합니다.

경고

IIS 정적 파일 처리기를 사용하도록 설정되었으며 그리고 ASP.NET Core 모듈이 올바르게 구성되지 않은 경우, 정적 파일이 제공됩니다. 예를 들어 web.config 파일이 배포되지 않았을 수 있습니다.

  • 코드 파일( .cs.cshtml 포함)을 앱 프로젝트의 웹 루트 외부에 배치합니다. 따라서 논리적 분리가 앱의 클라이언트 쪽 콘텐츠 및 서버 기반 코드 사이에 만들어집니다. 그러면 서버 쪽 코드가 유출되지 않습니다.

추가 자료