ASP.NET Core Blazor JavaScript 상호 운용성(JS interop)

참고 항목

이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

Important

이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.

현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

Blazor 앱은 .NET 메서드에서 JavaScript(JS) 함수를 호출하고 JS 함수에서 .NET 메서드를 호출할 수 있습니다. 이러한 시나리오를 JavaScript 상호 운용성(interop)이라고 합니다.JS

추가 JS interop 지침은 다음 문서에서 제공합니다.

참고 항목

JavaScript [JSImport]/[JSExport] interop API는 .NET 7 이상의 ASP.NET Core에서 클라이언트 쪽 구성 요소에 사용할 수 있습니다.

자세한 내용은 JavaScript JSImport/JSExport interop 및 ASP.NET Core Blazor를 참조하세요.

신뢰할 수 없는 데이터가 있는 대화형 서버 구성 요소에 대한 압축

기본적으로 사용하도록 설정된 압축을 사용하면 신뢰할 수 없는 원본에서 데이터를 렌더링하는 보안(인증/권한이 부여된) 대화형 서버 쪽 구성 요소를 만들지 마세요. 신뢰할 수 없는 원본에는 경로 매개 변수, 쿼리 문자열, interop의 데이터 JS 및 타사 사용자가 제어할 수 있는 기타 데이터 원본(데이터베이스, 외부 서비스)이 포함됩니다. 자세한 내용은 ASP.NET Core BlazorSignalR 대화형 서버 쪽 렌더링에 대한 ASP.NET Core Blazor 지침위협 완화 지침을 참조하세요.

JavaScript interop 추상화 및 기능 패키지

@microsoft/dotnet-js-interop 패키지()(npmjs.comMicrosoft.JSInteropNuGet 패키지)는 .NET과 JavaScript(JS) 코드 간의 interop에 대한 추상화 및 기능을 제공합니다. 참조 원본은 GitHub 리포지토리(폴더)에서 dotnet/aspnetcore 사용할 수 있습니다./src/JSInterop 자세한 내용은 GitHub 리포지토리의 README.md 파일을 참조하세요.

참고 항목

.NET 참조 원본의 설명서 링크는 일반적으로 다음 릴리스의 .NET을 위한 현재 개발을 나타내는 리포지토리의 기본 분기를 로드합니다. 특정 릴리스를 위한 태그를 선택하려면 Switch branches or tags(분기 또는 태그 전환) 드롭다운 목록을 사용합니다. 자세한 내용은 ASP.NET Core 소스 코드(dotnet/AspNetCore.Docs #26205)의 버전 태그를 선택하는 방법을 참조하세요.

TypeScript에서 interop 스크립트를 작성 JS 하기 위한 추가 리소스:

DOM과의 상호 작용

개체가 상호 작용Blazor하지 않는 경우에만 JavaScript(JS)를 사용하여 DOM을 변경합니다. Blazor는 DOM의 표현을 유지 관리하고 DOM 개체와 직접 상호 작용합니다. Blazor에 의해 렌더링된 요소가 JS를 직접 사용하거나 JS Interop을 통해 외부적으로 수정되는 경우 DOM이 더 이상 Blazor의 내부 표현과 일치하지 않아 정의되지 않은 동작이 발생할 수 있습니다. 정의되지 않은 동작은 단순히 요소 또는 해당 함수의 표시를 방해할 수 있지만 앱 또는 서버에 보안 위험을 초래할 수도 있습니다.

이 지침은 사용자 JS interop 코드뿐 아니라 부트스트랩 JSjQuery와 같은 타사 프레임워크에 제공되는 라이브러리를 비롯하여 앱에 사용되는 모든 JS 라이브러리에도 적용됩니다.

일부 설명서에서 JS interop은 ‘예시 데모’의 목적으로 요소를 변경하는 데 사용됩니다. 이러한 경우 경고 텍스트가 표시됩니다.

자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.

비동기 JavaScript 호출

JS interop 호출은 호출된 코드가 동기인지 비동기인지와 관계없이 기본적으로 비동기적입니다. 기본적으로 호출은 비동기적이므로 구성 요소가 서버 쪽 및 클라이언트 쪽 렌더링 모델에서 호환되도록 합니다. 서버 쪽 렌더링을 채택할 JS 때 interop 호출은 네트워크 연결을 통해 전송되므로 비동기적이어야 합니다. 클라이언트 쪽 렌더링만 채택하는 앱의 경우 동기 JS interop 호출이 지원됩니다.

개체 serialization

Blazor는 다음 요구 사항 및 기본 동작을 통해 System.Text.Json을 serialization에 사용합니다.

  • 형식에는 기본 생성자가 있어야 하고 get/set 접근자는 public이어야 하며 필드는 직렬화되면 안 됩니다.
  • 전역 기본 serialization은 기존 구성 요소 라이브러리의 손상, 성능 및 보안에 미치는 영향, 안정성 감소를 방지하기 위해 사용자 지정할 수 없습니다.
  • .NET 멤버 이름을 직렬화하면 소문자 JSON 키 이름이 생성됩니다.
  • JSON은 C# 인스턴스로 JsonElement 역직렬화되어 대/소문자를 혼합할 수 있습니다. JS ON 키 이름과 C# 속성 이름 간의 대/소문자 차이에도 불구하고 C# 모델 속성에 할당하기 위한 내부 캐스팅은 예상대로 적용됩니다.
  • 같은 KeyValuePair복잡한 프레임워크 형식은 게시 시 IL 트리머에 의해 잘릴 수 있으며 interop에는 JS 없을 수 있습니다. IL 트리머가 기본적으로 자르는 형식에 대한 사용자 지정 형식을 만드는 것이 좋습니다.

JsonConverter API는 사용자 지정 serialization에 사용할 수 있습니다. 속성에 [JsonConverter] 특성을 사용하여 주석을 추가함으로써 기존 데이터 형식의 기본 serialization을 재정의할 수 있습니다.

자세한 내용은 .NET 문서의 다음 리소스를 참조하세요.

Blazor는 최적화된 바이트 배열 JS interop을 지원하여 바이트 배열이 Base64로 인코딩/디코딩되는 것을 방지합니다. Blazor 앱은 사용자 지정 serialization을 적용하고 결과 바이트를 전달할 수 있습니다. 자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.

Blazor는 대량의 .NET 개체가 빠르게 직렬화되거나 많은 양의 .NET 개체 또는 여러 .NET 개체를 직렬화해야 하는 경우 역마샬링된 JS interop을 지원합니다. 자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.

구성 요소 삭제 중 DOM 클린up 작업

구성 요소를 삭제하는 동안 DOM 정리 작업에 대한 JS interop 코드를 실행하지 마세요. 대신 다음과 같은 이유로 클라이언트의 JavaScript(JS)에서 패턴을 사용합니다MutationObserver.

  • 구성 요소는 Dispose{Async}에서 정리 코드가 실행될 때 DOM에서 제거되었을 수 있습니다.
  • 서버 쪽 렌더링 중에 Blazor 는 클린up 코드가 실행될 때까지 프레임워크에서 렌더러가 삭제되었을 수 있습니다Dispose{Async}.

MutationObserver 패턴을 사용하면 요소가 DOM에서 제거될 때 함수를 실행할 수 있습니다.

다음 예제에서 DOMCleanup 구성 요소는 다음과 같습니다.

  • 의 with를 <div>idcleanupDiv포함합니다. <div> 구성 요소가 DOM에서 제거되면 구성 요소의 나머지 DOM 태그와 함께 요소가 DOM에서 제거됩니다.
  • 파일에서 클래스를 DOMCleanupJSDOMCleanup.razor.js 로드하고 해당 함수를 createObserver 호출하여 콜백을 MutationObserver 설정합니다. 이러한 작업은 수명 주기 메서드에서 OnAfterRenderAsync 수행됩니다.

DOMCleanup.razor:

@page "/dom-cleanup"
@implements IAsyncDisposable
@inject IJSRuntime JS

<h1>DOM Cleanup Example</h1>

<div id="cleanupDiv"></div>

@code {
    private IJSObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>(
                "import", "./Components/Pages/DOMCleanup.razor.js");

            await module.InvokeVoidAsync("DOMCleanup.createObserver");
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}

다음 예제 MutationObserver 에서는 DOM 변경이 발생할 때마다 콜백이 실행됩니다. 문이 대상 요소(cleanupDiv)가 제거되었음을if (targetRemoved) { ... } 확인하면 클린up 코드를 if 실행합니다. 클린up 코드가 실행된 MutationObserver 후 메모리 누수 방지를 위해 연결을 끊고 삭제하는 것이 중요합니다.

DOMCleanup.razor.js 이전 DOMCleanup 구성 요소와 나란히 배치:

export class DOMCleanup {
  static observer;

  static createObserver() {
    const target = document.querySelector('#cleanupDiv');

    this.observer = new MutationObserver(function (mutations) {
      const targetRemoved = mutations.some(function (mutation) {
        const nodes = Array.from(mutation.removedNodes);
        return nodes.indexOf(target) !== -1;
      });

      if (targetRemoved) {
        // Cleanup resources here
        // ...

        // Disconnect and delete MutationObserver
        this.observer && this.observer.disconnect();
        delete this.observer;
      }
    });

    this.observer.observe(target.parentNode, { childList: true });
  }
}

window.DOMCleanup = DOMCleanup;

회로가 없는 JavaScript interop 호출

이 섹션은 서버 쪽 앱에만 적용됩니다.

SignalR 회로 연결이 끊어진 후에는 JavaScript(JS) interop 호출을 실행할 수 없습니다. 구성 요소를 삭제하는 동안 또는 회로가 존재하지 않는 다른 시간에 회로가 없으면 다음 메서드 호출이 실패하고 회로 연결이 끊어졌다는 메시지를 JSDisconnectedException로 기록합니다.

JSDisconnectedException 로깅을 방지하거나 사용자 지정 정보를 기록하려면 try-catch 문에서 예외를 포착합니다.

다음 구성 요소 삭제 예제의 경우:

  • 구성 요소는 IAsyncDisposable을 구현합니다.
  • objInstanceIJSObjectReference입니다.
  • JSDisconnectedException가 포착되어 기록되지 않습니다.
  • 필요에 따라 원하는 로그 수준에서 catch 문에 사용자 지정 정보를 기록할 수 있습니다. 다음 예제에서는 개발자가 구성 요소를 삭제하는 동안 회로의 연결이 끊어진 시기 또는 위치에 대해 신경 쓰지 않는다고 가정하므로 사용자 지정 정보를 기록하지 않습니다.
async ValueTask IAsyncDisposable.DisposeAsync()
{
    try
    {
        if (objInstance is not null)
        {
            await objInstance.DisposeAsync();
        }
    }
    catch (JSDisconnectedException)
    {
    }
}

회로가 손실된 후 사용자 고유의 JS 개체를 정리하거나 클라이언트에서 다른 JS 코드를 실행해야 하는 경우 클라이언트에서 JS의 MutationObserver 패턴을 사용합니다. MutationObserver 패턴을 사용하면 요소가 DOM에서 제거될 때 함수를 실행할 수 있습니다.

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

캐시된 JavaScript 파일

JavaScript(JS) 파일 및 기타 정적 자산은 일반적으로 Development 환경에서 개발 중에 클라이언트에 캐시되지 않습니다. 개발 중에 정적 자산 요청에는 값이 0(0)인 no-cache 또는 max-age 값이 있는 Cache-Control 헤더가 포함됩니다.

Production 환경에서 프로덕션하는 동안 JS 파일은 일반적으로 클라이언트에 의해 캐시됩니다.

브라우저에서 클라이언트 쪽 캐싱을 사용하지 않도록 설정하기 위해 개발자는 일반적으로 다음 방식 중 하나를 채택합니다.

  • 브라우저의 개발자 도구 콘솔이 열려 있을 때 캐싱을 사용하지 않도록 설정합니다. 지침은 각 브라우저 기본tainer의 개발자 도구 설명서에서 찾을 수 있습니다.
  • Blazor 앱의 웹 페이지를 수동으로 브라우저 새로 고침을 수행하여 서버에서 JS 파일을 다시 로드합니다. ASP.NET Core의 HTTP 캐싱 미들웨어는 항상 클라이언트에서 보낸 유효한 캐시 없음 Cache-Control 헤더를 따릅니다.

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

JavaScript interop 호출의 크기 제한

이 섹션은 서버 쪽 앱의 대화형 구성 요소에만 적용됩니다. 클라이언트 쪽 구성 요소의 경우 프레임워크는 JavaScript(JS) interop 입력 및 출력의 크기에 제한을 두지 않습니다.

서버 쪽 앱 JS 의 대화형 구성 요소의 경우 클라이언트에서 서버로 데이터를 전달하는 interop 호출의 크기는 허브 메서드에 허용되는 최대 수신 SignalR 메시지 크기(기본값: 32KB)로 제한됩니다 HubOptions.MaximumReceiveMessageSize . .NET SignalR 메시지에 대한 JS가 MaximumReceiveMessageSize보다 크면 오류가 throw됩니다. 프레임워크는 허브에서 클라이언트로 전송되는 SignalR 메시지의 크기에 제한을 적용하지 않습니다. 크기 제한, 오류 메시지 및 메시지 크기 제한 처리에 대한 지침에 대한 자세한 내용은 ASP.NET Core BlazorSignalR 지침을 참조하세요.

앱이 실행되는 위치 확인

앱이 interop 호출에 대해 JS 코드가 실행되는 위치를 파악하는 것이 적절한 경우 구성 요소가 WebAssembly의 브라우저 컨텍스트에서 실행되고 있는지 확인하는 데 사용합니다 OperatingSystem.IsBrowser .