Webview API
Webview API를 사용하면 확장에서 Visual Studio Code 내부에 완전히 사용자 정의 가능한 보기를 만들 수 있습니다. 예를 들어, 내장 Markdown 확장은 웹뷰를 사용하여 Markdown 미리 보기를 렌더링합니다. 웹뷰는 VS Code의 기본 API가 지원하는 것 이상의 복잡한 사용자 인터페이스를 구축하는 데에도 사용할 수 있습니다.
웹뷰를 VS Code 내부에 있는 `iframe`이라고 생각하시면 됩니다. 확장 프로그램이 이 프레임을 제어합니다. 웹뷰는 이 프레임에서 거의 모든 HTML 콘텐츠를 렌더링할 수 있으며 메시지 전달을 사용하여 확장 프로그램과 통신합니다. 이러한 자유는 웹뷰를 매우 강력하게 만들고 확장 프로그램의 새로운 가능성을 열어줍니다.
웹뷰는 여러 VS Code API에서 사용됩니다.
- `createWebviewPanel`을 사용하여 생성된 Webview 패널. 이 경우 Webview 패널은 VS Code에서 별도의 편집기로 표시됩니다. 사용자 정의 UI 및 사용자 정의 시각화를 표시하는 데 유용합니다.
- 사용자 정의 편집기의 보기로 사용됩니다. 사용자 정의 편집기를 사용하면 확장에서 작업 공간의 모든 파일을 편집하기 위한 사용자 정의 UI를 제공할 수 있습니다. 사용자 정의 편집기 API를 사용하면 확장 프로그램에서 실행 취소 및 다시 실행과 같은 편집기 이벤트와 저장과 같은 파일 이벤트에 연결할 수도 있습니다.
- 사이드바 또는 패널 영역에 렌더링되는 Webview 보기에서 사용됩니다. 자세한 내용은 webview view sample extension을 참조하십시오.
이 페이지는 기본 웹뷰 패널 API에 중점을 두지만, 여기서 다루는 대부분의 내용은 사용자 정의 편집기 및 웹뷰 보기에서 사용되는 웹뷰에도 적용됩니다. 이러한 API에 더 관심이 있더라도 웹뷰 기본 사항을 숙지하기 위해 이 페이지를 먼저 읽어보는 것이 좋습니다.
링크
VS Code API 사용
웹뷰를 사용해야 하나요?
웹뷰는 훌륭하지만 신중하게 사용해야 하며 VS Code의 네이티브 API가 부족할 때만 사용해야 합니다. 웹뷰는 리소스를 많이 사용하며 일반 확장 프로그램과 별도의 컨텍스트에서 실행됩니다. 잘못 설계된 웹뷰는 VS Code 내에서 쉽게 어색하게 느껴질 수도 있습니다.
웹뷰를 사용하기 전에 다음 사항을 고려하십시오.
-
이 기능이 VS Code 내부에 있어야 합니까? 별도의 응용 프로그램이나 웹사이트로 만드는 것이 더 좋습니까?
-
웹뷰가 기능을 구현하는 유일한 방법입니까? 대신 일반 VS Code API를 사용할 수 있습니까?
-
웹뷰가 높은 리소스 비용을 정당화할 만큼 충분한 사용자 가치를 제공할 것입니까?
기억하십시오. 웹뷰로 할 수 있다고 해서 반드시 해야 하는 것은 아닙니다. 그러나 웹뷰를 사용해야 한다고 확신한다면 이 문서를 참조하십시오. 시작하겠습니다.
Webview API 기본 사항
웹뷰 API를 설명하기 위해 **Cat Coding**이라는 간단한 확장을 만들 것입니다. 이 확장은 웹뷰를 사용하여 고양이가 코드를 작성하는 GIF를 표시합니다(아마도 VS Code에서). API를 진행하면서 기능에 계속 추가할 것입니다. 여기에는 고양이가 작성한 소스 코드 줄 수를 추적하는 카운터와 고양이가 버그를 도입할 때 사용자에게 알리는 알림이 포함됩니다.
**Cat Coding** 확장 프로그램의 첫 번째 버전에 대한 `package.json`입니다. 예제 앱의 전체 코드는 여기에서 찾을 수 있습니다. 저희 확장 프로그램의 첫 번째 버전은 `catCoding.start`라는 명령을 기여합니다. 사용자가 이 명령을 호출하면 고양이가 있는 간단한 웹뷰가 표시됩니다. 사용자는 **명령 팔레트**에서 **Cat Coding: Start new cat coding session**으로 이 명령을 호출하거나 원하는 경우 바로 가기 키를 만들 수도 있습니다.
{
"name": "cat-coding",
"description": "Cat Coding",
"version": "0.0.1",
"publisher": "bierner",
"engines": {
"vscode": "^1.74.0"
},
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "catCoding.start",
"title": "Start new cat coding session",
"category": "Cat Coding"
}
]
},
"scripts": {
"vscode:prepublish": "tsc -p ./",
"compile": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install"
},
"dependencies": {
"vscode": "*"
},
"devDependencies": {
"@types/node": "^9.4.6",
"typescript": "^2.8.3"
}
}
**참고**: 확장이 1.74 이전 버전의 VS Code를 대상으로 하는 경우 `activationEvents`에 `onCommand:catCoding.start`를 명시적으로 나열해야 합니다.
이제 `catCoding.start` 명령을 구현해 보겠습니다. 확장 프로그램의 기본 파일에서 `catCoding.start` 명령을 등록하고 이를 사용하여 기본 웹뷰를 표시합니다.
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
// Create and show a new webview
const panel = vscode.window.createWebviewPanel(
'catCoding', // Identifies the type of the webview. Used internally
'Cat Coding', // Title of the panel displayed to the user
vscode.ViewColumn.One, // Editor column to show the new webview panel in.
{} // Webview options. More on these later.
);
})
);
}
`vscode.window.createWebviewPanel` 함수는 편집기에서 웹뷰를 생성하고 표시합니다. 현재 상태에서 `catCoding.start` 명령을 실행해 보면 다음과 같은 내용을 볼 수 있습니다.

저희 명령은 올바른 제목으로 새 웹뷰 패널을 열지만 내용은 비어 있습니다! 고양이를 새 패널에 추가하려면 `webview.html`을 사용하여 웹뷰의 HTML 콘텐츠를 설정해야 합니다.
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
// Create and show panel
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{}
);
// And set its HTML content
panel.webview.html = getWebviewContent();
})
);
}
function getWebviewContent() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cat Coding</title>
</head>
<body>
<img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" width="300" />
</body>
</html>`;
}
명령을 다시 실행하면 웹뷰가 다음과 같이 표시됩니다.

진행!
`webview.html`은 항상 완전한 HTML 문서여야 합니다. HTML 조각이나 잘못된 형식의 HTML은 예기치 않은 동작을 유발할 수 있습니다.
웹뷰 콘텐츠 업데이트
`webview.html`은 생성 후에도 웹뷰 콘텐츠를 업데이트할 수 있습니다. 이를 사용하여 고양이 회전을 도입하여 **Cat Coding**을 더욱 동적으로 만들어 보겠습니다.
import * as vscode from 'vscode';
const cats = {
'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif',
'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif'
};
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{}
);
let iteration = 0;
const updateWebview = () => {
const cat = iteration++ % 2 ? 'Compiling Cat' : 'Coding Cat';
panel.title = cat;
panel.webview.html = getWebviewContent(cat);
};
// Set initial content
updateWebview();
// And schedule updates to the content every second
setInterval(updateWebview, 1000);
})
);
}
function getWebviewContent(cat: keyof typeof cats) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cat Coding</title>
</head>
<body>
<img src="${cats[cat]}" width="300" />
</body>
</html>`;
}

`webview.html`을 설정하는 것은 iframe을 다시 로드하는 것과 유사하게 웹뷰 전체 콘텐츠를 대체합니다. 스크립트를 웹뷰에서 사용하기 시작하면 `webview.html`을 설정하는 것이 스크립트 상태도 재설정한다는 것을 의미하므로 이를 기억하는 것이 중요합니다.
위의 예에서는 `webview.title`을 사용하여 편집기에 표시되는 문서의 제목을 변경하기도 합니다. 제목을 설정해도 웹뷰가 다시 로드되지는 않습니다.
수명 주기
웹뷰 패널은 이를 생성한 확장에서 소유합니다. 확장은 `createWebviewPanel`에서 반환된 웹뷰를 계속 보유해야 합니다. 확장이 이 참조를 잃으면 웹뷰가 VS Code에 계속 표시되더라도 해당 웹뷰에 다시 액세스할 수 없습니다.
텍스트 편집기의 경우와 마찬가지로 사용자는 언제든지 웹뷰 패널을 닫을 수 있습니다. 웹뷰 패널이 사용자에 의해 닫히면 웹뷰 자체가 파괴됩니다. 파괴된 웹뷰를 사용하려고 하면 예외가 발생합니다. 이는 `setInterval`을 사용하는 위의 예시에 중요한 버그가 있다는 것을 의미합니다. 사용자가 패널을 닫으면 `setInterval`이 계속 실행되어 `panel.webview.html`을 업데이트하려고 시도하고, 물론 예외가 발생합니다. 고양이는 예외를 싫어합니다. 이것을 고쳐봅시다!
onDidDispose 이벤트는 웹뷰가 파괴될 때 발생합니다. 이 이벤트를 사용하여 추가 업데이트를 취소하고 웹뷰의 리소스를 정리할 수 있습니다.
import * as vscode from 'vscode';
const cats = {
'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif',
'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif'
};
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{}
);
let iteration = 0;
const updateWebview = () => {
const cat = iteration++ % 2 ? 'Compiling Cat' : 'Coding Cat';
panel.title = cat;
panel.webview.html = getWebviewContent(cat);
};
updateWebview();
const interval = setInterval(updateWebview, 1000);
panel.onDidDispose(
() => {
// When the panel is closed, cancel any future updates to the webview content
clearInterval(interval);
},
null,
context.subscriptions
);
})
);
}
확장은 웹뷰에서 `dispose()`를 호출하여 프로그래밍 방식으로 웹뷰를 닫을 수도 있습니다. 예를 들어 고양이의 작업 시간을 5초로 제한하고 싶다면
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{}
);
panel.webview.html = getWebviewContent('Coding Cat');
// After 5sec, programmatically close the webview panel
const timeout = setTimeout(() => panel.dispose(), 5000);
panel.onDidDispose(
() => {
// Handle user closing panel before the 5sec have passed
clearTimeout(timeout);
},
null,
context.subscriptions
);
})
);
}
가시성 및 이동
웹뷰 패널이 백그라운드 탭으로 이동하면 숨겨집니다. 하지만 파괴되지는 않습니다. VS Code는 패널이 다시 포그라운드로 가져와질 때 `webview.html`에서 웹뷰의 콘텐츠를 자동으로 복원합니다.

`.visible` 속성은 웹뷰 패널이 현재 보이는지 여부를 알려줍니다.
확장은 `reveal()`을 호출하여 웹뷰 패널을 프로그래밍 방식으로 포그라운드로 가져올 수 있습니다. 이 메서드는 패널을 표시할 대상 보기 열을 선택적으로 받습니다. 웹뷰 패널은 한 번에 하나의 편집기 열에만 표시될 수 있습니다. `reveal()`을 호출하거나 웹뷰 패널을 새 편집기 열로 끌면 웹뷰가 해당 새 열로 이동합니다.

확장을 업데이트하여 한 번에 하나의 웹뷰만 존재하도록 하겠습니다. 패널이 백그라운드에 있으면 `catCoding.start` 명령이 포그라운드로 가져옵니다.
export function activate(context: vscode.ExtensionContext) {
// Track the current panel with a webview
let currentPanel: vscode.WebviewPanel | undefined = undefined;
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const columnToShowIn = vscode.window.activeTextEditor
? vscode.window.activeTextEditor.viewColumn
: undefined;
if (currentPanel) {
// If we already have a panel, show it in the target column
currentPanel.reveal(columnToShowIn);
} else {
// Otherwise, create a new panel
currentPanel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
columnToShowIn || vscode.ViewColumn.One,
{}
);
currentPanel.webview.html = getWebviewContent('Coding Cat');
// Reset when the current panel is closed
currentPanel.onDidDispose(
() => {
currentPanel = undefined;
},
null,
context.subscriptions
);
}
})
);
}
새로운 확장이 작동하는 모습입니다.

웹뷰의 가시성이 변경될 때마다 또는 웹뷰가 새 열로 이동될 때마다 `onDidChangeViewState` 이벤트가 발생합니다. 저희 확장은 이 이벤트를 사용하여 웹뷰가 표시되는 열에 따라 고양이를 변경할 수 있습니다.
const cats = {
'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif',
'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif',
'Testing Cat': 'https://media.giphy.com/media/3oriO0OEd9QIDdllqo/giphy.gif'
};
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{}
);
panel.webview.html = getWebviewContent('Coding Cat');
// Update contents based on view state changes
panel.onDidChangeViewState(
e => {
const panel = e.webviewPanel;
switch (panel.viewColumn) {
case vscode.ViewColumn.One:
updateWebviewForCat(panel, 'Coding Cat');
return;
case vscode.ViewColumn.Two:
updateWebviewForCat(panel, 'Compiling Cat');
return;
case vscode.ViewColumn.Three:
updateWebviewForCat(panel, 'Testing Cat');
return;
}
},
null,
context.subscriptions
);
})
);
}
function updateWebviewForCat(panel: vscode.WebviewPanel, catName: keyof typeof cats) {
panel.title = catName;
panel.webview.html = getWebviewContent(catName);
}

웹뷰 검사 및 디버깅
**개발자: 개발자 도구 토글** 명령은 웹뷰를 디버그하고 검사하는 데 사용할 수 있는 개발자 도구 창을 엽니다.

VS Code 버전 1.56 이전 버전을 사용 중이거나 `enableFindWidget`을 설정하는 웹뷰를 디버깅하려는 경우 대신 **개발자: 웹뷰 개발자 도구 열기** 명령을 사용해야 합니다. 이 명령은 모든 웹뷰와 편집기 자체에서 공유되는 개발자 도구 페이지를 사용하는 대신 각 웹뷰에 대한 전용 개발자 도구 페이지를 엽니다.
개발자 도구에서 개발자 도구 창의 왼쪽 상단에 있는 검사 도구를 사용하여 웹뷰 콘텐츠 검사를 시작할 수 있습니다.

개발자 도구 콘솔에서 웹뷰의 모든 오류 및 로그를 볼 수도 있습니다.

웹뷰의 컨텍스트에서 표현식을 평가하려면 개발자 도구 콘솔 패널의 왼쪽 상단 드롭다운에서 **활성 프레임** 환경을 선택해야 합니다.

**활성 프레임** 환경은 웹뷰 스크립트 자체가 실행되는 곳입니다.
또한 **개발자: 웹뷰 새로고침** 명령은 모든 활성 웹뷰를 새로고침합니다. 웹뷰의 상태를 재설정해야 하거나 디스크의 웹뷰 콘텐츠가 변경되어 새 콘텐츠를 로드하려는 경우 유용할 수 있습니다.
로컬 콘텐츠 로드
웹뷰는 로컬 리소스에 직접 액세스할 수 없는 격리된 컨텍스트에서 실행됩니다. 이는 보안상의 이유로 이루어집니다. 즉, 확장에서 이미지, 스타일시트 및 기타 리소스를 로드하거나 사용자 현재 작업 공간의 콘텐츠를 로드하려면 `Webview.asWebviewUri` 함수를 사용하여 VS Code가 로컬 리소스의 하위 집합을 로드하는 데 사용할 수 있는 특수 URI로 `file:` URI를 변환해야 합니다.
고양이 GIF를 Giphy에서 가져오는 대신 확장 프로그램에 포함시키려고 한다고 상상해 봅시다. 이렇게 하려면 먼저 디스크의 파일에 대한 URI를 만든 다음 `asWebviewUri` 함수를 통해 이러한 URI를 전달합니다.
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{}
);
// Get path to resource on disk
const onDiskPath = vscode.Uri.joinPath(context.extensionUri, 'media', 'cat.gif');
// And get the special URI to use with the webview
const catGifSrc = panel.webview.asWebviewUri(onDiskPath);
panel.webview.html = getWebviewContent(catGifSrc);
})
);
}
function getWebviewContent(catGifSrc: vscode.Uri) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cat Coding</title>
</head>
<body>
<img src="${catGifSrc}" width="300" />
</body>
</html>`;
}
이 코드를 디버그하면 `catGifSrc`의 실제 값이 다음과 같다는 것을 알 수 있습니다.
vscode-resource:/Users/toonces/projects/vscode-cat-coding/media/cat.gif
VS Code는 이 특수 URI를 이해하고 디스크에서 GIF를 로드하는 데 사용합니다!
기본적으로 웹뷰는 다음 위치의 리소스에만 액세스할 수 있습니다.
- 확장 프로그램 설치 디렉토리 내.
- 사용자의 현재 활성 작업 공간 내.
`WebviewOptions.localResourceRoots`를 사용하여 추가 로컬 리소스에 대한 액세스를 허용합니다.
데이터 URI를 사용하여 리소스를 웹뷰에 직접 포함할 수도 있습니다.
로컬 리소스에 대한 액세스 제어
웹뷰는 `localResourceRoots` 옵션을 사용하여 사용자의 컴퓨터에서 로드할 수 있는 리소스를 제어할 수 있습니다. `localResourceRoots`는 로컬 콘텐츠를 로드할 수 있는 루트 URI 집합을 정의합니다.
확장 프로그램의 `media` 디렉토리에서만 리소스를 로드하도록 **Cat Coding** 웹뷰를 제한하기 위해 `localResourceRoots`를 사용할 수 있습니다.
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{
// Only allow the webview to access resources in our extension's media directory
localResourceRoots: [vscode.Uri.joinPath(context.extensionUri, 'media')]
}
);
const onDiskPath = vscode.Uri.joinPath(context.extensionUri, 'media', 'cat.gif');
const catGifSrc = panel.webview.asWebviewUri(onDiskPath);
panel.webview.html = getWebviewContent(catGifSrc);
})
);
}
모든 로컬 리소스 사용을 비활성화하려면 `localResourceRoots`를 `[]`로 설정하기만 하면 됩니다.
일반적으로 웹뷰는 로컬 리소스를 로드할 때 가능한 한 제한적이어야 합니다. 그러나 `localResourceRoots`가 자체적으로 완벽한 보안 보호를 제공하지는 않는다는 점을 명심하십시오. 웹뷰가 보안 모범 사례를 따르고 콘텐츠 보안 정책을 추가하여 로드할 수 있는 콘텐츠를 추가로 제한해야 합니다.
웹뷰 콘텐츠 테마 지정
웹뷰는 CSS를 사용하여 VS Code의 현재 테마에 따라 모양을 변경할 수 있습니다. VS Code는 테마를 세 가지 범주로 그룹화하고 현재 테마를 나타내기 위해 `body` 요소에 특수 클래스를 추가합니다.
- `vscode-light` - 밝은 테마.
- `vscode-dark` - 어두운 테마.
- `vscode-high-contrast` - 고대비 테마.
다음 CSS는 사용자의 현재 테마에 따라 웹뷰의 텍스트 색상을 변경합니다.
body.vscode-light {
color: black;
}
body.vscode-dark {
color: white;
}
body.vscode-high-contrast {
color: red;
}
웹뷰 애플리케이션을 개발할 때 세 가지 유형의 테마에 대해 작동하는지 확인하십시오. 또한 시각 장애가 있는 사용자도 사용할 수 있도록 고대비 모드에서 웹뷰를 항상 테스트하십시오.
웹뷰는 CSS 변수를 사용하여 VS Code 테마 색상에 액세스할 수도 있습니다. 이러한 변수 이름은 `vscode`로 접두사가 붙고 `.`는 `-`로 대체됩니다. 예를 들어 `editor.foreground`는 `var(--vscode-editor-foreground)`가 됩니다.
code {
color: var(--vscode-editor-foreground);
}
사용 가능한 테마 변수는 테마 색상 참조를 참조하십시오. 변수에 대한 IntelliSense 제안을 제공하는 확장 프로그램이 있습니다.
다음 글꼴 관련 변수도 정의됩니다.
- `--vscode-editor-font-family` - 편집기 글꼴 패밀리 (`editor.fontFamily` 설정).
- `--vscode-editor-font-weight` - 편집기 글꼴 두께 (`editor.fontWeight` 설정).
- `--vscode-editor-font-size` - 편집기 글꼴 크기 (`editor.fontSize` 설정).
마지막으로, 단일 테마를 대상으로 하는 CSS를 작성해야 하는 특별한 경우, 웹뷰의 body 요소에는 현재 활성 테마의 ID를 저장하는 `vscode-theme-id`라는 데이터 속성이 있습니다. 이를 통해 웹뷰에 대한 테마별 CSS를 작성할 수 있습니다.
body[data-vscode-theme-id="One Dark Pro"] {
background: hotpink;
}
지원되는 미디어 형식
웹뷰는 오디오 및 비디오를 지원하지만 모든 미디어 코덱 또는 미디어 파일 컨테이너 유형이 지원되는 것은 아닙니다.
웹뷰에서 사용할 수 있는 다음 오디오 형식
- Wav
- Mp3
- Ogg
- Flac
웹뷰에서 사용할 수 있는 다음 비디오 형식
- H.264
- VP8
비디오 파일의 경우 비디오 트랙과 오디오 트랙 모두의 미디어 형식이 지원되는지 확인하십시오. 예를 들어 많은 `.mp4` 파일은 비디오에 `H.264`를, 오디오에 `AAC`를 사용합니다. VS Code는 `mp4`의 비디오 부분을 재생할 수 있지만 `AAC` 오디오는 지원되지 않으므로 소리가 나지 않습니다. 대신 오디오 트랙에는 `mp3`를 사용해야 합니다.
컨텍스트 메뉴
고급 웹뷰는 사용자가 웹뷰를 마우스 오른쪽 버튼으로 클릭할 때 표시되는 컨텍스트 메뉴를 사용자 정의할 수 있습니다. 이는 VS Code의 일반 컨텍스트 메뉴와 유사하게 기여 포인트를 사용하여 수행되므로 사용자 정의 메뉴는 편집기의 나머지 부분과 잘 어울립니다. 웹뷰는 웹뷰의 다른 섹션에 대한 사용자 정의 컨텍스트 메뉴를 표시할 수도 있습니다.
새로운 컨텍스트 메뉴 항목을 웹뷰에 추가하려면 먼저 `menus` 아래의 새 `webview/context` 섹션에 항목을 추가합니다. 각 기여에는 `command`(항목 제목이 오는 곳이기도 함)와 `when` 절이 있습니다. `when` 절에는 `webviewId == 'YOUR_WEBVIEW_VIEW_TYPE'`을 포함하여 컨텍스트 메뉴가 확장 프로그램의 웹뷰에만 적용되도록 해야 합니다.
"contributes": {
"menus": {
"webview/context": [
{
"command": "catCoding.yarn",
"when": "webviewId == 'catCoding'"
},
{
"command": "catCoding.insertLion",
"when": "webviewId == 'catCoding' && webviewSection == 'editor'"
}
]
},
"commands": [
{
"command": "catCoding.yarn",
"title": "Yarn 🧶",
"category": "Cat Coding"
},
{
"command": "catCoding.insertLion",
"title": "Insert 🦁",
"category": "Cat Coding"
},
...
]
}
웹뷰 내부에서는 `data-vscode-context` 데이터 속성(또는 JavaScript에서 `dataset.vscodeContext`)을 사용하여 HTML의 특정 영역에 대한 컨텍스트를 설정할 수도 있습니다. `data-vscode-context` 값은 사용자가 요소를 마우스 오른쪽 버튼으로 클릭할 때 설정할 컨텍스트를 지정하는 JSON 객체입니다. 최종 컨텍스트는 문서 루트에서 클릭된 요소까지의 경로를 통해 결정됩니다.
예를 들어 이 HTML을 고려해 보세요.
<div class="main" data-vscode-context='{"webviewSection": "main", "mouseCount": 4}'>
<h1>Cat Coding</h1>
<textarea data-vscode-context='{"webviewSection": "editor", "preventDefaultContextMenuItems": true}'></textarea>
</div>
사용자가 `textarea`를 마우스 오른쪽 버튼으로 클릭하면 다음 컨텍스트가 설정됩니다.
- `webviewSection == 'editor'` - 이 컨텍스트는 부모 요소의 `webviewSection`을 재정의합니다.
- `mouseCount == 4` - 이 컨텍스트는 부모 요소에서 상속됩니다.
- `preventDefaultContextMenuItems == true` - 이것은 VS Code가 웹뷰 컨텍스트 메뉴에 일반적으로 추가하는 복사 및 붙여넣기 항목을 숨기는 특별한 컨텍스트입니다.
사용자가 `

분할 버튼과 같이 왼쪽/기본 클릭 시 메뉴를 표시하는 것이 유용한 경우가 있습니다. `onClick` 이벤트에서 `contextmenu` 이벤트를 디스패치하여 이를 수행할 수 있습니다.
<button data-vscode-context='{"preventDefaultContextMenuItems": true }' onClick='((e) => {
e.preventDefault();
e.target.dispatchEvent(new MouseEvent("contextmenu", { bubbles: true, clientX: e.clientX, clientY: e.clientY }));
e.stopPropagation();
})(event)'>Create</button>

스크립트 및 메시지 전달
웹뷰는 iframe과 마찬가지로 스크립트를 실행할 수 있습니다. 웹뷰에서는 기본적으로 JavaScript가 비활성화되어 있지만 `enableScripts: true` 옵션을 전달하여 쉽게 다시 활성화할 수 있습니다.
스크립트를 사용하여 고양이가 작성한 소스 코드 줄 수를 추적하는 카운터를 추가해 보겠습니다. 기본 스크립트를 실행하는 것은 매우 간단하지만 이 예제는 시연 목적으로만 사용된다는 점에 유의하십시오. 실제로는 웹뷰에서 콘텐츠 보안 정책을 사용하여 인라인 스크립트를 항상 비활성화해야 합니다.
import * as path from 'path';
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{
// Enable scripts in the webview
enableScripts: true
}
);
panel.webview.html = getWebviewContent();
})
);
}
function getWebviewContent() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cat Coding</title>
</head>
<body>
<img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" width="300" />
<h1 id="lines-of-code-counter">0</h1>
<script>
const counter = document.getElementById('lines-of-code-counter');
let count = 0;
setInterval(() => {
counter.textContent = count++;
}, 100);
</script>
</body>
</html>`;
}

와우! 정말 생산적인 고양이네요.
웹뷰 스크립트는 일반 웹페이지의 스크립트와 거의 모든 것을 할 수 있습니다. 그러나 웹뷰는 자체 컨텍스트에 존재하므로 웹뷰의 스크립트는 VS Code API에 액세스할 수 없다는 점을 명심하십시오. 메시지 전달이 필요한 부분입니다!
확장에서 웹뷰로 메시지 전달
확장은 `webview.postMessage()`를 사용하여 웹뷰로 데이터를 보낼 수 있습니다. 이 메서드는 JSON 직렬화 가능한 모든 데이터를 웹뷰로 보냅니다. 메시지는 웹뷰 내부에서 표준 `message` 이벤트를 통해 수신됩니다.
이를 시연하기 위해 **Cat Coding**에 현재 코딩 중인 고양이에게 코드를 리팩토링하도록 지시하는 새 명령을 추가해 보겠습니다(이를 통해 줄 수를 줄입니다). 새 `catCoding.doRefactor` 명령은 `postMessage`를 사용하여 현재 웹뷰로 지침을 보내고, 웹뷰 자체 내에서 `window.addEventListener('message', event => { ... })`를 사용하여 메시지를 처리합니다.
export function activate(context: vscode.ExtensionContext) {
// Only allow a single Cat Coder
let currentPanel: vscode.WebviewPanel | undefined = undefined;
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
if (currentPanel) {
currentPanel.reveal(vscode.ViewColumn.One);
} else {
currentPanel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{
enableScripts: true
}
);
currentPanel.webview.html = getWebviewContent();
currentPanel.onDidDispose(
() => {
currentPanel = undefined;
},
undefined,
context.subscriptions
);
}
})
);
// Our new command
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.doRefactor', () => {
if (!currentPanel) {
return;
}
// Send a message to our webview.
// You can send any JSON serializable data.
currentPanel.webview.postMessage({ command: 'refactor' });
})
);
}
function getWebviewContent() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cat Coding</title>
</head>
<body>
<img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" width="300" />
<h1 id="lines-of-code-counter">0</h1>
<script>
const counter = document.getElementById('lines-of-code-counter');
let count = 0;
setInterval(() => {
counter.textContent = count++;
}, 100);
// Handle the message inside the webview
window.addEventListener('message', event => {
const message = event.data; // The JSON data our extension sent
switch (message.command) {
case 'refactor':
count = Math.ceil(count * 0.5);
counter.textContent = count;
break;
}
});
</script>
</body>
</html>`;
}

웹뷰에서 확장 프로그램으로 메시지 전달
웹뷰는 확장 프로그램으로 메시지를 다시 전달할 수도 있습니다. 이는 웹뷰 내 특수 VS Code API 객체에서 `postMessage` 함수를 사용하여 수행됩니다. VS Code API 객체에 액세스하려면 웹뷰 내부에서 `acquireVsCodeApi`를 호출합니다. 이 함수는 세션당 한 번만 호출할 수 있습니다. 이 메서드에서 반환된 VS Code API 인스턴스를 보유해야 하며 이를 사용할 다른 모든 함수에 전달해야 합니다.
VS Code API와 `postMessage`를 **Cat Coding** 웹뷰에서 사용하여 고양이가 코드에 버그를 도입할 때 확장 프로그램에 알릴 수 있습니다.
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{
enableScripts: true
}
);
panel.webview.html = getWebviewContent();
// Handle messages from the webview
panel.webview.onDidReceiveMessage(
message => {
switch (message.command) {
case 'alert':
vscode.window.showErrorMessage(message.text);
return;
}
},
undefined,
context.subscriptions
);
})
);
}
function getWebviewContent() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cat Coding</title>
</head>
<body>
<img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" width="300" />
<h1 id="lines-of-code-counter">0</h1>
<script>
(function() {
const vscode = acquireVsCodeApi();
const counter = document.getElementById('lines-of-code-counter');
let count = 0;
setInterval(() => {
counter.textContent = count++;
// Alert the extension when our cat introduces a bug
if (Math.random() < 0.001 * count) {
vscode.postMessage({
command: 'alert',
text: '🐛 on line ' + count
})
}
}, 100);
}())
</script>
</body>
</html>`;
}

보안상의 이유로 VS Code API 객체를 비공개로 유지하고 전역 범위로 유출되지 않도록 해야 합니다.
웹 워커 사용
웹 워커는 웹뷰 내에서 지원되지만 몇 가지 중요한 제한 사항을 알아두어야 합니다.
먼저, 워커는 `data:` 또는 `blob:` URI만 사용하여 로드할 수 있습니다. 확장 프로그램 폴더에서 워커를 직접 로드할 수 없습니다.
확장 프로그램의 JavaScript 파일에서 워커 코드를 로드해야 하는 경우 `fetch`를 사용해 보세요.
const workerSource = 'absolute/path/to/worker.js';
fetch(workerSource)
.then(result => result.blob())
.then(blob => {
const blobUrl = URL.createObjectURL(blob);
new Worker(blobUrl);
});
워커 스크립트는 `importScripts` 또는 `import(...)`를 사용하여 소스 코드를 가져오는 것을 지원하지 않습니다. 워커가 동적으로 코드를 로드하는 경우 webpack과 같은 번들러를 사용하여 워커 스크립트를 단일 파일로 패키지화해 보세요.
`webpack`을 사용하면 `LimitChunkCountPlugin`을 사용하여 컴파일된 워커 JavaScript가 단일 파일이 되도록 강제할 수 있습니다.
const path = require('path');
const webpack = require('webpack');
module.exports = {
target: 'webworker',
entry: './worker/src/index.js',
output: {
filename: 'worker.js',
path: path.resolve(__dirname, 'media')
},
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1
})
]
};
보안
모든 웹페이지와 마찬가지로 웹뷰를 만들 때 몇 가지 기본 보안 모범 사례를 따라야 합니다.
기능 제한
웹뷰는 필요한 최소한의 기능을 가져야 합니다. 예를 들어 웹뷰에 스크립트를 실행할 필요가 없다면 `enableScripts: true`를 설정하지 마십시오. 웹뷰에 사용자의 작업 공간에서 리소스를 로드할 필요가 없다면 `localResourceRoots`를 `[vscode.Uri.file(extensionContext.extensionPath)]`로 설정하거나 심지어 `[]`로 설정하여 모든 로컬 리소스에 대한 액세스를 비활성화하십시오.
콘텐츠 보안 정책
콘텐츠 보안 정책은 웹뷰에서 로드 및 실행할 수 있는 콘텐츠를 추가로 제한합니다. 예를 들어 콘텐츠 보안 정책은 허용된 스크립트 목록만 웹뷰에서 실행되도록 하거나 웹뷰에 `https`를 통해서만 이미지를 로드하도록 지시할 수 있습니다.
콘텐츠 보안 정책을 추가하려면 웹뷰의 `
` 상단에 `` 지시문을 넣으십시오.function getWebviewContent() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none';">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cat Coding</title>
</head>
<body>
...
</body>
</html>`;
}
`default-src 'none';` 정책은 모든 콘텐츠를 비활성화합니다. 그런 다음 확장이 작동하는 데 필요한 최소한의 콘텐츠를 다시 활성화할 수 있습니다. 다음은 로컬 스크립트 및 스타일시트 로드를 허용하고 `https`를 통해 이미지를 로드하는 콘텐츠 보안 정책입니다.
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; img-src ${webview.cspSource} https:; script-src ${webview.cspSource}; style-src ${webview.cspSource};"
/>
`${webview.cspSource}` 값은 웹뷰 객체 자체에서 오는 값에 대한 자리 표시자입니다. 이 값을 사용하는 방법에 대한 전체 예제는 webview sample을 참조하십시오.
이 콘텐츠 보안 정책은 또한 인라인 스크립트와 스타일을 암시적으로 비활성화합니다. 모든 인라인 스타일과 스크립트를 외부 파일로 추출하여 콘텐츠 보안 정책을 완화하지 않고 올바르게 로드할 수 있도록 하는 것이 좋습니다.
HTTPS를 통해서만 콘텐츠 로드
웹뷰가 외부 리소스 로드를 허용하는 경우 이러한 리소스가 http가 아닌 https를 통해서만 로드되도록 하는 것이 좋습니다. 위의 예제 콘텐츠 보안 정책은 이미 `https:`를 통해서만 이미지를 로드하도록 허용하여 이를 수행합니다.
모든 사용자 입력에 대한 정리
일반 웹페이지와 마찬가지로 웹뷰의 HTML을 구성할 때 모든 사용자 입력을 정리해야 합니다. 입력을 제대로 정리하지 않으면 콘텐츠 주입이 발생하여 사용자가 보안 위험에 노출될 수 있습니다.
정리해야 하는 예제 값
- 파일 내용.
- 파일 및 폴더 경로.
- 사용자 및 작업 공간 설정.
HTML 문자열을 구성하는 데 도우미 라이브러리를 사용하는 것을 고려하거나 최소한 사용자 작업 공간의 모든 콘텐츠가 제대로 정리되도록 하십시오.
보안을 위해 정리만으로는 절대 의존하지 마십시오. 콘텐츠 보안 정책을 포함하여 잠재적인 콘텐츠 주입의 영향을 최소화하는 등 다른 보안 모범 사례를 따르십시오.
영속성
표준 웹뷰 수명 주기에서 웹뷰는 `createWebviewPanel`로 생성되고 사용자가 닫거나 `.dispose()`가 호출될 때 파괴됩니다. 그러나 웹뷰의 콘텐츠는 웹뷰가 보이는 시점에 생성되고 웹뷰가 백그라운드로 이동할 때 파괴됩니다. 웹뷰 내부의 모든 상태는 웹뷰가 백그라운드 탭으로 이동하면 손실됩니다.
이를 해결하는 가장 좋은 방법은 웹뷰를 상태 비저장으로 만드는 것입니다. 메시지 전달을 사용하여 웹뷰의 상태를 저장하고 웹뷰가 다시 보일 때 상태를 복원하십시오.
getState 및 setState
웹뷰 내에서 실행되는 스크립트는 `getState` 및 `setState` 메서드를 사용하여 JSON 직렬화 가능한 상태 객체를 저장하고 복원할 수 있습니다. 이 상태는 웹뷰 패널이 숨겨질 때 웹뷰 콘텐츠 자체가 파괴된 후에도 유지됩니다. 상태는 웹뷰 패널이 파괴될 때 파괴됩니다.
// Inside a webview script
const vscode = acquireVsCodeApi();
const counter = document.getElementById('lines-of-code-counter');
// Check if we have an old state to restore from
const previousState = vscode.getState();
let count = previousState ? previousState.count : 0;
counter.textContent = count;
setInterval(() => {
counter.textContent = count++;
// Update the saved state
vscode.setState({ count });
}, 100);
`getState` 및 `setState`는 `retainContextWhenHidden`보다 성능 오버헤드가 훨씬 낮기 때문에 상태를 유지하는 선호되는 방법입니다.
직렬화
`WebviewPanelSerializer`를 구현하면 VS Code가 다시 시작될 때 웹뷰가 자동으로 복원될 수 있습니다. 직렬화는 `getState` 및 `setState`를 기반으로 구축되며 확장이 웹뷰에 대해 `WebviewPanelSerializer`를 등록하는 경우에만 활성화됩니다.
코딩 고양이들이 VS Code 다시 시작을 거쳐 유지되도록 하려면 먼저 확장 프로그램의 `package.json`에 `onWebviewPanel` 활성화 이벤트를 추가합니다.
"activationEvents": [
...,
"onWebviewPanel:catCoding"
]
이 활성화 이벤트는 VS Code가 `catCoding` 보기 유형을 가진 웹뷰를 복원해야 할 때마다 확장이 활성화되도록 합니다.
그런 다음 확장 프로그램의 `activate` 메서드에서 `registerWebviewPanelSerializer`를 호출하여 새 `WebviewPanelSerializer`를 등록합니다. `WebviewPanelSerializer`는 지속된 상태에서 웹뷰의 콘텐츠를 복원하는 역할을 합니다. 이 상태는 웹뷰 콘텐츠가 `setState`를 사용하여 설정한 JSON 블롭입니다.
export function activate(context: vscode.ExtensionContext) {
// Normal setup...
// And make sure we register a serializer for our webview type
vscode.window.registerWebviewPanelSerializer('catCoding', new CatCodingSerializer());
}
class CatCodingSerializer implements vscode.WebviewPanelSerializer {
async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) {
// `state` is the state persisted using `setState` inside the webview
console.log(`Got state: ${state}`);
// Restore the content of our webview.
//
// Make sure we hold on to the `webviewPanel` passed in here and
// also restore any event listeners we need on it.
webviewPanel.webview.html = getWebviewContent();
}
}
이제 고양이 코딩 패널이 열린 상태로 VS Code를 다시 시작하면 패널이 동일한 편집기 위치에 자동으로 복원됩니다.
retainContextWhenHidden
UI 또는 상태가 복잡하여 빠르게 저장하고 복원할 수 없는 웹뷰의 경우 대신 `retainContextWhenHidden` 옵션을 사용할 수 있습니다. 이 옵션은 웹뷰 자체가 더 이상 포그라운드에 있지 않더라도 웹뷰가 콘텐츠를 숨겨진 상태로 유지합니다.
**Cat Coding**은 복잡한 상태를 가지고 있다고 거의 말할 수 없지만, `retainContextWhenHidden`을 활성화하여 옵션이 웹뷰의 동작을 어떻게 변경하는지 확인해 보겠습니다.
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCoding.start', () => {
const panel = vscode.window.createWebviewPanel(
'catCoding',
'Cat Coding',
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true
}
);
panel.webview.html = getWebviewContent();
})
);
}
function getWebviewContent() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cat Coding</title>
</head>
<body>
<img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" width="300" />
<h1 id="lines-of-code-counter">0</h1>
<script>
const counter = document.getElementById('lines-of-code-counter');
let count = 0;
setInterval(() => {
counter.textContent = count++;
}, 100);
</script>
</body>
</html>`;
}

웹뷰가 숨겨졌다가 다시 복원될 때 카운터가 재설정되지 않는 것을 확인하세요. 추가 코드가 필요하지 않습니다! `retainContextWhenHidden`을 사용하면 웹뷰는 백그라운드 탭과 유사하게 작동합니다. 스크립트 및 기타 동적 콘텐츠는 탭이 활성 상태가 아니거나 보이지 않아도 계속 실행됩니다. `retainContextWhenHidden`이 활성화된 경우 숨겨진 웹뷰로 메시지를 보낼 수도 있습니다.
`retainContextWhenHidden`이 매력적일 수 있지만 높은 메모리 오버헤드를 가지고 있으며 다른 영속성 기술이 작동하지 않을 때만 사용해야 한다는 점을 명심하십시오.
접근성
`vscode-using-screen-reader` 클래스는 사용자가 화면 판독기를 사용하여 VS Code를 작동시키는 컨텍스트에서 웹뷰의 기본 본문에 추가됩니다. 또한 사용자가 창에서 움직임을 줄이려는 선호도를 표시한 경우 `vscode-reduce-motion` 클래스가 문서의 기본 본문 요소에 추가됩니다. 이러한 클래스를 관찰하고 렌더링을 적절히 조정하면 웹뷰 콘텐츠가 사용자 환경 설정을 더 잘 반영할 수 있습니다.
다음 단계
VS Code 확장 기능에 대해 더 자세히 알고 싶으시면 다음 주제를 시도해 보세요.