새로 이사한 블로그에서 글 확인하기 🏡 blog.wonkooklee.com
DeepSeek는 중국의 AI 스타트업이 개발한 오픈소스 기반의 대규모 언어모델(LLM)입니다. GPT-4와 유사한 기능을 제공하면서도 훨씬 낮은 비용으로 개발되었다는 점에서 큰 주목을 받고 있습니다.
한편 DeepSeek는 개인정보 유출 우려로 여러 국가에셔 경계의 대상이 되고 있습니다. DeepSeek의 개인정보 보호 정책에 따르면, 사용자 데이터는 중국 내 서버에 저장되며, 이는 중국 법률의 적용을 받습니다.
이러한 데이터 처리 방식으로 인해 사용자 정보가 중국 정부에 유출될 수 있다는 우려가 제기되고 있습니다.
DeepSeek를 로컬 환경에서 실행하면 데이터 유출 위험은 최소화 할 수 있습니다. 로컬 실행은 데이터를 외부 서버로 전송하지 않고, 모든 처리를 사용자의 컴퓨터에서 수행하기 때문에 데이터 프라이버시와 보안 문제는 걱정이 없겠죠.
마침 제가 즐겨보는 fireship이라는 개발 관련 컨텐츠 유투버가 해당 예제를 올리길래 재빠르게 따라해보았습니다.
VS Code Extension 인터페이스에서 로컬 AI 서버 API와 통신하는 예제를 따라하며 Ollama, VS Code Extension API에 대해 간단히 배워보세요.
원본 링크 https://youtu.be/clJCDHml2cA
Ollama는 로컬 환경에서 대규모 언어 모델(LLM)을 실행할 수 있도록 설계된 플랫폼 또는 툴입니다. 주로 개인 사용자가 자신의 하드웨어에서 언어 모델을 실행하여 데이터 프라이버시를 보장하고, 클라우드 의존도를 줄이는 데 초점이 맞춰져 있습니다.
Ollama의 주요 특징
https://ollama.com/download
링크에 들어가 본인의 운영 체제에 맞는 설치 파일을 받으시면 됩니다.
설치하게 되면 자동으로 ollama CLI 툴을 설치하게 됩니다.
이제 원하는 AI 모델을 pull 받아서 실행할 수 있게 됩니다.
위 페이지에서 마치 도커 이미지처럼 원하는 모델을 찾습니다.
저는 deepseek-r1:7b 모델을 선택했습니다.
특정 태그를 선택하면 우측에 CLI 명령어를 복사할 수 있는 UI가 보이는데, 이걸 로컬 TTY 콘솔에서 실행합니다.
$ ollama run deepseek-r1
명령어를 입력하면 해당 모델을 다운로드 받은 후 CLI에서 프롬프트를 직접 띄워줍니다.
여기서 직접 질문, 답변을 할 수도 있지만 우리의 목적은 VS Code Extension을 호스트로 해서 통신을 주고받기 위함이므로 다음 스텝으로 넘어갑니다.
Yeoman(yo)는 프로젝트 템플릿을 자동으로 생성해주는 도구입니다.
VS Code 확장 프로그램을 만들 때, generator-code 템플릿을 사용하면 필요한 파일과 폴더 구조를 자동으로 생성할 수 있습니다.
npx --package yo --package generator-code -- yo code
위 명령어를 실행하면, VS Code 확장 개발을 위한 기본 프로젝트가 자동으로 생성되며, 확장 유형과 설정을 선택할 수 있는 마법사가 실행됩니다.
이를 통해 확장 개발을 빠르게 시작할 수 있습니다.
위와 같이 대화형 프롬프트가 나타나 어떤 익스텐션을 만들 것인지 차례대로 설정할 수 있습니다.
셋업이 완료되면 우리가 흔히 보던 npm 패키지 프로젝트가 생성됩니다. extension.ts가 VS Code Extension을 실행했을때 제일 처음 도달하는 진입점이 됩니다.
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "wonkooklee-ext" is now active!');
}
const disposable = vscode.commands.registerCommand(
'wonkooklee-ext.helloWorld',
() => {
vscode.window.showInformationMessage('Hello World from wonkooklee-ext!');
}
);
vscode.window.showInformationMessage('Hello World from wonkooklee-ext!');
context.subscriptions.push(disposable);
export function deactivate() {}
VS Code는 확장을 개발하고 디버깅할 수 있도록 Extension Host라는 별도의 실행 환경을 제공합니다.
이 환경은 확장을 격리된 상태에서 실행하며, 디버거를 통해 확장의 코드를 단계별로 추적할 수 있게 합니다.
launch.json은 VS Code 디버거의 설정 파일로, 확장 디버깅을 어떻게 실행할지 정의합니다.
이 설정 파일 덕분에, 개발 중인 확장을 디버깅할 수 있습니다.
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/out/**/*.js"],
"preLaunchTask": "${defaultBuildTask}"
}
]
}
export function activate(context: vscode.ExtensionContext) {
console.log("축하합니다! 당신의 첫 VS Code Extension");
const disposable = vscode.commands.registerCommand(
"wonkooklee-ext.helloWorld",
() => {
vscode.window.showErrorMessage(
"안녕하세요, VSCode Extension 처음 띄워보아요!"
);
}
);
context.subscriptions.push(disposable);
}
registerCommand를 사용하여 임의의 명령어 helloWorld를 입력하면 두 번째 인자로 전달된 콜백이 실행되도록 만들어봅니다.
커맨드 팔레트를 열어 Debug: Start Debugging을 통해 새로운 실행 환경을 열어줍니다. 그럼 아래와 같이 새로운 VS Code 실행 환경이 만들어집니다.
새로운 실행 환경에서 Command + Shift + P 단축키를 사용하여 커맨드 팔레트를 열어준 뒤 임의로 설정했던 Exntesion Commands를 입력해줍니다.
showErrorMessage는 이렇게 동작하는군요.
ollama에서는 파이썬과 자바스크립트로 ollama와 통할할 수 있는 SDK를 제공합니다.
VS Code Extension이 실행되었을 때 ollama와 통신할 수 있도록 SDK를 설치해줍니다.
$ npm install ollama
이제 registerCommand 콜백 함수 내부 내용을 용도에 맞춰 수정해봅니다.
export function activate(context: vscode.ExtensionContext) {
console.log(
'Congratulations, your extension "wonkooklee-ext" is now active!'
);
const disposable = vscode.commands.registerCommand(
"wonkooklee-ext.helloWorld",
() => {
// 1
const panel = vscode.window.createWebviewPanel(
"deepChat",
"Deep Seek Chat",
vscode.ViewColumn.One,
{ enableScripts: true }
);
// 2
panel.webview.html = getWebviewContent();
panel.webview.onDidReceiveMessage(async (message: any) => {
if (message.command === "chat") {
const userPrompt = message.text;
let responseText = "";
try {
const streamResponse = await ollama.chat({
model: "deepseek-r1:7b",
messages: [{ role: "user", content: userPrompt }],
stream: true,
});
for await (const part of streamResponse) {
responseText += part.message.content.replace(/\<\/?think\>/g, "");
panel.webview.postMessage({
command: "chatResponse",
text: responseText,
});
}
} catch (error) {
panel.webview.postMessage({
command: "chatResponse",
text: `Error: ${String(error)}`,
});
}
}
});
}
);
context.subscriptions.push(disposable);
}
이 코드는 사용자 명령(wonkooklee-ext.helloWorld) 실행 시 WebView 창을 띄우고, 사용자가 입력한 내용을 기반으로 Ollama의 deepseek-r1:7b
모델과 상호작용하여 응답을 표시하는 기능을 제공합니다. 주요 부분을 단계별로 설명하겠습니다.
const panel = vscode.window.createWebviewPanel(
"deepChat",
"Deep Seek Chat",
vscode.ViewColumn.One,
{ enableScripts: true }
);
vscode.ViewColumn.One
은 WebView가 VS Code 창의 첫 번째 열에 표시되도록 지정합니다.{ enableScripts: true }
로 WebView에서 JavaScript 실행을 허용합니다.panel.webview.html = getWebviewContent();
getWebviewContent
함수에서 생성된 HTML 콘텐츠를 WebView에 렌더링합니다.panel.webview.onDidReceiveMessage(async (message: any) => { ... });
message.text
)을 처리하여 Ollama 모델과 상호작용합니다.const streamResponse = await ollama.chat({
model: "deepseek-r1:7b",
messages: [{ role: "user", content: userPrompt }],
stream: true,
});
for await (const part of streamResponse) {
responseText += part.message.content.replace(/\<\/?think\>/g, "");
panel.webview.postMessage({
command: "chatResponse",
text: responseText,
});
}
<think>
와 </think>
태그를 제거한 후 responseText에 추가합니다.context.subscriptions.push(disposable);
사용자와 상호작용을 하기 위한 웹뷰 화면을 간단하게 인라인 HTML로 마크업 합니다.
각 요소에 상호작용 이벤트를 바인딩하는 작업도 스크립트 태그 안에 넣어줍니다.
function getWebviewContent(): string {
return /*html*/ `
<!DOCTYPE html>
<html>
<meta charset="UTF-8" />
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#container {
width: 100%;
display: flex;
flex-direction: column;
padding: 24px;
}
h2 {
margin: 8px 0 16px;
}
#prompt {
padding: 16px;
border-radius: 12px;
}
#askBtn {
margin-top: 4px;
height: 48px;
font-size: 18px;
border-radius: 12px;
border: none;
background-color: #1a73e8;
color: #fff;
cursor: pointer;
}
#askBtn:hover {
background-color: #174ea6;
}
#response {
padding-top: 24px;
line-height: 140%;
font-size: 18px;
word-break: keep-all;
}
</style>
<body>
<div id="container">
<h2>Local Deepseek VS Code Extension</h2>
<textarea id="prompt" rows="5" placeholder="Ask something..."></textarea
><br />
<button id="askBtn">Ask</button>
<div id="response"></div>
</div>
<script>
const vscode = acquireVsCodeApi();
document.getElementById('askBtn').addEventListener('click', () => {
const text = document.getElementById('prompt').value;
vscode.postMessage({ command: 'chat', text });
})
window.addEventListener('message', event => {
const {command, text} = event.data;
if (command === 'chatResponse') {
document.getElementById('response').innerTextㅎ = text;
}
})
</script>
</body>
</html>
`;
}
Extension이 정상적으로 실행되면 아래와 같은 화면이 보입니다.
비록 로컬 환경이지만 VS Code Extension 창을 띄워 LLM 모델과 대화를 할 수 있습니다.