---
id: vscode-extension-structure-lifecycle
title: "VSCode 확장 구조와 생명주기"
category: "Architecture"
status: "draft"
verification_status: "applied"
canonical_id: ""
aliases: ["VS Code Extension", "activate", "deactivate", "Disposable", "Webview", "command 등록", "extension API"]
duplicate_of: ""
source_trust_level: "A"
confidence_score: 0.93
created_at: 2026-06-13
updated_at: 2026-06-13
review_reason: ""
merge_history: []
tags: ["vscode", "extension", "lifecycle", "disposable", "webview", "astraai"]
raw_sources: ["AstraAI/src/extension.ts", "AstraAI/package.json", "AstraAI/src/features/_shared/eventSourcedStore.ts"]
applied_in: ["AstraAI"]
github_commit: ""
---

# [[VSCode 확장 구조와 생명주기]]

## 🎯 한 줄 통찰 (One-line insight)
VS Code 확장은 `activate(context)` 에서 깨어나 명령·뷰·이벤트를 등록하고 `deactivate()` 에서 정리되며, 모든 자원을 **`context.subscriptions` 에 Disposable 로 등록**해 두면 확장 종료 시 메모리/타이머/리스너가 누수 없이 일괄 정리된다 [S1].

## 🧠 핵심 개념 (Core concepts)
1. **`activate(context)`:** 확장 진입점. `package.json` 의 activationEvents 조건이 충족되면 호출된다. `context: ExtensionContext` 가 전역 상태·구독·시크릿 저장소를 제공 [S1].
2. **`deactivate()`:** 종료 훅. 명시적으로 풀어야 하는 자원(외부 프로세스, 봇, 모델 언로드)을 여기서 정리 [S1].
3. **Disposable 패턴:** `{ dispose(): void }` 를 가진 객체. `context.subscriptions.push(disposable)` 하면 VS Code 가 종료 시 `dispose()` 를 호출 [S1].
4. **Command:** `vscode.commands.registerCommand(id, handler)` 로 등록, `package.json` 의 `contributes.commands` 에 선언. 반환값이 Disposable [S1].
5. **Webview:** HTML 기반 커스텀 UI. 확장↔웹뷰는 `postMessage` 로 통신. AstraAI 는 사이드바 대신 *에디터 컬럼* 에 패널로 띄운다 [S1].
6. **Configuration:** `vscode.workspace.getConfiguration('g1nation')` 으로 설정 읽기, `onDidChangeConfiguration` 으로 변경 반응 [S1].
7. **SecretStorage:** 토큰 등 민감정보는 `context.secrets` 에 저장(평문 설정 금지) [S1].

## 🧩 추출된 패턴 (Extracted patterns)
- **모든 자원을 subscriptions 로:** 명령·리스너·상태바·타이머·커스텀 dispose 콜백까지 전부 `context.subscriptions.push(...)` — 정리를 잊지 않는 구조적 보장 [S1].
- **설정 변경 반응:** `onDidChangeConfiguration((e) => { if (!e.affectsConfiguration('g1nation.ollamaUrl')) return; ... })` — 관심 키만 필터링해 재설정 [S1].
- **lazy webview 전송:** 매우 이른 활성화 시점엔 웹뷰가 아직 없을 수 있어 `provider?._sendModels(...)` 옵셔널 체이닝 + best-effort [S1].
- **getter 콜백으로 늦은 바인딩:** 아직 안 만들어진 객체는 `getProvider: () => provider` 처럼 getter 로 주입 — 순환 의존/초기화 순서 문제 회피 [S1].
- **워처 = disposable 반환 함수:** `startStocksWatcher(context)` 가 타이머를 만들고 disposable 을 반환해 subscriptions 에 등록 → 종료 시 timer cleanup [S1].

## 📖 세부 내용 (Details)
### activate 의 책임 순서 (AstraAI 실제 순서)
1. 가시화(버전 popup/console) → 환경 진단.
2. 부트스트랩(brain 디렉터리, 임베딩 감지, 기능 인벤토리) — 일부는 `void` 비차단.
3. 인프라 초기화(health monitor, path resolver, config 검증).
4. 핵심 객체 생성·주입(LMStudioClient, lifecycle, AgentExecutor, SidebarChatProvider, BridgeServer).
5. 명령·리스너 등록.
6. 워처 시작(stocks, daily briefing, growth cycle, sleep digest).
7. 웹뷰 자동 오픈.

### deactivate 의 책임
`HealthCheckMonitor.dispose()`, 인메모리 인덱스 해제(`clearBrainTokenIndex()` — Map 이 프로세스 수명 동안 안 비는 것 방지), 텔레그램 봇 stop, 모델 lifecycle dispose+unload. *명시적 정리가 필요한 것만* 여기서 처리하고, subscriptions 로 등록된 것은 VS Code 가 알아서 정리한다 [S1].

### 설정·시크릿
```typescript
const cfg = vscode.workspace.getConfiguration('g1nation');
const timeout = cfg.get<number>('lmStudio.idleTimeoutMs', 300000);  // 기본값 동반
const token = await context.secrets.get(TELEGRAM_TOKEN_SECRET_KEY); // 민감정보는 secrets
```

### Webview 통신
확장→웹뷰는 `webview.postMessage({ type: 'streamChunk', value })`, 웹뷰→확장은 메시지 핸들러(`sidebar/chatHandlers`). 스트리밍은 `streamStart`/`streamChunk`/`streamEnd` 의 메시지 프로토콜로 표현 [참조: [[Agent 오케스트레이터 분해]]].

## ⚖️ 모순 및 업데이트 (Contradictions & updates)
- **사이드바 뷰 제거(v2.81):** 과거엔 activity bar 사이드바에 webview view 를 등록했으나, 현재는 에디터 컬럼 3에 패널로 띄운다. 대신 activity bar 에는 명령 링크만 있는 빈 TreeView 를 둔다 — 탭이 닫혀도 다시 열 수 있게 [S1].
- **activationEvents:** 너무 광범위하면(예: `*`) 시작이 느려진다. 필요한 이벤트로 좁히는 것이 좋다(AstraAI 는 명령/뷰 기반).

## 🛠️ 적용 사례 (Applied in summary)
- `AstraAI/src/extension.ts` — activate/deactivate 의 전 과정, subscriptions 등록, 설정 반응, secrets 사용, 워처 등록이 모두 한 파일에 [S1].
- `AstraAI/src/features/_shared/eventSourcedStore.ts` — `vscode.workspace.workspaceFolders` 로 워크스페이스 경로 해석(확장에서 파일 경로 다루는 표준 방식) [S3].

## 💻 코드 패턴 (Code patterns)
```typescript
// 1) 명령 등록 → 반환된 Disposable 을 subscriptions 로 (src/extension.ts)
context.subscriptions.push(
    vscode.commands.registerCommand('g1nation.clearChat', () => provider.clearChat()),
);

// 2) 설정 변경 반응 — 관심 키만 (src/extension.ts)
context.subscriptions.push(
    vscode.workspace.onDidChangeConfiguration((e) => {
        if (!e.affectsConfiguration('g1nation.ollamaUrl')) return;
        const newUrl = vscode.workspace.getConfiguration('g1nation').get<string>('ollamaUrl', '');
        lmStudioClient.setBaseUrl(newUrl);
    }),
);

// 3) 커스텀 dispose 콜백도 disposable 로 등록
context.subscriptions.push({ dispose: () => activityTracker.dispose() });

// 4) getter 콜백으로 늦은 바인딩 (순환/초기화 순서 회피)
const telegramBot = createTelegramBot(context, { telegramClient, getProvider: () => provider });

// 5) deactivate — 명시 정리가 필요한 것만
export async function deactivate() {
    HealthCheckMonitor.dispose();
    clearBrainTokenIndex();
    if (_telegramBot) { try { await _telegramBot.stop(); } catch {} }
}
```

## ✅ 검증 상태 및 신뢰도
- **상태:** draft
- **검증 단계:** applied
- **출처 신뢰도:** A
- **신뢰 점수:** 0.93
- **중복 검사 결과:** 신규 생성 (New discovery)

## 🔗 지식 그래프 (Knowledge Graph)
- **상위/루트:** [[AstraAI 아키텍처 개요]]
- **관련 개념:** [[의존성 주입과 서비스 인터페이스]], [[모듈 시스템과 프로젝트 구성]], [[동시성 제어 Lock Queue Transaction]]
- **참조 맥락:** 로컬 LLM 이 VS Code 확장의 명령/뷰/설정/수명관리 코드를 작성·수정할 때 참조.

## 📚 출처 (Sources)
- [S1] AstraAI/src/extension.ts — activate/deactivate, subscriptions, 명령/설정/시크릿/웹뷰/워처
- [S2] AstraAI/package.json — engines.vscode, main, contributes(명령/뷰)
- [S3] AstraAI/src/features/_shared/eventSourcedStore.ts — workspaceFolders 경로 해석

## 📝 변경 이력 (Change history)
- 2026-06-13: AstraAI 코드 분석 기반 초안 생성.
