이 출시되었습니다! 11월의 새로운 기능 및 수정 사항을 읽어보세요.

원격 개발 및 GitHub Codespaces 지원

Visual Studio Code 원격 개발을 사용하면 다른 컴퓨터(가상 또는 물리적)에 있는 소스 코드 및 런타임 환경과 투명하게 상호 작용할 수 있습니다. GitHub Codespaces는 VS Code와 브라우저 기반 편집기 모두에서 액세스할 수 있는 관리형 클라우드 호스팅 환경으로 이러한 기능을 확장하는 서비스입니다.

성능을 보장하기 위해 원격 개발 및 GitHub Codespaces는 모두 특정 VS Code 확장을 투명하게 원격으로 실행합니다. 그러나 이는 확장이 작동해야 하는 방식에 미묘한 영향을 줄 수 있습니다. 많은 확장은 수정 없이 작동하지만, 변경이 필요하지 않은 경우가 많으므로 확장이 모든 환경에서 제대로 작동하도록 변경해야 할 수 있습니다.

이 문서는 확장 작성자가 원격 개발 및 Codespaces에 대해 알아야 할 사항, 확장 아키텍처, 원격 작업 영역 또는 Codespaces에서 확장 디버그 방법, 그리고 확장이 제대로 작동하지 않을 경우 대처 방법에 대한 권장 사항을 요약합니다.

아키텍처 및 확장 종류

사용자에게 원격 개발 또는 Codespaces를 최대한 투명하게 만들기 위해 VS Code는 두 가지 종류의 확장을 구분합니다.

  • UI 확장: 이 확장은 VS Code 사용자 인터페이스에 기여하며 항상 사용자의 로컬 컴퓨터에서 실행됩니다. UI 확장은 원격 작업 영역의 파일에 직접 액세스하거나 해당 작업 영역 또는 컴퓨터에 설치된 스크립트/도구를 실행할 수 없습니다. UI 확장 예시로는 테마, 스니펫, 언어 문법, 키맵 등이 있습니다.

  • 작업 영역 확장: 이 확장은 작업 영역이 있는 동일한 컴퓨터에서 실행됩니다. 로컬 작업 영역에서는 작업 영역 확장이 로컬 컴퓨터에서 실행됩니다. 원격 작업 영역에 있거나 Codespaces를 사용할 때는 작업 영역 확장이 원격 컴퓨터/환경에서 실행됩니다. 작업 영역 확장은 작업 영역의 파일에 액세스하여 풍부한 다중 파일 언어 서비스, 디버거 지원을 제공하거나 작업 영역의 여러 파일에 대해 복잡한 작업을 수행할 수 있습니다(직접 또는 스크립트/도구 호출을 통해). 작업 영역 확장은 UI 수정에 중점을 두지 않지만 탐색기, 보기 및 기타 UI 요소를 기여할 수도 있습니다.

사용자가 확장을 설치하면 VS Code는 종류에 따라 올바른 위치에 자동으로 설치합니다. 확장이 두 종류 모두로 실행될 수 있다면 VS Code는 해당 상황에 최적의 확장을 선택하려고 시도합니다. UI 확장은 VS Code의 로컬 확장 호스트에서 실행되며, 작업 영역 확장은 원격 작업 영역에 존재하는 경우 작은 VS Code 서버에 있는 원격 확장 호스트에서 실행되고, 그렇지 않으면 로컬에 존재하는 경우 VS Code의 로컬 확장 호스트에서 실행됩니다. 최신 VS Code 클라이언트 기능이 사용 가능하도록 하려면 서버는 VS Code 클라이언트 버전과 정확히 일치해야 합니다. 따라서 서버는 컨테이너, 원격 SSH 호스트, Codespaces 또는 WSL(Windows Subsystem for Linux)에서 폴더를 열 때 원격 개발 또는 GitHub Codespaces 확장에 의해 자동으로 설치(또는 업데이트)됩니다. (VS Code는 서버 시작 및 중지를 자동으로 관리하므로 사용자는 해당 존재를 인지하지 못합니다.)

Architecture diagram

VS Code API는 UI 확장 또는 작업 영역 확장 모두에서 호출될 때 올바른 머신(로컬 또는 원격)에서 자동으로 실행되도록 설계되었습니다. 그러나 확장에서 Node API를 사용하거나 셸 스크립트를 실행하는 등 VS Code에서 제공하지 않는 API를 사용하는 경우 원격으로 실행될 때 제대로 작동하지 않을 수 있습니다. 모든 확장 기능이 로컬 및 원격 작업 영역 모두에서 제대로 작동하는지 테스트하는 것이 좋습니다.

확장 디버깅

원격 환경에 확장 개발 버전을 설치하여 테스트할 수 있지만, 문제가 발생하는 경우 원격 환경에서 직접 확장을 디버그하고 싶을 것입니다. 이 섹션에서는 GitHub Codespaces, 로컬 컨테이너, SSH 호스트 또는 WSL에서 확장 편집, 시작 및 디버그 방법을 설명합니다.

일반적으로 테스트를 위한 가장 좋은 시작점은 포트 액세스를 제한하는 원격 환경(예: Codespaces, 컨테이너 또는 제한적인 방화벽이 있는 원격 SSH 호스트)을 사용하는 것입니다. 이러한 환경에서 작동하는 확장은 WSL과 같이 덜 제한적인 환경에서도 작동하는 경향이 있습니다.

GitHub Codespaces 디버깅

GitHub Codespaces 미리 보기에 대한 확장을 디버깅하는 것은 테스트 및 문제 해결을 위해 VS Code와 Codespaces 브라우저 기반 편집기 모두를 사용할 수 있기 때문에 훌륭한 시작점이 될 수 있습니다. 선호하는 경우 사용자 지정 개발 컨테이너를 사용할 수도 있습니다.

다음 단계를 따르세요.

  1. GitHub에 있는 확장 프로그램이 포함된 리포지토리로 이동하여 브라우저 기반 편집기에서 작업할 수 있도록 codespace에서 열기하세요. 선호하는 경우 VS Code에서 codespace 열기도 할 수 있습니다.

  2. GitHub Codespaces의 기본 이미지에는 대부분의 확장에 필요한 모든 전제 조건이 포함되어 있어야 하지만, 새 VS Code 터미널 창에서 필요한 다른 종속성을 설치할 수 있습니다 (예: yarn install 또는 sudo apt-get 사용). (⌃⇧` (Windows, Linux Ctrl+Shift+`))

  3. 마지막으로 F5를 누르거나 실행 및 디버그 보기를 사용하여 codespace 내부에서 확장을 실행합니다.

    참고: 나타나는 창에서 확장 소스 코드 폴더를 열 수는 없지만, 하위 폴더 또는 codespace의 다른 위치를 열 수 있습니다.

나타나는 확장 개발 호스트 창에는 디버거가 연결된 codespace에서 실행 중인 확장이 포함됩니다.

사용자 지정 개발 컨테이너 디버깅

다음 단계를 따르세요.

  1. 개발 컨테이너를 로컬에서 사용하려면 Dev Containers 확장 설치 및 구성하고 파일 > 열기... / 폴더 열기...를 사용하여 VS Code에서 로컬로 소스 코드를 엽니다. 대신 Codespaces를 사용하려면 GitHub에 확장 프로그램이 포함된 리포지토리로 이동하여 브라우저 기반 편집기에서 작업할 수 있도록 codespace에서 열기하세요. 선호하는 경우 VS Code에서 codespace 열기도 할 수 있습니다.

  2. 명령 팔레트(F1)에서 Dev Containers: Dev Container 구성 파일 추가... 또는 Codespaces: Dev Container 구성 파일 추가...를 선택하고 Node.js & TypeScript(TypeScript를 사용하지 않는 경우 Node.js)을 선택하여 필요한 컨테이너 구성 파일을 추가합니다.

  3. 선택 사항: 이 명령을 실행한 후 .devcontainer 폴더의 내용을 수정하여 추가 빌드 또는 런타임 요구 사항을 포함할 수 있습니다. 자세한 내용은 Dev Container 만들기의 심층 문서를 참조하세요.

  4. 명령 팔레트(F1)에서 Dev Containers: 컨테이너에서 다시 열기 또는 Codespaces: Dev Container 구성 파일 추가...를 실행하고 잠시 후 VS Code가 컨테이너를 설정하고 연결합니다. 이제 로컬의 경우와 같이 컨테이너 내부에서 소스 코드를 개발할 수 있습니다.

  5. 새 VS Code 터미널 창에서 yarn install 또는 npm install을 실행하여 Linux 버전 Node.js 네이티브 종속성이 설치되었는지 확인합니다. (⌃⇧` (Windows, Linux Ctrl+Shift+`)) 다른 OS 또는 런타임 종속성을 설치할 수도 있지만, 컨테이너를 다시 빌드할 때 사용할 수 있도록 .devcontainer/Dockerfile에 추가하는 것이 좋습니다.

  6. 마지막으로 F5를 누르거나 실행 및 디버그 보기를 사용하여 이 동일한 컨테이너 내부에서 확장을 실행하고 디버거를 연결합니다.

    참고: 나타나는 창에서 확장 소스 코드 폴더를 열 수는 없지만, 컨테이너의 하위 폴더 또는 다른 위치를 열 수 있습니다.

나타나는 확장 개발 호스트 창에는 2단계에서 정의한 컨테이너에서 실행 중인 확장이 디버거가 연결된 상태로 포함됩니다.

SSH를 사용한 디버깅

단계 따르기

  1. Remote - SSH 확장 설치 및 구성 후, VS Code의 명령 팔레트(F1)에서 Remote-SSH: 호스트에 연결...을 선택하여 호스트에 연결합니다.

  2. 연결되면 파일 > 열기... / 폴더 열기...를 사용하여 확장 소스 코드가 있는 원격 폴더를 선택하거나 명령 팔레트(F1)에서 Git: 복제를 선택하여 복제하고 원격 호스트에서 엽니다.

  3. 누락되었을 수 있는 필요한 종속성을 새 VS Code 터미널 창에서 설치합니다 (예: yarn install 또는 apt-get 사용). (⌃⇧` (Windows, Linux Ctrl+Shift+`))

  4. 마지막으로 F5를 누르거나 실행 및 디버그 보기를 사용하여 원격 호스트에서 확장을 실행하고 디버거를 연결합니다.

    참고: 나타나는 창에서 확장 소스 코드 폴더를 열 수는 없지만, SSH 호스트의 하위 폴더 또는 다른 위치를 열 수 있습니다.

나타나는 확장 개발 호스트 창에는 SSH 호스트에서 실행 중인 확장이 디버거가 연결된 상태로 포함됩니다.

WSL을 사용한 디버깅

다음 단계를 따르세요.

  1. WSL 확장 설치 및 구성 후, VS Code의 명령 팔레트(F1)에서 WSL: 새 창을 선택합니다.

  2. 나타나는 새 창에서 파일 > 열기... / 폴더 열기...를 사용하여 확장 소스 코드가 있는 원격 폴더를 선택하거나 명령 팔레트(F1)에서 Git: 복제를 선택하여 복제하고 WSL에서 엽니다.

    팁: Windows에 복제한 소스 코드를 액세스하려면 /mnt/c 폴더를 선택할 수 있습니다.

  3. 누락되었을 수 있는 필요한 종속성을 새 VS Code 터미널 창에서 설치합니다 (예: apt-get 사용). (⌃⇧` (Windows, Linux Ctrl+Shift+`)) 최소한 yarn install 또는 npm install을 실행하여 Linux 버전의 네이티브 Node.js 종속성을 사용할 수 있도록 해야 합니다.

  4. 마지막으로 F5를 누르거나 실행 및 디버그 보기를 사용하여 로컬에서와 마찬가지로 확장을 실행하고 디버거를 연결합니다.

    참고: 나타나는 창에서 확장 소스 코드 폴더를 열 수는 없지만, WSL의 하위 폴더 또는 다른 위치를 열 수 있습니다.

나타나는 확장 개발 호스트 창에는 WSL에서 실행 중인 확장이 디버거가 연결된 상태로 포함됩니다.

확장 개발 버전 설치

VS Code가 SSH 호스트, 컨테이너 또는 WSL 내부, 또는 GitHub Codespaces를 통해 자동으로 확장을 설치하는 경우, 로컬 머신에 이미 설치된 버전이 아닌 Marketplace 버전이 사용됩니다.

대부분의 경우 이는 당연하지만, 디버깅 환경을 설정하지 않고도 테스트를 위해 게시되지 않은 확장 버전을 사용하거나 공유하고 싶을 수 있습니다. 게시되지 않은 확장 버전을 설치하려면 확장을 VSIX로 패키징하고 실행 중인 원격 환경에 이미 연결된 VS Code 창에 수동으로 설치할 수 있습니다.

다음 단계를 따르세요.

  1. 이미 게시된 확장인 경우, 최신 Marketplace 버전으로 자동 업데이트되는 것을 방지하기 위해 settings.json"extensions.autoUpdate": false를 추가하는 것이 좋습니다.
  2. 다음으로 vsce package를 사용하여 확장을 VSIX로 패키징합니다.
  3. codespace, Dev Containers, SSH 호스트 또는 WSL 환경에 연결합니다.
  4. 확장 보기의 추가 작업 (...) 메뉴에서 사용할 수 있는 VSIX에서 설치... 명령을 사용하여 이 특정 창(로컬 창이 아닌)에 확장을 설치합니다.
  5. 프롬프트가 나타나면 새로고침합니다.

팁: 설치 후 개발자: 실행 중인 확장 표시 명령을 사용하여 VS Code가 확장을 로컬 또는 원격으로 실행하고 있는지 확인할 수 있습니다.

원격 확장과의 종속성 처리

확장은 API에 대한 다른 확장에 종속될 수 있습니다. 예를 들어

  • 확장은 activate 함수에서 API를 내보낼 수 있습니다.
  • 이 API는 동일한 확장 호스트에서 실행되는 모든 확장에서 사용할 수 있습니다.
  • 소비자 확장은 package.json에서 extensionDependencies 속성을 사용하여 제공하는 확장에 종속됨을 선언합니다.

확장 종속성은 모든 확장이 로컬에서 실행되고 동일한 확장 호스트를 공유하는 경우 잘 작동합니다.

원격 시나리오를 처리할 때 원격으로 실행되는 확장이 로컬로 실행되는 확장에 대한 확장 종속성을 가질 수 있습니다. 예를 들어 로컬 확장은 원격 확장의 작동에 중요한 명령을 노출합니다. 이 경우 원격 확장이 로컬 확장을 extensionDependency로 선언하는 것이 좋습니다. 그러나 문제는 확장이 두 개의 다른 확장 호스트에서 실행되므로 제공자의 API가 소비자에게 제공되지 않는다는 것입니다. 따라서 제공 확장 프로그램은 확장 프로그램의 package.json에서 "api": "none"을 사용하여 API 내보내기 기능을 완전히 포기해야 합니다. 확장은 여전히 VS Code 명령(비동기)을 사용하여 통신할 수 있습니다.

이는 제공 확장 프로그램에 대해 불필요하게 엄격한 제약처럼 보일 수 있지만, "api": "none"을 사용하는 확장은 activate 메서드에서 API를 반환하는 기능만 포기합니다. 다른 확장 호스트에서 실행되는 소비자 확장은 여전히 해당 확장에 종속될 수 있으며 활성화됩니다.

일반적인 문제

VS Code API는 확장이 어디에 있든 올바른 위치에서 자동으로 실행되도록 설계되었습니다. 이를 염두에 두고 예상치 못한 동작을 피하는 데 도움이 되는 몇 가지 API가 있습니다.

잘못된 실행 위치

확장이 예상대로 작동하지 않으면 잘못된 위치에서 실행되고 있을 수 있습니다. 가장 일반적으로 이는 로컬에서만 실행될 것으로 예상했지만 원격으로 실행되는 확장으로 나타납니다. 명령 팔레트(F1)의 개발자: 실행 중인 확장 표시 명령을 사용하여 확장이 어디에서 실행되는지 확인할 수 있습니다.

개발자: 실행 중인 확장 표시 명령이 UI 확장이 잘못 취급되어 작업 영역 확장으로 간주되거나 그 반대인 경우, 확장의 package.json에서 확장 종류 섹션에 설명된 대로 extensionKind 속성을 설정해 보세요.

remote.extensionKind 설정을 사용하여 확장 종류 변경의 효과를 빠르게 **테스트**할 수 있습니다. 이 설정은 확장 ID에서 확장 종류로의 맵입니다. 예를 들어, Azure Databases 확장을 UI 확장(기본 작업 영역 대신)으로 강제하고 Remote - SSH: Configuration Files Editing 확장을 작업 영역 확장(기본 UI 대신)으로 강제하려면 다음과 같이 설정합니다.

{
  "remote.extensionKind": {
    "ms-azuretools.vscode-cosmosdb": ["ui"],
    "ms-vscode-remote.remote-ssh-edit": ["workspace"]
  }
}

remote.extensionKind를 사용하면 package.json을 수정하고 다시 빌드하지 않고도 게시된 확장 버전을 빠르게 테스트할 수 있습니다.

확장 데이터 또는 상태 지속

어떤 경우에는 확장에 settings.json 또는 별도의 작업 영역 구성 파일(예: .eslintrc)에 속하지 않는 상태 정보를 유지해야 할 수 있습니다. 이 문제를 해결하기 위해 VS Code는 확장이 활성화될 때 확장에게 전달되는 vscode.ExtensionContext 개체에 유용한 저장소 속성 집합을 제공합니다. 확장이 이미 이러한 속성을 활용하는 경우, 어디에서 실행되든 계속 작동해야 합니다.

그러나 확장이 기존 VS Code 경로 지정 규칙(예: ~/.vscode) 또는 특정 OS 폴더(예: Linux의 ~/.config/Code)의 존재를 데이터 유지에 의존하는 경우 문제가 발생할 수 있습니다. 다행히 확장을 업데이트하고 이러한 문제를 피하는 것은 간단해야 합니다.

단순 키-값 쌍을 유지하는 경우, vscode.ExtensionContext.workspaceState 또는 vscode.ExtensionContext.globalState를 사용하여 각각 작업 영역별 또는 전역 상태 정보를 저장할 수 있습니다. 데이터가 키-값 쌍보다 복잡한 경우, globalStorageUristorageUri 속성은 파일에 전역 작업 영역별 정보를 읽고 쓰는 데 사용할 수 있는 "안전한" URI를 제공합니다.

API를 사용하려면

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
    context.subscriptions.push(
        vscode.commands.registerCommand('myAmazingExtension.persistWorkspaceData', async () => {
            if (!context.storageUri) {
                return;
            }

            // Create the extension's workspace storage folder if it doesn't already exist
            try {
                // When folder doesn't exist, and error gets thrown
                await vscode.workspace.fs.stat(context.storageUri);
            } catch {
                // Create the extension's workspace storage folder
                await vscode.workspace.fs.createDirectory(context.storageUri)
            }

            const workspaceData = vscode.Uri.joinPath(context.storageUri, 'workspace-data.json');
            const writeData = new TextEncoder().encode(JSON.stringify({ now: Date.now() }));
            vscode.workspace.fs.writeFile(workspaceData, writeData);
        }
    ));

    context.subscriptions.push(
        vscode.commands.registerCommand('myAmazingExtension.persistGlobalData', async () => {

        if (!context.globalStorageUri) {
            return;
        }

        // Create the extension's global (cross-workspace) folder if it doesn't already exist
        try {
            // When folder doesn't exist, and error gets thrown
            await vscode.workspace.fs.stat(context.globalStorageUri);
        } catch {
            await vscode.workspace.fs.createDirectory(context.globalStorageUri)
        }

        const workspaceData = vscode.Uri.joinPath(context.globalStorageUri, 'global-data.json');
        const writeData = new TextEncoder().encode(JSON.stringify({ now: Date.now() }));
        vscode.workspace.fs.writeFile(workspaceData, writeData);
    ));
}

머신 간 사용자 전역 상태 동기화

확장에 여러 머신 간에 사용자 상태를 보존해야 하는 경우, vscode.ExtensionContext.globalState.setKeysForSync를 사용하여 상태를 설정 동기화에 제공하십시오. 이는 여러 머신에서 사용자에게 동일한 환영 또는 업데이트 페이지를 표시하는 것을 방지하는 데 도움이 될 수 있습니다.

setKeysforSync를 사용하는 예시는 확장 기능 주제에 있습니다.

암호 유지

확장에 암호 또는 기타 비밀 정보를 유지해야 하는 경우, Visual Studio Code의 SecretStorage API를 사용하는 것이 좋습니다. 이 API는 암호화로 백업된 파일 시스템에 텍스트를 안전하게 저장하는 방법을 제공합니다. 예를 들어 데스크톱에서는 Electron의 safeStorage API를 사용하여 비밀 정보를 암호화한 후 파일 시스템에 저장합니다. 이 API는 항상 클라이언트 측에 비밀 정보를 저장하지만, 확장이 어디에서 실행되든 이 API를 사용하고 동일한 비밀 정보를 검색할 수 있습니다.

참고: 이 API는 암호 및 비밀 정보 유지에 권장되는 방법입니다. vscode.ExtensionContext.workspaceState 또는 vscode.ExtensionContext.globalState를 사용하여 비밀 정보를 저장해서는 안 됩니다. 이러한 API는 일반 텍스트로 데이터를 저장하기 때문입니다.

다음은 예시입니다.

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
  // ...
  const myApiKey = context.secrets.get('apiKey');
  // ...
  context.secrets.delete('apiKey');
  // ...
  context.secrets.store('apiKey', myApiKey);
}

클립보드 사용

과거에 확장 작성자는 clipboardy와 같은 Node.js 모듈을 사용하여 클립보드와 상호 작용했습니다. 불행히도 작업 영역 확장에서 이러한 모듈을 사용하면 로컬 클립보드 대신 원격 클립보드를 사용하게 됩니다. VS Code 클립보드 API는 이 문제를 해결합니다. 확장이 어떤 종류이든 상관없이 항상 로컬에서 실행됩니다.

확장에서 VS Code 클립보드 API를 사용하려면

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('myAmazingExtension.clipboardIt', async () => {
      // Read from clipboard
      const text = await vscode.env.clipboard.readText();

      // Write to clipboard
      await vscode.env.clipboard.writeText(
        `It looks like you're copying "${text}". Would you like help?`
      );
    })
  );
}

로컬 브라우저 또는 애플리케이션에서 열기

특정 URI에 대해 브라우저 또는 기타 애플리케이션을 시작하기 위해 opn과 같은 프로세스를 생성하거나 모듈을 사용하는 것은 로컬 시나리오에서 잘 작동하지만, 작업 영역 확장은 원격으로 실행되므로 애플리케이션이 잘못된 쪽에서 시작될 수 있습니다. VS Code 원격 개발은 기존 확장이 작동하도록 opn 노드 모듈을 **부분적으로** 셰밍합니다. URI와 함께 모듈을 호출하면 VS Code가 클라이언트 측에서 URI의 기본 애플리케이션을 표시합니다. 그러나 이는 옵션이 지원되지 않고 child_process 객체가 반환되지 않기 때문에 완전한 구현은 아닙니다.

타사 노드 모듈에 의존하는 대신, 확장이 vscode.env.openExternal 메서드를 활용하여 지정된 URI에 대한 로컬 운영 체제의 기본 등록 애플리케이션을 시작하는 것이 좋습니다. 더 나아가, vscode.env.openExternal은 **자동 로컬호스트 포트 전달을 수행합니다!** 원격 머신 또는 codespace의 로컬 웹 서버를 가리키는 데 사용할 수 있으며 해당 포트가 외부에서 차단되어 있어도 콘텐츠를 제공할 수 있습니다.

참고: 현재 Codespaces 브라우저 기반 편집기의 전달 메커니즘은 **http 및 https 요청**만 지원합니다. 그러나 VS Code에서 codespace에 연결할 때 모든 TCP 연결과 상호 작용할 수 있습니다.

vscode.env.openExternal API를 사용하려면

import * as vscode from 'vscode';

export async function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('myAmazingExtension.openExternal', () => {
      // Example 1 - Open the VS Code homepage in the default browser.
      vscode.env.openExternal(vscode.Uri.parse('https://vscode.gisul.kr'));

      // Example 2 - Open an auto-forwarded localhost HTTP server.
      vscode.env.openExternal(vscode.Uri.parse('https://:3000'));

      // Example 3 - Open the default email application.
      vscode.env.openExternal(vscode.Uri.parse('mailto:<fill in your email here>'));
    })
  );
}

로컬호스트 전달

vscode.env.openExternal의 로컬호스트 전달 메커니즘이 유용하지만, 새 브라우저 창이나 애플리케이션을 실제로 시작하지 않고 무언가를 전달하고 싶을 때도 있습니다. 이것이 vscode.env.asExternalUri API가 등장하는 이유입니다.

참고: 현재 Codespaces 브라우저 기반 편집기의 전달 메커니즘은 **http 및 https 요청**만 지원합니다. 그러나 VS Code에서 codespace에 연결할 때 모든 TCP 연결과 상호 작용할 수 있습니다.

vscode.env.asExternalUri API를 사용하려면

import * as vscode from 'vscode';
import { getExpressServerPort } from './server';

export async function activate(context: vscode.ExtensionContext) {

    const dynamicServerPort = await getWebServerPort();

    context.subscriptions.push(vscode.commands.registerCommand('myAmazingExtension.forwardLocalhost', async () =>

        // Make the port available locally and get the full URI
        const fullUri = await vscode.env.asExternalUri(
            vscode.Uri.parse(`https://:${dynamicServerPort}`));

        // ... do something with the fullUri ...

    }));
}

API에서 반환되는 URI가 **로컬호스트를 전혀 참조하지 않을 수 있으므로** 전체를 그대로 사용해야 한다는 점에 유의하는 것이 중요합니다. 이는 로컬호스트를 사용할 수 없는 Codespaces 브라우저 기반 편집기에 특히 중요합니다.

콜백 및 URI 핸들러

vscode.window.registerUriHandler API를 사용하면 확장에서 사용자 지정 URI를 등록할 수 있습니다. 이 URI가 브라우저에서 열리면 확장에서 콜백 함수를 트리거합니다. URI 핸들러 등록의 일반적인 사용 사례는 OAuth 2.0 인증 제공자(예: Azure AD)를 사용하는 서비스 로그인 구현입니다. 그러나 외부 애플리케이션 또는 브라우저가 확장에 정보를 보내려고 하는 모든 시나리오에서 사용할 수 있습니다.

VS Code의 원격 개발 및 Codespaces 확장은 확장이 실제로 실행되는 위치(로컬 또는 원격)에 관계없이 URI를 확장에 투명하게 전달합니다. 그러나 vscode:// URI는 vscode:// URI를 브라우저와 같은 곳에서 열면 브라우저 기반 편집기가 아닌 로컬 VS Code 클라이언트로 전달하려고 시도하므로 Codespaces 브라우저 기반 편집기에서는 작동하지 않습니다. 다행히 vscode.env.asExternalUri API를 사용하여 쉽게 해결할 수 있습니다.

vscode.window.registerUriHandlervscode.env.asExternalUri를 조합하여 예제 OAuth 인증 콜백을 설정해 보겠습니다.

import * as vscode from 'vscode';

// This is ${publisher}.${name} from package.json
const extensionId = 'my.amazing-extension';

export async function activate(context: vscode.ExtensionContext) {
  // Register a URI handler for the authentication callback
  vscode.window.registerUriHandler({
    handleUri(uri: vscode.Uri): vscode.ProviderResult<void> {
      // Add your code for what to do when the authentication completes here.
      if (uri.path === '/auth-complete') {
        vscode.window.showInformationMessage('Sign in successful!');
      }
    }
  });

  // Register a sign in command
  context.subscriptions.push(
    vscode.commands.registerCommand(`${extensionId}.signin`, async () => {
      // Get an externally addressable callback URI for the handler that the authentication provider can use
      const callbackUri = await vscode.env.asExternalUri(
        vscode.Uri.parse(`${vscode.env.uriScheme}://${extensionId}/auth-complete`)
      );

      // Add your code to integrate with an authentication provider here - we'll fake it.
      vscode.env.clipboard.writeText(callbackUri.toString());
      await vscode.window.showInformationMessage(
        'Open the URI copied to the clipboard in a browser window to authorize.'
      );
    })
  );
}

VS Code에서 이 샘플을 실행하면 인증 제공업체의 콜백으로 사용할 수 있는 vscode:// 또는 vscode-insiders:// URI가 설정됩니다. Codespaces 브라우저 기반 편집기에서 실행할 때 코드 변경이나 특별한 조건 없이 https://*.github.dev URI가 설정됩니다.

OAuth는 이 문서의 범위를 벗어나지만, 이 샘플을 실제 인증 제공업체에 적용하는 경우 제공업체 앞에 프록시 서비스를 구축해야 할 수 있습니다. 이는 모든 제공업체가 vscode:// 콜백 URI를 허용하지 않거나 HTTPS를 통한 콜백에 와일드카드 호스트 이름을 허용하지 않는 제공업체가 있기 때문입니다. 또한 가능한 한 PKCE를 사용한 OAuth 2.0 인증 코드 흐름을 사용하는 것이 좋습니다(예: Azure AD는 PKCE 지원). 콜백의 보안을 개선하기 위해.

원격 또는 Codespaces 브라우저 편집기에서 실행할 때 동작 변경

어떤 경우에는 작업 영역 확장이 원격으로 실행될 때 동작을 변경해야 할 수 있습니다. 다른 경우에는 Codespaces 브라우저 기반 편집기에서 실행될 때 동작을 변경하고 싶을 수 있습니다. VS Code는 이러한 상황을 감지하기 위해 vscode.env.uiKind, extension.extensionKindvscode.env.remoteName의 세 가지 API를 제공합니다.

다음으로 세 가지 API를 다음과 같이 사용할 수 있습니다.

import * as vscode from 'vscode';

export async function activate(context: vscode.ExtensionContext) {
  // extensionKind returns ExtensionKind.UI when running locally, so use this to detect remote
  const extension = vscode.extensions.getExtension('your.extensionId');
  if (extension.extensionKind === vscode.ExtensionKind.Workspace) {
    vscode.window.showInformationMessage('I am running remotely!');
  }

  // Codespaces browser-based editor will return UIKind.Web for uiKind
  if (vscode.env.uiKind === vscode.UIKind.Web) {
    vscode.window.showInformationMessage('I am running in the Codespaces browser editor!');
  }

  // VS Code will return undefined for remoteName if working with a local workspace
  if (typeof vscode.env.remoteName === 'undefined') {
    vscode.window.showInformationMessage('Not currently connected to a remote workspace.');
  }
}

명령을 사용하여 확장 간 통신

일부 확장은 다른 확장 프로그램(vscode.extension.getExtension(extensionName).exports를 통해)에서 사용하기 위해 활성화의 일부로 API를 반환합니다. 관련된 모든 확장이 동일한 측(UI 확장 또는 작업 영역 확장 모두)에 있는 경우 작동하지만, UI 확장과 작업 영역 확장 사이에서는 작동하지 않습니다.

다행히 VS Code는 실행되는 확장 프로그램의 위치에 관계없이 실행되는 모든 명령을 올바른 확장 프로그램으로 자동 라우팅합니다. 영향에 대해 걱정할 필요 없이 모든 명령(다른 확장 프로그램에서 제공하는 명령 포함)을 자유롭게 호출할 수 있습니다.

서로 상호 작용해야 하는 확장 프로그램 집합이 있는 경우, 비공개 명령을 사용하여 기능을 노출하면 예상치 못한 영향을 피하는 데 도움이 될 수 있습니다. 그러나 매개변수로 전달하는 개체는 전송 전에 "문자열화"(JSON.stringify)되므로 순환 참조를 가질 수 없으며 다른 쪽에서는 "일반 JavaScript 개체"가 됩니다.

예를 들어,

import * as vscode from 'vscode';

export async function activate(context: vscode.ExtensionContext) {
  // Register the private echo command
  const echoCommand = vscode.commands.registerCommand(
    '_private.command.called.echo',
    (value: string) => {
      return value;
    }
  );
  context.subscriptions.push(echoCommand);
}

명령과 함께 작업하는 방법에 대한 자세한 내용은 명령 API 가이드를 참조하세요.

Webview API 사용

클립보드 API와 마찬가지로 Webview API는 작업 영역 확장 프로그램에서 사용되는 경우에도 항상 사용자의 로컬 컴퓨터 또는 브라우저에서 실행됩니다. 이는 많은 웹뷰 기반 확장 프로그램이 원격 작업 영역 또는 Codespaces에서도 작동해야 함을 의미합니다. 그러나 원격으로 실행될 때 웹뷰 확장이 올바르게 작동하도록 하기 위해 주의해야 할 몇 가지 고려 사항이 있습니다.

항상 asWebviewUri 사용

확장 리소스를 관리하려면 asWebviewUri API를 사용해야 합니다. vscode-resource:// URI를 하드 코딩하는 대신 이 API를 사용하면 Codespaces 브라우저 기반 편집기가 확장과 함께 작동하도록 보장하는 데 필요합니다. 자세한 내용은 Webview API 가이드를 참조하세요. 다음은 간단한 예시입니다.

콘텐츠에서 다음과 같이 API를 사용할 수 있습니다.

// Create the webview
const panel = vscode.window.createWebviewPanel(
  'catWebview',
  'Cat Webview',
  vscode.ViewColumn.One
);

// Get the content Uri
const catGifUri = panel.webview.asWebviewUri(
  vscode.Uri.joinPath(context.extensionUri, 'media', 'cat.gif')
);

// Reference it in your content
panel.webview.html = `<!DOCTYPE html>
<html>
<body>
    <img src="${catGifUri}" width="300" />
</body>
</html>`;

동적 웹뷰 콘텐츠에 메시지 전달 API 사용

VS Code 웹뷰에는 로컬 웹 서버를 사용하지 않고도 웹뷰 콘텐츠를 동적으로 업데이트할 수 있는 메시지 전달 API가 포함되어 있습니다. 확장 프로그램이 웹뷰 콘텐츠를 업데이트하기 위해 로컬 웹 서비스를 실행하는 경우에도 HTML 콘텐츠에서 직접이 아닌 확장 프로그램 자체에서 이 작업을 수행할 수 있습니다.

이는 원격 개발 및 GitHub Codespaces에서 웹뷰 코드가 VS Code 및 Codespaces 브라우저 기반 편집기 모두에서 작동하도록 보장하는 데 중요한 패턴입니다.

로컬호스트 웹 서버 대신 메시지 전달을 사용하는 이유

대체 패턴은 iframe에서 웹 콘텐츠를 제공하거나 웹뷰 콘텐츠가 로컬호스트 서버와 직접 상호 작용하도록 하는 것입니다. 그러나 기본적으로 웹뷰 내부의 localhost는 개발자의 로컬 컴퓨터를 확인합니다. 즉, 원격으로 실행되는 작업 영역 확장 프로그램의 경우 생성된 웹뷰는 확장 프로그램이 시작한 로컬 서버에 액세스할 수 없습니다. 머신의 IP를 사용하더라도 연결하는 포트는 일반적으로 클라우드 VM 또는 컨테이너에서 기본적으로 차단됩니다. VS Code에서 작동하더라도 Codespaces 브라우저 기반 편집기에서는 작동하지 않습니다.

원격 - SSH 확장을 사용할 때 로컬호스트를 사용하는 문제에 대한 설명입니다. 그러나 이 문제는 Dev Containers 및 GitHub Codespaces에도 존재합니다.

Webview problem

가능하다면 **이 작업을 피해야 합니다**. 확장이 훨씬 복잡해지기 때문입니다. 메시지 전달 API는 이러한 종류의 번거로움 없이도 동일한 사용자 경험을 제공할 수 있습니다. 확장 프로그램 자체는 원격 측의 VS Code 서버에서 실행되므로 웹뷰에서 전달된 메시지의 결과로 시작하는 웹 서버와 투명하게 상호 작용할 수 있습니다.

웹뷰에서 로컬호스트 사용을 위한 해결 방법

어떤 이유로든 메시지 전달 API를 사용할 수 없는 경우, VS Code의 원격 개발 및 GitHub Codespaces 확장에서 작동하는 두 가지 옵션이 있습니다.

각 옵션을 사용하면 웹뷰 콘텐츠가 VS Code가 VS Code 서버와 통신하는 데 사용하는 것과 동일한 채널을 통해 라우팅됩니다. 예를 들어 이전 섹션의 원격 - SSH 확장에 대한 일러스트레이션을 업데이트하면 다음과 같습니다.

Webview Solution

옵션 1 - asExternalUri 사용

VS Code 1.40은 vscode.env.asExternalUri API를 도입하여 확장이 프로그래밍 방식으로 원격으로 로컬 httphttps 요청을 전달할 수 있도록 했습니다. VS Code에서 확장이 실행될 때 웹뷰에서 localhost 웹 서버로 요청을 전달하는 데 이 동일한 API를 사용할 수 있습니다.

API를 사용하여 iframe의 전체 URI를 가져와 HTML에 추가하세요. 웹뷰에서 스크립트를 사용하도록 설정하고 HTML 콘텐츠에 CSP를 추가해야 합니다.

// Use asExternalUri to get the URI for the web server
const dynamicWebServerPort = await getWebServerPort();
const fullWebServerUri = await vscode.env.asExternalUri(
  vscode.Uri.parse(`https://:${dynamicWebServerPort}`)
);

// Create the webview
const panel = vscode.window.createWebviewPanel(
  'asExternalUriWebview',
  'asExternalUri Example',
  vscode.ViewColumn.One,
  {
    enableScripts: true
  }
);

const cspSource = panel.webview.cspSource;
panel.webview.html = `<!DOCTYPE html>
        <head>
            <meta
                http-equiv="Content-Security-Policy"
                content="default-src 'none'; frame-src ${fullWebServerUri} ${cspSource} https:; img-src ${cspSource} https:; script-src ${cspSource}; style-src ${cspSource};"
            />
        </head>
        <body>
        <!-- All content from the web server must be in an iframe -->
        <iframe src="${fullWebServerUri}">
    </body>
    </html>`;

위 예제의 iframe에 제공되는 HTML 콘텐츠는 localhost를 하드 코딩하는 대신 **상대 경로를 사용해야 한다**는 점에 유의하세요.

옵션 2 - 포트 매핑 사용

Codespaces 브라우저 기반 편집기를 지원할 의도가 없다면, 웹뷰 API에서 사용할 수 있는 portMapping 옵션을 사용할 수 있습니다. (이 접근 방식은 VS Code 클라이언트의 Codespaces에서도 작동하지만 브라우저에서는 작동하지 않습니다.)

포트 매핑을 사용하려면 웹뷰를 만들 때 portMapping 개체를 전달합니다.

const LOCAL_STATIC_PORT = 3000;
const dynamicServerPort = await getWebServerPort();

// Create webview and pass portMapping in
const panel = vscode.window.createWebviewPanel(
  'remoteMappingExample',
  'Remote Mapping Example',
  vscode.ViewColumn.One,
  {
    portMapping: [
      // This maps localhost:3000 in the webview to the web server port on the remote host.
      { webviewPort: LOCAL_STATIC_PORT, extensionHostPort: dynamicServerPort }
    ]
  }
);

// Reference the port in any full URIs you reference in your HTML.
panel.webview.html = `<!DOCTYPE html>
    <body>
        <!-- This will resolve to the dynamic server port on the remote machine -->
        <img src="https://:${LOCAL_STATIC_PORT}/canvas.png">
    </body>
    </html>`;

이 예에서는 원격 및 로컬 경우 모두에서 https://:3000으로 보낸 모든 요청이 Express.js 웹 서버가 실행 중인 동적 포트로 자동으로 매핑됩니다.

기본 Node.js 모듈 사용

VS Code 확장 프로그램에 번들로 포함되거나 동적으로 획득된 네이티브 모듈은 Electron의 electron-rebuild를 사용하여 다시 컴파일해야 합니다. 그러나 VS Code 서버는 표준(비 Electron) Node.js 버전을 실행하므로 바이너리가 원격으로 사용될 때 실패할 수 있습니다.

이 문제를 해결하려면

  1. VS Code가 제공하는 Node.js의 "모듈" 버전에 대해 두 세트의 바이너리(Electron 및 표준 Node.js)를 모두 포함하거나(또는 동적으로 획득)합니다.
  2. 확장이 원격으로 실행되는지 로컬로 실행되는지에 따라 올바른 바이너리를 설정하기 위해 vscode.extensions.getExtension('your.extensionId').extensionKind === vscode.ExtensionKind.Workspace인지 확인합니다.
  3. 비 x86_64 호스트 또는 Alpine Linux 컨테이너에 대한 지원을 동시에 추가하려면 유사한 논리를 따르는 것을 고려할 수 있습니다.

VS Code가 사용하는 "모듈" 버전은 **도움말 > 개발자 도구**로 이동하여 콘솔에서 process.versions.modules를 입력하여 찾을 수 있습니다. 그러나 다양한 Node.js 환경에서 네이티브 모듈이 원활하게 작동하도록 하려면 지원하려는 모든 가능한 Node.js "모듈" 버전 및 플랫폼(Electron Node.js, 공식 Node.js Windows/Darwin/Linux, 모든 버전)에 대해 네이티브 모듈을 컴파일하는 것이 좋습니다. node-tree-sitter 모듈은 이를 잘 수행하는 모듈의 좋은 예입니다.

non-x86_64 호스트 또는 Alpine Linux 컨테이너 지원

확장이 순수하게 JavaScript/TypeScript로 작성된 경우, 다른 프로세서 아키텍처 또는 musl 기반 Alpine Linux에 대한 지원을 확장 프로그램에 추가하기 위해 아무것도 할 필요가 없을 수 있습니다.

하지만, 확장 프로그램이 Debian 9+, Ubuntu 16.04+ 또는 RHEL / CentOS 7+ 원격 SSH 호스트, 컨테이너 또는 WSL에서 작동하지만 지원되는 비-x86_64 호스트 (예: ARMv7l) 또는 Alpine Linux 컨테이너에서 실패하는 경우, 확장 프로그램에 x86_64 glibc 특정 네이티브 코드 또는 런타임이 포함되어 있어 해당 아키텍처/운영 체제에서 실패할 수 있습니다.

예를 들어, 확장 프로그램에 x86_64로 컴파일된 네이티브 모듈 또는 런타임 버전만 포함될 수 있습니다. Alpine Linux의 경우, Alpine Linux (musl)와 다른 배포판 (glibc)의 libc 구현 방식 간의 근본적인 차이 때문에 포함된 네이티브 코드 또는 런타임이 작동하지 않을 수 있습니다.

이 문제 해결 방법

  1. 컴파일된 코드를 동적으로 가져오는 경우, process.arch를 사용하여 비-x86_64 대상을 감지하고 올바른 아키텍처용으로 컴파일된 버전을 다운로드하여 지원을 추가할 수 있습니다. 대신 지원되는 모든 아키텍처용 바이너리를 확장 프로그램 내에 포함하는 경우, 이 로직을 사용하여 올바른 바이너리를 사용할 수 있습니다.

  2. Alpine Linux의 경우, await fs.exists('/etc/alpine-release')를 사용하여 운영 체제를 감지하고 다시 한번 musl 기반 운영 체제용 올바른 바이너리를 다운로드하거나 사용할 수 있습니다.

  3. 이러한 플랫폼을 지원하고 싶지 않다면, 동일한 로직을 사용하여 대신 좋은 오류 메시지를 제공할 수 있습니다.

일부 타사 npm 모듈에는 이 문제를 야기할 수 있는 네이티브 코드가 포함되어 있다는 점에 유의하는 것이 중요합니다. 따라서 경우에 따라 추가 컴파일 대상을 추가하기 위해 npm 모듈 작성자와 협력해야 할 수도 있습니다.

Electron 모듈 사용 방지

확장 프로그램 API에서 노출되지 않은 내장 Electron 또는 VS Code 모듈에 의존하는 것이 편리할 수 있지만, VS Code Server는 Node.js의 표준 (비-Electron) 버전을 실행한다는 점에 유의하는 것이 중요합니다. 이러한 모듈은 원격으로 실행할 때 누락됩니다. 이를 작동하게 하기 위한 특정 코드가 있는 몇 가지 예외가 있습니다.

이러한 문제를 피하려면 기본 Node.js 모듈 또는 확장 프로그램 VSIX 내의 모듈을 사용하십시오. 절대적으로 Electron 모듈을 사용해야 하는 경우, 모듈이 누락된 경우를 대비한 폴백(fallback)을 마련해야 합니다.

아래 예제는 Electron original-fs 노드 모듈을 찾으면 사용하고, 찾을 수 없으면 기본 Node.js fs 모듈로 폴백합니다.

function requireWithFallback(electronModule: string, nodeModule: string) {
  try {
    return require(electronModule);
  } catch (err) {}
  return require(nodeModule);
}

const fs = requireWithFallback('original-fs', 'fs');

가능한 한 이러한 상황을 피하도록 노력하십시오.

알려진 문제

Workspace Extensions에 대한 추가 기능으로 해결될 수 있는 몇 가지 확장 프로그램 문제가 있습니다. 다음 표는 고려 중인 알려진 문제 목록입니다.

문제 설명
Workspace 확장 프로그램에서 연결된 장치에 액세스할 수 없음 로컬에 연결된 장치에 액세스하는 확장 프로그램은 원격으로 실행될 때 해당 장치에 연결할 수 없습니다. 이를 극복하는 한 가지 방법은 연결된 장치에 액세스하는 것이 임무인 동반 UI 확장 프로그램을 만들고 원격 확장 프로그램이 호출할 수 있는 명령을 제공하는 것입니다.
또 다른 접근 방식은 리버스 터널링으로, 이는 VS Code 저장소 문제에서 추적 중입니다.

질문 및 피드백

© . This site is unofficial and not affiliated with Microsoft.