ASP.NET Core 호스트 및 배포 Blazor WebAssembly

이 문서에서는 ASP.NET Core, CDN(콘텐츠 배달 네트워크), 파일 서버, GitHub 페이지를 사용하여 Blazor WebAssembly를 호스트하고 배포하는 방법을 설명합니다.

Blazor WebAssembly 호스팅 모델을 사용하면 다음과 같이 실행됩니다.

  • Blazor 앱, 해당 앱의 종속성 및 .NET 런타임이 병렬로 브라우저에 다운로드됩니다.
  • 해당 앱은 브라우저 UI 스레드에서 직접 실행됩니다.

다음 배포 전략이 지원됩니다.

  • Blazor 앱은 ASP.NET Core 앱에서 제공됩니다. 이 전략은 ASP.NET Core를 사용하여 호스트된 배포 섹션에서 설명합니다.
  • Blazor 앱은 정적 호스팅 웹 서버 또는 서비스에 배치되며, 이 경우 Blazor 앱을 처리하기 위해 .NET을 사용하지 않습니다. 이 전략은 Blazor WebAssembly 앱을 IIS 하위 앱으로 호스트하는 방법에 대한 정보를 포함하는 독립 실행형 배포 섹션에서 설명합니다.

AOT(Ahead-Of-Time) 컴파일

Blazor WebAssembly는 .NET 코드를 WebAssembly로 직접 컴파일할 수 있는 AOT(ahead-of-time) 컴파일을 지원합니다. AOT 컴파일을 수행하면 앱 크기가 커지는 대신 런타임 성능이 향상됩니다.

AOT 컴파일을 사용하도록 설정하지 않으면 Blazor WebAssembly 앱은 WebAssembly에서 구현된 .NET IL(중간 언어) 인터프리터를 사용하여 브라우저에서 실행됩니다. .NET 코드가 해석되기 때문에 앱은 일반적으로 서버 쪽 .NET JIT(Just-In-Time) 런타임보다 느리게 실행됩니다. AOT 컴파일은 브라우저에서 기본 WebAssembly가 실행되도록 앱의 .NET 코드를 WebAssembly로 직접 컴파일하여 이 성능 문제를 해결합니다. AOT 성능 향상은 CPU 집약적인 작업을 실행하는 앱을 크게 향상할 수 있습니다. AOT 컴파일을 사용하는 경우의 단점은 AOT로 컴파일된 앱이 일반적으로 IL로 해석된 앱보다 크기 때문에 처음 요청 시 클라이언트에 다운로드하는 데 시간이 더 오래 걸린다는 것입니다.

.NET WebAssembly 빌드 도구를 설치하는 방법에 대한 지침은 ASP.NET Core Blazor용 도구를 참조하세요.

WebAssembly AOT 컴파일을 사용하도록 설정하려면 true로 설정된 <RunAOTCompilation> 속성을 Blazor WebAssembly 앱의 프로젝트 파일에 추가합니다.

<PropertyGroup>
  <RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>

앱을 WebAssembly로 컴파일하려면 앱을 게시합니다. Release 구성을 게시하면 게시된 앱의 크기를 줄일 수 있도록 .NET IL(중간 언어) 연결도 실행됩니다.

dotnet publish -c Release

WebAssembly AOT 컴파일은 프로젝트가 게시될 때만 수행됩니다. AOT 컴파일은 일반적으로 작은 프로젝트에서 몇 분 정도 걸리고 더 큰 프로젝트에서는 훨씬 더 오래 걸릴 수 있으므로, 개발(Development 환경) 중에 프로젝트가 실행될 때는 AOT 컴파일이 사용되지 않습니다. ASP.NET Core의 향후 릴리스를 위해 AOT 컴파일을 위한 빌드 시간 단축이 개발 중입니다.

AOT로 컴파일된 Blazor WebAssembly 앱의 크기는 일반적으로 .NET IL로 컴파일된 경우의 앱보다 큽니다.

  • 크기 차이는 앱에 따라 다르지만 대부분의 AOT로 컴파일된 앱은 IL로 컴파일된 버전의 약 두 배입니다. 즉, AOT 컴파일을 사용하면 로드 성능이 저하되는 대신 런타임 성능이 향상됩니다. 이 득실을 감안하며 AOT 컴파일을 사용할 가치가 있는지는 앱에 따라 달라집니다. CPU를 많이 사용하는 Blazor WebAssembly 앱이 일반적으로 AOT 컴파일의 이점을 가장 많이 누립니다.

  • AOT 컴파일 앱은 다음 두 가지 조건 때문에 크기가 증가합니다.

    • 네이티브 WebAssembly의 상위 수준 .NET IL 지침을 나타내려면 더 많은 코드가 필요합니다.
    • AOT는 앱이 게시될 때 관리되는 DLL을 자르지 않습니다. Blazor는 리플렉션 메타데이터에 대한 DLL이 필요하고 특정 .NET 런타임 기능을 지원해야 합니다. 클라이언트에서 DLL을 요구하면 다운로드 크기가 증가하지만 보다 호환성 높은 .NET 환경을 제공합니다.

참고

Mono/WebAssembly MSBuild 속성 및 대상은 WasmApp.targets(dotnet/runtime GitHub 리포지토리)를 참조하세요. 일반적인 MSBuild 속성에 대한 공식 설명서는 문서 blazor msbuild 구성 옵션(dotnet/docs #27395)따라 계획됩니다.

런타임 다시 링크

Blazor WebAssembly 앱의 가장 큰 부분 중 하나는 사용자의 브라우저에서 앱에 처음 액세스할 때 브라우저가 다운로드해야 하는 WebAssembly 기반 .NET 런타임(dotnet.wasm)입니다. .NET WebAssembly 런타임을 다시 링크하면 사용되지 않는 런타임 코드가 삭제되므로 다운로드 속도가 향상됩니다.

런타임 다시 링크를 사용하려면 .NET WebAssembly 빌드 도구를 설치해야 합니다. 자세한 내용은 ASP.NET Core Blazor 도구를 참조하세요.

.NET WebAssembly 빌드 도구를 설치하면 앱이 Release 구성에 게시될 때 런타임 다시 연결이 자동으로 수행됩니다. 크기 감소는 세계화를 사용하지 않도록 설정할 때 특히 효과가 큽니다. 자세한 내용은 ASP.NET Core Blazor 세계화 및 지역화를 참조하세요.

부팅 리소스를 로드하는 방법 사용자 지정

loadBootResource API를 사용하여 부팅 리소스를 로드하는 방법 사용자 지정 자세한 내용은 ASP.NET Core Blazor 시작을 참조하세요.

압축

Blazor WebAssembly 앱이 게시될 때 게시하는 도중에 출력을 정적으로 압축하여 앱의 크기를 줄이고 런타임 압축의 오버헤드를 제거합니다. 다음 압축 알고리즘이 사용됩니다.

Blazor는 호스트를 사용하여 적절한 압축 파일을 제공합니다. ASP.NET Core 호스트 프로젝트를 사용하는 경우 호스트 프로젝트는 콘텐츠 협상을 수행하고 정적으로 압축된 파일을 제공할 수 있습니다. Blazor WebAssembly 독립 실행형 앱을 호스트하는 경우 정적으로 압축된 파일이 제공되도록 추가 작업이 필요할 수도 있습니다.

  • IIS web.config 압축 구성에 대해서는 IIS: Brotli 및 Gzip 압축 섹션을 참조하세요.

  • GitHub 페이지와 같이 정적으로 압축된 파일 콘텐츠 협상을 지원하지 않는 정적 호스팅 솔루션에서 호스트하는 경우 Brotli로 압축된 파일을 가져와 디코딩하는 앱을 구성하는 것이 좋습니다.

    • google/brotli GitHub 리포지토리에서 JavaScript Brotli 디코더를 가져옵니다. 축소된 디코더 파일은 이름이 decode.min.js이며 리포지토리의 js 폴더에 있습니다.

      참고

      decode.js 스크립트(decode.min.js)의 축소된 버전이 실패하는 경우 축소 해제한 버전(decode.js)을 대신 사용해 보세요.

    • 디코더를 사용하도록 앱을 업데이트합니다.

      wwwroot/index.html 파일의 Blazor의 <script> 태그에서 autostartfalse로 설정합니다.

      <script src="_framework/blazor.webassembly.js" autostart="false"></script>
      

      Blazor의 <script> 태그 뒤, 닫는 </body> 태그 앞에 다음 JavaScript 코드 <script> 블록을 추가합니다.

      <script type="module">
        import { BrotliDecode } from './decode.min.js';
        Blazor.start({
          loadBootResource: function (type, name, defaultUri, integrity) {
            if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
              return (async function () {
                const response = await fetch(defaultUri + '.br', { cache: 'no-cache' });
                if (!response.ok) {
                  throw new Error(response.statusText);
                }
                const originalResponseBuffer = await response.arrayBuffer();
                const originalResponseArray = new Int8Array(originalResponseBuffer);
                const decompressedResponseArray = BrotliDecode(originalResponseArray);
                const contentType = type === 
                  'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
                return new Response(decompressedResponseArray, 
                  { headers: { 'content-type': contentType } });
              })();
            }
          }
        });
      </script>
      

      부팅 리소스를 로드하는 방법에 대한 자세한 내용은 ASP.NET Core Blazor 시작을 참조하세요.

압축을 해제하려면 앱의 프로젝트 파일에 BlazorEnableCompression MSBuild 속성을 추가하고 값을 false로 설정합니다.

<PropertyGroup>
  <BlazorEnableCompression>false</BlazorEnableCompression>
</PropertyGroup>

명령 셸에서 다음 구문을 사용하여 BlazorEnableCompression 속성을 dotnet publish 명령에 전달할 수 있습니다.

dotnet publish -p:BlazorEnableCompression=false

올바른 라우팅을 위해 URL 다시 생성

Blazor WebAssembly 앱의 페이지 구성 요소에 대한 라우팅 요청은 Blazor Server에서 호스트된 앱의 요청을 라우팅하는 것처럼 간단하지 않습니다. 다음 두 구성 요소를 사용하는 Blazor WebAssembly 앱을 살펴보겠습니다.

  • Main.razor: 앱의 루트에 로드되며 About 구성 요소에 대한 링크(href="About")를 포함합니다.
  • About.razor: About 구성 요소입니다.

브라우저의 주소 표시줄을 사용하여 앱의 기본 문서를 요청하는 경우(예: https://www.contoso.com/):

  1. 브라우저가 요청을 합니다.
  2. 기본 페이지(일반적으로 index.html)가 반환됩니다.
  3. index.html이 앱을 부트스트랩합니다.
  4. Blazor의 라우터가 로드되고 RazorMain 구성 요소가 렌더링됩니다.

Blazor 라우터는 브라우저가 인터넷에서 www.contoso.com으로 About을 요청하는 것을 중단하고 렌더링된 About 구성 요소를 직접 제공하므로 기본 페이지에서 About 구성 요소에 대한 링크 선택은 클라이언트에서 작동합니다. ‘Blazor WebAssembly 앱 내’의 내부 엔드포인트에 대한 모든 요청도 같은 방법으로 작동합니다. 요청은 인터넷상에서 서버가 호스트하는 리소스에 대한 브라우저 기반 요청을 트리거하지 않습니다. 라우터가 내부적으로 요청을 처리합니다.

브라우저의 주소 표시줄을 사용하여 www.contoso.com/About을 요청하면 해당 요청이 실패합니다. 앱의 인터넷 호스트에 해당 리소스가 없으므로 404 - 찾을 수 없음 응답이 반환됩니다.

브라우저는 인터넷 기반 호스트에 클라이언트 쪽 페이지를 요청하므로, 웹 서버 및 호스팅 서비스는 서버에 실제로 존재하지 않는 리소스에 대한 모든 요청을 index.html 페이지에 다시 써야 합니다. index.html이 반환되는 경우 앱의 Blazor 라우터가 넘겨받아 올바른 리소스로 응답합니다.

IIS 서버에 배포하는 경우 앱의 게시된 web.config 파일과 함께 URL 재작성 모듈을 사용할 수 있습니다. 자세한 내용은 IIS 섹션을 참조하세요.

ASP.NET Core를 사용하여 호스트된 배포

‘호스트된 배포’는 웹 서버에서 실행되는 ASP.NET Core 앱에서 Blazor WebAssembly 앱을 브라우저에 제공하지 않습니다.

클라이언트 Blazor WebAssembly 앱이 서버 앱의 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 폴더에 서버 앱의 다른 정적 웹 자산과 함께 게시됩니다. 두 앱이 함께 배포됩니다. ASP.NET Core 앱을 호스트할 수 있는 웹 서버가 필요합니다. 호스트된 배포의 경우 Visual Studio는 Hosted (dotnet new 명령을 사용하는 경우 -ho|--hosted) 옵션이 선택된 Blazor WebAssembly 앱 프로젝트 템플릿(dotnet new 명령을 사용하는 경우 blazorwasm 템플릿)을 포함합니다.

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

여러 Blazor WebAssembly 앱을 사용하여 호스트된 배포

앱 구성

호스트된 Blazor 솔루션은 여러 Blazor WebAssembly 앱을 제공할 수 있습니다.

참고

이 섹션의 예제는 Visual Studio 솔루션 사용을 참조하지만 호스트된 Blazor WebAssembly 앱 시나리오에서 여러 클라이언트 앱이 작동하기 위해 Visual Studio 및 Visual Studio 솔루션을 사용하지 않아도 됩니다. Visual Studio를 사용하지 않는 경우 {SOLUTION NAME}.sln 파일 및 Visual Studio용으로 만든 다른 파일을 무시하세요.

다음 예제에서는

  • 초기(첫 번째) 클라이언트 앱은 Blazor WebAssembly 프로젝트 템플릿에서 만든 솔루션의 기본 클라이언트 프로젝트입니다. 첫 번째 클라이언트 앱은 포트가 5001이거나 호스트가 firstapp.com인 URL /FirstApp에서 브라우저로 액세스할 수 있습니다.
  • 두 번째 클라이언트 앱은 SecondBlazorApp.Client 솔루션에 추가됩니다. 두 번째 클라이언트 앱은 포트가 5002이거나 호스트가 secondapp.com인 URL /SecondApp에서 브라우저로 액세스할 수 있습니다.

호스트된 기존 Blazor 솔루션을 사용하거나 Blazor 호스트 프로젝트 템플릿에서 새 솔루션을 만듭니다.

  • 클라이언트 앱의 프로젝트 파일에서 값이 FirstApp<PropertyGroup><StaticWebAssetBasePath> 속성을 추가하여 프로젝트의 정적 자산에 대한 기본 경로를 설정합니다.

    <PropertyGroup>
      ...
      <StaticWebAssetBasePath>FirstApp</StaticWebAssetBasePath>
    </PropertyGroup>
    
  • 두 번째 클라이언트 앱을 솔루션에 추가합니다.

    • SecondClient라는 폴더를 솔루션의 폴더에 추가합니다. 프로젝트 템플릿에서 만든 솔루션 폴더에는 SecondClient 폴더 추가 후 다음 솔루션 파일 및 폴더가 포함됩니다.

      • Client(폴더)
      • SecondClient(폴더)
      • Server(폴더)
      • Shared(폴더)
      • {SOLUTION NAME}.sln(파일)

      자리 표시자 {SOLUTION NAME}은 솔루션의 이름입니다.

    • Blazor WebAssembly 프로젝트 템플릿의 SecondClient 폴더에 SecondBlazorApp.Client라는 Blazor WebAssembly 앱을 만듭니다.

    • SecondBlazorApp.Client 앱의 프로젝트 파일에서 다음을 수행합니다.

      • SecondApp 값을 사용하여 <PropertyGroup><StaticWebAssetBasePath> 속성을 추가합니다.

        <PropertyGroup>
          ...
          <StaticWebAssetBasePath>SecondApp</StaticWebAssetBasePath>
        </PropertyGroup>
        
      • Shared 프로젝트에 프로젝트 참조를 추가합니다.

        <ItemGroup>
          <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
        </ItemGroup>
        

        자리 표시자 {SOLUTION NAME}은 솔루션의 이름입니다.

  • 서버 앱의 프로젝트 파일에서 추가된 SecondBlazorApp.Client 클라이언트 앱에 대한 프로젝트 참조를 만듭니다.

    <ItemGroup>
      <ProjectReference Include="..\Client\{SOLUTION NAME}.Client.csproj" />
      <ProjectReference Include="..\SecondClient\SecondBlazorApp.Client.csproj" />
      <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
    </ItemGroup>
    

    자리 표시자 {SOLUTION NAME}은 솔루션의 이름입니다.

  • 서버 앱의 Properties/launchSettings.json 파일에서 포트 5001 및 5002의 클라이언트 앱에 액세스하도록 Kestrel 프로필({SOLUTION NAME}.Server)의 applicationUrl을 구성합니다.

    "applicationUrl": "https://localhost:5001;https://localhost:5002",
    
  • 서버 앱의 Program.cs 파일에서 UseHttpsRedirection에 대한 호출 뒤에 표시되는 다음 줄을 제거합니다.

    -app.UseBlazorFrameworkFiles();
    -app.UseStaticFiles();
    
    -app.UseRouting();
    
    -app.MapRazorPages();
    -app.MapControllers();
    -app.MapFallbackToFile("index.html");
    

    클라이언트 앱에 요청을 매핑하는 미들웨어를 추가합니다. 다음 예제는 다음 경우에 실행되도록 미들웨어를 구성합니다.

    • 요청 포트가 원래 클라이언트 앱의 경우 5001이고 추가된 클라이언트 앱의 경우 5002입니다.

    • 요청 호스트가 원래 클라이언트 앱의 경우 firstapp.com이고 추가된 클라이언트 앱의 경우 secondapp.com입니다.

      참고

      이 섹션에 표시된 예제에는 다음에 대한 추가 구성이 필요합니다.

      • 예제 호스트 도메인, firstapp.comsecondapp.com에 있는 앱에 액세스.
      • TLS 보안(HTTPS)을 사용하도록 설정하는 클라이언트 앱 인증서.

      필요한 구성은 이 문서의 범위를 벗어나며 솔루션이 호스트되는 방법에 따라 달라집니다. 자세한 내용은 호스트 및 배포 문서를 참조하세요.

    이전에 줄이 제거된 곳에 다음 코드를 넣습니다.

    app.MapWhen(ctx => ctx.Request.Host.Port == 5001 || 
        ctx.Request.Host.Equals("firstapp.com"), first =>
    {
        first.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/FirstApp" + ctx.Request.Path;
            return nxt();
        });
    
        first.UseBlazorFrameworkFiles("/FirstApp");
        first.UseStaticFiles();
        first.UseStaticFiles("/FirstApp");
        first.UseRouting();
    
        first.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/FirstApp/{*path:nonfile}", 
                "FirstApp/index.html");
        });
    });
    
    app.MapWhen(ctx => ctx.Request.Host.Port == 5002 || 
        ctx.Request.Host.Equals("secondapp.com"), second =>
    {
        second.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/SecondApp" + ctx.Request.Path;
            return nxt();
        });
    
        second.UseBlazorFrameworkFiles("/SecondApp");
        second.UseStaticFiles();
        second.UseStaticFiles("/SecondApp");
        second.UseRouting();
    
        second.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/SecondApp/{*path:nonfile}", 
                "SecondApp/index.html");
        });
    });
    

    UseStaticFiles에 대한 자세한 내용은 ASP.NET Core Blazor 정적 파일을 참조하세요.

    UseBlazorFrameworkFilesMapFallbackToFile에 대한 내용은 다음 리소스를 참조하세요.

    참고

    ASP.NET Core 참조 원본에 대한 설명서 링크는 ASP.NET Core의 다음 릴리스를 위한 제품 단위의 현재 개발을 나타내는 리포지토리의 main 분기를 로드합니다. 다른 릴리스에 대한 분기를 선택하려면 Switch branches or tags(분기 또는 태그 전환) 드롭다운 목록을 사용하여 분기를 선택합니다. 예를 들어 release/6.0 ASP.NET Core 6.0 릴리스에 대한 분기를 선택합니다.

  • 서버 앱의 일기 예보 컨트롤러(Controllers/WeatherForecastController.cs)에서 기존 WeatherForecastController 경로([Route("[controller]")])를 다음 경로로 바꿉니다.

    [Route("FirstApp/[controller]")]
    [Route("SecondApp/[controller]")]
    

    이전에 서버 앱의 요청 처리 파이프라인에 추가된 미들웨어는 /WeatherForecast로 들어오는 요청을 포트(5001/5002) 또는 도메인(firstapp.com/secondapp.com)에 따라 /FirstApp/WeatherForecast 또는 /SecondApp/WeatherForecast로 수정합니다. 서버 앱에서 클라이언트 앱으로 날씨 데이터를 반환하려면 이전 컨트롤러 경로가 필요합니다.

여러 Blazor WebAssembly 앱에 대한 정적 자산 및 클래스 라이브러리

정적 자산 참고에는 다음 방법을 사용합니다.

  • 자산이 클라이언트 앱의 wwwroot 폴더에 있는 경우 정상적으로 해당 경로를 제공합니다.

    <img alt="..." src="/{PATH AND FILE NAME}" />
    

    {PATH AND FILE NAME} 자리 표시자는 wwwroot 아래의 경로 및 파일 이름입니다.

  • 자산이 Razor 클래스 라이브러리(RCL)wwwroot 폴더에 있는 경우 ASP.NET Core를 사용하여 클래스 라이브러리에서 재사용 가능한 Razor UI의 지침에 따라 클라이언트 앱에서 정적 자산을 참조합니다.

    <img alt="..." src="_content/{PACKAGE ID}/{PATH AND FILE NAME}" />
    

    {PACKAGE ID} 자리 표시자는 라이브러리의 {PACKAGE ID}입니다. <PackageId>가 프로젝트 파일에 지정되지 않은 경우 패키지 ID는 기본적으로 프로젝트의 어셈블리 이름으로 설정됩니다. {PATH AND FILE NAME} 자리 표시자는 wwwroot 아래의 경로 및 파일 이름입니다.

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

독립 실행형 배포

‘독립 실행형 배포’는 Blazor WebAssembly 앱을 클라이언트가 직접 요청하는 정적 파일 세트로 제공합니다. 모든 정적 파일 서버는 Blazor 앱을 사용할 수 있습니다.

독립 실행형 배포 자산은 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 폴더에 게시됩니다.

Azure App Service

Blazor WebAssembly 앱은 IIS에서 앱을 호스트하는 Windows의 Azure App Services에 배포할 수 있습니다.

Linux용 Azure App Service에 독립 실행형 Blazor WebAssembly 앱 배포는 현재 지원되지 않습니다. 이 시나리오를 지원하는 Azure Static Web Apps를 사용하는 독립 실행형 Blazor WebAssembly 앱을 호스팅하는 것이 좋습니다.

Azure Static Web Apps

더 많은 정보는 자습서: Azure Static Web Apps에서 Blazor를 사용하여 정적 웹앱 빌드를 참고하세요.

IIS

IIS는 Blazor 앱에 사용할 수 있는 정적 파일 서버입니다. Blazor를 호스트하도록 IIS를 구성하려면 IIS에서 정적 웹 사이트 작성을 참조하세요.

게시된 자산은 /bin/Release/{TARGET FRAMEWORK}/publish 폴더에 생성됩니다. 웹 서버 또는 호스팅 서비스에서 publish 폴더의 콘텐츠를 호스트합니다.

web.config

Blazor 프로젝트가 게시되면 다음 IIS 구성을 사용하여 web.config 파일이 생성됩니다.

  • MIME 형식
  • 다음 MIME 형식에 대해 HTTP 압축을 사용합니다.
    • application/octet-stream
    • application/wasm
  • URL 재작성 모듈 규칙을 설정합니다.
    • 앱의 정적 자산이 상주하는 하위 디렉터리(wwwroot/{PATH REQUESTED})를 제공합니다.
    • 파일이 아닌 자산 요청이 정적 자산 폴더에 있는 앱의 기본 문서(wwwroot/index.html)로 리디렉션되도록 SPA 대체(fallback) 라우팅을 만듭니다.

사용자 지정 web.config 사용

사용자 지정 web.config 파일을 사용하려면 다음을 수행합니다.

  1. 프로젝트의 루트 폴더에 사용자 지정 web.config 파일을 저장합니다. 호스트된 Blazor WebAssembly 솔루션의 경우 Server 프로젝트의 폴더에 파일을 저장합니다.
  2. 프로젝트를 게시합니다. 호스트된 Blazor WebAssembly 솔루션의 경우 Server 프로젝트에서 솔루션을 게시합니다. 자세한 내용은 ASP.NET Core Blazor 호스트 및 배포를 참조하세요.

URL 재작성 모듈 설치

URL을 다시 생성하려면 URL 다시 생성 모듈이 필요합니다. 이 모듈은 기본적으로 설치되지 않으며 웹 서버(IIS) 역할 서비스 기능으로 설치하는 데 사용할 수 없습니다. 이 모듈은 IIS 웹 사이트에서 다운로드해야 합니다. 웹 플랫폼 설치 관리자를 사용하여 이 모듈을 설치합니다.

  1. 로컬에서 URL 다시 생성 모듈 다운로드 페이지로 이동합니다. 영어 버전의 경우 WebPI를 선택하여 WebPI 설치 관리자를 다운로드합니다. 다른 언어의 경우 서버에 맞는 아키텍처(x86 x64)를 선택하여 설치 관리자를 다운로드합니다.
  2. 설치 관리자를 서버에 복사합니다. 설치 관리자를 실행합니다. 설치 버튼을 선택하고 사용 조건에 동의합니다. 설치가 완료된 후 서버를 다시 시작하지 않아도 됩니다.

웹 사이트 구성

웹 사이트의 실제 경로를 앱의 폴더로 설정합니다. 이 폴더는 다음을 포함합니다.

  • 필요한 리디렉션 규칙 및 파일 콘텐츠 형식 등 IIS가 웹 사이트를 구성하기 위해 사용하는 web.config 파일
  • 앱의 정적 자산 폴더입니다.

IIS 하위 앱으로 호스트

독립 실행형 앱이 IIS 하위 앱으로 호스트되는 경우 다음 중 하나를 수행합니다.

  • 상속된 ASP.NET Core 모듈 처리기를 사용하지 않도록 설정합니다.

    파일의 <system.webServer> 섹션에 <handlers> 섹션을 추가하여 Blazor 앱의 게시된 web.config 파일에서 처리기를 제거합니다.

    <handlers>
      <remove name="aspNetCore" />
    </handlers>
    
  • inheritInChildApplicationsfalse로 설정한 상태에서 <location> 요소를 사용하여 루트(상위) 앱의 <system.webServer> 섹션의 상속을 사용하지 않도록 설정합니다.

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <location path="." inheritInChildApplications="false">
        <system.webServer>
          <handlers>
            <add name="aspNetCore" ... />
          </handlers>
          <aspNetCore ... />
        </system.webServer>
      </location>
    </configuration>
    

    참고

    루트(부모) 앱의 <system.webServer> 섹션의 상속을 사용하지 않도록 하는 것은 .NET SDK를 사용하는 게시된 앱에 대한 기본 구성입니다.

처리기 제거 또는 상속을 사용하지 않도록 하는 설정은 앱의 기본 경로 구성에 더하여 수행됩니다. 앱의 index.html 파일에 있는 앱 기본 경로를 IIS에서 하위 앱을 구성할 때 사용되는 IIS 별칭으로 설정합니다.

Brotli 및 Gzip 압축

이 섹션은 독립 실행형 Blazor WebAssembly 앱에만 적용됩니다. 호스트된 Blazor 앱은 이 섹션에 연결된 파일이 아니라 기본 ASP.NET Core 앱 web.config 파일을 사용합니다.

web.config를 통해 IIS를 구성하여 독립 실행형 Blazor WebAssembly 앱을 위한 Brotli 또는 Gzip 압축 Blazor 자산을 제공할 수 있습니다. 구성 파일 예제는 web.config를 참조하세요.

다음 시나리오에서는 예제 web.config 파일의 추가 구성이 필요할 수 있습니다.

  • 앱 사양에는 다음 중 하나가 필요합니다.
    • 예제 web.config 파일에 의해 구성되지 않은 압축 파일 제공.
    • 예제 web.config 파일에 의해 구성된 압축 파일을 압축되지 않은 형식으로 제공.
  • 서버의 IIS 구성(예: applicationHost.config)은 서버 수준 IIS 기본값을 제공합니다. 서버 수준 구성에 따라 예제 web.config 파일에 포함된 것과 다른 IIS 구성이 앱에 필요할 수 있습니다.

문제 해결

500 - 내부 서버 오류가 수신되고 웹 사이트의 구성에 액세스를 시도할 때 IIS 관리자가 오류를 표시하면 URL 다시 생성 모듈이 설치되었는지 확인합니다. 모듈이 설치되지 않은 경우 IIS가 web.config 파일을 구문 분석할 수 없습니다. 그러면 IIS 관리자가 웹 사이트의 구성을 로드할 수 없으며 웹 사이트가 Blazor의 정적 파일을 제공할 수 없습니다.

IIS로의 배포 문제 해결에 대한 자세한 내용은 Azure App Service 및 IIS에서 ASP.NET Core 문제 해결을 참조하세요.

Azure Storage

Azure Storage 정적 파일 호스팅으로 서버리스 Blazor 앱 호스팅이 가능합니다. 사용자 지정 도메인 이름, Azure 콘텐츠 배달 네트워크(CDN) 및 HTTPS가 지원됩니다.

스토리지 계정에서 정적 웹 사이트 호스팅을 위해 BLOB 서비스를 사용할 수 있는 경우:

  • 인덱스 문서 이름index.html로 설정합니다.
  • 오류 문서 경로index.html로 설정합니다. Razor 구성 요소 및 기타 파일이 아닌 엔드포인트는 Blob 서비스에 의해 저장된 정적 콘텐츠의 실제 경로에 존재하지 않습니다. 이러한 리소스 중 하나를 Blazor 라우터가 처리해야 한다는 요청이 수신되면 BLOB 서비스에 의해 생성된 404 - 찾을 수 없음 오류가 요청을 오류 문서 경로로 라우팅합니다. index.html BLOB이 반환되고 Blazor 라우터가 로드되어 경로를 처리합니다.

파일의 Content-Type 헤더에 부적절한 MIME 형식으로 인해 런타임에 파일이 로드되지 않을 경우 다음 작업 중 하나를 수행합니다.

  • 파일이 배포될 때 올바른 MIME 형식(Content-Type 헤더)을 설정하도록 도구를 구성합니다.

  • 앱이 배포된 후 파일에 대한 MIME 형식(Content-Type 헤더)을 변경합니다.

    각 파일의 스토리지 탐색기(Azure Portal)에서

    1. 파일을 마우스 오른쪽 단추로 클릭한 다음 속성을 선택합니다.
    2. ContentType을 설정하고 저장 단추를 선택합니다.

자세한 내용은 Azure Storage에서 정적 웹 사이트 호스팅을 참조하세요.

Nginx

다음 nginx.conf 파일은 Nginx가 디스크에서 대응하는 파일을 찾을 수 없을 때마다 index.html 파일을 보내도록 구성하는 방법을 보여 주기 위해 단순화되었습니다.

events { }
http {
    server {
        listen 80;

        location / {
            root      /usr/share/nginx/html;
            try_files $uri $uri/ /index.html =404;
        }
    }
}

limit_req를 사용하여 NGINX 버스트 속도 한도를 설정하는 경우 Blazor WebAssembly 앱에서 수행하는 비교적 많은 수의 요청을 수용하기 위해 큰 burst 매개 변수 값이 필요할 수 있습니다. 처음에는 값을 60 이상으로 설정합니다.

http {
    server {
        ...

        location / {
            ...

            limit_req zone=one burst=60 nodelay;
        }
    }
}

브라우저 개발자 도구 또는 네트워크 트래픽 도구에서 요청이 ‘503 - 서비스를 사용할 수 없음’ 상태 코드를 수신하는 것으로 확인되는 경우 값을 늘립니다.

프로덕션 Nginx 웹 서버 구성에 대한 자세한 내용은 NGINX Plus 및 NGINX 구성 파일 만들기를 참조하세요.

Apache

CentOS 7 이상에 Blazor WebAssembly 앱을 배포하려면 다음을 수행합니다.

  1. Apache 구성 파일을 만듭니다. 다음 예제는 단순화된 구성 파일입니다(blazorapp.config).

    <VirtualHost *:80>
        ServerName www.example.com
        ServerAlias *.example.com
    
        DocumentRoot "/var/www/blazorapp"
        ErrorDocument 404 /index.html
    
        AddType application/wasm .wasm
        AddType application/octet-stream .dll
    
        <Directory "/var/www/blazorapp">
            Options -Indexes
            AllowOverride None
        </Directory>
    
        <IfModule mod_deflate.c>
            AddOutputFilterByType DEFLATE text/css
            AddOutputFilterByType DEFLATE application/javascript
            AddOutputFilterByType DEFLATE text/html
            AddOutputFilterByType DEFLATE application/octet-stream
            AddOutputFilterByType DEFLATE application/wasm
            <IfModule mod_setenvif.c>
          BrowserMatch ^Mozilla/4 gzip-only-text/html
          BrowserMatch ^Mozilla/4.0[678] no-gzip
          BrowserMatch bMSIE !no-gzip !gzip-only-text/html
      </IfModule>
        </IfModule>
    
        ErrorLog /var/log/httpd/blazorapp-error.log
        CustomLog /var/log/httpd/blazorapp-access.log common
    </VirtualHost>
    
  2. Apache 구성 파일을 CentOS 7의 기본 Apache 구성 디렉터리인 /etc/httpd/conf.d/ 디렉터리에 저장합니다.

  3. 앱의 파일을 /var/www/blazorapp 디렉터리(구성 파일의 DocumentRoot에 지정된 위치)에 저장합니다.

  4. Apache 서비스를 다시 시작합니다.

자세한 내용은 mod_mimemod_deflate를 참조하세요.

GitHub 페이지

URL 다시 쓰기를 처리하려면 index.html 페이지로 요청 리디렉션을 처리하는 스크립트를 사용하여 wwwroot/404.html 파일을 추가합니다. 예제는 SteveSandersonMS/BlazorOnGitHubPages GitHub 리포지토리를 참조하세요.

조직 사이트 대신 프로젝트 사이트를 사용하는 경우 wwwroot/index.html<base> 태그를 업데이트합니다. href 특성 값을 후행 슬래시가 있는 GitHub 리포지토리 이름으로 설정합니다(예: /my-repository/). SteveSandersonMS/BlazorOnGitHubPages GitHub 리포지토리에서 기본 href.github/workflows/main.yml 구성 파일에 의해 게시 시 업데이트됩니다.

참고

SteveSandersonMS/BlazorOnGitHubPages GitHub 리포지토리는 .NET Foundation 또는 Microsoft에서 소유, 유지 관리 또는 지원하지 않습니다.

호스트 구성 값

Blazor WebAssembly 앱은 개발 환경의 런타임에 다음 호스트 구성 값을 명령줄 인수로 허용할 수 있습니다.

콘텐츠 루트

--contentroot 인수는 앱의 콘텐츠 파일을 포함하는 디렉터리(콘텐츠 루트)에 대한 절대 경로를 설정합니다. 다음 예제에서 /content-root-path는 앱의 콘텐츠 루트 경로입니다.

  • 명령 프롬프트에서 앱을 로컬로 실행할 때 인수를 전달합니다. 앱의 디렉터리에서 다음을 실행합니다.

    dotnet run --contentroot=/content-root-path
    
  • IIS Express 프로필에서 앱의 launchSettings.json 파일에 항목을 추가합니다. 앱이 Visual Studio 디버거를 통해 실행되거나 dotnet run을 사용하여 명령 프롬프트에서 실행되는 경우 이 설정이 사용됩니다.

    "commandLineArgs": "--contentroot=/content-root-path"
    
  • Visual Studio의 속성>디버그>애플리케이션 인수에서 인수를 지정합니다. Visual Studio 속성 페이지에서 인수를 설정하면 해당 인수가 launchSettings.json 파일에 추가됩니다.

    --contentroot=/content-root-path
    

경로 기준

--pathbase 인수는 루트가 아닌 상대 URL 경로를 사용하여 로컬로 앱을 실행하기 위한 앱 기본 경로를 설정합니다. (준비 및 프로덕션의 경우 <base> 태그 href/ 이외의 경로로 설정됩니다.) 다음 예제에서 /relative-URL-path는 앱의 경로 기준입니다. 자세한 내용은 앱 기본 경로를 참조하세요.

중요

<base> 태그의 href에 대해 제공되는 경로와 달리 --pathbase 인수 값을 전달할 때 뒤에 슬래시(/)를 포함하지 않습니다. 앱 기본 경로가 <base> 태그에 <base href="/CoolApp/">로 제공되는 경우(뒤에 슬래시 포함) 명령줄 인수 값을 --pathbase=/CoolApp로 전달합니다(뒤에 슬래시 없음).

  • 명령 프롬프트에서 앱을 로컬로 실행할 때 인수를 전달합니다. 앱의 디렉터리에서 다음을 실행합니다.

    dotnet run --pathbase=/relative-URL-path
    
  • IIS Express 프로필에서 앱의 launchSettings.json 파일에 항목을 추가합니다. 앱을 Visual Studio 디버거를 통해 실행하거나 dotnet run을 사용하여 명령 프롬프트에서 실행하는 경우 이 설정이 사용됩니다.

    "commandLineArgs": "--pathbase=/relative-URL-path"
    
  • Visual Studio의 속성>디버그>애플리케이션 인수에서 인수를 지정합니다. Visual Studio 속성 페이지에서 인수를 설정하면 해당 인수가 launchSettings.json 파일에 추가됩니다.

    --pathbase=/relative-URL-path
    

URL

--urls 인수는 요청을 수신하기 위한 포트 및 프로토콜을 포함하는 IP 주소 또는 호스트 주소를 설정합니다.

  • 명령 프롬프트에서 앱을 로컬로 실행할 때 인수를 전달합니다. 앱의 디렉터리에서 다음을 실행합니다.

    dotnet run --urls=http://127.0.0.1:0
    
  • IIS Express 프로필에서 앱의 launchSettings.json 파일에 항목을 추가합니다. 앱을 Visual Studio 디버거를 통해 실행하거나 dotnet run을 사용하여 명령 프롬프트에서 실행하는 경우 이 설정이 사용됩니다.

    "commandLineArgs": "--urls=http://127.0.0.1:0"
    
  • Visual Studio의 속성>디버그>애플리케이션 인수에서 인수를 지정합니다. Visual Studio 속성 페이지에서 인수를 설정하면 해당 인수가 launchSettings.json 파일에 추가됩니다.

    --urls=http://127.0.0.1:0
    

Linux의 호스트된 배포(Nginx)

프록시 서버 및 부하 분산 장치를 사용하도록 ASP.NET Core 구성의 지침에 따라 X-Forwarded-ForX-Forwarded-Proto 헤더를 전달하도록 ForwardedHeadersOptions를 사용하여 앱을 구성합니다.

하위 앱 경로 구성을 포함하여 앱의 기본 경로 설정에 대한 자세한 내용은 ASP.NET Core Blazor 호스트 및 배포를 참조하세요.

다음 변경 내용에 대해 ASP.NET Core SignalR 앱의 지침을 따릅니다.

  • 이 설정은 Blazor 앱 클라이언트-서버 상호 작용과 관련이 없는 SSE(Server-Sent 이벤트)에 적용되므로 프록시 버퍼링(proxy_buffering off;)에 대한 구성을 제거합니다.

  • location 경로를 /hubroute(location /hubroute { ... })에서 하위 앱 경로 /{PATH}(location /{PATH} { ... })로 변경합니다. 여기서 {PATH} 자리 표시자는 하위 앱 경로입니다.

    다음 예제에서는 루트 경로 /에서 요청에 응답하는 앱에 대해 서버를 구성합니다.

    http {
        server {
            ...
            location / {
                ...
            }
        }
    }
    

    다음 예제에서는 /blazor의 하위 앱 경로를 다음과 같이 구성합니다.

    http {
        server {
            ...
            location /blazor {
                ...
            }
        }
    }
    

자세한 내용 및 구성 지침은 다음 리소스를 참조하세요.

트리머 구성

Blazor는 각 릴리스 빌드에 IL(중간 언어) 트리밍을 수행하여 출력 어셈블리에서 필요 없는 IL을 제거합니다. 자세한 내용은 ASP.NET Core Blazor용 트리머 구성을 참조하세요.

DLL 파일의 파일 이름 확장명 변경

앱 게시 .dll 파일의 파일 이름 확장명을 변경해야 하는 경우 이 섹션의 지침을 따르세요.

앱을 게시한 후 셸 스크립트 또는 DevOps 빌드 파이프라인을 사용하여 다른 파일 확장명을 사용하도록 .dll 파일의 이름을 바꿉니다. 앱 게시 출력(예: {CONTENT ROOT}/bin/Release/netstandard2.1/publish/wwwroot)의 wwwroot 디렉터리에 있는 .dll 파일을 대상으로 합니다.

다음 예제에서는 .bin 파일 확장명을 사용하도록 .dll 파일의 이름이 바뀝니다.

Windows에서:

dir .\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content .\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content .\_framework\blazor.boot.json

서비스 작업자 자산도 사용 중인 경우 다음 명령을 추가합니다.

((Get-Content .\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content .\service-worker-assets.js

Linux 또는 macOS에서:

for f in _framework/_bin/*; do mv "$f" "`echo $f | sed -e 's/\.dll/.bin/g'`"; done
sed -i 's/\.dll"/.bin"/g' _framework/blazor.boot.json

서비스 작업자 자산도 사용 중인 경우 다음 명령을 추가합니다.

sed -i 's/\.dll"/.bin"/g' service-worker-assets.js

.bin 이외의 다른 파일 확장명을 사용하려면 이전 명령에서 .bin을 바꿉니다.

압축된 blazor.boot.json.gzblazor.boot.json.br 파일을 처리하려면 다음 방법 중 하나를 채택합니다.

  • 압축된 blazor.boot.json.gzblazor.boot.json.br 파일을 제거합니다. 압축은 이 접근 방법으로 사용하지 않도록 설정됩니다.
  • 업데이트된 blazor.boot.json 파일을 다시 압축합니다.

위의 지침은 서비스 작업자 자산을 사용 중인 경우에도 적용됩니다. wwwroot/service-worker-assets.js.brwwwroot/service-worker-assets.js.gz를 제거하거나 다시 압축합니다. 그렇지 않으면 브라우저에서 파일 무결성 검사가 실패합니다.

다음 Windows 예제에서는 프로젝트의 루트에 배치된 PowerShell 스크립트를 사용합니다.

ChangeDLLExtensions.ps1::

param([string]$filepath,[string]$tfm)
dir $filepath\bin\Release\$tfm\wwwroot\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json
Remove-Item $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json.gz

서비스 작업자 자산도 사용 중인 경우 다음 명령을 추가합니다.

((Get-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js

프로젝트 파일에서 스크립트는 앱을 게시한 후에 실행됩니다.

<Target Name="ChangeDLLFileExtensions" AfterTargets="Publish" Condition="'$(Configuration)'=='Release'">
  <Exec Command="powershell.exe -command &quot;&amp; { .\ChangeDLLExtensions.ps1 '$(SolutionDir)' '$(TargetFramework)'}&quot;" />
</Target>

참고

동일한 어셈블리의 이름을 변경하고 지연 로드하는 경우 ASP.NET Core Blazor WebAssembly에서 어셈블리 지연 로드의 지침을 참조하세요.

이전 배포 손상

일반적으로 배포 시 다음 현상이 나타납니다.

  • 변경된 파일만 대체되어 일반적으로 배포 속도가 빨라집니다.
  • 새 배포에 포함되지 않은 기존 파일은 새 배포에서 사용할 수 있도록 남아 있습니다.

드문 경우에 이전 배포에서 남은 파일 때문에 새 배포가 손상될 수 있습니다. 기존 배포(또는 배포 전에 로컬로 게시된 앱)를 완전히 삭제하면 손상된 배포와 관련된 문제가 해결될 수 있습니다. 종종 기존 배포를 한번 삭제하면 DevOps 빌드 및 배포 파이프라인을 포함하는 문제를 해결할 수 있습니다.

DevOps 빌드 및 배포 파이프라인을 사용할 때 항상 이전 배포를 지워야 하는 경우 손상의 정확한 원인을 해결할 때까지 새 배포마다 이전 배포를 삭제하는 단계를 빌드 파이프라인에 일시적으로 추가할 수 있습니다.

무결성 검사 실패 해결

Blazor WebAssembly는 앱의 시작 파일을 다운로드할 때 응답에 대한 무결성 검사를 수행하도록 브라우저에 지시합니다. Blazor는 blazor.boot.json 파일 내의 DLL(.dll), WebAssembly(.wasm) 및 다른 파일에 대한 SHA-256 해시 값을 전송하며, 이 값은 클라이언트에 캐시되지 않습니다. 캐시된 파일의 파일 해시는 blazor.boot.json 파일의 해시와 비교됩니다. 캐시된 파일에 일치하는 해시가 있는 경우 Blazor가 캐시된 파일을 사용합니다. 그렇지 않으면 서버에 파일을 요청합니다. 파일을 다운로드한 후 무결성 유효성 검사를 위해 해당 해시를 다시 확인합니다. 다운로드한 파일의 무결성 검사가 실패하면 브라우저에서 오류가 생성됩니다.

파일 무결성을 관리하기 위한 Blazor의 알고리즘:

  • 사용자가 애플리케이션 파일을 다운로드하는 동안 새 배포가 웹 서버에 적용되는 경우와 같이 앱이 일관되지 않은 파일 세트를 로드할 위험이 없도록 합니다. 파일이 일관되지 않으면 앱이 오작동할 수 있습니다.
  • 사용자의 브라우저가 일관되지 않은 또는 잘못된 응답을 캐시하지 않도록 합니다. 일관되지 않거나 잘못된 응답을 캐시할 경우 사용자가 수동으로 페이지를 새로 고치더라도 앱이 시작하지 않을 수 있습니다.
  • 응답을 안전하게 캐시하며 예상된 SHA-256 해시가 변경될 때까지 서버 쪽 변경 내용을 확인하지 않도록 하므로 후속 페이지 로드는 더 적은 요청을 포함하여 더 빠르게 완료됩니다.

웹 서버가 예상된 SHA-256 해시와 일치하지 않는 응답을 반환하는 경우에는 브라우저의 개발자 콘솔에 다음과 같은 오류가 표시됩니다.

컴퓨팅된 SHA-256 무결성 ‘IIa70iwvmEg5WiDV17OpQ5eCztNYqL186J56852RpJY=’가 지정된 ‘https://myapp.example.com/_framework/MyBlazorApp.dlll’ 리소스의 ‘integrity’ 특성에서 유효한 다이제스트를 찾지 못했습니다. 리소스가 차단되었습니다.

대부분의 경우 경고는 무결성 검사에 문제가 있음을 나타내지 않습니다. 대신, 경고는 일반적으로 다른 문제가 있음을 의미합니다.

Blazor WebAssembly의 부팅 참조 원본은 dotnet/aspnetcore GitHub 리포지토리의 Boot.WebAssembly.ts 파일을 참조하세요.

참고

ASP.NET Core 참조 원본에 대한 설명서 링크는 ASP.NET Core의 다음 릴리스를 위한 제품 단위의 현재 개발을 나타내는 리포지토리의 main 분기를 로드합니다. 다른 릴리스에 대한 분기를 선택하려면 Switch branches or tags(분기 또는 태그 전환) 드롭다운 목록을 사용하여 분기를 선택합니다. 예를 들어 release/6.0 ASP.NET Core 6.0 릴리스에 대한 분기를 선택합니다.

무결성 문제 진단

앱이 빌드되면 생성된 blazor.boot.json 매니페스트는 빌드 출력이 생성될 때 부팅 리소스의 SHA-256 해시를 설명합니다. blazor.boot.json의 SHA-256 해시가 브라우저에 배달된 파일과 일치하는 경우 무결성 검사가 통과합니다.

이 작업이 실패하는 일반적인 이유는 다음과 같습니다.

  • 웹 서버의 응답은 브라우저가 요청한 파일이 아닌 오류(예: ‘404 - 찾을 수 없음’ 또는 ‘500 - 내부 서버 오류’)입니다. 이는 브라우저에서 응답 실패가 아닌 무결성 검사 실패로 보고됩니다.
  • 무엇인가 브라우저에 대한 파일의 빌드와 제공 사이에 파일 콘텐츠를 변경했습니다. 이 문제가 발생할 수 있는 경우는 다음과 같습니다.
    • 사용자 또는 빌드 도구가 빌드 출력을 수동으로 수정하는 경우.
    • 배포 프로세스의 일부 측면이 파일을 수정한 경우. 예를 들어 Git 기반 배포 메커니즘을 사용하는 경우 파일을 Windows에서 커밋하고 Linux에서 체크 아웃하는 경우 Git에서는 Windows 스타일 줄 끝을 Unix 스타일 줄 끝으로 투명하게 변환합니다. 파일 줄 끝을 변경하면 SHA-256 해시가 변경됩니다. 이 문제를 방지하려면 .gitattributes를 사용하여 빌드 아티팩트를 binary 파일로 처리하는 것이 좋습니다.
    • 웹 서버는 파일 콘텐츠를 제공하는 동안 수정합니다. 예를 들어 일부 CDN(Content Delivery Network)은 자동으로 HTML을 축소함으로써 수정합니다. 관련 기능을 사용하지 않도록 설정해야 할 수 있습니다.
  • blazor.boot.json 파일이 제대로 로드되지 않거나 클라이언트에 잘못 캐시됩니다. 일반적인 원인은 다음 중 하나입니다.
    • 사용자 지정 개발자 코드가 잘못 구성되었거나 오작동합니다.
    • 하나 이상의 중간 캐싱 레이어가 잘못 구성되었습니다.

어떤 기능이 해당 사례에서 적용되는지 진단하려면:

  1. 오류 메시지를 읽어서 어떤 파일이 오류를 트리거하는지 확인합니다.
  2. 브라우저의 개발자 도구를 열고 ‘네트워크’ 탭을 확인합니다. 필요한 경우 페이지를 다시 로드하여 요청 및 응답 목록을 확인합니다. 해당 목록에서 오류를 트리거하는 파일을 찾습니다.
  3. 응답의 HTTP 상태 코드를 확인합니다. 서버가 200 - OK(또는 또 다른 2xx 상태 코드) 이외의 값을 반환하면 진단해야 하는 서버 쪽 문제가 있는 것입니다. 예를 들어 상태 코드 403은 권한 부여 문제가 있음을 의미하는 반면, 상태 코드 500은 서버가 지정되지 않은 방식으로 실패함을 의미합니다. 서버 쪽 로그를 참조하여 앱을 진단하고 수정합니다.
  4. 리소스의 상태 코드가 200 - OK인 경우 브라우저의 개발자 도구에서 응답 콘텐츠를 보고 예상되는 데이터와 콘텐츠가 일치하는지 확인합니다. 예를 들어 일반적인 문제는 요청이 다른 파일에 대해서도 index.html 데이터를 반환하도록 라우팅을 잘못 구성하는 것입니다. .wasm 요청에 대한 응답이 WebAssembly 이진 파일이고 .dll 요청에 대한 응답이 .NET 어셈블리 이진 파일인지 확인합니다. 그렇지 않으면 진단해야 할 서버 쪽 라우팅 문제가 있는 것입니다.
  5. 무결성 PowerShell 스크립트 문제 해결을 사용하여 게시 및 배포된 앱 출력의 유효성을 검사해 보세요.

서버가 올바른 것 같은 데이터를 반환하고 있는지 확인하는 경우 파일의 빌드와 제공 사이에 콘텐츠를 수정하는 다른 항목이 있어야 합니다. 이를 조사하려면:

  • 파일이 빌드된 후 파일을 수정 중인 경우 빌드 도구 체인 및 배포 메커니즘을 검토합니다. 여기에 해당하는 예제는 앞에서 설명한 대로 Git이 파일 줄 끝을 변환하는 경우입니다.
  • 응답을 동적으로 수정하도록(예: HTML 축소 시도) 설정된 경우 웹 서버 또는 CDN 구성을 검토합니다. 압축을 푼 후 결과에 영향을 주지 않으므로 웹 서버가 HTTP 압축을 구현해도 괜찮습니다(예: content-encoding: br 또는 content-encoding: gzip 반환). 그러나 웹 서버가 압축되지 않은 데이터를 수정하는 것은 괜찮지 ‘않습니다’.

무결성 PowerShell 스크립트 문제 해결

integrity.ps1 PowerShell 스크립트를 사용하여 게시 및 배포된 Blazor 앱의 유효성을 검사합니다. 이 스크립트는 앱에 Blazor 프레임워크가 식별할 수 없는 무결성 문제가 있는 경우 PowerShell Core 7 이상에 시작 지점으로 제공됩니다. 버전 7.2.0 이상 버전의 PowerShell을 실행하는 경우를 포함하여 앱을 위해 스크립트를 사용자 지정해야 할 수 있습니다.

이 스크립트는 publish 폴더의 파일과 배포된 앱에서 다운로드한 파일을 검사하여 무결성 해시가 포함된 다른 매니페스트의 문제를 검색합니다. 이 검사에서 다음과 같은 가장 일반적인 문제가 검색되어야 합니다.

  • 게시된 출력의 파일을 사용자가 무심코 수정했습니다.
  • 앱이 배포 대상에 올바르게 배포되지 않았거나 배포 대상 환경 내에 변경된 내용이 있습니다.
  • 배포된 앱과 앱 게시의 출력 간에 차이가 있습니다.

PowerShell 명령 셸에서 다음 명령을 사용하여 스크립트를 호출합니다.

.\integrity.ps1 {BASE URL} {PUBLISH OUTPUT FOLDER}

다음 예제에서 스크립트는 https://localhost:5001/에서 로컬로 실행 중인 앱에서 실행됩니다.

.\integrity.ps1 https://localhost:5001/ C:\TestApps\BlazorSample\bin\Release\net6.0\publish\

자리 표시자:

  • {BASE URL}: 배포된 앱의 URL입니다. 후행 슬래시(/)가 필요합니다.
  • {PUBLISH OUTPUT FOLDER}: 앱의 publish 폴더 또는 배포를 위해 앱이 게시된 위치의 경로입니다.

참고

dotnet/AspNetCore.Docs GitHub 리포지토리를 복제하는 경우 integrity.ps1 스크립트는 Bitdefender 또는 시스템에 있는 다른 바이러스 검색 프로그램으로 격리될 수 있습니다. 일반적으로 파일은 바이러스 검사 프로그램의 추론 검색 기술을 통해 트래핑됩니다. 이 기술은 파일에서 맬웨어가 있음을 나타낼 수 있는 패턴만 찾습니다. 바이러스 검사 프로그램이 파일을 격리하지 않도록 하려면 리포지토리를 복제하기 전에 바이러스 검사 프로그램에 예외를 추가합니다. 다음 예제는 Windows 시스템의 일반적인 스크립트 경로입니다. 다른 시스템의 필요에 따라 경로를 조정합니다. 자리 표시자 {USER}는 사용자의 패스 세그먼트입니다.

C:\Users\{USER}\Documents\GitHub\AspNetCore.Docs\aspnetcore\blazor\host-and-deploy\webassembly\_samples\integrity.ps1

경고: 바이러스 검사 예외를 만드는 것은 위험하며 파일이 안전하다는 것을 확신할 때만 수행해야 합니다.

파일의 체크섬을 유효한 체크섬 값과 비교해도 파일 보안을 보장할 수 없지만 체크섬 값을 유지하는 방식으로 파일을 수정하는 것은 악의적인 사용자에게 사소하지 않습니다. 따라서 체크섬은 일반적인 보안 방법으로 유용합니다. 로컬 integrity.ps1 파일의 체크섬을 다음 값 중 하나와 비교합니다.

  • SHA256: 32c24cb667d79a701135cb72f6bae490d81703323f61b8af2c7e5e5dc0f0c2bb
  • MD5: 9cee7d7ec86ee809a329b5406fbf21a8

다음 명령을 사용하여 Windows OS에서 파일의 체크섬을 가져옵니다. {PATH AND FILE NAME} 자리 표시자의 경로 및 파일 이름을 제공하고 {SHA512|MD5} 자리 표시자에 대해 생성할 체크섬의 형식(SHA256 또는 MD5)을 나타냅니다.

CertUtil -hashfile {PATH AND FILE NAME} {SHA256|MD5}

사용자 환경에서 체크섬 유효성 검사가 충분히 안전하지 않을 수 있다고 생각될 경우 조직의 보안 책임자에게 문의하세요.

자세한 내용은 맬웨어 및 기타 위협 이해를 참조하세요.

비 PWA 앱에 대한 무결성 검사 사용 안 함

대부분의 경우 무결성 검사를 사용하지 않도록 설정하지 마세요. 무결성 검사를 사용하지 않으면 예기치 않은 응답을 발생시킨 근본적인 문제가 해결되지 않아 앞서 언급한 이점을 얻을 수 없습니다.

웹 서버를 사용하여 일관된 응답을 반환할 수 없고 근본 문제가 해결될 때까지 일시적으로 무결성 검사를 사용하지 않도록 설정할 수밖에 없는 경우가 있습니다.

무결성 검사를 사용하지 않도록 설정하려면 Blazor WebAssembly 앱의 프로젝트 파일(.csproj)에서 속성 그룹에 다음을 추가합니다.

<BlazorCacheBootResources>false</BlazorCacheBootResources>

또한, BlazorCacheBootResources 속성은 SHA-256 해시의 정확성을 기대할 수 없음을 나타내기 때문에 .dll, .wasm 및 SHA-256 해시 기반 기타 파일을 캐시하는 Blazor의 기본 동작을 사용하지 않도록 설정합니다. 이 설정을 사용하는 경우에도 브라우저의 일반 HTTP 캐시는 해당 파일을 캐시할 수 있지만 이 상황이 발생하는지는 웹 서버 구성 및 해당 구성이 제공하는 cache-control 헤더에 따라 달라집니다.

참고

BlazorCacheBootResources 속성은 PWA(프로그레시브 웹 애플리케이션)에 대한 무결성 검사를 사용하지 않도록 설정하지 않습니다. PWA 관련 지침은 PWA에 대한 무결성 검사 사용 안 함 섹션을 참조하세요.

무결성 검사를 사용하지 않도록 설정해야 하는 시나리오를 모두 나열할 수는 없습니다. 서버는 Blazor 프레임워크의 범위를 벗어나는 임의의 방법으로 요청에 응답할 수 있습니다. 프레임워크는 앱이 제공할 수 있는 무결성에 대한 보장을 하지 않는 대신 앱을 계속 실행할 수 있도록 BlazorCacheBootResources 설정을 제공합니다. 다시 한 번 말하지만, 특히 프로덕션 배포의 경우, 무결성 검사를 사용하지 않도록 설정하는 것은 권장하지 않습니다. 개발자는 무결성 검사가 실패하는 근본적인 무결성 문제를 해결하려고 노력해야 합니다.

무결성 문제를 일으킬 수 있는 몇 가지 일반적인 사례는 다음과 같습니다.

  • 무결성을 검사할 수 없는 HTTP에서 실행하는 경우
  • 배포 프로세스에서 어떤 방식으로든 게시 후 파일을 수정하는 경우
  • 호스트가 어떤 방식으로든 파일을 수정하는 경우

PWA에 대한 무결성 검사 사용 안 함

Blazor의 PWA(프로그레시브 웹 애플리케이션) 템플릿에는 오프라인에서 사용하기 위해 애플리케이션 파일을 페치하고 저장해야 하는 제안된 service-worker.published.js 파일이 포함됩니다. 이는 일반적인 앱 시작 메커니즘과 별도의 프로세스이며 자체적인 별도의 무결성 검사 논리를 포함합니다.

service-worker.published.js 파일 내에 다음 줄이 있습니다.

.map(asset => new Request(asset.url, { integrity: asset.hash }));

무결성 검사를 사용하지 않으려면 줄을 다음으로 변경하여 integrity 매개 변수를 제거합니다.

.map(asset => new Request(asset.url));

또한 무결성 검사를 사용하지 않도록 설정하면 무결성 검사를 통해 제공되는 안전 보증이 손실됩니다. 예를 들어 사용자의 브라우저가 새 버전을 배포하는 바로 그 순간에 앱을 캐시 중인 경우 이전 배포의 일부 파일과 새 배포의 일부 파일을 캐시할 수 있는 위험이 있습니다. 이 경우 추가 업데이트를 배포할 때까지 앱이 중단됨 상태로 중단됩니다.

Blazor WebAssembly 호스팅 모델을 사용하면 다음과 같이 실행됩니다.

  • Blazor 앱, 해당 앱의 종속성 및 .NET 런타임이 병렬로 브라우저에 다운로드됩니다.
  • 해당 앱은 브라우저 UI 스레드에서 직접 실행됩니다.

다음 배포 전략이 지원됩니다.

  • Blazor 앱은 ASP.NET Core 앱에서 제공됩니다. 이 전략은 ASP.NET Core를 사용하여 호스트된 배포 섹션에서 설명합니다.
  • Blazor 앱은 정적 호스팅 웹 서버 또는 서비스에 배치되며, 이 경우 Blazor 앱을 처리하기 위해 .NET을 사용하지 않습니다. 이 전략은 Blazor WebAssembly 앱을 IIS 하위 앱으로 호스트하는 방법에 대한 정보를 포함하는 독립 실행형 배포 섹션에서 설명합니다.

부팅 리소스를 로드하는 방법 사용자 지정

loadBootResource API를 사용하여 부팅 리소스를 로드하는 방법 사용자 지정 자세한 내용은 ASP.NET Core Blazor 시작을 참조하세요.

압축

Blazor WebAssembly 앱이 게시될 때 게시하는 도중에 출력을 정적으로 압축하여 앱의 크기를 줄이고 런타임 압축의 오버헤드를 제거합니다. 다음 압축 알고리즘이 사용됩니다.

Blazor는 호스트를 사용하여 적절한 압축 파일을 제공합니다. ASP.NET Core 호스트 프로젝트를 사용하는 경우 호스트 프로젝트는 콘텐츠 협상을 수행하고 정적으로 압축된 파일을 제공할 수 있습니다. Blazor WebAssembly 독립 실행형 앱을 호스트하는 경우 정적으로 압축된 파일이 제공되도록 추가 작업이 필요할 수도 있습니다.

  • IIS web.config 압축 구성에 대해서는 IIS: Brotli 및 Gzip 압축 섹션을 참조하세요.

  • GitHub 페이지와 같이 정적으로 압축된 파일 콘텐츠 협상을 지원하지 않는 정적 호스팅 솔루션에서 호스트하는 경우 Brotli로 압축된 파일을 가져와 디코딩하는 앱을 구성하는 것이 좋습니다.

    • google/brotli GitHub 리포지토리에서 JavaScript Brotli 디코더를 가져옵니다. 축소된 디코더 파일은 이름이 decode.min.js이며 리포지토리의 js 폴더에 있습니다.

      참고

      decode.js 스크립트(decode.min.js)의 축소된 버전이 실패하는 경우 축소 해제한 버전(decode.js)을 대신 사용해 보세요.

    • 디코더를 사용하도록 앱을 업데이트합니다.

      wwwroot/index.html 파일의 Blazor의 <script> 태그에서 autostartfalse로 설정합니다.

      <script src="_framework/blazor.webassembly.js" autostart="false"></script>
      

      Blazor의 <script> 태그 뒤, 닫는 </body> 태그 앞에 다음 JavaScript 코드 <script> 블록을 추가합니다.

      <script type="module">
        import { BrotliDecode } from './decode.min.js';
        Blazor.start({
          loadBootResource: function (type, name, defaultUri, integrity) {
            if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
              return (async function () {
                const response = await fetch(defaultUri + '.br', { cache: 'no-cache' });
                if (!response.ok) {
                  throw new Error(response.statusText);
                }
                const originalResponseBuffer = await response.arrayBuffer();
                const originalResponseArray = new Int8Array(originalResponseBuffer);
                const decompressedResponseArray = BrotliDecode(originalResponseArray);
                const contentType = type === 
                  'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
                return new Response(decompressedResponseArray, 
                  { headers: { 'content-type': contentType } });
              })();
            }
          }
        });
      </script>
      

      부팅 리소스를 로드하는 방법에 대한 자세한 내용은 ASP.NET Core Blazor 시작을 참조하세요.

압축을 해제하려면 앱의 프로젝트 파일에 BlazorEnableCompression MSBuild 속성을 추가하고 값을 false로 설정합니다.

<PropertyGroup>
  <BlazorEnableCompression>false</BlazorEnableCompression>
</PropertyGroup>

명령 셸에서 다음 구문을 사용하여 BlazorEnableCompression 속성을 dotnet publish 명령에 전달할 수 있습니다.

dotnet publish -p:BlazorEnableCompression=false

올바른 라우팅을 위해 URL 다시 생성

Blazor WebAssembly 앱의 페이지 구성 요소에 대한 라우팅 요청은 Blazor Server에서 호스트된 앱의 요청을 라우팅하는 것처럼 간단하지 않습니다. 다음 두 구성 요소를 사용하는 Blazor WebAssembly 앱을 살펴보겠습니다.

  • Main.razor: 앱의 루트에 로드되며 About 구성 요소에 대한 링크(href="About")를 포함합니다.
  • About.razor: About 구성 요소입니다.

브라우저의 주소 표시줄을 사용하여 앱의 기본 문서를 요청하는 경우(예: https://www.contoso.com/):

  1. 브라우저가 요청을 합니다.
  2. 기본 페이지(일반적으로 index.html)가 반환됩니다.
  3. index.html이 앱을 부트스트랩합니다.
  4. Blazor의 라우터가 로드되고 RazorMain 구성 요소가 렌더링됩니다.

Blazor 라우터는 브라우저가 인터넷에서 www.contoso.com으로 About을 요청하는 것을 중단하고 렌더링된 About 구성 요소를 직접 제공하므로 기본 페이지에서 About 구성 요소에 대한 링크 선택은 클라이언트에서 작동합니다. ‘Blazor WebAssembly 앱 내’의 내부 엔드포인트에 대한 모든 요청도 같은 방법으로 작동합니다. 요청은 인터넷상에서 서버가 호스트하는 리소스에 대한 브라우저 기반 요청을 트리거하지 않습니다. 라우터가 내부적으로 요청을 처리합니다.

브라우저의 주소 표시줄을 사용하여 www.contoso.com/About을 요청하면 해당 요청이 실패합니다. 앱의 인터넷 호스트에 해당 리소스가 없으므로 404 - 찾을 수 없음 응답이 반환됩니다.

브라우저는 인터넷 기반 호스트에 클라이언트 쪽 페이지를 요청하므로, 웹 서버 및 호스팅 서비스는 서버에 실제로 존재하지 않는 리소스에 대한 모든 요청을 index.html 페이지에 다시 써야 합니다. index.html이 반환되는 경우 앱의 Blazor 라우터가 넘겨받아 올바른 리소스로 응답합니다.

IIS 서버에 배포하는 경우 앱의 게시된 web.config 파일과 함께 URL 재작성 모듈을 사용할 수 있습니다. 자세한 내용은 IIS 섹션을 참조하세요.

ASP.NET Core를 사용하여 호스트된 배포

‘호스트된 배포’는 웹 서버에서 실행되는 ASP.NET Core 앱에서 Blazor WebAssembly 앱을 브라우저에 제공하지 않습니다.

클라이언트 Blazor WebAssembly 앱이 서버 앱의 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 폴더에 서버 앱의 다른 정적 웹 자산과 함께 게시됩니다. 두 앱이 함께 배포됩니다. ASP.NET Core 앱을 호스트할 수 있는 웹 서버가 필요합니다. 호스트된 배포의 경우 Visual Studio는 Hosted (dotnet new 명령을 사용하는 경우 -ho|--hosted) 옵션이 선택된 Blazor WebAssembly 앱 프로젝트 템플릿(dotnet new 명령을 사용하는 경우 blazorwasm 템플릿)을 포함합니다.

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

여러 Blazor WebAssembly 앱을 사용하여 호스트된 배포

앱 구성

호스트된 Blazor 솔루션은 여러 Blazor WebAssembly 앱을 제공할 수 있습니다.

참고

이 섹션의 예제는 Visual Studio 솔루션 사용을 참조하지만 호스트된 Blazor WebAssembly 앱 시나리오에서 여러 클라이언트 앱이 작동하기 위해 Visual Studio 및 Visual Studio 솔루션을 사용하지 않아도 됩니다. Visual Studio를 사용하지 않는 경우 {SOLUTION NAME}.sln 파일 및 Visual Studio용으로 만든 다른 파일을 무시하세요.

다음 예제에서는

  • 초기(첫 번째) 클라이언트 앱은 Blazor WebAssembly 프로젝트 템플릿에서 만든 솔루션의 기본 클라이언트 프로젝트입니다. 첫 번째 클라이언트 앱은 포트가 5001이거나 호스트가 firstapp.com인 URL /FirstApp에서 브라우저로 액세스할 수 있습니다.
  • 두 번째 클라이언트 앱은 SecondBlazorApp.Client 솔루션에 추가됩니다. 두 번째 클라이언트 앱은 포트가 5002이거나 호스트가 secondapp.com인 URL /SecondApp에서 브라우저로 액세스할 수 있습니다.

호스트된 기존 Blazor 솔루션을 사용하거나 Blazor 호스트 프로젝트 템플릿에서 새 솔루션을 만듭니다.

  • 클라이언트 앱의 프로젝트 파일에서 값이 FirstApp<PropertyGroup><StaticWebAssetBasePath> 속성을 추가하여 프로젝트의 정적 자산에 대한 기본 경로를 설정합니다.

    <PropertyGroup>
      ...
      <StaticWebAssetBasePath>FirstApp</StaticWebAssetBasePath>
    </PropertyGroup>
    
  • 두 번째 클라이언트 앱을 솔루션에 추가합니다.

    • SecondClient라는 폴더를 솔루션의 폴더에 추가합니다. 프로젝트 템플릿에서 만든 솔루션 폴더에는 SecondClient 폴더 추가 후 다음 솔루션 파일 및 폴더가 포함됩니다.

      • Client(폴더)
      • SecondClient(폴더)
      • Server(폴더)
      • Shared(폴더)
      • {SOLUTION NAME}.sln(파일)

      자리 표시자 {SOLUTION NAME}은 솔루션의 이름입니다.

    • Blazor WebAssembly 프로젝트 템플릿의 SecondClient 폴더에 SecondBlazorApp.Client라는 Blazor WebAssembly 앱을 만듭니다.

    • SecondBlazorApp.Client 앱의 프로젝트 파일에서 다음을 수행합니다.

      • SecondApp 값을 사용하여 <PropertyGroup><StaticWebAssetBasePath> 속성을 추가합니다.

        <PropertyGroup>
          ...
          <StaticWebAssetBasePath>SecondApp</StaticWebAssetBasePath>
        </PropertyGroup>
        
      • Shared 프로젝트에 프로젝트 참조를 추가합니다.

        <ItemGroup>
          <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
        </ItemGroup>
        

        자리 표시자 {SOLUTION NAME}은 솔루션의 이름입니다.

  • 서버 앱의 프로젝트 파일에서 추가된 SecondBlazorApp.Client 클라이언트 앱에 대한 프로젝트 참조를 만듭니다.

    <ItemGroup>
      <ProjectReference Include="..\Client\{SOLUTION NAME}.Client.csproj" />
      <ProjectReference Include="..\SecondClient\SecondBlazorApp.Client.csproj" />
      <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
    </ItemGroup>
    

    자리 표시자 {SOLUTION NAME}은 솔루션의 이름입니다.

  • 서버 앱의 Properties/launchSettings.json 파일에서 포트 5001 및 5002의 클라이언트 앱에 액세스하도록 Kestrel 프로필({SOLUTION NAME}.Server)의 applicationUrl을 구성합니다.

    "applicationUrl": "https://localhost:5001;https://localhost:5002",
    
  • 서버 앱의 Startup.Configure 메서드(Startup.cs)에서 UseHttpsRedirection에 대한 호출 뒤에 표시되는 다음 줄을 제거합니다.

    -app.UseBlazorFrameworkFiles();
    -app.UseStaticFiles();
    
    -app.UseRouting();
    
    -app.UseEndpoints(endpoints =>
    -{
    -    endpoints.MapRazorPages();
    -    endpoints.MapControllers();
    -    endpoints.MapFallbackToFile("index.html");
    -});
    

    클라이언트 앱에 요청을 매핑하는 미들웨어를 추가합니다. 다음 예제는 다음 경우에 실행되도록 미들웨어를 구성합니다.

    • 요청 포트가 원래 클라이언트 앱의 경우 5001이고 추가된 클라이언트 앱의 경우 5002입니다.

    • 요청 호스트가 원래 클라이언트 앱의 경우 firstapp.com이고 추가된 클라이언트 앱의 경우 secondapp.com입니다.

      참고

      이 섹션에 표시된 예제에는 다음에 대한 추가 구성이 필요합니다.

      • 예제 호스트 도메인, firstapp.comsecondapp.com에 있는 앱에 액세스.
      • TLS 보안(HTTPS)을 사용하도록 설정하는 클라이언트 앱 인증서.

      필요한 구성은 이 문서의 범위를 벗어나며 솔루션이 호스트되는 방법에 따라 달라집니다. 자세한 내용은 호스트 및 배포 문서를 참조하세요.

    이전에 줄이 제거된 곳에 다음 코드를 넣습니다.

    app.MapWhen(ctx => ctx.Request.Host.Port == 5001 || 
        ctx.Request.Host.Equals("firstapp.com"), first =>
    {
        first.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/FirstApp" + ctx.Request.Path;
            return nxt();
        });
    
        first.UseBlazorFrameworkFiles("/FirstApp");
        first.UseStaticFiles();
        first.UseStaticFiles("/FirstApp");
        first.UseRouting();
    
        first.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/FirstApp/{*path:nonfile}", 
                "FirstApp/index.html");
        });
    });
    
    app.MapWhen(ctx => ctx.Request.Host.Port == 5002 || 
        ctx.Request.Host.Equals("secondapp.com"), second =>
    {
        second.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/SecondApp" + ctx.Request.Path;
            return nxt();
        });
    
        second.UseBlazorFrameworkFiles("/SecondApp");
        second.UseStaticFiles();
        second.UseStaticFiles("/SecondApp");
        second.UseRouting();
    
        second.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/SecondApp/{*path:nonfile}", 
                "SecondApp/index.html");
        });
    });
    

    UseStaticFiles에 대한 자세한 내용은 ASP.NET Core Blazor 정적 파일을 참조하세요.

    UseBlazorFrameworkFilesMapFallbackToFile에 대한 내용은 다음 리소스를 참조하세요.

    참고

    ASP.NET Core 참조 원본에 대한 설명서 링크는 ASP.NET Core의 다음 릴리스를 위한 제품 단위의 현재 개발을 나타내는 리포지토리의 main 분기를 로드합니다. 다른 릴리스에 대한 분기를 선택하려면 Switch branches or tags(분기 또는 태그 전환) 드롭다운 목록을 사용하여 분기를 선택합니다. 예를 들어 release/6.0 ASP.NET Core 6.0 릴리스에 대한 분기를 선택합니다.

  • 서버 앱의 일기 예보 컨트롤러(Controllers/WeatherForecastController.cs)에서 기존 WeatherForecastController 경로([Route("[controller]")])를 다음 경로로 바꿉니다.

    [Route("FirstApp/[controller]")]
    [Route("SecondApp/[controller]")]
    

    이전에 서버 앱의 Startup.Configure 메서드에 추가된 미들웨어는 /WeatherForecast로 들어오는 요청을 포트(5001/5002) 또는 도메인(firstapp.com/secondapp.com)에 따라 /FirstApp/WeatherForecast 또는 /SecondApp/WeatherForecast로 수정합니다. 서버 앱에서 클라이언트 앱으로 날씨 데이터를 반환하려면 이전 컨트롤러 경로가 필요합니다.

여러 Blazor WebAssembly 앱에 대한 정적 자산 및 클래스 라이브러리

정적 자산 참고에는 다음 방법을 사용합니다.

  • 자산이 클라이언트 앱의 wwwroot 폴더에 있는 경우 정상적으로 해당 경로를 제공합니다.

    <img alt="..." src="/{PATH AND FILE NAME}" />
    

    {PATH AND FILE NAME} 자리 표시자는 wwwroot 아래의 경로 및 파일 이름입니다.

  • 자산이 Razor 클래스 라이브러리(RCL)wwwroot 폴더에 있는 경우 ASP.NET Core를 사용하여 클래스 라이브러리에서 재사용 가능한 Razor UI의 지침에 따라 클라이언트 앱에서 정적 자산을 참조합니다.

    <img alt="..." src="_content/{PACKAGE ID}/{PATH AND FILE NAME}" />
    

    {PACKAGE ID} 자리 표시자는 라이브러리의 {PACKAGE ID}입니다. <PackageId>가 프로젝트 파일에 지정되지 않은 경우 패키지 ID는 기본적으로 프로젝트의 어셈블리 이름으로 설정됩니다. {PATH AND FILE NAME} 자리 표시자는 wwwroot 아래의 경로 및 파일 이름입니다.

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

독립 실행형 배포

‘독립 실행형 배포’는 Blazor WebAssembly 앱을 클라이언트가 직접 요청하는 정적 파일 세트로 제공합니다. 모든 정적 파일 서버는 Blazor 앱을 사용할 수 있습니다.

독립 실행형 배포 자산은 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 폴더에 게시됩니다.

Azure App Service

Blazor WebAssembly 앱은 IIS에서 앱을 호스트하는 Windows의 Azure App Services에 배포할 수 있습니다.

Linux용 Azure App Service에 독립 실행형 Blazor WebAssembly 앱 배포는 현재 지원되지 않습니다. 이 시나리오를 지원하는 Azure Static Web Apps를 사용하는 독립 실행형 Blazor WebAssembly 앱을 호스팅하는 것이 좋습니다.

Azure Static Web Apps

더 많은 정보는 자습서: Azure Static Web Apps에서 Blazor를 사용하여 정적 웹앱 빌드를 참고하세요.

IIS

IIS는 Blazor 앱에 사용할 수 있는 정적 파일 서버입니다. Blazor를 호스트하도록 IIS를 구성하려면 IIS에서 정적 웹 사이트 작성을 참조하세요.

게시된 자산은 /bin/Release/{TARGET FRAMEWORK}/publish 폴더에 생성됩니다. 웹 서버 또는 호스팅 서비스에서 publish 폴더의 콘텐츠를 호스트합니다.

web.config

Blazor 프로젝트가 게시되면 다음 IIS 구성을 사용하여 web.config 파일이 생성됩니다.

  • MIME 형식
  • 다음 MIME 형식에 대해 HTTP 압축을 사용합니다.
    • application/octet-stream
    • application/wasm
  • URL 재작성 모듈 규칙을 설정합니다.
    • 앱의 정적 자산이 상주하는 하위 디렉터리(wwwroot/{PATH REQUESTED})를 제공합니다.
    • 파일이 아닌 자산 요청이 정적 자산 폴더에 있는 앱의 기본 문서(wwwroot/index.html)로 리디렉션되도록 SPA 대체(fallback) 라우팅을 만듭니다.

사용자 지정 web.config 사용

사용자 지정 web.config 파일을 사용하려면 다음을 수행합니다.

  1. 프로젝트의 루트 폴더에 사용자 지정 web.config 파일을 저장합니다. 호스트된 Blazor WebAssembly 솔루션의 경우 Server 프로젝트의 폴더에 파일을 저장합니다.

  2. 프로젝트 파일(.csproj)에서 <PublishIISAssets> 속성을 true로 설정합니다. 호스트된 Blazor WebAssembly 솔루션의 경우 Server 프로젝트의 프로젝트 파일에서 속성을 설정합니다.

    <PropertyGroup>
      <PublishIISAssets>true</PublishIISAssets>
    </PropertyGroup>
    
  3. 프로젝트를 게시합니다. 호스트된 Blazor WebAssembly 솔루션의 경우 Server 프로젝트에서 솔루션을 게시합니다. 자세한 내용은 ASP.NET Core Blazor 호스트 및 배포를 참조하세요.

URL 재작성 모듈 설치

URL을 다시 생성하려면 URL 다시 생성 모듈이 필요합니다. 이 모듈은 기본적으로 설치되지 않으며 웹 서버(IIS) 역할 서비스 기능으로 설치하는 데 사용할 수 없습니다. 이 모듈은 IIS 웹 사이트에서 다운로드해야 합니다. 웹 플랫폼 설치 관리자를 사용하여 이 모듈을 설치합니다.

  1. 로컬에서 URL 다시 생성 모듈 다운로드 페이지로 이동합니다. 영어 버전의 경우 WebPI를 선택하여 WebPI 설치 관리자를 다운로드합니다. 다른 언어의 경우 서버에 맞는 아키텍처(x86 x64)를 선택하여 설치 관리자를 다운로드합니다.
  2. 설치 관리자를 서버에 복사합니다. 설치 관리자를 실행합니다. 설치 버튼을 선택하고 사용 조건에 동의합니다. 설치가 완료된 후 서버를 다시 시작하지 않아도 됩니다.

웹 사이트 구성

웹 사이트의 실제 경로를 앱의 폴더로 설정합니다. 이 폴더는 다음을 포함합니다.

  • 필요한 리디렉션 규칙 및 파일 콘텐츠 형식 등 IIS가 웹 사이트를 구성하기 위해 사용하는 web.config 파일
  • 앱의 정적 자산 폴더입니다.

IIS 하위 앱으로 호스트

독립 실행형 앱이 IIS 하위 앱으로 호스트되는 경우 다음 중 하나를 수행합니다.

  • 상속된 ASP.NET Core 모듈 처리기를 사용하지 않도록 설정합니다.

    파일의 <system.webServer> 섹션에 <handlers> 섹션을 추가하여 Blazor 앱의 게시된 web.config 파일에서 처리기를 제거합니다.

    <handlers>
      <remove name="aspNetCore" />
    </handlers>
    
  • inheritInChildApplicationsfalse로 설정한 상태에서 <location> 요소를 사용하여 루트(상위) 앱의 <system.webServer> 섹션의 상속을 사용하지 않도록 설정합니다.

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <location path="." inheritInChildApplications="false">
        <system.webServer>
          <handlers>
            <add name="aspNetCore" ... />
          </handlers>
          <aspNetCore ... />
        </system.webServer>
      </location>
    </configuration>
    

    참고

    루트(부모) 앱의 <system.webServer> 섹션의 상속을 사용하지 않도록 하는 것은 .NET SDK를 사용하는 게시된 앱에 대한 기본 구성입니다.

처리기 제거 또는 상속을 사용하지 않도록 하는 설정은 앱의 기본 경로 구성에 더하여 수행됩니다. 앱의 index.html 파일에 있는 앱 기본 경로를 IIS에서 하위 앱을 구성할 때 사용되는 IIS 별칭으로 설정합니다.

Brotli 및 Gzip 압축

이 섹션은 독립 실행형 Blazor WebAssembly 앱에만 적용됩니다. 호스트된 Blazor 앱은 이 섹션에 연결된 파일이 아니라 기본 ASP.NET Core 앱 web.config 파일을 사용합니다.

web.config를 통해 IIS를 구성하여 독립 실행형 Blazor WebAssembly 앱을 위한 Brotli 또는 Gzip 압축 Blazor 자산을 제공할 수 있습니다. 구성 파일 예제는 web.config를 참조하세요.

다음 시나리오에서는 예제 web.config 파일의 추가 구성이 필요할 수 있습니다.

  • 앱 사양에는 다음 중 하나가 필요합니다.
    • 예제 web.config 파일에 의해 구성되지 않은 압축 파일 제공.
    • 예제 web.config 파일에 의해 구성된 압축 파일을 압축되지 않은 형식으로 제공.
  • 서버의 IIS 구성(예: applicationHost.config)은 서버 수준 IIS 기본값을 제공합니다. 서버 수준 구성에 따라 예제 web.config 파일에 포함된 것과 다른 IIS 구성이 앱에 필요할 수 있습니다.

문제 해결

500 - 내부 서버 오류가 수신되고 웹 사이트의 구성에 액세스를 시도할 때 IIS 관리자가 오류를 표시하면 URL 다시 생성 모듈이 설치되었는지 확인합니다. 모듈이 설치되지 않은 경우 IIS가 web.config 파일을 구문 분석할 수 없습니다. 그러면 IIS 관리자가 웹 사이트의 구성을 로드할 수 없으며 웹 사이트가 Blazor의 정적 파일을 제공할 수 없습니다.

IIS로의 배포 문제 해결에 대한 자세한 내용은 Azure App Service 및 IIS에서 ASP.NET Core 문제 해결을 참조하세요.

Azure Storage

Azure Storage 정적 파일 호스팅으로 서버리스 Blazor 앱 호스팅이 가능합니다. 사용자 지정 도메인 이름, Azure 콘텐츠 배달 네트워크(CDN) 및 HTTPS가 지원됩니다.

스토리지 계정에서 정적 웹 사이트 호스팅을 위해 BLOB 서비스를 사용할 수 있는 경우:

  • 인덱스 문서 이름index.html로 설정합니다.
  • 오류 문서 경로index.html로 설정합니다. Razor 구성 요소 및 기타 파일이 아닌 엔드포인트는 Blob 서비스에 의해 저장된 정적 콘텐츠의 실제 경로에 존재하지 않습니다. 이러한 리소스 중 하나를 Blazor 라우터가 처리해야 한다는 요청이 수신되면 BLOB 서비스에 의해 생성된 404 - 찾을 수 없음 오류가 요청을 오류 문서 경로로 라우팅합니다. index.html BLOB이 반환되고 Blazor 라우터가 로드되어 경로를 처리합니다.

파일의 Content-Type 헤더에 부적절한 MIME 형식으로 인해 런타임에 파일이 로드되지 않을 경우 다음 작업 중 하나를 수행합니다.

  • 파일이 배포될 때 올바른 MIME 형식(Content-Type 헤더)을 설정하도록 도구를 구성합니다.

  • 앱이 배포된 후 파일에 대한 MIME 형식(Content-Type 헤더)을 변경합니다.

    각 파일의 스토리지 탐색기(Azure Portal)에서

    1. 파일을 마우스 오른쪽 단추로 클릭한 다음 속성을 선택합니다.
    2. ContentType을 설정하고 저장 단추를 선택합니다.

자세한 내용은 Azure Storage에서 정적 웹 사이트 호스팅을 참조하세요.

Nginx

다음 nginx.conf 파일은 Nginx가 디스크에서 대응하는 파일을 찾을 수 없을 때마다 index.html 파일을 보내도록 구성하는 방법을 보여 주기 위해 단순화되었습니다.

events { }
http {
    server {
        listen 80;

        location / {
            root      /usr/share/nginx/html;
            try_files $uri $uri/ /index.html =404;
        }
    }
}

limit_req를 사용하여 NGINX 버스트 속도 한도를 설정하는 경우 Blazor WebAssembly 앱에서 수행하는 비교적 많은 수의 요청을 수용하기 위해 큰 burst 매개 변수 값이 필요할 수 있습니다. 처음에는 값을 60 이상으로 설정합니다.

http {
    server {
        ...

        location / {
            ...

            limit_req zone=one burst=60 nodelay;
        }
    }
}

브라우저 개발자 도구 또는 네트워크 트래픽 도구에서 요청이 ‘503 - 서비스를 사용할 수 없음’ 상태 코드를 수신하는 것으로 확인되는 경우 값을 늘립니다.

프로덕션 Nginx 웹 서버 구성에 대한 자세한 내용은 NGINX Plus 및 NGINX 구성 파일 만들기를 참조하세요.

Apache

CentOS 7 이상에 Blazor WebAssembly 앱을 배포하려면 다음을 수행합니다.

  1. Apache 구성 파일을 만듭니다. 다음 예제는 단순화된 구성 파일입니다(blazorapp.config).

    <VirtualHost *:80>
        ServerName www.example.com
        ServerAlias *.example.com
    
        DocumentRoot "/var/www/blazorapp"
        ErrorDocument 404 /index.html
    
        AddType application/wasm .wasm
        AddType application/octet-stream .dll
    
        <Directory "/var/www/blazorapp">
            Options -Indexes
            AllowOverride None
        </Directory>
    
        <IfModule mod_deflate.c>
            AddOutputFilterByType DEFLATE text/css
            AddOutputFilterByType DEFLATE application/javascript
            AddOutputFilterByType DEFLATE text/html
            AddOutputFilterByType DEFLATE application/octet-stream
            AddOutputFilterByType DEFLATE application/wasm
            <IfModule mod_setenvif.c>
          BrowserMatch ^Mozilla/4 gzip-only-text/html
          BrowserMatch ^Mozilla/4.0[678] no-gzip
          BrowserMatch bMSIE !no-gzip !gzip-only-text/html
      </IfModule>
        </IfModule>
    
        ErrorLog /var/log/httpd/blazorapp-error.log
        CustomLog /var/log/httpd/blazorapp-access.log common
    </VirtualHost>
    
  2. Apache 구성 파일을 CentOS 7의 기본 Apache 구성 디렉터리인 /etc/httpd/conf.d/ 디렉터리에 저장합니다.

  3. 앱의 파일을 /var/www/blazorapp 디렉터리(구성 파일의 DocumentRoot에 지정된 위치)에 저장합니다.

  4. Apache 서비스를 다시 시작합니다.

자세한 내용은 mod_mimemod_deflate를 참조하세요.

GitHub 페이지

URL 다시 쓰기를 처리하려면 index.html 페이지로 요청 리디렉션을 처리하는 스크립트를 사용하여 wwwroot/404.html 파일을 추가합니다. 예제는 SteveSandersonMS/BlazorOnGitHubPages GitHub 리포지토리를 참조하세요.

조직 사이트 대신 프로젝트 사이트를 사용하는 경우 wwwroot/index.html<base> 태그를 업데이트합니다. href 특성 값을 후행 슬래시가 있는 GitHub 리포지토리 이름으로 설정합니다(예: /my-repository/). SteveSandersonMS/BlazorOnGitHubPages GitHub 리포지토리에서 기본 href.github/workflows/main.yml 구성 파일에 의해 게시 시 업데이트됩니다.

참고

SteveSandersonMS/BlazorOnGitHubPages GitHub 리포지토리는 .NET Foundation 또는 Microsoft에서 소유, 유지 관리 또는 지원하지 않습니다.

호스트 구성 값

Blazor WebAssembly 앱은 개발 환경의 런타임에 다음 호스트 구성 값을 명령줄 인수로 허용할 수 있습니다.

콘텐츠 루트

--contentroot 인수는 앱의 콘텐츠 파일을 포함하는 디렉터리(콘텐츠 루트)에 대한 절대 경로를 설정합니다. 다음 예제에서 /content-root-path는 앱의 콘텐츠 루트 경로입니다.

  • 명령 프롬프트에서 앱을 로컬로 실행할 때 인수를 전달합니다. 앱의 디렉터리에서 다음을 실행합니다.

    dotnet run --contentroot=/content-root-path
    
  • IIS Express 프로필에서 앱의 launchSettings.json 파일에 항목을 추가합니다. 앱이 Visual Studio 디버거를 통해 실행되거나 dotnet run을 사용하여 명령 프롬프트에서 실행되는 경우 이 설정이 사용됩니다.

    "commandLineArgs": "--contentroot=/content-root-path"
    
  • Visual Studio의 속성>디버그>애플리케이션 인수에서 인수를 지정합니다. Visual Studio 속성 페이지에서 인수를 설정하면 해당 인수가 launchSettings.json 파일에 추가됩니다.

    --contentroot=/content-root-path
    

경로 기준

--pathbase 인수는 루트가 아닌 상대 URL 경로를 사용하여 로컬로 앱을 실행하기 위한 앱 기본 경로를 설정합니다. (준비 및 프로덕션의 경우 <base> 태그 href/ 이외의 경로로 설정됩니다.) 다음 예제에서 /relative-URL-path는 앱의 경로 기준입니다. 자세한 내용은 앱 기본 경로를 참조하세요.

중요

<base> 태그의 href에 대해 제공되는 경로와 달리 --pathbase 인수 값을 전달할 때 뒤에 슬래시(/)를 포함하지 않습니다. 앱 기본 경로가 <base> 태그에 <base href="/CoolApp/">로 제공되는 경우(뒤에 슬래시 포함) 명령줄 인수 값을 --pathbase=/CoolApp로 전달합니다(뒤에 슬래시 없음).

  • 명령 프롬프트에서 앱을 로컬로 실행할 때 인수를 전달합니다. 앱의 디렉터리에서 다음을 실행합니다.

    dotnet run --pathbase=/relative-URL-path
    
  • IIS Express 프로필에서 앱의 launchSettings.json 파일에 항목을 추가합니다. 앱을 Visual Studio 디버거를 통해 실행하거나 dotnet run을 사용하여 명령 프롬프트에서 실행하는 경우 이 설정이 사용됩니다.

    "commandLineArgs": "--pathbase=/relative-URL-path"
    
  • Visual Studio의 속성>디버그>애플리케이션 인수에서 인수를 지정합니다. Visual Studio 속성 페이지에서 인수를 설정하면 해당 인수가 launchSettings.json 파일에 추가됩니다.

    --pathbase=/relative-URL-path
    

URL

--urls 인수는 요청을 수신하기 위한 포트 및 프로토콜을 포함하는 IP 주소 또는 호스트 주소를 설정합니다.

  • 명령 프롬프트에서 앱을 로컬로 실행할 때 인수를 전달합니다. 앱의 디렉터리에서 다음을 실행합니다.

    dotnet run --urls=http://127.0.0.1:0
    
  • IIS Express 프로필에서 앱의 launchSettings.json 파일에 항목을 추가합니다. 앱을 Visual Studio 디버거를 통해 실행하거나 dotnet run을 사용하여 명령 프롬프트에서 실행하는 경우 이 설정이 사용됩니다.

    "commandLineArgs": "--urls=http://127.0.0.1:0"
    
  • Visual Studio의 속성>디버그>애플리케이션 인수에서 인수를 지정합니다. Visual Studio 속성 페이지에서 인수를 설정하면 해당 인수가 launchSettings.json 파일에 추가됩니다.

    --urls=http://127.0.0.1:0
    

트리머 구성

Blazor는 각 릴리스 빌드에 IL(중간 언어) 트리밍을 수행하여 출력 어셈블리에서 필요 없는 IL을 제거합니다. 자세한 내용은 ASP.NET Core Blazor용 트리머 구성을 참조하세요.

DLL 파일의 파일 이름 확장명 변경

앱 게시 .dll 파일의 파일 이름 확장명을 변경해야 하는 경우 이 섹션의 지침을 따르세요.

앱을 게시한 후 셸 스크립트 또는 DevOps 빌드 파이프라인을 사용하여 다른 파일 확장명을 사용하도록 .dll 파일의 이름을 바꿉니다. 앱 게시 출력(예: {CONTENT ROOT}/bin/Release/netstandard2.1/publish/wwwroot)의 wwwroot 디렉터리에 있는 .dll 파일을 대상으로 합니다.

다음 예제에서는 .bin 파일 확장명을 사용하도록 .dll 파일의 이름이 바뀝니다.

Windows에서:

dir .\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content .\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content .\_framework\blazor.boot.json

서비스 작업자 자산도 사용 중인 경우 다음 명령을 추가합니다.

((Get-Content .\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content .\service-worker-assets.js

Linux 또는 macOS에서:

for f in _framework/_bin/*; do mv "$f" "`echo $f | sed -e 's/\.dll/.bin/g'`"; done
sed -i 's/\.dll"/.bin"/g' _framework/blazor.boot.json

서비스 작업자 자산도 사용 중인 경우 다음 명령을 추가합니다.

sed -i 's/\.dll"/.bin"/g' service-worker-assets.js

.bin 이외의 다른 파일 확장명을 사용하려면 이전 명령에서 .bin을 바꿉니다.

압축된 blazor.boot.json.gzblazor.boot.json.br 파일을 처리하려면 다음 방법 중 하나를 채택합니다.

  • 압축된 blazor.boot.json.gzblazor.boot.json.br 파일을 제거합니다. 압축은 이 접근 방법으로 사용하지 않도록 설정됩니다.
  • 업데이트된 blazor.boot.json 파일을 다시 압축합니다.

위의 지침은 서비스 작업자 자산을 사용 중인 경우에도 적용됩니다. wwwroot/service-worker-assets.js.brwwwroot/service-worker-assets.js.gz를 제거하거나 다시 압축합니다. 그렇지 않으면 브라우저에서 파일 무결성 검사가 실패합니다.

다음 Windows 예제에서는 프로젝트의 루트에 배치된 PowerShell 스크립트를 사용합니다.

ChangeDLLExtensions.ps1::

param([string]$filepath,[string]$tfm)
dir $filepath\bin\Release\$tfm\wwwroot\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json
Remove-Item $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json.gz

서비스 작업자 자산도 사용 중인 경우 다음 명령을 추가합니다.

((Get-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js

프로젝트 파일에서 스크립트는 앱을 게시한 후에 실행됩니다.

<Target Name="ChangeDLLFileExtensions" AfterTargets="Publish" Condition="'$(Configuration)'=='Release'">
  <Exec Command="powershell.exe -command &quot;&amp; { .\ChangeDLLExtensions.ps1 '$(SolutionDir)' '$(TargetFramework)'}&quot;" />
</Target>

참고

동일한 어셈블리의 이름을 변경하고 지연 로드하는 경우 ASP.NET Core Blazor WebAssembly에서 어셈블리 지연 로드의 지침을 참조하세요.

이전 배포 손상

일반적으로 배포 시 다음 현상이 나타납니다.

  • 변경된 파일만 대체되어 일반적으로 배포 속도가 빨라집니다.
  • 새 배포에 포함되지 않은 기존 파일은 새 배포에서 사용할 수 있도록 남아 있습니다.

드문 경우에 이전 배포에서 남은 파일 때문에 새 배포가 손상될 수 있습니다. 기존 배포(또는 배포 전에 로컬로 게시된 앱)를 완전히 삭제하면 손상된 배포와 관련된 문제가 해결될 수 있습니다. 종종 기존 배포를 한번 삭제하면 DevOps 빌드 및 배포 파이프라인을 포함하는 문제를 해결할 수 있습니다.

DevOps 빌드 및 배포 파이프라인을 사용할 때 항상 이전 배포를 지워야 하는 경우 손상의 정확한 원인을 해결할 때까지 새 배포마다 이전 배포를 삭제하는 단계를 빌드 파이프라인에 일시적으로 추가할 수 있습니다.

무결성 검사 실패 해결

Blazor WebAssembly는 앱의 시작 파일을 다운로드할 때 응답에 대한 무결성 검사를 수행하도록 브라우저에 지시합니다. Blazor는 blazor.boot.json 파일 내의 DLL(.dll), WebAssembly(.wasm) 및 다른 파일에 대한 SHA-256 해시 값을 전송하며, 이 값은 클라이언트에 캐시되지 않습니다. 캐시된 파일의 파일 해시는 blazor.boot.json 파일의 해시와 비교됩니다. 캐시된 파일에 일치하는 해시가 있는 경우 Blazor가 캐시된 파일을 사용합니다. 그렇지 않으면 서버에 파일을 요청합니다. 파일을 다운로드한 후 무결성 유효성 검사를 위해 해당 해시를 다시 확인합니다. 다운로드한 파일의 무결성 검사가 실패하면 브라우저에서 오류가 생성됩니다.

파일 무결성을 관리하기 위한 Blazor의 알고리즘:

  • 사용자가 애플리케이션 파일을 다운로드하는 동안 새 배포가 웹 서버에 적용되는 경우와 같이 앱이 일관되지 않은 파일 세트를 로드할 위험이 없도록 합니다. 파일이 일관되지 않으면 앱이 오작동할 수 있습니다.
  • 사용자의 브라우저가 일관되지 않은 또는 잘못된 응답을 캐시하지 않도록 합니다. 일관되지 않거나 잘못된 응답을 캐시할 경우 사용자가 수동으로 페이지를 새로 고치더라도 앱이 시작하지 않을 수 있습니다.
  • 응답을 안전하게 캐시하며 예상된 SHA-256 해시가 변경될 때까지 서버 쪽 변경 내용을 확인하지 않도록 하므로 후속 페이지 로드는 더 적은 요청을 포함하여 더 빠르게 완료됩니다.

웹 서버가 예상된 SHA-256 해시와 일치하지 않는 응답을 반환하는 경우에는 브라우저의 개발자 콘솔에 다음과 같은 오류가 표시됩니다.

컴퓨팅된 SHA-256 무결성 ‘IIa70iwvmEg5WiDV17OpQ5eCztNYqL186J56852RpJY=’가 지정된 ‘https://myapp.example.com/_framework/MyBlazorApp.dlll’ 리소스의 ‘integrity’ 특성에서 유효한 다이제스트를 찾지 못했습니다. 리소스가 차단되었습니다.

대부분의 경우 경고는 무결성 검사에 문제가 있음을 나타내지 않습니다. 대신, 경고는 일반적으로 다른 문제가 있음을 의미합니다.

Blazor WebAssembly의 부팅 참조 원본은 dotnet/aspnetcore GitHub 리포지토리의 Boot.WebAssembly.ts 파일을 참조하세요.

참고

ASP.NET Core 참조 원본에 대한 설명서 링크는 ASP.NET Core의 다음 릴리스를 위한 제품 단위의 현재 개발을 나타내는 리포지토리의 main 분기를 로드합니다. 다른 릴리스에 대한 분기를 선택하려면 Switch branches or tags(분기 또는 태그 전환) 드롭다운 목록을 사용하여 분기를 선택합니다. 예를 들어 release/6.0 ASP.NET Core 6.0 릴리스에 대한 분기를 선택합니다.

무결성 문제 진단

앱이 빌드되면 생성된 blazor.boot.json 매니페스트는 빌드 출력이 생성될 때 부팅 리소스의 SHA-256 해시를 설명합니다. blazor.boot.json의 SHA-256 해시가 브라우저에 배달된 파일과 일치하는 경우 무결성 검사가 통과합니다.

이 작업이 실패하는 일반적인 이유는 다음과 같습니다.

  • 웹 서버의 응답은 브라우저가 요청한 파일이 아닌 오류(예: ‘404 - 찾을 수 없음’ 또는 ‘500 - 내부 서버 오류’)입니다. 이는 브라우저에서 응답 실패가 아닌 무결성 검사 실패로 보고됩니다.
  • 무엇인가 브라우저에 대한 파일의 빌드와 제공 사이에 파일 콘텐츠를 변경했습니다. 이 문제가 발생할 수 있는 경우는 다음과 같습니다.
    • 사용자 또는 빌드 도구가 빌드 출력을 수동으로 수정하는 경우.
    • 배포 프로세스의 일부 측면이 파일을 수정한 경우. 예를 들어 Git 기반 배포 메커니즘을 사용하는 경우 파일을 Windows에서 커밋하고 Linux에서 체크 아웃하는 경우 Git에서는 Windows 스타일 줄 끝을 Unix 스타일 줄 끝으로 투명하게 변환합니다. 파일 줄 끝을 변경하면 SHA-256 해시가 변경됩니다. 이 문제를 방지하려면 .gitattributes를 사용하여 빌드 아티팩트를 binary 파일로 처리하는 것이 좋습니다.
    • 웹 서버는 파일 콘텐츠를 제공하는 동안 수정합니다. 예를 들어 일부 CDN(Content Delivery Network)은 자동으로 HTML을 축소함으로써 수정합니다. 관련 기능을 사용하지 않도록 설정해야 할 수 있습니다.
  • blazor.boot.json 파일이 제대로 로드되지 않거나 클라이언트에 잘못 캐시됩니다. 일반적인 원인은 다음 중 하나입니다.
    • 사용자 지정 개발자 코드가 잘못 구성되었거나 오작동합니다.
    • 하나 이상의 중간 캐싱 레이어가 잘못 구성되었습니다.

어떤 기능이 해당 사례에서 적용되는지 진단하려면:

  1. 오류 메시지를 읽어서 어떤 파일이 오류를 트리거하는지 확인합니다.
  2. 브라우저의 개발자 도구를 열고 ‘네트워크’ 탭을 확인합니다. 필요한 경우 페이지를 다시 로드하여 요청 및 응답 목록을 확인합니다. 해당 목록에서 오류를 트리거하는 파일을 찾습니다.
  3. 응답의 HTTP 상태 코드를 확인합니다. 서버가 200 - OK(또는 또 다른 2xx 상태 코드) 이외의 값을 반환하면 진단해야 하는 서버 쪽 문제가 있는 것입니다. 예를 들어 상태 코드 403은 권한 부여 문제가 있음을 의미하는 반면, 상태 코드 500은 서버가 지정되지 않은 방식으로 실패함을 의미합니다. 서버 쪽 로그를 참조하여 앱을 진단하고 수정합니다.
  4. 리소스의 상태 코드가 200 - OK인 경우 브라우저의 개발자 도구에서 응답 콘텐츠를 보고 예상되는 데이터와 콘텐츠가 일치하는지 확인합니다. 예를 들어 일반적인 문제는 요청이 다른 파일에 대해서도 index.html 데이터를 반환하도록 라우팅을 잘못 구성하는 것입니다. .wasm 요청에 대한 응답이 WebAssembly 이진 파일이고 .dll 요청에 대한 응답이 .NET 어셈블리 이진 파일인지 확인합니다. 그렇지 않으면 진단해야 할 서버 쪽 라우팅 문제가 있는 것입니다.
  5. 무결성 PowerShell 스크립트 문제 해결을 사용하여 게시 및 배포된 앱 출력의 유효성을 검사해 보세요.

서버가 올바른 것 같은 데이터를 반환하고 있는지 확인하는 경우 파일의 빌드와 제공 사이에 콘텐츠를 수정하는 다른 항목이 있어야 합니다. 이를 조사하려면:

  • 파일이 빌드된 후 파일을 수정 중인 경우 빌드 도구 체인 및 배포 메커니즘을 검토합니다. 여기에 해당하는 예제는 앞에서 설명한 대로 Git이 파일 줄 끝을 변환하는 경우입니다.
  • 응답을 동적으로 수정하도록(예: HTML 축소 시도) 설정된 경우 웹 서버 또는 CDN 구성을 검토합니다. 압축을 푼 후 결과에 영향을 주지 않으므로 웹 서버가 HTTP 압축을 구현해도 괜찮습니다(예: content-encoding: br 또는 content-encoding: gzip 반환). 그러나 웹 서버가 압축되지 않은 데이터를 수정하는 것은 괜찮지 ‘않습니다’.

무결성 PowerShell 스크립트 문제 해결

integrity.ps1 PowerShell 스크립트를 사용하여 게시 및 배포된 Blazor 앱의 유효성을 검사합니다. 이 스크립트는 앱에 Blazor 프레임워크가 식별할 수 없는 무결성 문제가 있는 경우 PowerShell Core 7 이상에 시작 지점으로 제공됩니다. 버전 7.2.0 이상 버전의 PowerShell을 실행하는 경우를 포함하여 앱을 위해 스크립트를 사용자 지정해야 할 수 있습니다.

이 스크립트는 publish 폴더의 파일과 배포된 앱에서 다운로드한 파일을 검사하여 무결성 해시가 포함된 다른 매니페스트의 문제를 검색합니다. 이 검사에서 다음과 같은 가장 일반적인 문제가 검색되어야 합니다.

  • 게시된 출력의 파일을 사용자가 무심코 수정했습니다.
  • 앱이 배포 대상에 올바르게 배포되지 않았거나 배포 대상 환경 내에 변경된 내용이 있습니다.
  • 배포된 앱과 앱 게시의 출력 간에 차이가 있습니다.

PowerShell 명령 셸에서 다음 명령을 사용하여 스크립트를 호출합니다.

.\integrity.ps1 {BASE URL} {PUBLISH OUTPUT FOLDER}

다음 예제에서 스크립트는 https://localhost:5001/에서 로컬로 실행 중인 앱에서 실행됩니다.

.\integrity.ps1 https://localhost:5001/ C:\TestApps\BlazorSample\bin\Release\net6.0\publish\

자리 표시자:

  • {BASE URL}: 배포된 앱의 URL입니다. 후행 슬래시(/)가 필요합니다.
  • {PUBLISH OUTPUT FOLDER}: 앱의 publish 폴더 또는 배포를 위해 앱이 게시된 위치의 경로입니다.

참고

dotnet/AspNetCore.Docs GitHub 리포지토리를 복제하는 경우 integrity.ps1 스크립트는 Bitdefender 또는 시스템에 있는 다른 바이러스 검색 프로그램으로 격리될 수 있습니다. 일반적으로 파일은 바이러스 검사 프로그램의 추론 검색 기술을 통해 트래핑됩니다. 이 기술은 파일에서 맬웨어가 있음을 나타낼 수 있는 패턴만 찾습니다. 바이러스 검사 프로그램이 파일을 격리하지 않도록 하려면 리포지토리를 복제하기 전에 바이러스 검사 프로그램에 예외를 추가합니다. 다음 예제는 Windows 시스템의 일반적인 스크립트 경로입니다. 다른 시스템의 필요에 따라 경로를 조정합니다. 자리 표시자 {USER}는 사용자의 패스 세그먼트입니다.

C:\Users\{USER}\Documents\GitHub\AspNetCore.Docs\aspnetcore\blazor\host-and-deploy\webassembly\_samples\integrity.ps1

경고: 바이러스 검사 예외를 만드는 것은 위험하며 파일이 안전하다는 것을 확신할 때만 수행해야 합니다.

파일의 체크섬을 유효한 체크섬 값과 비교해도 파일 보안을 보장할 수 없지만 체크섬 값을 유지하는 방식으로 파일을 수정하는 것은 악의적인 사용자에게 사소하지 않습니다. 따라서 체크섬은 일반적인 보안 방법으로 유용합니다. 로컬 integrity.ps1 파일의 체크섬을 다음 값 중 하나와 비교합니다.

  • SHA256: 32c24cb667d79a701135cb72f6bae490d81703323f61b8af2c7e5e5dc0f0c2bb
  • MD5: 9cee7d7ec86ee809a329b5406fbf21a8

다음 명령을 사용하여 Windows OS에서 파일의 체크섬을 가져옵니다. {PATH AND FILE NAME} 자리 표시자의 경로 및 파일 이름을 제공하고 {SHA512|MD5} 자리 표시자에 대해 생성할 체크섬의 형식(SHA256 또는 MD5)을 나타냅니다.

CertUtil -hashfile {PATH AND FILE NAME} {SHA256|MD5}

사용자 환경에서 체크섬 유효성 검사가 충분히 안전하지 않을 수 있다고 생각될 경우 조직의 보안 책임자에게 문의하세요.

자세한 내용은 맬웨어 및 기타 위협 이해를 참조하세요.

비 PWA 앱에 대한 무결성 검사 사용 안 함

대부분의 경우 무결성 검사를 사용하지 않도록 설정하지 마세요. 무결성 검사를 사용하지 않으면 예기치 않은 응답을 발생시킨 근본적인 문제가 해결되지 않아 앞서 언급한 이점을 얻을 수 없습니다.

웹 서버를 사용하여 일관된 응답을 반환할 수 없고 근본 문제가 해결될 때까지 일시적으로 무결성 검사를 사용하지 않도록 설정할 수밖에 없는 경우가 있습니다.

무결성 검사를 사용하지 않도록 설정하려면 Blazor WebAssembly 앱의 프로젝트 파일(.csproj)에서 속성 그룹에 다음을 추가합니다.

<BlazorCacheBootResources>false</BlazorCacheBootResources>

또한, BlazorCacheBootResources 속성은 SHA-256 해시의 정확성을 기대할 수 없음을 나타내기 때문에 .dll, .wasm 및 SHA-256 해시 기반 기타 파일을 캐시하는 Blazor의 기본 동작을 사용하지 않도록 설정합니다. 이 설정을 사용하는 경우에도 브라우저의 일반 HTTP 캐시는 해당 파일을 캐시할 수 있지만 이 상황이 발생하는지는 웹 서버 구성 및 해당 구성이 제공하는 cache-control 헤더에 따라 달라집니다.

참고

BlazorCacheBootResources 속성은 PWA(프로그레시브 웹 애플리케이션)에 대한 무결성 검사를 사용하지 않도록 설정하지 않습니다. PWA 관련 지침은 PWA에 대한 무결성 검사 사용 안 함 섹션을 참조하세요.

무결성 검사를 사용하지 않도록 설정해야 하는 시나리오를 모두 나열할 수는 없습니다. 서버는 Blazor 프레임워크의 범위를 벗어나는 임의의 방법으로 요청에 응답할 수 있습니다. 프레임워크는 앱이 제공할 수 있는 무결성에 대한 보장을 하지 않는 대신 앱을 계속 실행할 수 있도록 BlazorCacheBootResources 설정을 제공합니다. 다시 한 번 말하지만, 특히 프로덕션 배포의 경우, 무결성 검사를 사용하지 않도록 설정하는 것은 권장하지 않습니다. 개발자는 무결성 검사가 실패하는 근본적인 무결성 문제를 해결하려고 노력해야 합니다.

무결성 문제를 일으킬 수 있는 몇 가지 일반적인 사례는 다음과 같습니다.

  • 무결성을 검사할 수 없는 HTTP에서 실행하는 경우
  • 배포 프로세스에서 어떤 방식으로든 게시 후 파일을 수정하는 경우
  • 호스트가 어떤 방식으로든 파일을 수정하는 경우

PWA에 대한 무결성 검사 사용 안 함

Blazor의 PWA(프로그레시브 웹 애플리케이션) 템플릿에는 오프라인에서 사용하기 위해 애플리케이션 파일을 페치하고 저장해야 하는 제안된 service-worker.published.js 파일이 포함됩니다. 이는 일반적인 앱 시작 메커니즘과 별도의 프로세스이며 자체적인 별도의 무결성 검사 논리를 포함합니다.

service-worker.published.js 파일 내에 다음 줄이 있습니다.

.map(asset => new Request(asset.url, { integrity: asset.hash }));

무결성 검사를 사용하지 않으려면 줄을 다음으로 변경하여 integrity 매개 변수를 제거합니다.

.map(asset => new Request(asset.url));

또한 무결성 검사를 사용하지 않도록 설정하면 무결성 검사를 통해 제공되는 안전 보증이 손실됩니다. 예를 들어 사용자의 브라우저가 새 버전을 배포하는 바로 그 순간에 앱을 캐시 중인 경우 이전 배포의 일부 파일과 새 배포의 일부 파일을 캐시할 수 있는 위험이 있습니다. 이 경우 추가 업데이트를 배포할 때까지 앱이 중단됨 상태로 중단됩니다.

Blazor WebAssembly 호스팅 모델을 사용하면 다음과 같이 실행됩니다.

  • Blazor 앱, 해당 앱의 종속성 및 .NET 런타임이 병렬로 브라우저에 다운로드됩니다.
  • 해당 앱은 브라우저 UI 스레드에서 직접 실행됩니다.

다음 배포 전략이 지원됩니다.

  • Blazor 앱은 ASP.NET Core 앱에서 제공됩니다. 이 전략은 ASP.NET Core를 사용하여 호스트된 배포 섹션에서 설명합니다.
  • Blazor 앱은 정적 호스팅 웹 서버 또는 서비스에 배치되며, 이 경우 Blazor 앱을 처리하기 위해 .NET을 사용하지 않습니다. 이 전략은 Blazor WebAssembly 앱을 IIS 하위 앱으로 호스트하는 방법에 대한 정보를 포함하는 독립 실행형 배포 섹션에서 설명합니다.

부팅 리소스를 로드하는 방법 사용자 지정

loadBootResource API를 사용하여 부팅 리소스를 로드하는 방법 사용자 지정 자세한 내용은 ASP.NET Core Blazor 시작을 참조하세요.

압축

Blazor WebAssembly 앱이 게시될 때 게시하는 도중에 출력을 정적으로 압축하여 앱의 크기를 줄이고 런타임 압축의 오버헤드를 제거합니다. 다음 압축 알고리즘이 사용됩니다.

Blazor는 호스트를 사용하여 적절한 압축 파일을 제공합니다. ASP.NET Core 호스트 프로젝트를 사용하는 경우 호스트 프로젝트는 콘텐츠 협상을 수행하고 정적으로 압축된 파일을 제공할 수 있습니다. Blazor WebAssembly 독립 실행형 앱을 호스트하는 경우 정적으로 압축된 파일이 제공되도록 추가 작업이 필요할 수도 있습니다.

  • IIS web.config 압축 구성에 대해서는 IIS: Brotli 및 Gzip 압축 섹션을 참조하세요.

  • GitHub 페이지와 같이 정적으로 압축된 파일 콘텐츠 협상을 지원하지 않는 정적 호스팅 솔루션에서 호스트하는 경우 Brotli로 압축된 파일을 가져와 디코딩하는 앱을 구성하는 것이 좋습니다.

    • google/brotli GitHub 리포지토리에서 JavaScript Brotli 디코더를 가져옵니다. 축소된 디코더 파일은 이름이 decode.min.js이며 리포지토리의 js 폴더에 있습니다.

      참고

      decode.js 스크립트(decode.min.js)의 축소된 버전이 실패하는 경우 축소 해제한 버전(decode.js)을 대신 사용해 보세요.

    • 디코더를 사용하도록 앱을 업데이트합니다.

      wwwroot/index.html 파일의 Blazor의 <script> 태그에서 autostartfalse로 설정합니다.

      <script src="_framework/blazor.webassembly.js" autostart="false"></script>
      

      Blazor의 <script> 태그 뒤, 닫는 </body> 태그 앞에 다음 JavaScript 코드 <script> 블록을 추가합니다.

      <script type="module">
        import { BrotliDecode } from './decode.min.js';
        Blazor.start({
          loadBootResource: function (type, name, defaultUri, integrity) {
            if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
              return (async function () {
                const response = await fetch(defaultUri + '.br', { cache: 'no-cache' });
                if (!response.ok) {
                  throw new Error(response.statusText);
                }
                const originalResponseBuffer = await response.arrayBuffer();
                const originalResponseArray = new Int8Array(originalResponseBuffer);
                const decompressedResponseArray = BrotliDecode(originalResponseArray);
                const contentType = type === 
                  'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
                return new Response(decompressedResponseArray, 
                  { headers: { 'content-type': contentType } });
              })();
            }
          }
        });
      </script>
      

      부팅 리소스를 로드하는 방법에 대한 자세한 내용은 ASP.NET Core Blazor 시작을 참조하세요.

압축을 해제하려면 앱의 프로젝트 파일에 BlazorEnableCompression MSBuild 속성을 추가하고 값을 false로 설정합니다.

<PropertyGroup>
  <BlazorEnableCompression>false</BlazorEnableCompression>
</PropertyGroup>

명령 셸에서 다음 구문을 사용하여 BlazorEnableCompression 속성을 dotnet publish 명령에 전달할 수 있습니다.

dotnet publish -p:BlazorEnableCompression=false

올바른 라우팅을 위해 URL 다시 생성

Blazor WebAssembly 앱의 페이지 구성 요소에 대한 라우팅 요청은 Blazor Server에서 호스트된 앱의 요청을 라우팅하는 것처럼 간단하지 않습니다. 다음 두 구성 요소를 사용하는 Blazor WebAssembly 앱을 살펴보겠습니다.

  • Main.razor: 앱의 루트에 로드되며 About 구성 요소에 대한 링크(href="About")를 포함합니다.
  • About.razor: About 구성 요소입니다.

브라우저의 주소 표시줄을 사용하여 앱의 기본 문서를 요청하는 경우(예: https://www.contoso.com/):

  1. 브라우저가 요청을 합니다.
  2. 기본 페이지(일반적으로 index.html)가 반환됩니다.
  3. index.html이 앱을 부트스트랩합니다.
  4. Blazor의 라우터가 로드되고 RazorMain 구성 요소가 렌더링됩니다.

Blazor 라우터는 브라우저가 인터넷에서 www.contoso.com으로 About을 요청하는 것을 중단하고 렌더링된 About 구성 요소를 직접 제공하므로 기본 페이지에서 About 구성 요소에 대한 링크 선택은 클라이언트에서 작동합니다. ‘Blazor WebAssembly 앱 내’의 내부 엔드포인트에 대한 모든 요청도 같은 방법으로 작동합니다. 요청은 인터넷상에서 서버가 호스트하는 리소스에 대한 브라우저 기반 요청을 트리거하지 않습니다. 라우터가 내부적으로 요청을 처리합니다.

브라우저의 주소 표시줄을 사용하여 www.contoso.com/About을 요청하면 해당 요청이 실패합니다. 앱의 인터넷 호스트에 해당 리소스가 없으므로 404 - 찾을 수 없음 응답이 반환됩니다.

브라우저는 인터넷 기반 호스트에 클라이언트 쪽 페이지를 요청하므로, 웹 서버 및 호스팅 서비스는 서버에 실제로 존재하지 않는 리소스에 대한 모든 요청을 index.html 페이지에 다시 써야 합니다. index.html이 반환되는 경우 앱의 Blazor 라우터가 넘겨받아 올바른 리소스로 응답합니다.

IIS 서버에 배포하는 경우 앱의 게시된 web.config 파일과 함께 URL 재작성 모듈을 사용할 수 있습니다. 자세한 내용은 IIS 섹션을 참조하세요.

ASP.NET Core를 사용하여 호스트된 배포

‘호스트된 배포’는 웹 서버에서 실행되는 ASP.NET Core 앱에서 Blazor WebAssembly 앱을 브라우저에 제공하지 않습니다.

클라이언트 Blazor WebAssembly 앱이 서버 앱의 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 폴더에 서버 앱의 다른 정적 웹 자산과 함께 게시됩니다. 두 앱이 함께 배포됩니다. ASP.NET Core 앱을 호스트할 수 있는 웹 서버가 필요합니다. 호스트된 배포의 경우 Visual Studio는 Hosted (dotnet new 명령을 사용하는 경우 -ho|--hosted) 옵션이 선택된 Blazor WebAssembly 앱 프로젝트 템플릿(dotnet new 명령을 사용하는 경우 blazorwasm 템플릿)을 포함합니다.

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

여러 Blazor WebAssembly 앱을 사용하여 호스트된 배포

앱 구성

호스트된 Blazor 솔루션은 여러 Blazor WebAssembly 앱을 제공할 수 있습니다.

참고

이 섹션의 예제는 Visual Studio 솔루션 사용을 참조하지만 호스트된 Blazor WebAssembly 앱 시나리오에서 여러 클라이언트 앱이 작동하기 위해 Visual Studio 및 Visual Studio 솔루션을 사용하지 않아도 됩니다. Visual Studio를 사용하지 않는 경우 {SOLUTION NAME}.sln 파일 및 Visual Studio용으로 만든 다른 파일을 무시하세요.

다음 예제에서는

  • 초기(첫 번째) 클라이언트 앱은 Blazor WebAssembly 프로젝트 템플릿에서 만든 솔루션의 기본 클라이언트 프로젝트입니다. 첫 번째 클라이언트 앱은 포트가 5001이거나 호스트가 firstapp.com인 URL /FirstApp에서 브라우저로 액세스할 수 있습니다.
  • 두 번째 클라이언트 앱은 SecondBlazorApp.Client 솔루션에 추가됩니다. 두 번째 클라이언트 앱은 포트가 5002이거나 호스트가 secondapp.com인 URL /SecondApp에서 브라우저로 액세스할 수 있습니다.

호스트된 기존 Blazor 솔루션을 사용하거나 Blazor 호스트 프로젝트 템플릿에서 새 솔루션을 만듭니다.

  • 클라이언트 앱의 프로젝트 파일에서 값이 FirstApp<PropertyGroup><StaticWebAssetBasePath> 속성을 추가하여 프로젝트의 정적 자산에 대한 기본 경로를 설정합니다.

    <PropertyGroup>
      ...
      <StaticWebAssetBasePath>FirstApp</StaticWebAssetBasePath>
    </PropertyGroup>
    
  • 두 번째 클라이언트 앱을 솔루션에 추가합니다.

    • SecondClient라는 폴더를 솔루션의 폴더에 추가합니다. 프로젝트 템플릿에서 만든 솔루션 폴더에는 SecondClient 폴더 추가 후 다음 솔루션 파일 및 폴더가 포함됩니다.

      • Client(폴더)
      • SecondClient(폴더)
      • Server(폴더)
      • Shared(폴더)
      • {SOLUTION NAME}.sln(파일)

      자리 표시자 {SOLUTION NAME}은 솔루션의 이름입니다.

    • Blazor WebAssembly 프로젝트 템플릿의 SecondClient 폴더에 SecondBlazorApp.Client라는 Blazor WebAssembly 앱을 만듭니다.

    • SecondBlazorApp.Client 앱의 프로젝트 파일에서 다음을 수행합니다.

      • SecondApp 값을 사용하여 <PropertyGroup><StaticWebAssetBasePath> 속성을 추가합니다.

        <PropertyGroup>
          ...
          <StaticWebAssetBasePath>SecondApp</StaticWebAssetBasePath>
        </PropertyGroup>
        
      • Shared 프로젝트에 프로젝트 참조를 추가합니다.

        <ItemGroup>
          <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
        </ItemGroup>
        

        자리 표시자 {SOLUTION NAME}은 솔루션의 이름입니다.

  • 서버 앱의 프로젝트 파일에서 추가된 SecondBlazorApp.Client 클라이언트 앱에 대한 프로젝트 참조를 만듭니다.

    <ItemGroup>
      <ProjectReference Include="..\Client\{SOLUTION NAME}.Client.csproj" />
      <ProjectReference Include="..\SecondClient\SecondBlazorApp.Client.csproj" />
      <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
    </ItemGroup>
    

    자리 표시자 {SOLUTION NAME}은 솔루션의 이름입니다.

  • 서버 앱의 Properties/launchSettings.json 파일에서 포트 5001 및 5002의 클라이언트 앱에 액세스하도록 Kestrel 프로필({SOLUTION NAME}.Server)의 applicationUrl을 구성합니다.

    "applicationUrl": "https://localhost:5001;https://localhost:5002",
    
  • 서버 앱의 Startup.Configure 메서드(Startup.cs)에서 UseHttpsRedirection에 대한 호출 뒤에 표시되는 다음 줄을 제거합니다.

    -app.UseBlazorFrameworkFiles();
    -app.UseStaticFiles();
    
    -app.UseRouting();
    
    -app.UseEndpoints(endpoints =>
    -{
    -    endpoints.MapRazorPages();
    -    endpoints.MapControllers();
    -    endpoints.MapFallbackToFile("index.html");
    -});
    

    클라이언트 앱에 요청을 매핑하는 미들웨어를 추가합니다. 다음 예제는 다음 경우에 실행되도록 미들웨어를 구성합니다.

    • 요청 포트가 원래 클라이언트 앱의 경우 5001이고 추가된 클라이언트 앱의 경우 5002입니다.

    • 요청 호스트가 원래 클라이언트 앱의 경우 firstapp.com이고 추가된 클라이언트 앱의 경우 secondapp.com입니다.

      참고

      이 섹션에 표시된 예제에는 다음에 대한 추가 구성이 필요합니다.

      • 예제 호스트 도메인, firstapp.comsecondapp.com에 있는 앱에 액세스.
      • TLS 보안(HTTPS)을 사용하도록 설정하는 클라이언트 앱 인증서.

      필요한 구성은 이 문서의 범위를 벗어나며 솔루션이 호스트되는 방법에 따라 달라집니다. 자세한 내용은 호스트 및 배포 문서를 참조하세요.

    이전에 줄이 제거된 곳에 다음 코드를 넣습니다.

    app.MapWhen(ctx => ctx.Request.Host.Port == 5001 || 
        ctx.Request.Host.Equals("firstapp.com"), first =>
    {
        first.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/FirstApp" + ctx.Request.Path;
            return nxt();
        });
    
        first.UseBlazorFrameworkFiles("/FirstApp");
        first.UseStaticFiles();
        first.UseStaticFiles("/FirstApp");
        first.UseRouting();
    
        first.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/FirstApp/{*path:nonfile}", 
                "FirstApp/index.html");
        });
    });
    
    app.MapWhen(ctx => ctx.Request.Host.Port == 5002 || 
        ctx.Request.Host.Equals("secondapp.com"), second =>
    {
        second.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/SecondApp" + ctx.Request.Path;
            return nxt();
        });
    
        second.UseBlazorFrameworkFiles("/SecondApp");
        second.UseStaticFiles();
        second.UseStaticFiles("/SecondApp");
        second.UseRouting();
    
        second.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/SecondApp/{*path:nonfile}", 
                "SecondApp/index.html");
        });
    });
    

    UseStaticFiles에 대한 자세한 내용은 ASP.NET Core Blazor 정적 파일을 참조하세요.

    UseBlazorFrameworkFilesMapFallbackToFile에 대한 내용은 다음 리소스를 참조하세요.

    참고

    ASP.NET Core 참조 원본에 대한 설명서 링크는 ASP.NET Core의 다음 릴리스를 위한 제품 단위의 현재 개발을 나타내는 리포지토리의 main 분기를 로드합니다. 다른 릴리스에 대한 분기를 선택하려면 Switch branches or tags(분기 또는 태그 전환) 드롭다운 목록을 사용하여 분기를 선택합니다. 예를 들어 release/6.0 ASP.NET Core 6.0 릴리스에 대한 분기를 선택합니다.

  • 서버 앱의 일기 예보 컨트롤러(Controllers/WeatherForecastController.cs)에서 기존 WeatherForecastController 경로([Route("[controller]")])를 다음 경로로 바꿉니다.

    [Route("FirstApp/[controller]")]
    [Route("SecondApp/[controller]")]
    

    이전에 서버 앱의 Startup.Configure 메서드에 추가된 미들웨어는 /WeatherForecast로 들어오는 요청을 포트(5001/5002) 또는 도메인(firstapp.com/secondapp.com)에 따라 /FirstApp/WeatherForecast 또는 /SecondApp/WeatherForecast로 수정합니다. 서버 앱에서 클라이언트 앱으로 날씨 데이터를 반환하려면 이전 컨트롤러 경로가 필요합니다.

여러 Blazor WebAssembly 앱에 대한 정적 자산 및 클래스 라이브러리

정적 자산 참고에는 다음 방법을 사용합니다.

  • 자산이 클라이언트 앱의 wwwroot 폴더에 있는 경우 정상적으로 해당 경로를 제공합니다.

    <img alt="..." src="/{PATH AND FILE NAME}" />
    

    {PATH AND FILE NAME} 자리 표시자는 wwwroot 아래의 경로 및 파일 이름입니다.

  • 자산이 Razor 클래스 라이브러리(RCL)wwwroot 폴더에 있는 경우 ASP.NET Core를 사용하여 클래스 라이브러리에서 재사용 가능한 Razor UI의 지침에 따라 클라이언트 앱에서 정적 자산을 참조합니다.

    <img alt="..." src="_content/{PACKAGE ID}/{PATH AND FILE NAME}" />
    

    {PACKAGE ID} 자리 표시자는 라이브러리의 {PACKAGE ID}입니다. <PackageId>가 프로젝트 파일에 지정되지 않은 경우 패키지 ID는 기본적으로 프로젝트의 어셈블리 이름으로 설정됩니다. {PATH AND FILE NAME} 자리 표시자는 wwwroot 아래의 경로 및 파일 이름입니다.

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

독립 실행형 배포

‘독립 실행형 배포’는 Blazor WebAssembly 앱을 클라이언트가 직접 요청하는 정적 파일 세트로 제공합니다. 모든 정적 파일 서버는 Blazor 앱을 사용할 수 있습니다.

독립 실행형 배포 자산은 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 폴더에 게시됩니다.

Azure App Service

Blazor WebAssembly 앱은 IIS에서 앱을 호스트하는 Windows의 Azure App Services에 배포할 수 있습니다.

Linux용 Azure App Service에 독립 실행형 Blazor WebAssembly 앱 배포는 현재 지원되지 않습니다. 이 시나리오를 지원하는 Azure Static Web Apps를 사용하는 독립 실행형 Blazor WebAssembly 앱을 호스팅하는 것이 좋습니다.

Azure Static Web Apps

더 많은 정보는 자습서: Azure Static Web Apps에서 Blazor를 사용하여 정적 웹앱 빌드를 참고하세요.

IIS

IIS는 Blazor 앱에 사용할 수 있는 정적 파일 서버입니다. Blazor를 호스트하도록 IIS를 구성하려면 IIS에서 정적 웹 사이트 작성을 참조하세요.

게시된 자산은 /bin/Release/{TARGET FRAMEWORK}/publish 폴더에 생성됩니다. 웹 서버 또는 호스팅 서비스에서 publish 폴더의 콘텐츠를 호스트합니다.

web.config

Blazor 프로젝트가 게시되면 다음 IIS 구성을 사용하여 web.config 파일이 생성됩니다.

  • MIME 형식
  • 다음 MIME 형식에 대해 HTTP 압축을 사용합니다.
    • application/octet-stream
    • application/wasm
  • URL 재작성 모듈 규칙을 설정합니다.
    • 앱의 정적 자산이 상주하는 하위 디렉터리(wwwroot/{PATH REQUESTED})를 제공합니다.
    • 파일이 아닌 자산 요청이 정적 자산 폴더에 있는 앱의 기본 문서(wwwroot/index.html)로 리디렉션되도록 SPA 대체(fallback) 라우팅을 만듭니다.

사용자 지정 web.config 사용

사용자 지정 web.config 파일을 사용하려면 다음을 수행합니다.

  1. 프로젝트의 루트 폴더에 사용자 지정 web.config 파일을 저장합니다. 호스트된 Blazor WebAssembly 솔루션의 경우 Server 프로젝트의 폴더에 파일을 저장합니다.

  2. 프로젝트 파일(.csproj)에서 <PublishIISAssets> 속성을 true로 설정합니다. 호스트된 Blazor WebAssembly 솔루션의 경우 Server 프로젝트의 프로젝트 파일에서 속성을 설정합니다.

    <PropertyGroup>
      <PublishIISAssets>true</PublishIISAssets>
    </PropertyGroup>
    
  3. 프로젝트를 게시합니다. 호스트된 Blazor WebAssembly 솔루션의 경우 Server 프로젝트에서 솔루션을 게시합니다. 자세한 내용은 ASP.NET Core Blazor 호스트 및 배포를 참조하세요.

URL 재작성 모듈 설치

URL을 다시 생성하려면 URL 다시 생성 모듈이 필요합니다. 이 모듈은 기본적으로 설치되지 않으며 웹 서버(IIS) 역할 서비스 기능으로 설치하는 데 사용할 수 없습니다. 이 모듈은 IIS 웹 사이트에서 다운로드해야 합니다. 웹 플랫폼 설치 관리자를 사용하여 이 모듈을 설치합니다.

  1. 로컬에서 URL 다시 생성 모듈 다운로드 페이지로 이동합니다. 영어 버전의 경우 WebPI를 선택하여 WebPI 설치 관리자를 다운로드합니다. 다른 언어의 경우 서버에 맞는 아키텍처(x86 x64)를 선택하여 설치 관리자를 다운로드합니다.
  2. 설치 관리자를 서버에 복사합니다. 설치 관리자를 실행합니다. 설치 버튼을 선택하고 사용 조건에 동의합니다. 설치가 완료된 후 서버를 다시 시작하지 않아도 됩니다.

웹 사이트 구성

웹 사이트의 실제 경로를 앱의 폴더로 설정합니다. 이 폴더는 다음을 포함합니다.

  • 필요한 리디렉션 규칙 및 파일 콘텐츠 형식 등 IIS가 웹 사이트를 구성하기 위해 사용하는 web.config 파일
  • 앱의 정적 자산 폴더입니다.

IIS 하위 앱으로 호스트

독립 실행형 앱이 IIS 하위 앱으로 호스트되는 경우 다음 중 하나를 수행합니다.

  • 상속된 ASP.NET Core 모듈 처리기를 사용하지 않도록 설정합니다.

    파일의 <system.webServer> 섹션에 <handlers> 섹션을 추가하여 Blazor 앱의 게시된 web.config 파일에서 처리기를 제거합니다.

    <handlers>
      <remove name="aspNetCore" />
    </handlers>
    
  • inheritInChildApplicationsfalse로 설정한 상태에서 <location> 요소를 사용하여 루트(상위) 앱의 <system.webServer> 섹션의 상속을 사용하지 않도록 설정합니다.

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <location path="." inheritInChildApplications="false">
        <system.webServer>
          <handlers>
            <add name="aspNetCore" ... />
          </handlers>
          <aspNetCore ... />
        </system.webServer>
      </location>
    </configuration>
    

    참고

    루트(부모) 앱의 <system.webServer> 섹션의 상속을 사용하지 않도록 하는 것은 .NET SDK를 사용하는 게시된 앱에 대한 기본 구성입니다.

처리기 제거 또는 상속을 사용하지 않도록 하는 설정은 앱의 기본 경로 구성에 더하여 수행됩니다. 앱의 index.html 파일에 있는 앱 기본 경로를 IIS에서 하위 앱을 구성할 때 사용되는 IIS 별칭으로 설정합니다.

Brotli 및 Gzip 압축

이 섹션은 독립 실행형 Blazor WebAssembly 앱에만 적용됩니다. 호스트된 Blazor 앱은 이 섹션에 연결된 파일이 아니라 기본 ASP.NET Core 앱 web.config 파일을 사용합니다.

web.config를 통해 IIS를 구성하여 독립 실행형 Blazor WebAssembly 앱을 위한 Brotli 또는 Gzip 압축 Blazor 자산을 제공할 수 있습니다. 구성 파일 예제는 web.config를 참조하세요.

다음 시나리오에서는 예제 web.config 파일의 추가 구성이 필요할 수 있습니다.

  • 앱 사양에는 다음 중 하나가 필요합니다.
    • 예제 web.config 파일에 의해 구성되지 않은 압축 파일 제공.
    • 예제 web.config 파일에 의해 구성된 압축 파일을 압축되지 않은 형식으로 제공.
  • 서버의 IIS 구성(예: applicationHost.config)은 서버 수준 IIS 기본값을 제공합니다. 서버 수준 구성에 따라 예제 web.config 파일에 포함된 것과 다른 IIS 구성이 앱에 필요할 수 있습니다.

문제 해결

500 - 내부 서버 오류가 수신되고 웹 사이트의 구성에 액세스를 시도할 때 IIS 관리자가 오류를 표시하면 URL 다시 생성 모듈이 설치되었는지 확인합니다. 모듈이 설치되지 않은 경우 IIS가 web.config 파일을 구문 분석할 수 없습니다. 그러면 IIS 관리자가 웹 사이트의 구성을 로드할 수 없으며 웹 사이트가 Blazor의 정적 파일을 제공할 수 없습니다.

IIS로의 배포 문제 해결에 대한 자세한 내용은 Azure App Service 및 IIS에서 ASP.NET Core 문제 해결을 참조하세요.

Azure Storage

Azure Storage 정적 파일 호스팅으로 서버리스 Blazor 앱 호스팅이 가능합니다. 사용자 지정 도메인 이름, Azure 콘텐츠 배달 네트워크(CDN) 및 HTTPS가 지원됩니다.

스토리지 계정에서 정적 웹 사이트 호스팅을 위해 BLOB 서비스를 사용할 수 있는 경우:

  • 인덱스 문서 이름index.html로 설정합니다.
  • 오류 문서 경로index.html로 설정합니다. Razor 구성 요소 및 기타 파일이 아닌 엔드포인트는 Blob 서비스에 의해 저장된 정적 콘텐츠의 실제 경로에 존재하지 않습니다. 이러한 리소스 중 하나를 Blazor 라우터가 처리해야 한다는 요청이 수신되면 BLOB 서비스에 의해 생성된 404 - 찾을 수 없음 오류가 요청을 오류 문서 경로로 라우팅합니다. index.html BLOB이 반환되고 Blazor 라우터가 로드되어 경로를 처리합니다.

파일의 Content-Type 헤더에 부적절한 MIME 형식으로 인해 런타임에 파일이 로드되지 않을 경우 다음 작업 중 하나를 수행합니다.

  • 파일이 배포될 때 올바른 MIME 형식(Content-Type 헤더)을 설정하도록 도구를 구성합니다.

  • 앱이 배포된 후 파일에 대한 MIME 형식(Content-Type 헤더)을 변경합니다.

    각 파일의 스토리지 탐색기(Azure Portal)에서

    1. 파일을 마우스 오른쪽 단추로 클릭한 다음 속성을 선택합니다.
    2. ContentType을 설정하고 저장 단추를 선택합니다.

자세한 내용은 Azure Storage에서 정적 웹 사이트 호스팅을 참조하세요.

Nginx

다음 nginx.conf 파일은 Nginx가 디스크에서 대응하는 파일을 찾을 수 없을 때마다 index.html 파일을 보내도록 구성하는 방법을 보여 주기 위해 단순화되었습니다.

events { }
http {
    server {
        listen 80;

        location / {
            root      /usr/share/nginx/html;
            try_files $uri $uri/ /index.html =404;
        }
    }
}

limit_req를 사용하여 NGINX 버스트 속도 한도를 설정하는 경우 Blazor WebAssembly 앱에서 수행하는 비교적 많은 수의 요청을 수용하기 위해 큰 burst 매개 변수 값이 필요할 수 있습니다. 처음에는 값을 60 이상으로 설정합니다.

http {
    server {
        ...

        location / {
            ...

            limit_req zone=one burst=60 nodelay;
        }
    }
}

브라우저 개발자 도구 또는 네트워크 트래픽 도구에서 요청이 ‘503 - 서비스를 사용할 수 없음’ 상태 코드를 수신하는 것으로 확인되는 경우 값을 늘립니다.

프로덕션 Nginx 웹 서버 구성에 대한 자세한 내용은 NGINX Plus 및 NGINX 구성 파일 만들기를 참조하세요.

Apache

CentOS 7 이상에 Blazor WebAssembly 앱을 배포하려면 다음을 수행합니다.

  1. Apache 구성 파일을 만듭니다. 다음 예제는 단순화된 구성 파일입니다(blazorapp.config).

    <VirtualHost *:80>
        ServerName www.example.com
        ServerAlias *.example.com
    
        DocumentRoot "/var/www/blazorapp"
        ErrorDocument 404 /index.html
    
        AddType application/wasm .wasm
        AddType application/octet-stream .dll
    
        <Directory "/var/www/blazorapp">
            Options -Indexes
            AllowOverride None
        </Directory>
    
        <IfModule mod_deflate.c>
            AddOutputFilterByType DEFLATE text/css
            AddOutputFilterByType DEFLATE application/javascript
            AddOutputFilterByType DEFLATE text/html
            AddOutputFilterByType DEFLATE application/octet-stream
            AddOutputFilterByType DEFLATE application/wasm
            <IfModule mod_setenvif.c>
          BrowserMatch ^Mozilla/4 gzip-only-text/html
          BrowserMatch ^Mozilla/4.0[678] no-gzip
          BrowserMatch bMSIE !no-gzip !gzip-only-text/html
      </IfModule>
        </IfModule>
    
        ErrorLog /var/log/httpd/blazorapp-error.log
        CustomLog /var/log/httpd/blazorapp-access.log common
    </VirtualHost>
    
  2. Apache 구성 파일을 CentOS 7의 기본 Apache 구성 디렉터리인 /etc/httpd/conf.d/ 디렉터리에 저장합니다.

  3. 앱의 파일을 /var/www/blazorapp 디렉터리(구성 파일의 DocumentRoot에 지정된 위치)에 저장합니다.

  4. Apache 서비스를 다시 시작합니다.

자세한 내용은 mod_mimemod_deflate를 참조하세요.

GitHub 페이지

URL 다시 쓰기를 처리하려면 index.html 페이지로 요청 리디렉션을 처리하는 스크립트를 사용하여 wwwroot/404.html 파일을 추가합니다. 예제는 SteveSandersonMS/BlazorOnGitHubPages GitHub 리포지토리를 참조하세요.

조직 사이트 대신 프로젝트 사이트를 사용하는 경우 wwwroot/index.html<base> 태그를 업데이트합니다. href 특성 값을 후행 슬래시가 있는 GitHub 리포지토리 이름으로 설정합니다(예: /my-repository/). SteveSandersonMS/BlazorOnGitHubPages GitHub 리포지토리에서 기본 href.github/workflows/main.yml 구성 파일에 의해 게시 시 업데이트됩니다.

참고

SteveSandersonMS/BlazorOnGitHubPages GitHub 리포지토리는 .NET Foundation 또는 Microsoft에서 소유, 유지 관리 또는 지원하지 않습니다.

호스트 구성 값

Blazor WebAssembly 앱은 개발 환경의 런타임에 다음 호스트 구성 값을 명령줄 인수로 허용할 수 있습니다.

콘텐츠 루트

--contentroot 인수는 앱의 콘텐츠 파일을 포함하는 디렉터리(콘텐츠 루트)에 대한 절대 경로를 설정합니다. 다음 예제에서 /content-root-path는 앱의 콘텐츠 루트 경로입니다.

  • 명령 프롬프트에서 앱을 로컬로 실행할 때 인수를 전달합니다. 앱의 디렉터리에서 다음을 실행합니다.

    dotnet run --contentroot=/content-root-path
    
  • IIS Express 프로필에서 앱의 launchSettings.json 파일에 항목을 추가합니다. 앱이 Visual Studio 디버거를 통해 실행되거나 dotnet run을 사용하여 명령 프롬프트에서 실행되는 경우 이 설정이 사용됩니다.

    "commandLineArgs": "--contentroot=/content-root-path"
    
  • Visual Studio의 속성>디버그>애플리케이션 인수에서 인수를 지정합니다. Visual Studio 속성 페이지에서 인수를 설정하면 해당 인수가 launchSettings.json 파일에 추가됩니다.

    --contentroot=/content-root-path
    

경로 기준

--pathbase 인수는 루트가 아닌 상대 URL 경로를 사용하여 로컬로 앱을 실행하기 위한 앱 기본 경로를 설정합니다. (준비 및 프로덕션의 경우 <base> 태그 href/ 이외의 경로로 설정됩니다.) 다음 예제에서 /relative-URL-path는 앱의 경로 기준입니다. 자세한 내용은 앱 기본 경로를 참조하세요.

중요

<base> 태그의 href에 대해 제공되는 경로와 달리 --pathbase 인수 값을 전달할 때 뒤에 슬래시(/)를 포함하지 않습니다. 앱 기본 경로가 <base> 태그에 <base href="/CoolApp/">로 제공되는 경우(뒤에 슬래시 포함) 명령줄 인수 값을 --pathbase=/CoolApp로 전달합니다(뒤에 슬래시 없음).

  • 명령 프롬프트에서 앱을 로컬로 실행할 때 인수를 전달합니다. 앱의 디렉터리에서 다음을 실행합니다.

    dotnet run --pathbase=/relative-URL-path
    
  • IIS Express 프로필에서 앱의 launchSettings.json 파일에 항목을 추가합니다. 앱을 Visual Studio 디버거를 통해 실행하거나 dotnet run을 사용하여 명령 프롬프트에서 실행하는 경우 이 설정이 사용됩니다.

    "commandLineArgs": "--pathbase=/relative-URL-path"
    
  • Visual Studio의 속성>디버그>애플리케이션 인수에서 인수를 지정합니다. Visual Studio 속성 페이지에서 인수를 설정하면 해당 인수가 launchSettings.json 파일에 추가됩니다.

    --pathbase=/relative-URL-path
    

URL

--urls 인수는 요청을 수신하기 위한 포트 및 프로토콜을 포함하는 IP 주소 또는 호스트 주소를 설정합니다.

  • 명령 프롬프트에서 앱을 로컬로 실행할 때 인수를 전달합니다. 앱의 디렉터리에서 다음을 실행합니다.

    dotnet run --urls=http://127.0.0.1:0
    
  • IIS Express 프로필에서 앱의 launchSettings.json 파일에 항목을 추가합니다. 앱을 Visual Studio 디버거를 통해 실행하거나 dotnet run을 사용하여 명령 프롬프트에서 실행하는 경우 이 설정이 사용됩니다.

    "commandLineArgs": "--urls=http://127.0.0.1:0"
    
  • Visual Studio의 속성>디버그>애플리케이션 인수에서 인수를 지정합니다. Visual Studio 속성 페이지에서 인수를 설정하면 해당 인수가 launchSettings.json 파일에 추가됩니다.

    --urls=http://127.0.0.1:0
    

링커 구성

Blazor는 각 릴리스 빌드에 대해 IL(중간 언어) 연결을 수행하여 출력 어셈블리에서 불필요한 IL을 제거합니다. 자세한 내용은 ASP.NET Core Blazor용 링커 구성을 참조하세요.

DLL 파일의 파일 이름 확장명 변경

앱 게시 .dll 파일의 파일 이름 확장명을 변경해야 하는 경우 이 섹션의 지침을 따르세요.

앱을 게시한 후 셸 스크립트 또는 DevOps 빌드 파이프라인을 사용하여 다른 파일 확장명을 사용하도록 .dll 파일의 이름을 바꿉니다. 앱 게시 출력(예: {CONTENT ROOT}/bin/Release/netstandard2.1/publish/wwwroot)의 wwwroot 디렉터리에 있는 .dll 파일을 대상으로 합니다.

다음 예제에서는 .bin 파일 확장명을 사용하도록 .dll 파일의 이름이 바뀝니다.

Windows에서:

dir .\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content .\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content .\_framework\blazor.boot.json

서비스 작업자 자산도 사용 중인 경우 다음 명령을 추가합니다.

((Get-Content .\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content .\service-worker-assets.js

Linux 또는 macOS에서:

for f in _framework/_bin/*; do mv "$f" "`echo $f | sed -e 's/\.dll/.bin/g'`"; done
sed -i 's/\.dll"/.bin"/g' _framework/blazor.boot.json

서비스 작업자 자산도 사용 중인 경우 다음 명령을 추가합니다.

sed -i 's/\.dll"/.bin"/g' service-worker-assets.js

.bin 이외의 다른 파일 확장명을 사용하려면 이전 명령에서 .bin을 바꿉니다.

압축된 blazor.boot.json.gzblazor.boot.json.br 파일을 처리하려면 다음 방법 중 하나를 채택합니다.

  • 압축된 blazor.boot.json.gzblazor.boot.json.br 파일을 제거합니다. 압축은 이 접근 방법으로 사용하지 않도록 설정됩니다.
  • 업데이트된 blazor.boot.json 파일을 다시 압축합니다.

위의 지침은 서비스 작업자 자산을 사용 중인 경우에도 적용됩니다. wwwroot/service-worker-assets.js.brwwwroot/service-worker-assets.js.gz를 제거하거나 다시 압축합니다. 그렇지 않으면 브라우저에서 파일 무결성 검사가 실패합니다.

다음 Windows 예제에서는 프로젝트의 루트에 배치된 PowerShell 스크립트를 사용합니다.

ChangeDLLExtensions.ps1::

param([string]$filepath,[string]$tfm)
dir $filepath\bin\Release\$tfm\wwwroot\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json
Remove-Item $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json.gz

서비스 작업자 자산도 사용 중인 경우 다음 명령을 추가합니다.

((Get-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js

프로젝트 파일에서 스크립트는 앱을 게시한 후에 실행됩니다.

<Target Name="ChangeDLLFileExtensions" AfterTargets="Publish" Condition="'$(Configuration)'=='Release'">
  <Exec Command="powershell.exe -command &quot;&amp; { .\ChangeDLLExtensions.ps1 '$(SolutionDir)' '$(TargetFramework)'}&quot;" />
</Target>

참고

동일한 어셈블리의 이름을 변경하고 지연 로드하는 경우 ASP.NET Core Blazor WebAssembly에서 어셈블리 지연 로드의 지침을 참조하세요.

이전 배포 손상

일반적으로 배포 시 다음 현상이 나타납니다.

  • 변경된 파일만 대체되어 일반적으로 배포 속도가 빨라집니다.
  • 새 배포에 포함되지 않은 기존 파일은 새 배포에서 사용할 수 있도록 남아 있습니다.

드문 경우에 이전 배포에서 남은 파일 때문에 새 배포가 손상될 수 있습니다. 기존 배포(또는 배포 전에 로컬로 게시된 앱)를 완전히 삭제하면 손상된 배포와 관련된 문제가 해결될 수 있습니다. 종종 기존 배포를 한번 삭제하면 DevOps 빌드 및 배포 파이프라인을 포함하는 문제를 해결할 수 있습니다.

DevOps 빌드 및 배포 파이프라인을 사용할 때 항상 이전 배포를 지워야 하는 경우 손상의 정확한 원인을 해결할 때까지 새 배포마다 이전 배포를 삭제하는 단계를 빌드 파이프라인에 일시적으로 추가할 수 있습니다.

무결성 검사 실패 해결

Blazor WebAssembly는 앱의 시작 파일을 다운로드할 때 응답에 대한 무결성 검사를 수행하도록 브라우저에 지시합니다. Blazor는 blazor.boot.json 파일 내의 DLL(.dll), WebAssembly(.wasm) 및 다른 파일에 대한 SHA-256 해시 값을 전송하며, 이 값은 클라이언트에 캐시되지 않습니다. 캐시된 파일의 파일 해시는 blazor.boot.json 파일의 해시와 비교됩니다. 캐시된 파일에 일치하는 해시가 있는 경우 Blazor가 캐시된 파일을 사용합니다. 그렇지 않으면 서버에 파일을 요청합니다. 파일을 다운로드한 후 무결성 유효성 검사를 위해 해당 해시를 다시 확인합니다. 다운로드한 파일의 무결성 검사가 실패하면 브라우저에서 오류가 생성됩니다.

파일 무결성을 관리하기 위한 Blazor의 알고리즘:

  • 사용자가 애플리케이션 파일을 다운로드하는 동안 새 배포가 웹 서버에 적용되는 경우와 같이 앱이 일관되지 않은 파일 세트를 로드할 위험이 없도록 합니다. 파일이 일관되지 않으면 앱이 오작동할 수 있습니다.
  • 사용자의 브라우저가 일관되지 않은 또는 잘못된 응답을 캐시하지 않도록 합니다. 일관되지 않거나 잘못된 응답을 캐시할 경우 사용자가 수동으로 페이지를 새로 고치더라도 앱이 시작하지 않을 수 있습니다.
  • 응답을 안전하게 캐시하며 예상된 SHA-256 해시가 변경될 때까지 서버 쪽 변경 내용을 확인하지 않도록 하므로 후속 페이지 로드는 더 적은 요청을 포함하여 더 빠르게 완료됩니다.

웹 서버가 예상된 SHA-256 해시와 일치하지 않는 응답을 반환하는 경우에는 브라우저의 개발자 콘솔에 다음과 같은 오류가 표시됩니다.

컴퓨팅된 SHA-256 무결성 ‘IIa70iwvmEg5WiDV17OpQ5eCztNYqL186J56852RpJY=’가 지정된 ‘https://myapp.example.com/_framework/MyBlazorApp.dlll’ 리소스의 ‘integrity’ 특성에서 유효한 다이제스트를 찾지 못했습니다. 리소스가 차단되었습니다.

대부분의 경우 경고는 무결성 검사에 문제가 있음을 나타내지 않습니다. 대신, 경고는 일반적으로 다른 문제가 있음을 의미합니다.

Blazor WebAssembly의 부팅 참조 원본은 dotnet/aspnetcore GitHub 리포지토리의 Boot.WebAssembly.ts 파일을 참조하세요.

참고

ASP.NET Core 참조 원본에 대한 설명서 링크는 ASP.NET Core의 다음 릴리스를 위한 제품 단위의 현재 개발을 나타내는 리포지토리의 main 분기를 로드합니다. 다른 릴리스에 대한 분기를 선택하려면 Switch branches or tags(분기 또는 태그 전환) 드롭다운 목록을 사용하여 분기를 선택합니다. 예를 들어 release/6.0 ASP.NET Core 6.0 릴리스에 대한 분기를 선택합니다.

무결성 문제 진단

앱이 빌드되면 생성된 blazor.boot.json 매니페스트는 빌드 출력이 생성될 때 부팅 리소스의 SHA-256 해시를 설명합니다. blazor.boot.json의 SHA-256 해시가 브라우저에 배달된 파일과 일치하는 경우 무결성 검사가 통과합니다.

이 작업이 실패하는 일반적인 이유는 다음과 같습니다.

  • 웹 서버의 응답은 브라우저가 요청한 파일이 아닌 오류(예: ‘404 - 찾을 수 없음’ 또는 ‘500 - 내부 서버 오류’)입니다. 이는 브라우저에서 응답 실패가 아닌 무결성 검사 실패로 보고됩니다.
  • 무엇인가 브라우저에 대한 파일의 빌드와 제공 사이에 파일 콘텐츠를 변경했습니다. 이 문제가 발생할 수 있는 경우는 다음과 같습니다.
    • 사용자 또는 빌드 도구가 빌드 출력을 수동으로 수정하는 경우.
    • 배포 프로세스의 일부 측면이 파일을 수정한 경우. 예를 들어 Git 기반 배포 메커니즘을 사용하는 경우 파일을 Windows에서 커밋하고 Linux에서 체크 아웃하는 경우 Git에서는 Windows 스타일 줄 끝을 Unix 스타일 줄 끝으로 투명하게 변환합니다. 파일 줄 끝을 변경하면 SHA-256 해시가 변경됩니다. 이 문제를 방지하려면 .gitattributes를 사용하여 빌드 아티팩트를 binary 파일로 처리하는 것이 좋습니다.
    • 웹 서버는 파일 콘텐츠를 제공하는 동안 수정합니다. 예를 들어 일부 CDN(Content Delivery Network)은 자동으로 HTML을 축소함으로써 수정합니다. 관련 기능을 사용하지 않도록 설정해야 할 수 있습니다.
  • blazor.boot.json 파일이 제대로 로드되지 않거나 클라이언트에 잘못 캐시됩니다. 일반적인 원인은 다음 중 하나입니다.
    • 사용자 지정 개발자 코드가 잘못 구성되었거나 오작동합니다.
    • 하나 이상의 중간 캐싱 레이어가 잘못 구성되었습니다.

어떤 기능이 해당 사례에서 적용되는지 진단하려면:

  1. 오류 메시지를 읽어서 어떤 파일이 오류를 트리거하는지 확인합니다.
  2. 브라우저의 개발자 도구를 열고 ‘네트워크’ 탭을 확인합니다. 필요한 경우 페이지를 다시 로드하여 요청 및 응답 목록을 확인합니다. 해당 목록에서 오류를 트리거하는 파일을 찾습니다.
  3. 응답의 HTTP 상태 코드를 확인합니다. 서버가 200 - OK(또는 또 다른 2xx 상태 코드) 이외의 값을 반환하면 진단해야 하는 서버 쪽 문제가 있는 것입니다. 예를 들어 상태 코드 403은 권한 부여 문제가 있음을 의미하는 반면, 상태 코드 500은 서버가 지정되지 않은 방식으로 실패함을 의미합니다. 서버 쪽 로그를 참조하여 앱을 진단하고 수정합니다.
  4. 리소스의 상태 코드가 200 - OK인 경우 브라우저의 개발자 도구에서 응답 콘텐츠를 보고 예상되는 데이터와 콘텐츠가 일치하는지 확인합니다. 예를 들어 일반적인 문제는 요청이 다른 파일에 대해서도 index.html 데이터를 반환하도록 라우팅을 잘못 구성하는 것입니다. .wasm 요청에 대한 응답이 WebAssembly 이진 파일이고 .dll 요청에 대한 응답이 .NET 어셈블리 이진 파일인지 확인합니다. 그렇지 않으면 진단해야 할 서버 쪽 라우팅 문제가 있는 것입니다.
  5. 무결성 PowerShell 스크립트 문제 해결을 사용하여 게시 및 배포된 앱 출력의 유효성을 검사해 보세요.

서버가 올바른 것 같은 데이터를 반환하고 있는지 확인하는 경우 파일의 빌드와 제공 사이에 콘텐츠를 수정하는 다른 항목이 있어야 합니다. 이를 조사하려면:

  • 파일이 빌드된 후 파일을 수정 중인 경우 빌드 도구 체인 및 배포 메커니즘을 검토합니다. 여기에 해당하는 예제는 앞에서 설명한 대로 Git이 파일 줄 끝을 변환하는 경우입니다.
  • 응답을 동적으로 수정하도록(예: HTML 축소 시도) 설정된 경우 웹 서버 또는 CDN 구성을 검토합니다. 압축을 푼 후 결과에 영향을 주지 않으므로 웹 서버가 HTTP 압축을 구현해도 괜찮습니다(예: content-encoding: br 또는 content-encoding: gzip 반환). 그러나 웹 서버가 압축되지 않은 데이터를 수정하는 것은 괜찮지 ‘않습니다’.

무결성 PowerShell 스크립트 문제 해결

integrity.ps1 PowerShell 스크립트를 사용하여 게시 및 배포된 Blazor 앱의 유효성을 검사합니다. 이 스크립트는 앱에 Blazor 프레임워크가 식별할 수 없는 무결성 문제가 있는 경우 PowerShell Core 7 이상에 시작 지점으로 제공됩니다. 버전 7.2.0 이상 버전의 PowerShell을 실행하는 경우를 포함하여 앱을 위해 스크립트를 사용자 지정해야 할 수 있습니다.

이 스크립트는 publish 폴더의 파일과 배포된 앱에서 다운로드한 파일을 검사하여 무결성 해시가 포함된 다른 매니페스트의 문제를 검색합니다. 이 검사에서 다음과 같은 가장 일반적인 문제가 검색되어야 합니다.

  • 게시된 출력의 파일을 사용자가 무심코 수정했습니다.
  • 앱이 배포 대상에 올바르게 배포되지 않았거나 배포 대상 환경 내에 변경된 내용이 있습니다.
  • 배포된 앱과 앱 게시의 출력 간에 차이가 있습니다.

PowerShell 명령 셸에서 다음 명령을 사용하여 스크립트를 호출합니다.

.\integrity.ps1 {BASE URL} {PUBLISH OUTPUT FOLDER}

다음 예제에서 스크립트는 https://localhost:5001/에서 로컬로 실행 중인 앱에서 실행됩니다.

.\integrity.ps1 https://localhost:5001/ C:\TestApps\BlazorSample\bin\Release\net6.0\publish\

자리 표시자:

  • {BASE URL}: 배포된 앱의 URL입니다. 후행 슬래시(/)가 필요합니다.
  • {PUBLISH OUTPUT FOLDER}: 앱의 publish 폴더 또는 배포를 위해 앱이 게시된 위치의 경로입니다.

참고

dotnet/AspNetCore.Docs GitHub 리포지토리를 복제하는 경우 integrity.ps1 스크립트는 Bitdefender 또는 시스템에 있는 다른 바이러스 검색 프로그램으로 격리될 수 있습니다. 일반적으로 파일은 바이러스 검사 프로그램의 추론 검색 기술을 통해 트래핑됩니다. 이 기술은 파일에서 맬웨어가 있음을 나타낼 수 있는 패턴만 찾습니다. 바이러스 검사 프로그램이 파일을 격리하지 않도록 하려면 리포지토리를 복제하기 전에 바이러스 검사 프로그램에 예외를 추가합니다. 다음 예제는 Windows 시스템의 일반적인 스크립트 경로입니다. 다른 시스템의 필요에 따라 경로를 조정합니다. 자리 표시자 {USER}는 사용자의 패스 세그먼트입니다.

C:\Users\{USER}\Documents\GitHub\AspNetCore.Docs\aspnetcore\blazor\host-and-deploy\webassembly\_samples\integrity.ps1

경고: 바이러스 검사 예외를 만드는 것은 위험하며 파일이 안전하다는 것을 확신할 때만 수행해야 합니다.

파일의 체크섬을 유효한 체크섬 값과 비교해도 파일 보안을 보장할 수 없지만 체크섬 값을 유지하는 방식으로 파일을 수정하는 것은 악의적인 사용자에게 사소하지 않습니다. 따라서 체크섬은 일반적인 보안 방법으로 유용합니다. 로컬 integrity.ps1 파일의 체크섬을 다음 값 중 하나와 비교합니다.

  • SHA256: 32c24cb667d79a701135cb72f6bae490d81703323f61b8af2c7e5e5dc0f0c2bb
  • MD5: 9cee7d7ec86ee809a329b5406fbf21a8

다음 명령을 사용하여 Windows OS에서 파일의 체크섬을 가져옵니다. {PATH AND FILE NAME} 자리 표시자의 경로 및 파일 이름을 제공하고 {SHA512|MD5} 자리 표시자에 대해 생성할 체크섬의 형식(SHA256 또는 MD5)을 나타냅니다.

CertUtil -hashfile {PATH AND FILE NAME} {SHA256|MD5}

사용자 환경에서 체크섬 유효성 검사가 충분히 안전하지 않을 수 있다고 생각될 경우 조직의 보안 책임자에게 문의하세요.

자세한 내용은 맬웨어 및 기타 위협 이해를 참조하세요.

비 PWA 앱에 대한 무결성 검사 사용 안 함

대부분의 경우 무결성 검사를 사용하지 않도록 설정하지 마세요. 무결성 검사를 사용하지 않으면 예기치 않은 응답을 발생시킨 근본적인 문제가 해결되지 않아 앞서 언급한 이점을 얻을 수 없습니다.

웹 서버를 사용하여 일관된 응답을 반환할 수 없고 근본 문제가 해결될 때까지 일시적으로 무결성 검사를 사용하지 않도록 설정할 수밖에 없는 경우가 있습니다.

무결성 검사를 사용하지 않도록 설정하려면 Blazor WebAssembly 앱의 프로젝트 파일(.csproj)에서 속성 그룹에 다음을 추가합니다.

<BlazorCacheBootResources>false</BlazorCacheBootResources>

또한, BlazorCacheBootResources 속성은 SHA-256 해시의 정확성을 기대할 수 없음을 나타내기 때문에 .dll, .wasm 및 SHA-256 해시 기반 기타 파일을 캐시하는 Blazor의 기본 동작을 사용하지 않도록 설정합니다. 이 설정을 사용하는 경우에도 브라우저의 일반 HTTP 캐시는 해당 파일을 캐시할 수 있지만 이 상황이 발생하는지는 웹 서버 구성 및 해당 구성이 제공하는 cache-control 헤더에 따라 달라집니다.

참고

BlazorCacheBootResources 속성은 PWA(프로그레시브 웹 애플리케이션)에 대한 무결성 검사를 사용하지 않도록 설정하지 않습니다. PWA 관련 지침은 PWA에 대한 무결성 검사 사용 안 함 섹션을 참조하세요.

무결성 검사를 사용하지 않도록 설정해야 하는 시나리오를 모두 나열할 수는 없습니다. 서버는 Blazor 프레임워크의 범위를 벗어나는 임의의 방법으로 요청에 응답할 수 있습니다. 프레임워크는 앱이 제공할 수 있는 무결성에 대한 보장을 하지 않는 대신 앱을 계속 실행할 수 있도록 BlazorCacheBootResources 설정을 제공합니다. 다시 한 번 말하지만, 특히 프로덕션 배포의 경우, 무결성 검사를 사용하지 않도록 설정하는 것은 권장하지 않습니다. 개발자는 무결성 검사가 실패하는 근본적인 무결성 문제를 해결하려고 노력해야 합니다.

무결성 문제를 일으킬 수 있는 몇 가지 일반적인 사례는 다음과 같습니다.

  • 무결성을 검사할 수 없는 HTTP에서 실행하는 경우
  • 배포 프로세스에서 어떤 방식으로든 게시 후 파일을 수정하는 경우
  • 호스트가 어떤 방식으로든 파일을 수정하는 경우

PWA에 대한 무결성 검사 사용 안 함

Blazor의 PWA(프로그레시브 웹 애플리케이션) 템플릿에는 오프라인에서 사용하기 위해 애플리케이션 파일을 페치하고 저장해야 하는 제안된 service-worker.published.js 파일이 포함됩니다. 이는 일반적인 앱 시작 메커니즘과 별도의 프로세스이며 자체적인 별도의 무결성 검사 논리를 포함합니다.

service-worker.published.js 파일 내에 다음 줄이 있습니다.

.map(asset => new Request(asset.url, { integrity: asset.hash }));

무결성 검사를 사용하지 않으려면 줄을 다음으로 변경하여 integrity 매개 변수를 제거합니다.

.map(asset => new Request(asset.url));

또한 무결성 검사를 사용하지 않도록 설정하면 무결성 검사를 통해 제공되는 안전 보증이 손실됩니다. 예를 들어 사용자의 브라우저가 새 버전을 배포하는 바로 그 순간에 앱을 캐시 중인 경우 이전 배포의 일부 파일과 새 배포의 일부 파일을 캐시할 수 있는 위험이 있습니다. 이 경우 추가 업데이트를 배포할 때까지 앱이 중단됨 상태로 중단됩니다.