0. 들어가기 전에…
- 팀에 컬러 시스템이 정착되기 전, 프로젝트에서 전역 컬러 변수를 바꾸거나 추가 및 제거하는 과정이 있었다. 그 당시에는 scss 파일에서 탐색하고 수정하는 게 번거롭다고 느끼지 않았다.
- 그러다 어느 날 문득, “컴포넌트는 Storybook으로 관리하는데 컬러 변수는 어떻게 관리하지?”라는 생각이 들었다. 컬러 변수도 Storybook 형태로 한 눈에 파악하고 관리할 수는 없을까?
- 이렇게 이어지다 보니, vscode 사이드바(하단 핑크영역)에서 컬러 변수를 확인하고 검색할 수 있는 extension이 있으면 좋겠다는 생각이 들었다.
- 그래서 전역 컬러 변수를 보여주고, 복사는 물론 검색까지 되는 extension을 만들어보기로 했다.
- 이번 포스팅은 시리즈로 작성했는데, 첫 번째는 “[vscode] 컬러 변수 뷰어 만들기(1) - webview API 사용법”,
- 두 번째는 “[vscode] 컬러 변수 뷰어 만들기(2) - extension 구현하기” 로 나누었다.
- 이번 시간에는 시리즈의 첫 번째로, 커스텀 extension의 구조와 Webview API 사용법을 알아보았다.
1. 커스텀 extension 구조 살펴보기
🙂
webivew API
를 살펴보기 전, 커스텀 extension(확장)의 기본 구조를 해보자.
1) 명령어로 환경 설정하기
- 먼저, 아래 명령어로 프로젝트 환경 설정을 한다.
npx --package yo --package generator-code -- yo code
2) 폴더 구조는?
- 그러면 폴더가 자동 생성되는데, 이 중
src/extension.ts
,package.json
이 중요하다. src/extension.ts
는 커스텀 extension의 기능을 정의할 때 사용하며,package.json
은 커스텀 extension 이름, 버전 그리고 뷰 형태, 명령어 등을 정의한다.
(1) src/extension.ts 살펴보기
extension.ts
은 vscode extension의 생명주기별 함수를 제공한다.activate()
는 extension이 활성화될 때 실행되는 함수로, 기능을 초기화하고 등록한다.deactivate()
는 extension이 비활성화될 때 실행되며, 리소스를 정리하는 역할을 한다.
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) { ... }
export function deactivate() { ... }
(2) package.json 살펴보기
package.json
에는 크게 2가지 설정을 한다.- 첫 번째 설정은 우리가 대부분 알고 있듯이 프로젝트의 이름, 버전 등이다.
{
"name": "simplewebview",
"displayName": "simplewebview",
"description": "view webview simple example",
"version": "0.0.1",
"engines": {
"vscode": "^1.85.0"
},
"categories": [ "Other" ],
...
}
- 두 번째 설정은 커스텀 extension를 위한 설정이다.
- 커스텀 extension을 어떤 방식으로 표시할지, 그리고 사용자와 상호작용할 명령어 등을 정의한다.
{
...
"activationEvents": [], // (a)
"main": "./out/extension.js",
"contributes": {
"viewsContainers": {}, // (b)
"views": {} // (c)
"commands": [] // (d)
},
...
}
번호 | 속성 | 설명 |
---|---|---|
(a) | activationEvents |
커스텀 extension을 활성화시키는 이벤트 정의 |
(b) | viewsContainers |
커스텀 extension을 어떤 방식으로 표시할 지 정의(ex. 사이드바, 활동표시줄 등) |
(c) | views |
viewsContainers에 대한 상세 설정(ex. 사이드바를 사용한다면, 사이드바 버튼의 이미지 지정하기 등) |
(d) | commands |
커스텀 extenstion과 사용자와의 상호작용을 위한 명령어 정의 |
2. vscode Webview API 사용법
1) Webview API란?
- Webview API는 여러 형태의 UI를 vscode에서 띄우는 기능으로, 복잡한 사용자 인터페이스를 구현할 때 유용하다.
- 이 API를 사용하면 vscode 내에 HTML 콘텐츠를 렌더링할 수 있어, 버튼이나 검색창을 추가하여 사용자와 상호작용할 수 있다.
- 아래 예시는,
webview API
를 사용해 HTML 형식으로 버튼과 검색창을 렌더링한 모습이다. - 이렇게 추가한 요소에 이벤트를 정의하면 메시지를 띄우거나 컨텐츠를 업데이트할 수도 있다.
😆 Webview를 사용하면 원하는 UI를 추가할 수도 있고, 이벤트까지 정의할 수 있다니! 너무 좋은데??
그럼 extension에서 복잡한 계산이나 더 큰 형태의 UI도 추가해도 될까?
- 복잡한 계산이나 큰 형태의 UI는 지양하는 게 좋다..ㅜ
- 왜냐하면, vscode 공식문서에서도 언급했듯이
Webview
는 많은 리소스를 소모한다. - 그래서 아래 3가지 사항을 체크한 뒤, 사용을 고려해보는 게 좋다 😢
2) webview API 동작 살펴보기
📌 앞서, 커스텀 extension 구조와 역할 그리고
webview API
의 기본 개념도 알아 보았다.
이제 vscode 사이드바에 “할일 목록을 추가하는 기능”을 추가하면서webview API
동작을 살펴보자!
(1) 폴더 구조
- 구현한 코드의 프로젝트 구조는 아래와 같다.
- icon.svg은 extension 메뉴 아이콘이며, reset.css은 view 컨텐츠에서 사용할 스타일이다.
- extension.ts은 커스텀 extension을 활성화할 때 쓰인다.
- TodoProvider.ts은 커스텀 extension(메모 기능)의 기능 정의하고, package.json은 커스텀 extension의 view 형태, 아이콘 등을 설정한다.
(2) extension 기본 설정하기(package.json)
- package.json에서 커스텀 extension의 View 형태, 아이콘 등을 설정한다.
{
"name": "todoView",
"displayName": "todoView",
"description": "view webview simple example",
"version": "0.0.1",
"engines": {
"vscode": "^1.87.0"
},
"categories": [ "Other" ],
"activationEvents": [ "onView:todoView" ], // (a)
"main": "./out/extension.js",
"contributes": {
"viewsContainers": {
"activitybar": [ // (b)
{
"id": "todoView",
"title": "Todo",
"icon": "media/icon.svg"
}
]
},
"views": {
"todoView": [ // (c)
{
"type": "webview",
"id": "todoView",
"name": "Todo Viewer",
"contextualTitle": "CVT"
}
]
},
},
...
}
번호 | 설명 |
---|---|
(a) | - extension이 활성화되는 이벤트를 지정한다. - onView:todoView 라고 지정하면, "todoView"라는 뷰가 나타날 때 확장을 활성화한다. |
(b) | - 추가하려는 뷰 컨테이너(예: Activity Bar)를 정의한다. - 사이드바 영역에 커스텀 View를 추가하기위해, "activitybar"에 "todoView"라는 뷰를 추가한다. |
(c) | - (b)에서 선언한 뷰("id": "todoView")에 대한 상세 정의를 한다. - todoView 뷰의 타입을 webview 로 설정해 html 형식으로 커스텀이 가능토록 한다. |
(3) 메모 기능 구현하기(TodoProvider.ts)
- TodoProvider.ts의
TodoProvider
클래스에서 메모 기능과 뷰 화면을 구현해보자! - 클래스 기본 형태는 아래와 같다.
import * as vscode from "vscode";
export class TodoProvider implements vscode.WebviewViewProvider {
_view?: vscode.WebviewView;
_docTextList: string[] = [];
constructor(private readonly _extensionUri: vscode.Uri) {}
...
}
- 웹뷰(Webview)를 만들기 위해
revive()
,resolveWebviewView()
을 정의한다.
export class TodoProvider implements vscode.WebviewViewProvider {
...
// (d)
revive(panel: vscode.WebviewView) {
this._view = panel;
}
// (e)
resolveWebviewView(webviewView: vscode.WebviewView) {
this._view = webviewView;
webviewView.webview.options = { enableScripts: true };
// (f)
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
// (g)
webviewView.webview.onDidReceiveMessage(this.onReceiveMessage.bind(this));
}
}
번호 | 설명 |
---|---|
(d) | revive() 는 웹뷰가 다시 로드될 때 TodoProvider를 재활성화한다. |
(e) | resolveWebviewView() 에서 웹뷰의 HTML을 설정하고, 메시지 수신을 처리할 콜백 함수를 등록한다. |
(f) | _getHtmlForWebview 의 리턴값을 웹뷰의 HTML으로 설정한다. |
(g) | onReceiveMessage 에서 선언한 메시지와 동작을 수신한다. |
export class TodoProvider implements vscode.WebviewViewProvider {
...
// (h)
private async onReceiveMessage(data: { type: string; text: string }) {
switch (data.type) {
case "addTodo":
this._addTodo(data.text);
return;
case "update":
this._updateWebview();
return;
default:
return;
}
}
// (i)
private _addTodo(text: string) {
this._docTextList.push(text);
}
// (j)
private async _updateWebview() {
if (this._view) {
this._view.webview.html = this._getHtmlForWebview(this._view.webview);
}
}
...
}
번호 | 설명 |
---|---|
(h) | onReceiveMessage() 는 vscode.postMessage() 로 송신한 메시지를 수신하는 역할을 한다. |
(i) | 송신된 메시지 타입이 addTodo 라면, 메모 리스트에 메모를 추가한다. |
(j) | 송신된 메시지 타입이 update 라면, HTML을 재렌더링한다. |
export class TodoProvider implements vscode.WebviewViewProvider {
...
// (k)
private _getHtmlForWebview(webview: vscode.Webview) {
// (l)
const styleResetUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, "media", "reset.css")
);
const styleVSCodeUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, "media", "vscode.css")
);
// (m)
const listHtml = this._docTextList.map((text) => `<li>${text}</li>`).join("");
...
}
}
번호 | 설명 |
---|---|
(k) | _getHtmlForWebview 은 웹뷰에 띄워줄 콘텐츠를 리턴한다. |
(l) | 웹뷰에 css를 적용하기 위해, extension 프로젝트에 선언된 css 파일의 파일 위치를 가져온다. |
(m) | listHtml는 웹뷰에 할일 목록 리스트를 뿌려주는 역할을 한다. |
export class TodoProvider implements vscode.WebviewViewProvider {
...
private _getHtmlForWebview(webview: vscode.Webview) {
...
return `<!DOCTYPE html>
<html lang="en">
<head>
<!-- (o) -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel='stylesheet' href='${styleResetUri}' />
<link rel='stylesheet' href='${styleVSCodeUri}' />
<title>Todo</title>
</head>
<body>
<h1>Todo</h1>
<!-- (p) -->
<form id="todo-form">
<input id="todo-input" type="text" />
<button id="add-todo">Add</button>
</form>
<!-- (q) -->
<ul id="todo-list">
${ listHtml }
</ul>
<script>
// (r)
(function() {
// (s)
const vscode = acquireVsCodeApi();
const btnAdd = document.getElementById('add-todo');
// (t)
btnAdd.addEventListener('click', () => {
const input = document.getElementById('todo-input');
vscode.postMessage({ type: 'addTodo', text: input.value });
vscode.postMessage({ type: 'update' });
input.value = '';
});
}())
</script>
</body>
</html>`;
}
}
번호 | 설명 |
---|---|
(o) | 스타일 경로를 헤더에 등록해준다. |
(p) | 할 일 목록을 작성하고 저장하기 위해, 버튼과 입력창을 추가한다. |
(q) | 할 일 목록을 HTML 형태로 보여준다. |
(r) | 스크립트에 즉시실행함수 형태로 선언하여, 이벤트 동작이 가능하도록 설정한다. |
(s) | acquireVsCodeApi() 를 호출해, VSCode의 기능에 접근할 수 있는 변수를 선언한다.이 변수를 통해, vscode 내장 함수나 기능을 사용할 수 있다. |
(t) | add 버튼 클릭시 동작이다.postMessage({ type: 'addTodo', text: input.value }) 로 입력창의 정보를 전송한다.그 다음, 업데이트된 데이터를 화면에 렌더링하기 위해 HTML을 리렌더링하는 메세지를 전송한다. ( type: 'update' ) |
(5) 기능 활성화시키기 (extension.ts)
- 마지막으로 vscode가 활성화될 때, 할일 목록 화면을 렌더링해야한다.
import * as vscode from 'vscode';
import { TodoProvider } from './TodoProvider';
export function activate(context: vscode.ExtensionContext) {
const todo = new TodoProvider(context.extensionUri);
// (u)
context.subscriptions.push(
// (v)
vscode.window.registerWebviewViewProvider(
'todoView',
new TodoProvider(context.extensionUri
))
);
);
}
export function deactivate() {}
번호 | 설명 |
---|---|
(u) | registerWebviewViewProvider 로 todoView 뷰가 열릴 때, 할일 목록 화면을 랜더링한다. |
(v) | context.subscriptions.push() 은 일회용 리소스를 관리하고 메모리 누수를 방지한다. 이 함수에 할일 목록 화면 렌더링 작업(u)을 넘겨, 리소스를 관리해준다. |
📌 앞선 과정을 거치면, 아래와 같이 할일 목록을 추가할 수 있는 WebView를 만들 수 있다. (최종 코드)
3. 마치며…
- 이번 시간에는 “[vscode] 컬러 변수 뷰어 만들기” 시리즈의 첫 번째인 “webview API 사용법”을 알아보았다. vscode에는 extension 커스텀이 가능하도록 여러 API를 제공하고 있었으며, 이 중 webview API는 UI 커스텀이 자유롭다는 장점이 있었다. 다만 아쉬운 점은 HTML 방식으로 커스텀이 가능하나, 많은 리소스를 필요로 하는 단점이 있었다.
- 다음 시간에는 앞선 webview API 사용법을 토대로, 컬러 변수 뷰어를 구현하면서 여러 기능을 추가로 알아보겠다.
반응형
'개발 기술 > 개발 이야기' 카테고리의 다른 글
[React] 아이콘 컴포넌트를 선언하는 3가지 방법 (0) | 2024.04.14 |
---|---|
[vscode] 컬러 변수 뷰어 만들기(2) - colorvariabletracker (0) | 2024.03.31 |
Vue 3.4 변경점 파헤치기 (0) | 2024.02.29 |
feature flag로 지속적 배포하기(with. postHog, react) (0) | 2024.01.20 |
[JS] 메모리 누수는 왜 발생할까?(feat. 메모리 측정법) (10) | 2023.12.22 |
댓글