참고
이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.
중요
이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.
현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.
Blazor 앱은 .NET 메서드에서 JavaScript(JS) 함수를 호출하고 JS 함수에서 .NET 메서드를 호출할 수 있습니다. 이러한 시나리오를 JavaScript 상호 운용성(interop)이라고 합니다.JS
추가 JS interop 지침은 다음 문서에서 제공합니다.
- ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출
- ASP.NET Core Blazor의 JavaScript 함수에서 .NET 메서드 호출
- ASP.NET Core Blazor JavaScript 상호 운용성(JS interop) 성능 모범 사례
참고
JavaScript [JSImport]
/[JSExport]
interop API는 .NET 7 이상의 ASP.NET Core에서 클라이언트 쪽 구성 요소에 사용할 수 있습니다.
자세한 내용은 ASP.NET Core와의 JavaScript JSImport/JSExport interop를 참조하세요 Blazor.
신뢰할 수 없는 데이터가 있는 대화형 서버 구성 요소에 대한 압축
기본적으로 사용하도록 설정된 압축을 사용하면 신뢰할 수 없는 원본에서 데이터를 렌더링하는 보안(인증/권한이 부여된) 대화형 서버 쪽 구성 요소를 만들지 마세요. 신뢰할 수 없는 원본에는 경로 매개 변수, 쿼리 문자열, interop의 데이터 JS 및 타사 사용자가 제어할 수 있는 기타 데이터 원본(데이터베이스, 외부 서비스)이 포함됩니다. 자세한 내용은 ASP.NET Core BlazorSignalR 대화형 서버 쪽 렌더링에 대한 ASP.NET Core 지침 및 Blazor하세요.
JavaScript interop 추상화 및 기능 패키지
@microsoft/dotnet-js-interop
(npmjs.com
)는 .NET과 JavaScript(Microsoft.JSInterop
) 코드 간의 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과의 상호 작용
개체가 JS와 상호 작용하지 않을 때만 JavaScript(Blazor)를 사용하여 DOM을 변경합니다. Blazor는 DOM의 표현을 유지 관리하고 DOM 개체와 직접 상호 작용합니다. Blazor에 의해 렌더링된 요소가 JS를 직접 사용하거나 JS Interop을 통해 외부적으로 수정되는 경우 DOM이 더 이상 Blazor의 내부 표현과 일치하지 않아 정의되지 않은 동작이 발생할 수 있습니다. 정의되지 않은 동작은 단순히 요소 또는 해당 함수의 표시를 방해할 수 있지만 앱 또는 서버에 보안 위험을 초래할 수도 있습니다.
이 지침은 사용자 JS interop 코드뿐 아니라 JS 및 JS와 같은 타사 프레임워크에 제공되는 라이브러리를 비롯하여 앱에 사용되는 모든 라이브러리에도 적용됩니다.
일부 설명서에서 JS interop은 ‘예시 데모’의 목적으로 요소를 변경하는 데 사용됩니다. 이러한 경우 경고 텍스트가 표시됩니다.
자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.
형식 함수 필드가 있는 JavaScript 클래스
형식 함수 필드가 있는 JavaScript 클래스는 interop에서 BlazorJS 지원되지 않습니다. 클래스에서 Javascript 함수를 사용합니다.
지원되지 않음:GreetingHelpers.sayHello
다음 클래스에서는 함수 형식의 필드가 Blazor의 JS 상호 운용성에 의해 검색되지 않으며 C# 코드에서 실행할 수 없습니다.
export class GreetingHelpers {
sayHello = function() {
...
}
}
다음 클래스에서 함수로 지원됩니다.
export class GreetingHelpers {
sayHello() {
...
}
}
화살표 함수도 지원됩니다.
export class GreetingHelpers {
sayHello = () => {
...
}
}
인라인 이벤트 처리기 사용을 피하십시오
JavaScript 함수는 인라인 이벤트 처리기에서 직접 호출할 수 있습니다. 다음 예제 alertUser
에서는 사용자가 단추를 선택할 때 호출되는 JavaScript 함수입니다.
<button onclick="alertUser">Click Me!</button>
그러나 인라인 이벤트 처리기를 사용하는 것은 JavaScript 함수를 호출하기 위한 잘못된 디자인 선택 입니다.
- HTML 태그와 JavaScript 코드를 혼합하면 종종 확인할 수 없는 코드가 발생합니다.
- 인라인 이벤트 처리기 실행은 CSP(콘텐츠 보안 정책)(MDN 설명서)에 의해 차단될 수 있습니다.
다음 예제와 같이 JavaScript addEventListener
에서 처리기를 할당하는 접근 방식을 위해 인라인 이벤트 처리기를 사용하지 않는 것이 좋습니다.
AlertUser.razor.js
:
export function alertUser() {
alert('The button was selected!');
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", alertUser);
}
AlertUser.razor
:
@page "/alert-user"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Alert User</h1>
<p>
<button id="btn">Click Me!</button>
</p>
@code {
private IJSObjectReference? module;
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/AlertUser.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
앞의 예제에서는 JSDisconnectedException가 Blazor 회로가 손실될 경우 SignalR의 모듈 폐기 중에 트랩됩니다. 코드가 Blazor WebAssembly 앱에서 사용되는 경우, 손실할 SignalR 연결이 없으므로 try
-catch
블록을 제거하고 모듈을 삭제하는 줄(await module.DisposeAsync();
)은 그대로 둘 수 있습니다. 자세한 내용은 ASP.NET Core Blazor JavaScript 상호 운용성(JS)을 참조하세요.
자세한 내용은 다음 리소스를 참조하세요.
비동기 JavaScript 호출
JS interop 호출은 호출된 코드가 동기 또는 비동기인지 여부에 관계없이 비동기입니다. 호출은 구성 요소가 서버 쪽 및 클라이언트 쪽 렌더링 모델에서 호환되도록 하기 위해 비동기적입니다. 서버 쪽 렌더링을 채택할 JS 때 interop 호출은 네트워크 연결을 통해 전송되므로 비동기적이어야 합니다. 클라이언트 쪽 렌더링만 채택하는 앱의 경우 동기 JS interop 호출이 지원됩니다.
자세한 내용은 다음 문서를 참조하세요.
자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.
개체 직렬화
Blazor는 다음 요구 사항 및 기본 동작을 통해 System.Text.Json을 serialization에 사용합니다.
- 형식에는 기본 생성자가 있어야 하고
get
/set
접근자는 public이어야 하며 필드는 직렬화되면 안 됩니다. - 전역 기본 serialization은 사용자 지정할 수 없는데, 이는 기존 구성 요소 라이브러리에 손상을 주거나 성능과 보안에 영향을 미치고, 안정성을 감소시키는 일을 방지하기 위해서입니다.
- .NET 멤버 이름을 직렬화하면 소문자 JSON 키 이름이 생성됩니다.
- JSON은 C# 인스턴스로 JsonElement 역직렬화되며, 이 인스턴스는 혼합 대/소문자를 허용합니다. C# 모델 속성에 할당하기 위한 내부 캐스팅은 JSON 키 이름과 C# 속성 이름 간의 대/소문자 차이에도 불구하고 예상대로 작동합니다.
- KeyValuePair같은 복잡한 프레임워크 형식은 게시 시 IL 트리머에 의해 잘릴 수 있으며, JS interop이나 JSON 직렬화/역직렬화에 존재하지 않을 수 있습니다. IL 트리머가 자르는 형식에 대한 사용자 지정 형식을 만드는 것이 좋습니다.
-
Blazor항상 C# 원본 생성을 사용하는 경우를 포함하여 JSON serialization에 대한 리플렉션을 사용합니다.
JsonSerializerIsReflectionEnabledByDefault
를false
로 설정하면 앱의 프로젝트 파일에서도 직렬화를 시도할 때 오류가 발생합니다.
JsonConverter API는 사용자 지정 serialization에 사용할 수 있습니다. 속성에 [JsonConverter]
특성을 주석하려면, 기존 데이터 형식의 기본 직렬화를 재정의할 수 있습니다.
자세한 내용은 .NET 문서의 다음 리소스를 참조하세요.
- .NET의 JSON 직렬화 및 역직렬화(마샬링 및 경계 해제)
-
System.Text.Json
으로 속성 이름과 값을 사용자 지정하는 방법 - .NET에서 JSON serialization(마샬링)용 사용자 지정 변환기를 작성하는 방법
Blazor는 최적화된 바이트 배열 JS interop을 지원하여 바이트 배열이 Base64로 인코딩/디코딩되는 것을 방지합니다. 앱은 맞춤형 직렬화를 적용하고 생성된 바이트를 전달할 수 있습니다. 자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.
Blazor는 대량의 .NET 개체가 빠르게 직렬화되거나 많은 양의 .NET 개체 또는 여러 .NET 개체를 직렬화해야 하는 경우 역마샬링된 JS interop을 지원합니다. 자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.
구성 요소 삭제 중 DOM 정리 작업
구성 요소를 삭제하는 동안 DOM 정리 작업에 대한 JS interop 코드를 실행하지 마세요. 대신, 다음과 같은 이유로 클라이언트 쪽 JavaScript(MutationObserver
)에서 JS 패턴을 사용하세요.
- 구성 요소는
Dispose{Async}
에서 정리 코드가 실행될 때 DOM에서 제거되었을 수 있습니다. - 서버 측 렌더링 중에 정리 코드가 실행될 때, 프레임워크에서 Blazor 렌더러가
Dispose{Async}
이미 삭제되었을 수 있습니다.
MutationObserver
패턴을 사용하면 요소가 DOM에서 제거될 때 함수를 실행할 수 있습니다.
다음 예제에서 DOMCleanup
구성 요소는 다음과 같습니다.
-
<div>
에는id
와cleanupDiv
가 포함되어 있습니다.<div>
요소는 구성 요소가 DOM에서 제거될 때 구성 요소의 나머지 DOM 태그와 함께 DOM에서 제거됩니다. -
DOMCleanup
파일에서 JSDOMCleanup.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)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
앞의 예제에서는 JSDisconnectedException가 Blazor 회로가 손실될 경우 SignalR의 모듈 폐기 중에 트랩됩니다. 코드가 Blazor WebAssembly 앱에서 사용되는 경우, 손실할 SignalR 연결이 없으므로 try
-catch
블록을 제거하고 모듈을 삭제하는 줄(await module.DisposeAsync();
)은 그대로 둘 수 있습니다. 자세한 내용은 ASP.NET Core Blazor JavaScript 상호 운용성(JS)을 참조하세요.
다음 예제 MutationObserver
에서는 DOM 변경이 발생할 때마다 콜백이 실행됩니다.
if
문이 대상 요소(cleanupDiv
)가 제거되었음을 확인할 때 정리 코드를 실행합니다if (targetRemoved) { ... }
. 정리 코드가 실행된 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;
위의 방법은 MutationObserver
target.parentNode
연결합니다. 이 방법은 parentNode
자체가 DOM에서 제거될 때까지 작동합니다. 예를 들어 새 페이지로 이동할 때 전체 페이지 구성 요소가 DOM에서 제거되는 일반적인 시나리오입니다. 그런 경우, 페이지 내의 변경 사항을 관찰하는 모든 자식 구성 요소가 제대로 정리되지 않습니다.
document.body
대신 target.parentNode
관찰하는 것이 더 나은 대상이라고 가정하지 마세요.
document.body
을 관찰하는 것은 성능에 영향을 미칩니다. 이는 콜백 논리가 요소와 관련이 있는지 여부와 관계없이 모든 DOM 업데이트에 대해 실행되기 때문입니다. 다음 방법 중 하나를 사용합니다.
- 관찰할 적합한 상위 노드를 식별할 수 있는 경우
MutationObserver
사용합니다. 이상적으로, 이 부모 항목은document.body
대신 관찰하려는 변경 사항에 범위가 지정됩니다. -
MutationObserver
사용하는 대신 사용자 지정 요소 및disconnectedCallback
사용하는 것이 좋습니다. 이 이벤트는 DOM 변경과 관련하여 DOM에 상주하는 위치에 관계없이 사용자 지정 요소의 연결이 끊어질 때 항상 발생합니다.
회로가 없는 JavaScript interop 호출
이 섹션은 서버 쪽 앱에만 적용됩니다.
JavaScript(JS) interop 호출은 Blazor의 SignalR 회로가 연결 해제된 후에는 실행할 수 없습니다. 구성 요소를 삭제하는 동안 또는 회로가 존재하지 않는 다른 시간에 회로가 없으면 다음 메서드 호출이 실패하고 회로 연결이 끊어졌다는 메시지를 JSDisconnectedException로 기록합니다.
-
JS interop 메서드 호출
- IJSRuntime.InvokeAsync
- JSRuntimeExtensions.InvokeAsync
- JSRuntimeExtensions.InvokeVoidAsync
InvokeNewAsync
GetValueAsync
SetValueAsync
-
Dispose
/DisposeAsync
은 모든 IJSObjectReference를 호출합니다.
- JS interop 메서드 호출
-
Dispose
/DisposeAsync
은 모든 IJSObjectReference를 호출합니다.
JSDisconnectedException 로깅을 방지하거나 사용자 지정 정보를 기록하려면 try-catch
문에서 예외를 포착합니다.
다음 구성 요소 삭제 예제의 경우:
- 서버 측 구성 요소는 IAsyncDisposable를 구현합니다.
-
module
는 IJSObjectReference 모듈의 JS입니다. - JSDisconnectedException가 포착되어 기록되지 않습니다.
- 필요에 따라 원하는 로그 수준에서
catch
문에 사용자 지정 정보를 기록할 수 있습니다. 다음 예제에서는 개발자가 구성 요소를 삭제하는 동안 회로의 연결이 끊어진 시기 또는 위치에 대해 신경 쓰지 않는다고 가정하므로 사용자 지정 정보를 기록하지 않습니다.
async ValueTask IAsyncDisposable.DisposeAsync()
{
try
{
if (module is not null)
{
await module.DisposeAsync();
}
}
catch (JSDisconnectedException)
{
}
}
서버 쪽 앱에서 회로가 손실된 후, 사용자가 자신의 JS 개체를 정리하거나 클라이언트에서 다른 JS 코드를 실행해야 한다면, 클라이언트에서 Blazor의 MutationObserver
패턴을 사용하세요.
MutationObserver
패턴을 사용하면 요소가 DOM에서 제거될 때 함수를 실행할 수 있습니다.
자세한 내용은 다음 문서를 참조하세요.
- ASP.NET Core Blazor 앱의 오류 처리: JavaScript interop 섹션에서 JS interop 시나리오의 오류 처리에 대해 설명합니다.
- ASP.NET Core Razor 구성 요소 삭제: 이 문서에서는 Razor 구성 요소에서 삭제 패턴을 구현하는 방법을 설명합니다.
캐시된 JavaScript 파일
JavaScript(JS) 파일 및 기타 정적 자산은 일반적으로 Development
환경에서 개발 중에 클라이언트에 캐시되지 않습니다. 개발 중에, 정적 자산 요청에는
Production
환경에서 프로덕션하는 동안 JS 파일은 일반적으로 클라이언트에 의해 캐시됩니다.
브라우저에서 클라이언트 쪽 캐싱을 사용하지 않도록 설정하기 위해 개발자는 일반적으로 다음 방식 중 하나를 채택합니다.
- 브라우저의 개발자 도구 콘솔이 열려 있을 때 캐싱을 사용하지 않도록 설정합니다. 지침은 각 브라우저 유지 관리자의 개발자 도구 설명서에서 찾을 수 있습니다.
-
Blazor 앱의 웹 페이지를 수동으로 브라우저 새로 고침을 수행하여 서버에서 JS 파일을 다시 로드합니다. ASP.NET Core의 HTTP 캐싱 미들웨어는 항상 클라이언트에서 보낸 유효한 캐시 없음
Cache-Control
헤더를 따릅니다.
자세한 내용은 다음을 참조하세요.
JavaScript interop 호출의 크기 제한
이 섹션은 서버 쪽 앱의 대화형 구성 요소에만 적용됩니다. 클라이언트 쪽 구성 요소의 경우 프레임워크는 JavaScript(JS) interop 입력 및 출력의 크기에 제한을 두지 않습니다.
서버 쪽 앱 JS 의 대화형 구성 요소의 경우 클라이언트에서 서버로 데이터를 전달하는 interop 호출의 크기는 허브 메서드에 허용되는 최대 수신 SignalR 메시지 크기(기본값: 32KB)로 제한됩니다 HubOptions.MaximumReceiveMessageSize . JS에서 .NET SignalR 메시지가 MaximumReceiveMessageSize보다 크면 오류가 발생합니다. 프레임워크는 허브에서 클라이언트로 전송되는 SignalR 메시지의 크기에 제한을 적용하지 않습니다. 크기 제한, 오류 메시지 및 메시지 크기 제한 처리에 대한 지침에 대한 자세한 내용은 ASP.NET Core BlazorSignalR 지침을 참조하세요.
앱이 실행되는 위치 확인
앱에서 interop 호출을 위해 JS 코드가 어디에서 실행되고 있는지를 파악하는 것이 중요하다면, OperatingSystem.IsBrowser을 사용하여 구성 요소가 WebAssembly의 브라우저 컨텍스트에서 실행 중인지를 확인합니다.
ASP.NET Core