wiki: Topic_Blog 신규 문서 일괄 추가 + ASTRA 성장 자산 동기화

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Antigravity Agent
2026-06-16 09:55:38 +09:00
parent d77ff5c625
commit e2c5471046
444 changed files with 88916 additions and 231 deletions
@@ -0,0 +1,142 @@
---
id: typescript-advanced-types
title: "TypeScript 고급 타입"
category: "Programming_Language"
status: "draft"
verification_status: "applied"
canonical_id: ""
aliases: ["제네릭", "Generics", "타입 가드", "discriminated union", "유틸리티 타입", "Partial", "Omit"]
duplicate_of: ""
source_trust_level: "A"
confidence_score: 0.94
created_at: 2026-06-13
updated_at: 2026-06-13
review_reason: ""
merge_history: []
tags: ["typescript", "language", "generics", "type-guard", "advanced", "connectai"]
raw_sources: ["ConnectAI/src/features/_shared/eventSourcedStore.ts", "ConnectAI/src/core/queue.ts", "ConnectAI/src/features/providers/types.ts", "ConnectAI/src/memory/index.ts", "ConnectAI/src/intelligence/criticAgent.ts"]
applied_in: ["ConnectAI"]
github_commit: ""
---
# [[TypeScript 고급 타입]]
## 🎯 한 줄 통찰 (One-line insight)
제네릭·판별 유니온·타입 가드·유틸리티 타입은 "중복을 없애면서도 타입 안전을 유지"하는 도구이며, ConnectAI 는 이들로 **하나의 제네릭 스토어가 4개 도메인을 안전하게 처리**하고 **함수 결과를 성공/실패로 명확히 분기**한다 [S1].
## 🧠 핵심 개념 (Core concepts)
1. **제네릭 (Generics):** `createEventStore<E>(...)` 처럼 타입을 파라미터로 받아, 호출부가 `CustomerEvent`/`HireEvent` 등 구체 타입을 끼워 넣으면 그 타입으로 동작한다. 코드는 한 벌, 타입은 N 벌 [S1].
2. **판별 유니온 (Discriminated union):** 공통 *판별 필드* 로 갈래를 나눈 union. `{ ok: true; filePath } | { ok: false; error }` 처럼 `ok` 를 검사하면 컴파일러가 나머지 필드를 자동으로 좁힌다 [S1].
3. **타입 가드 (Type guard):** 반환 타입을 `x is T` 로 선언한 함수. `validate: (e: unknown) => e is E` 가 true 면 컴파일러가 그 블록에서 `e``E` 로 취급한다 — 런타임 검증과 컴파일 타입 좁히기를 동시에 [S1].
4. **유틸리티 타입 (Utility types):** `Partial<T>`(모든 필드 옵셔널), `Omit<T, K>`(특정 키 제외), `Record<K, V>`(키-값 맵), `Pick<T, K>`. 기존 타입에서 파생 타입을 *연산* 으로 만든다 [S2][S3].
5. **`keyof` / 인덱스 시그니처:** 객체 키 집합을 타입으로 (`Record<string, number>`), 동적 키 접근을 안전하게.
## 🧩 추출된 패턴 (Extracted patterns)
- **제네릭 팩토리 + 검증 주입:** `createEventStore<E>({ relPath, validate })` — 타입 파라미터와 런타임 검증을 함께 받아 I/O 를 추상화하고 도메인 로직은 호출부에 남긴다 [S1].
- **결과를 판별 유니온으로:** 예외를 던지는 대신 `{ ok: true } | { ok: false; error }` 를 반환해 호출부가 분기를 강제로 처리하게 한다 (Rust 의 `Result` 와 유사) [S1].
- **`Partial<T>` 로 설정 병합:** 생성자가 `config?: Partial<MemoryConfig>` 를 받아 기본값과 스프레드 병합 (`{ ...defaults, ...config }`) — 부분 오버라이드 패턴 [S4].
- **`Omit<T, 'model'>` 로 파라미터 일부만:** `streamCloudCompletion``Omit<StreamParams, 'model'>` 을 받아, model 은 내부에서 채운다 — "이미 아는 필드는 받지 않는다" [S3].
- **함수 타입 별칭으로 의존성 주입:** `type CritiqueLlmCall = (system, user, maxTokens) => Promise<string>` — LLM 호출을 타입으로 추상화해 모듈을 순수하게 유지 [S5].
## 📖 세부 내용 (Details)
### 제네릭의 실전 가치
ConnectAI 는 customers/hire/runway/feedback 4개 스토어가 같은 CRUD 패턴을 반복해 ~240줄 중복이 있었다. 이를 `createEventStore<E>` 하나로 통합 — 타입 파라미터 `E` 덕분에 각 스토어는 자기 이벤트 타입을 그대로 유지하면서 코드는 공유한다. 제네릭이 없었다면 `any` 로 타입을 잃거나 4벌을 복붙해야 했다 [S1].
```typescript
export interface EventStore<E> {
read(): E[];
append(event: E): { ok: true; filePath: string } | { ok: false; error: string };
count(): number;
}
export function createEventStore<E>(opts: EventStoreOptions<E>): EventStore<E> { /* ... */ }
```
### 판별 유니온으로 안전한 분기
`append` 의 반환값을 보면 호출부는 반드시 `ok` 를 먼저 검사해야 한다:
```typescript
const r = store.append(ev);
if (r.ok) { use(r.filePath); } // 이 블록에서 r.error 는 타입상 존재하지 않음
else { show(r.error); } // 이 블록에서 r.filePath 는 존재하지 않음
```
컴파일러가 갈래별로 접근 가능한 필드를 제한하므로, "성공 경로에서 error 를 읽는" 류의 버그가 원천 차단된다.
### 타입 가드 = 런타임 검증 + 컴파일 좁히기
```typescript
validate: (e: unknown) => e is CustomerEvent // 호출부 시그니처
// 내부에서
if (opts.validate(parsed)) out.push(parsed); // parsed 가 여기서 E 로 좁혀짐
```
`unknown` 으로 받은 외부 JSON 을 가드 통과 후에만 `E[]` 로 쌓는다 — type erasure 로 런타임에 타입이 없는 한계를 가드로 메운다.
### 제네릭 제약과 기본 타입 파라미터
`enqueue<T>(task: () => Promise<T>): Promise<T>` 처럼 메서드 단위 제네릭도 흔하다. 태스크의 반환 타입 `T` 가 그대로 큐의 반환 타입으로 흐른다 [S2].
## ⚖️ 비교 및 선택 기준 (Comparison & decision criteria)
| 항목 (Option) | 장점 | 단점 | 언제 선택 |
|---|---|---|---|
| 예외 throw | 작성 간단, 호출부 코드 짧음 | 호출부가 처리를 잊기 쉬움 | 진짜 예외적 상황(I/O 실패) |
| 판별 유니온 결과 | 호출부가 분기를 강제 처리 | 보일러플레이트 증가 | 실패가 정상 흐름의 일부일 때 (`append`) |
| 제네릭 함수 | 타입 안전 + 중복 제거 | 시그니처 복잡해질 수 있음 | 같은 로직이 여러 타입에 반복될 때 |
| `any` 캐스팅 | 즉시 통과 | 타입 안전 상실 | 최후의 수단, 외부 라이브러리 경계 |
## ⚖️ 모순 및 업데이트 (Contradictions & updates)
- **과도한 제네릭은 독:** 타입 파라미터가 3개를 넘거나 조건부 타입이 중첩되면 가독성이 급락한다. ConnectAI 는 대부분 단일 `<E>`/`<T>` 수준으로 절제한다.
- **결과 유니온 vs 예외:** ConnectAI 는 둘을 혼용한다 — 파일 append 같은 "흔한 실패"는 유니온, 트랜잭션 위반 같은 "계약 위반"은 커스텀 예외(throw). 일관 규칙은 "호출부가 정상적으로 마주칠 실패면 유니온".
## 🛠️ 적용 사례 (Applied in summary)
- `ConnectAI/src/features/_shared/eventSourcedStore.ts` — 제네릭 + 타입 가드 + 판별 유니온이 한 파일에 모두 [S1].
- `ConnectAI/src/memory/index.ts``constructor(brainPath, config?: Partial<MemoryConfig>)` + `{ ...defaults, ...config }` 병합 [S4].
- `ConnectAI/src/features/providers/index.ts``Omit<StreamParams, 'model'>` 로 일부 필드만 받는 함수 [S3].
## 💻 코드 패턴 (Code patterns)
```typescript
// 1) 제네릭 팩토리 + 타입 가드 주입 (src/features/_shared/eventSourcedStore.ts)
export interface EventStoreOptions<E> {
relPath: string;
validate: (e: unknown) => e is E; // 타입 가드: 런타임 검증 + 컴파일 좁히기
}
export function createEventStore<E>(opts: EventStoreOptions<E>): EventStore<E> { /* ... */ }
// 사용처:
const store = createEventStore<CustomerEvent>({
relPath: '.astra/customers.jsonl',
validate: (e): e is CustomerEvent =>
typeof (e as any).id === 'string' && typeof (e as any).customerId === 'string',
});
// 2) 판별 유니온 결과 — 호출부가 분기 강제
function append(event: E): { ok: true; filePath: string } | { ok: false; error: string } {
try { /* write */ return { ok: true, filePath }; }
catch (e: any) { return { ok: false, error: e?.message || String(e) }; }
}
// 3) Partial<T> 로 부분 설정 병합 (src/memory/index.ts)
constructor(brainPath: string, config?: Partial<MemoryConfig>) {
this.config = { enabled: true, shortTermLimit: 8, /* ...defaults */ ...config };
}
// 4) 함수 타입 별칭으로 의존성 주입 (src/intelligence/criticAgent.ts)
export type CritiqueLlmCall = (system: string, user: string, maxTokens: number) => Promise<string>;
```
## ✅ 검증 상태 및 신뢰도
- **상태:** draft
- **검증 단계:** applied (ConnectAI 1차 코드에서 모든 패턴 확인)
- **출처 신뢰도:** A
- **신뢰 점수:** 0.94
- **중복 검사 결과:** 신규 생성 (New discovery)
## 🔗 지식 그래프 (Knowledge Graph)
- **상위/루트:** [[TypeScript 기초와 타입 시스템]]
- **관련 개념:** [[이벤트 소싱 스토어 패턴]], [[에러 처리와 커스텀 에러]], [[의존성 주입과 서비스 인터페이스]]
- **참조 맥락:** 로컬 LLM 이 중복 제거·안전한 결과 반환·검증 코드를 작성할 때 참조.
## 📚 출처 (Sources)
- [S1] ConnectAI/src/features/_shared/eventSourcedStore.ts — 제네릭, 타입 가드, 판별 유니온 결과
- [S2] ConnectAI/src/core/queue.ts — 메서드 제네릭 `enqueue<T>`
- [S3] ConnectAI/src/features/providers/index.ts, types.ts — Omit, union literal, ReadonlyArray
- [S4] ConnectAI/src/memory/index.ts — Partial<T> + 스프레드 병합
- [S5] ConnectAI/src/intelligence/criticAgent.ts — 함수 타입 별칭(의존성 주입)
## 📝 변경 이력 (Change history)
- 2026-06-13: ConnectAI 코드 분석 기반 초안 생성.
@@ -0,0 +1,113 @@
---
id: typescript-basics-type-system
title: "TypeScript 기초와 타입 시스템"
category: "Programming_Language"
status: "draft"
verification_status: "applied"
canonical_id: ""
aliases: ["TypeScript", "TS", "타입스크립트", "정적 타이핑", "strict mode"]
duplicate_of: ""
source_trust_level: "A"
confidence_score: 0.95
created_at: 2026-06-13
updated_at: 2026-06-13
review_reason: ""
merge_history: []
tags: ["typescript", "language", "type-system", "fundamentals", "connectai"]
raw_sources: ["ConnectAI/tsconfig.json", "ConnectAI/src/core/services.ts", "ConnectAI/src/memory/types.ts", "TypeScript Handbook (general knowledge)"]
applied_in: ["ConnectAI"]
github_commit: ""
---
# [[TypeScript 기초와 타입 시스템]]
## 🎯 한 줄 통찰 (One-line insight)
TypeScript 는 JavaScript 에 **정적 타입 계층**을 얹어 "실행 전에 오류를 잡는" 언어이며, ConnectAI 처럼 `strict: true` 로 운영하면 컴파일러가 `null`/`undefined`/타입 불일치를 코드 작성 시점에 강제로 드러낸다 [S1].
## 🧠 핵심 개념 (Core concepts)
1. **점진적 타이핑 (Gradual typing):** JS 코드에 타입을 점진적으로 추가할 수 있다. 타입을 안 쓰면 `any` 로 동작하지만, ConnectAI 는 `strict` 모드로 `any` 를 최대한 배제한다 [S1].
2. **구조적 타이핑 (Structural typing):** 이름이 아니라 *구조(모양)* 가 같으면 호환된다. `interface IAIService` 를 명시적으로 `implements` 하지 않아도 같은 메서드를 가지면 그 타입으로 통한다 — 다만 ConnectAI 는 가독성을 위해 `implements` 를 명시한다 [S2].
3. **컴파일 타임 vs 런타임:** 타입은 컴파일 후 *지워진다(type erasure)*. 런타임에는 타입 정보가 없으므로, 외부 입력 검증은 **타입 가드 함수**로 직접 해야 한다 (`validate: (e: unknown) => e is E`) [S3].
4. **`strict` 플래그:** `strictNullChecks`, `noImplicitAny` 등을 한 번에 켠다. `null`/`undefined` 가 다른 타입에 섞이지 않게 하여, "정의되지 않음" 버그를 구조적으로 차단한다 [S1].
5. **타입 추론 (Inference):** 명시하지 않아도 컴파일러가 초기값·반환값에서 타입을 추론한다. 불필요한 타입 주석은 생략하고, 경계(함수 시그니처·공개 API)에만 명시하는 것이 ConnectAI 스타일이다.
## 🧩 추출된 패턴 (Extracted patterns)
- **경계에 타입, 내부에 추론:** 공개 함수/인터페이스는 타입을 명시하고(`public async chat(req: AIChatRequest): Promise<AIChatResult>`), 함수 내부 지역 변수는 추론에 맡긴다 [S2].
- **`interface` 로 데이터 형태 선언:** 도메인 데이터는 `interface` 로 모양을 먼저 정의한다 (`LongTermEntry`, `EpisodicEntry`). 구현보다 *형태* 를 먼저 설계하는 타입 우선 접근 [S3].
- **`type` 별칭으로 유한 집합 표현:** 고정된 문자열 집합은 union literal type 으로 (`type MemoryLayer = 'short-term' | 'long-term' | ...`). 오타를 컴파일러가 잡고, switch 에서 누락 케이스를 경고한다 [S3].
- **`readonly` / `as const` 로 불변 데이터:** 상수 배열은 `as const` 또는 `ReadonlyArray<...>` 로 변경 불가를 표현 (`PROVIDER_PREFIXES`) [S4].
## 📖 세부 내용 (Details)
### 기본 타입
`string`, `number`, `boolean`, `null`, `undefined`, `bigint`, `symbol`, 그리고 객체 타입(`object`, 배열 `T[]`, 튜플 `[A, B]`). 특수 타입으로 `any`(타입 검사 끔 — 지양), `unknown`(any 의 안전판 — 사용 전 좁히기 강제), `never`(도달 불가), `void`(반환값 없음)가 있다.
### `interface` vs `type`
- `interface`: 객체 *형태* 선언에 적합. 선언 병합(declaration merging)·`extends` 가능. ConnectAI 는 도메인 엔티티에 주로 `interface` 사용 (`MemoryContextResult`, `ProjectMemoryStore`) [S3].
- `type`: union/intersection/조건부 타입 등 *타입 연산* 에 적합. ConnectAI 는 union literal (`type ProviderId = 'openrouter' | 'anthropic' | 'gemini'`) 과 함수 타입 별칭에 사용 [S4].
- 실무 규칙: "객체 모양이면 `interface`, 합집합·매핑·연산이면 `type`".
### `strict` 가 강제하는 것 (ConnectAI tsconfig 기준)
`module: commonjs`, `target: ES2022`, `lib: ["ES2022", "DOM"]`, `strict: true`, `skipLibCheck: true` 로 설정 [S1]. `strict` 가 켜지면:
- `strictNullChecks`: `string | undefined``string` 에 바로 못 넣는다. 옵셔널 필드(`expiresAt?: number`)는 사용 전 `if (entry.expiresAt)` 같은 좁히기 필요 [S3].
- `noImplicitAny`: 타입을 추론할 수 없는 파라미터에 `any` 를 암묵 허용하지 않음 → 명시 강제.
- `strictFunctionTypes`, `strictBindCallApply` 등 함수 타입의 안전성 강화.
### 옵셔널·기본값·널 처리
- 옵셔널 프로퍼티 `field?: T` 는 값이 `T | undefined`. 코드에서 `req.timeoutMs ?? config.timeout` 처럼 **nullish 병합(`??`)** 으로 기본값을 준다. `||` 와 달리 `0`/`''`/`false` 를 유효값으로 보존한다 [S2].
- 옵셔널 체이닝 `data.choices?.[0]?.message?.content` 로 중첩 접근 중 `undefined` 를 안전하게 통과 [S2].
## ⚖️ 모순 및 업데이트 (Contradictions & updates)
- **`interface` vs `type` 논쟁:** 둘은 많은 경우 호환되며 "무엇을 써야 하는가"에 절대 정답은 없다. ConnectAI 의 실제 관례(객체=interface, 연산=type)를 따르는 것이 일관성 측면에서 안전하다.
- **`any` vs `unknown`:** 레거시 호환을 위해 ConnectAI 도 일부 `as any` 캐스팅을 쓰지만(예: `data` JSON 파싱 후), 새 코드에서는 외부 입력에 `unknown` + 타입 가드를 쓰는 것이 안전하다 [S3].
## 🛠️ 적용 사례 (Applied in summary)
- `ConnectAI/src/memory/types.ts` — 5계층 메모리의 모든 데이터 형태를 `interface`/`type` 로 선언. 옵셔널 temporal marker(`expiresAt?`)와 union category(`LongTermCategory`)가 strict 환경에서 어떻게 쓰이는지 보여준다 [S3].
- `ConnectAI/src/core/services.ts``interface IAIService` + `class AIService implements IAIService` 로 "인터페이스 선언 → 구현" 패턴 [S2].
## 💻 코드 패턴 (Code patterns)
```typescript
// 1) union literal type 으로 유한 집합 — 오타를 컴파일러가 차단 (src/memory/types.ts)
export type MemoryLayer = 'short-term' | 'long-term' | 'project' | 'procedural' | 'episodic';
// 2) interface 로 데이터 형태 선언 + 옵셔널 필드 (strictNullChecks 대응)
export interface LongTermEntry {
id: string;
category: LongTermCategory;
confidence: number; // 0.0~1.0
expiresAt?: number; // undefined = 영구. 사용 전 좁히기 필요
}
// 3) nullish 병합으로 기본값 — 0/''/false 를 보존 (src/core/services.ts)
const timeoutMs = req.timeoutMs ?? config.timeout; // ?? : null/undefined 일 때만 기본값
const model = (req.model || config.defaultModel || '').trim() || 'gemma4:e2b'; // || : falsy 전부 대체
// 4) 옵셔널 체이닝으로 안전한 중첩 접근
const content = data.choices?.[0]?.message?.content || '';
// 5) interface → implements 로 계약 명시
export interface IAIService { call(prompt: string): Promise<string>; }
export class AIService implements IAIService {
public async call(prompt: string): Promise<string> { /* ... */ return ''; }
}
```
## ✅ 검증 상태 및 신뢰도
- **상태:** draft
- **검증 단계:** applied (ConnectAI 실제 소스에서 패턴 확인됨)
- **출처 신뢰도:** A (언어 사양 + 1차 코드)
- **신뢰 점수:** 0.95
- **중복 검사 결과:** 신규 생성 (New discovery)
## 🔗 지식 그래프 (Knowledge Graph)
- **상위/루트:** [[TypeScript 기초와 타입 시스템]]
- **관련 개념:** [[TypeScript 고급 타입]], [[비동기 프로그래밍 Promise async await]], [[에러 처리와 커스텀 에러]]
- **참조 맥락:** 로컬 LLM 이 TypeScript 코드를 작성/수정할 때 타입 선언·null 안전·strict 규칙의 기본기로 참조.
## 📚 출처 (Sources)
- [S1] ConnectAI/tsconfig.json — strict/target/module 컴파일러 설정
- [S2] ConnectAI/src/core/services.ts — IAIService 인터페이스, ?? vs || 기본값, 옵셔널 체이닝
- [S3] ConnectAI/src/memory/types.ts — interface/type 선언, 옵셔널 필드, union literal
- [S4] ConnectAI/src/features/providers/types.ts — type 별칭, as const, ReadonlyArray
## 📝 변경 이력 (Change history)
- 2026-06-13: ConnectAI 코드 분석 기반 초안 생성 (로컬 LLM 코딩 지식 베이스 구축).
@@ -0,0 +1,122 @@
---
id: module-system-project-structure
title: "모듈 시스템과 프로젝트 구성"
category: "Programming_Language"
status: "draft"
verification_status: "applied"
canonical_id: ""
aliases: ["import", "export", "모듈", "barrel", "side-effect import", "dynamic import", "esbuild", "번들링"]
duplicate_of: ""
source_trust_level: "A"
confidence_score: 0.92
created_at: 2026-06-13
updated_at: 2026-06-13
review_reason: ""
merge_history: []
tags: ["typescript", "module", "import", "esbuild", "project-structure", "connectai"]
raw_sources: ["ConnectAI/src/extension.ts", "ConnectAI/src/memory/index.ts", "ConnectAI/src/retrieval/index.ts", "ConnectAI/package.json", "ConnectAI/tsconfig.json"]
applied_in: ["ConnectAI"]
github_commit: ""
---
# [[모듈 시스템과 프로젝트 구성]]
## 🎯 한 줄 통찰 (One-line insight)
모듈 시스템은 "무엇을 공개하고(`export`) 무엇을 가져올지(`import`)"로 코드 경계를 긋는 것이며, ConnectAI 는 **barrel(index.ts) 재수출·side-effect import 자기등록·동적 import 지연로딩**을 조합해 308개 파일을 esbuild 단일 번들로 묶는다 [S1][S4].
## 🧠 핵심 개념 (Core concepts)
1. **ES Module 문법:** `import { x } from './m'` / `export function x()`. TypeScript 는 이 문법을 쓰고, tsconfig `module: commonjs` 로 CommonJS 로 컴파일된다 [S5].
2. **named vs default export:** ConnectAI 는 거의 전부 *named export* 만 쓴다 — 자동완성·일관된 이름·리팩터링 안전성 때문. default export 는 사실상 배제 [S2].
3. **Barrel 파일 (index.ts):** 하위 모듈을 한 곳에서 재수출(`export * from './types'`)해 외부가 깔끔한 진입점 하나만 import 하게 한다 [S2][S3].
4. **Side-effect import:** `import './features/teamops/handlers'` — 값을 가져오지 않고 *모듈 로드의 부수효과*(핸들러 자기등록)만 노린다 [S1].
5. **Dynamic import (`await import(...)`):** 무겁거나 드물게 쓰는 모듈을 실제 호출 시점에 지연 로딩 — 활성화 시간 단축 [S1].
6. **번들링 (esbuild):** 모든 모듈을 `out/extension.js` 하나로 묶되 `vscode` 는 external (런타임 제공) [S4].
## 🧩 추출된 패턴 (Extracted patterns)
- **상대경로 import + 명확한 트리:** `../config`, `./types` 같은 상대경로로 모듈을 참조하고, 폴더 구조가 곧 도메인 경계(`features/`, `core/`, `memory/`, `retrieval/`, `intelligence/`).
- **barrel 재수출로 진입점 단일화:** `src/memory/index.ts` 가 5개 메모리 클래스 + distillation API + `export * from './types'` 를 한 번에 노출 [S3].
- **side-effect import 로 핸들러 자기등록:** entry point 가 `import './features/system/handlers'` 만 하면 그 모듈이 slashRouter 에 자기를 등록 — 등록 코드를 한 곳에 모으지 않는 분산 등록 [S1].
- **동적 import 로 무거운 기능 지연:** `const { runDatacollectSetup } = await import('./features/setup/datacollectSetup')` — 명령 실행 시에만 로드 [S1].
- **타입 전용 import:** `import type { ChatMessage } from '../../agent'` — 컴파일 후 사라지는 타입만 가져와 순환참조·번들 부담 회피 [S6].
## 📖 세부 내용 (Details)
### 폴더 = 도메인 경계
```
src/
core/ 공통 인프라 (lock, queue, transaction, errors, services, events)
features/ 기능 도메인 (각 폴더가 독립 기능: stocks, calendar, company, datacollect…)
memory/ 5계층 메모리
retrieval/ RAG 검색
intelligence/ 검증·자기평가 (critic, confidence, correctionLoop)
lib/ 순수 헬퍼 + contextBuilders (프롬프트 컨텍스트 조립)
agent/ 에이전트 실행 세부 (handlePrompt/, llm/, actions/, multiAgent/)
```
각 폴더 안에 `index.ts`(barrel)가 있으면 외부는 그 하나만 import 한다.
### import 순서·스타일
실제 코드는 (1) Node 표준(`fs`, `path`), (2) vscode, (3) 내부 모듈 순으로 import 하며, 내부는 도메인별로 묶어 가독성을 유지한다. 거대한 orchestrator(agent.ts)는 import 가 100줄을 넘는데, 이는 *기능을 작은 모듈로 추출하고 다시 끌어모으는* 구조의 자연스러운 결과다 [S1].
### side-effect import 의 순서 민감성
```typescript
// slashRouter 가 먼저 로드된 뒤 핸들러가 자기 등록되도록 entry point 에서 import
import './features/teamops/handlers';
import './features/system/handlers';
import './features/datacollect/handlers';
```
주석이 "왜 여기서 import 하는지"(로드 순서 보장)를 명시한다 — side-effect import 는 순서가 동작에 영향을 주므로 의도를 적는 것이 필수 [S1].
### 번들/빌드
- `compile`: `esbuild src/extension.ts --bundle --platform=node --external:vscode --outfile=out/extension.js` — 단일 파일 번들 [S4].
- `watch`: `tsc -watch` (타입 체크용), `test`: `jest`. 런타임 의존성은 `@lmstudio/sdk`, `pdf-parse` 둘뿐이고 axios 대신 native `fetch` 사용 [S4].
## ⚖️ 모순 및 업데이트 (Contradictions & updates)
- **barrel 의 양날:** index.ts 재수출은 진입점을 깔끔히 하지만, 과하면 순환참조와 "한 줄 import 가 거대한 그래프를 끌어옴" 문제를 낳는다. ConnectAI 는 무거운 기능을 동적 import 로 분리해 이를 완화 [S1].
- **commonjs vs ESM:** tsconfig 는 `commonjs` 로 컴파일하지만 소스는 ESM 문법으로 작성한다 — VS Code 확장 런타임(Node)이 CJS 를 기대하기 때문. 새 프로젝트라면 ESM 출력도 가능하나 호환성 고려 필요 [S5].
## 🛠️ 적용 사례 (Applied in summary)
- `ConnectAI/src/extension.ts` — side-effect import, 동적 import, 100+줄 named import 의 실제 예 [S1].
- `ConnectAI/src/memory/index.ts`, `src/retrieval/index.ts` — barrel 재수출 [S2][S3].
## 💻 코드 패턴 (Code patterns)
```typescript
// 1) side-effect import — 핸들러 자기등록 (순서 주석 필수) (src/extension.ts)
import './features/teamops/handlers';
import './features/system/handlers';
// 2) barrel 재수출로 진입점 단일화 (src/memory/index.ts)
export { ShortTermMemory } from './ShortTermMemory';
export { LongTermMemory } from './LongTermMemory';
export * from './types';
// 3) 동적 import 로 무거운 기능 지연 (src/extension.ts)
vscode.commands.registerCommand('g1nation.setupDatacollect', async () => {
const { runDatacollectSetup } = await import('./features/setup/datacollectSetup');
await runDatacollectSetup();
});
// 4) 타입 전용 import — 런타임 부담/순환참조 회피
import type { AgentExecutorOptions, ChatMessage } from '../../agent';
```
## ✅ 검증 상태 및 신뢰도
- **상태:** draft
- **검증 단계:** applied
- **출처 신뢰도:** A
- **신뢰 점수:** 0.92
- **중복 검사 결과:** 신규 생성 (New discovery)
## 🔗 지식 그래프 (Knowledge Graph)
- **상위/루트:** [[TypeScript 기초와 타입 시스템]]
- **관련 개념:** [[ConnectAI 아키텍처 개요]], [[VSCode 확장 구조와 생명주기]], [[코딩 컨벤션과 주석 철학]]
- **참조 맥락:** 로컬 LLM 이 파일을 나누고 import/export 를 구성할 때 참조.
## 📚 출처 (Sources)
- [S1] ConnectAI/src/extension.ts — side-effect/동적 import, named import 구성
- [S2] ConnectAI/src/features/providers/index.ts — named export, 재수출
- [S3] ConnectAI/src/memory/index.ts, src/retrieval/index.ts — barrel(export *) 패턴
- [S4] ConnectAI/package.json — esbuild 번들 스크립트, 의존성
- [S5] ConnectAI/tsconfig.json — module/target 설정
- [S6] ConnectAI/src/agent/multiAgent/workflow.ts — import type
## 📝 변경 이력 (Change history)
- 2026-06-13: ConnectAI 코드 분석 기반 초안 생성.
@@ -0,0 +1,131 @@
---
id: async-programming-promise-async-await
title: "비동기 프로그래밍 Promise async await"
category: "Programming_Language"
status: "draft"
verification_status: "applied"
canonical_id: ""
aliases: ["Promise", "async", "await", "비동기", "AbortSignal", "동시성", "스트리밍", "concurrency"]
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: ["typescript", "javascript", "async", "promise", "abortsignal", "connectai"]
raw_sources: ["ConnectAI/src/core/services.ts", "ConnectAI/src/core/lock.ts", "ConnectAI/src/core/queue.ts", "ConnectAI/src/features/providers/index.ts"]
applied_in: ["ConnectAI"]
github_commit: ""
---
# [[비동기 프로그래밍 Promise async await]]
## 🎯 한 줄 통찰 (One-line insight)
`async/await` 는 비동기 코드를 동기처럼 읽히게 하는 문법이고, `Promise` 는 그 토대이며, ConnectAI 는 여기에 **`AbortSignal` 결합·타임아웃 경쟁(race)·동시성 제한**을 더해 "취소 가능하고 폭주하지 않는" 비동기를 구현한다 [S1][S2].
## 🧠 핵심 개념 (Core concepts)
1. **Promise:** 미래의 값을 담는 객체. `pending → fulfilled/rejected` 상태를 가진다. `new Promise((resolve, reject) => ...)` 로 직접 만들거나 `async` 함수가 자동 반환한다 [S2].
2. **async/await:** `async` 함수 안에서 `await promise` 는 Promise 가 풀릴 때까지 기다린 ** 을 돌려준다. 실패하면 예외로 던져져 `try/catch` 로 잡는다 [S1].
3. **`Promise.all` / `Promise.race`:** `all` 은 모두 완료될 때까지 병렬 대기(하나라도 실패 시 전체 reject), `race` 는 가장 먼저 끝난 하나를 채택 — ConnectAI 는 race 로 "작업 vs 타임아웃" 경쟁을 만든다 [S3].
4. **`AbortSignal` / `AbortController`:** 진행 중인 비동기(특히 `fetch`)를 외부에서 취소하는 표준 메커니즘. `AbortSignal.timeout(ms)`, `AbortSignal.any([...])` 로 타임아웃·사용자 취소를 결합 [S1].
5. **동시성 제한 (Concurrency limiting):** 무한 병렬은 자원을 고갈시킨다. 큐로 동시 실행 수를 `max(2, cpus-1)` 로 제한 [S4].
## 🧩 추출된 패턴 (Extracted patterns)
- **타임아웃 + 외부 취소 신호 결합:** `AbortSignal.any([req.signal, AbortSignal.timeout(timeoutMs)])` — 둘 중 무엇이 먼저 fire 돼도 fetch 가 즉시 중단된다. 사용자가 "Stop" 을 누르면 LLM 생성 도중에도 끊긴다 [S1].
- **race 로 데드락 방지:** lock 획득 시 `Promise.race([previousPromise, timeoutPromise])` — 앞 작업이 영원히 안 끝나도 timeout 이 깨운다 [S2].
- **resolve 를 밖으로 빼내는 deferred:** `let release; new Promise(r => { release = r; })` — Promise 를 만들고 그 resolve 함수를 외부에서 호출 가능하게 보관(락 해제 함수로 반환) [S2].
- **큐 기반 동시성 캡:** `enqueue<T>` 가 Promise 를 반환하되 실제 실행은 `activeCount < limit` 일 때만 — 초과분은 대기 [S4].
- **best-effort 비차단:** `void ensureEmbeddingConfigured(context)` — 결과를 기다리지 않고 백그라운드로 흘려보내는 fire-and-forget (`void` 로 의도 명시) [S1].
## 📖 세부 내용 (Details)
### await 의 실패는 예외다
`await fetch(...)` 가 네트워크 오류로 reject 되면 그 지점에서 throw 된다. ConnectAI 의 `AIService.chat` 은 엔진별 루프 안에서 `try/catch` 로 잡아 `lastError` 에 저장하고 다음 엔진으로 폴백한다 — "한 엔진 실패가 전체 실패가 아니다" [S1].
### AbortSignal 결합 (핵심 패턴)
```typescript
const timeoutSignal = AbortSignal.timeout(timeoutMs);
const combinedSignal = req.signal
? AbortSignal.any([req.signal, timeoutSignal]) // 사용자 취소 OR 타임아웃
: timeoutSignal;
const res = await fetch(apiUrl, { /* ... */ signal: combinedSignal });
```
이 패턴 덕분에 (1) 응답이 너무 느리면 타임아웃으로, (2) 사용자가 멈추면 외부 signal 로 즉시 중단된다. 긴 multi-turn 경로(dispatcher 등)에는 반드시 `signal` 을 전달하는 것이 규칙 [S1].
### 직접 만드는 Promise (deferred 패턴)
락 매니저는 "다른 코드가 부를 때 풀리는 Promise" 가 필요하다:
```typescript
let release!: () => void;
const newPromise = new Promise<void>((resolve) => { release = resolve; });
// ... 작업이 끝나면 호출부가 release() 를 부르면 newPromise 가 fulfilled
return () => { release(); /* cleanup */ };
```
### 병렬 vs 순차
- 독립 작업은 `Promise.all([a(), b()])` 로 병렬 (provider 모델 목록 동시 조회) [S5].
- 의존 작업은 순차 `await a(); await b();`.
- 자원 부담이 큰 대량 작업은 `Promise.all` 대신 동시성 제한 큐를 쓴다 [S4].
## ⚖️ 모순 및 업데이트 (Contradictions & updates)
- **`Promise.all` 의 함정:** 하나라도 reject 되면 전체가 reject 되고 나머지 성공 결과를 잃는다. 부분 실패를 허용해야 하면 `Promise.allSettled` 를 쓰거나 각 작업을 try/catch 로 감싸야 한다.
- **`await` in loop vs 병렬:** 루프 안 `await` 는 순차 실행이라 느릴 수 있다. 단, ConnectAI 의 엔진 폴백 루프는 *의도적으로 순차* (앞 엔진이 성공하면 뒤는 안 부름).
- **`forEach` + async 주의:** `array.forEach(async ...)` 는 완료를 기다리지 않는다. 대기하려면 `for...of` + `await` 또는 `Promise.all(array.map(...))`.
## 🛠️ 적용 사례 (Applied in summary)
- `ConnectAI/src/core/services.ts` — AbortSignal 결합 + 엔진 폴백 루프(try/catch 순차) [S1].
- `ConnectAI/src/core/lock.ts` — deferred Promise + `Promise.race` 타임아웃 [S2].
- `ConnectAI/src/core/queue.ts` — 동시성 제한 큐 [S4].
- `ConnectAI/src/features/providers/index.ts``Promise.all(tasks)` 로 provider 목록 병렬 조회 [S5].
## 💻 코드 패턴 (Code patterns)
```typescript
// 1) 타임아웃 + 외부 취소 결합 (src/core/services.ts)
const timeoutSignal = AbortSignal.timeout(timeoutMs);
const combinedSignal = req.signal ? AbortSignal.any([req.signal, timeoutSignal]) : timeoutSignal;
const res = await fetch(apiUrl, { method: 'POST', body: JSON.stringify(payload), signal: combinedSignal });
// 2) try/catch 폴백 루프 — 한 엔진 실패가 전체 실패가 아님 (src/core/services.ts)
let lastError: Error | null = null;
for (const engine of engines) {
try { const r = await callEngine(engine); if (r) return r; }
catch (e: any) { lastError = e instanceof Error ? e : new Error(String(e)); }
}
throw lastError ?? new Error('All engines failed.');
// 3) deferred Promise + race 타임아웃 (src/core/lock.ts)
let release!: () => void;
const newPromise = new Promise<void>((resolve) => { release = resolve; });
const timeoutPromise = new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error('Lock timed out')), timeoutMs));
await Promise.race([previousPromise, timeoutPromise]);
// 4) fire-and-forget (의도적 비대기) — void 로 명시 (src/extension.ts)
void ensureEmbeddingConfigured(context);
// 5) 병렬 수집 (src/features/providers/index.ts)
const tasks: Array<Promise<void>> = [];
tasks.push(listOpenRouterModels(ctx).then((ids) => ids.forEach(pushModel)));
await Promise.all(tasks);
```
## ✅ 검증 상태 및 신뢰도
- **상태:** draft
- **검증 단계:** applied
- **출처 신뢰도:** A
- **신뢰 점수:** 0.93
- **중복 검사 결과:** 신규 생성 (New discovery)
## 🔗 지식 그래프 (Knowledge Graph)
- **상위/루트:** [[TypeScript 기초와 타입 시스템]]
- **관련 개념:** [[동시성 제어 Lock Queue Transaction]], [[LLM 프로바이더 추상화]], [[에러 처리와 커스텀 에러]]
- **참조 맥락:** 로컬 LLM 이 fetch/취소/타임아웃/병렬 처리를 작성할 때 참조.
## 📚 출처 (Sources)
- [S1] ConnectAI/src/core/services.ts — AbortSignal.any/timeout, 엔진 폴백, void fire-and-forget(extension.ts 포함)
- [S2] ConnectAI/src/core/lock.ts — deferred Promise, Promise.race 타임아웃
- [S3] (general) Promise.all/race 의미론
- [S4] ConnectAI/src/core/queue.ts — 동시성 제한 큐
- [S5] ConnectAI/src/features/providers/index.ts — Promise.all 병렬 수집
## 📝 변경 이력 (Change history)
- 2026-06-13: ConnectAI 코드 분석 기반 초안 생성.
@@ -0,0 +1,150 @@
---
id: error-handling-custom-errors
title: "에러 처리와 커스텀 에러"
category: "Programming_Language"
status: "draft"
verification_status: "applied"
canonical_id: ""
aliases: ["error handling", "try catch", "커스텀 에러", "graceful degradation", "에러 클래스", "rollback"]
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: ["typescript", "error-handling", "robustness", "connectai"]
raw_sources: ["ConnectAI/src/core/errors.ts", "ConnectAI/src/core/errorHandler.ts", "ConnectAI/src/core/transaction.ts", "ConnectAI/src/memory/index.ts", "ConnectAI/src/core/services.ts"]
applied_in: ["ConnectAI"]
github_commit: ""
---
# [[에러 처리와 커스텀 에러]]
## 🎯 한 줄 통찰 (One-line insight)
견고한 코드는 "실패를 *예측해서 분류하고*, 사용자에게는 친절히 번역하며, 부가 작업의 실패가 본류를 망가뜨리지 않게" 만든다 — ConnectAI 는 커스텀 에러 계층 + 사용자 친화 번역기 + "절대 본 흐름을 깨지 않는 try/catch" 로 이를 구현한다 [S1][S2][S4].
## 🧠 핵심 개념 (Core concepts)
1. **`Error` 상속 계층:** 도메인별 에러를 `class XError extends Error` 로 만들어, `instanceof` 로 분기하고 추가 컨텍스트(경로, 엔진, 상태코드)를 담는다 [S1].
2. **추상 베이스 클래스:** `abstract class G1Error extends Error` 가 공통 형태(`details`, `getTypeCode()`)를 강제하고, 구체 에러가 타입 코드를 구현 [S1].
3. **에러 번역 (Error translation):** 내부 기술 에러 메시지를 *사용자 행동 지침* 으로 변환 (`title`/`message`/`action`) [S2].
4. **Graceful degradation:** 부가 기능(메모리 추출, 증류, 텔레메트리)의 실패는 삼키고(`catch {}`) 본 흐름을 계속한다 [S4].
5. **트랜잭션/롤백:** 여러 파일 변경을 묶고, 실패 시 백업으로 되돌리는 보상 트랜잭션 [S3].
## 🧩 추출된 패턴 (Extracted patterns)
- **커스텀 에러에 컨텍스트 부착:** `FileSystemError(message, path, details)`, `APICommunicationError(message, engine, status)` — 잡는 쪽이 어디서 왜 실패했는지 알 수 있게 [S1].
- **`this.name = this.constructor.name`:** 스택 트레이스에 정확한 클래스명이 찍히도록 베이스에서 설정 [S1].
- **"본 흐름을 깨지 않는" catch:** `try { extract(); } catch { /* memory extraction should never break the main flow */ }` — 의도를 주석으로 명시한 의도적 삼킴 [S4].
- **에러를 Error 로 정규화:** `error instanceof Error ? error : new Error(String(error))` — catch 의 `unknown`/`any` 를 항상 Error 로 변환 [S5].
- **사용자 친화 번역기:** 키워드 매칭(`fetch`/`timeout`/`404`)으로 정형화된 안내 카드를 반환 [S2].
- **보상 트랜잭션:** 변경 전 원본을 백업(`record`), 성공 시 `commit`(백업 폐기), 실패 시 `rollback`(원복) [S3].
## 📖 세부 내용 (Details)
### 에러 클래스 계층
```typescript
abstract class G1Error extends Error {
constructor(public message: string, public details?: any) {
super(message);
this.name = this.constructor.name; // 스택에 실제 클래스명
}
abstract getTypeCode(): string; // 하위가 타입 코드 구현 강제
}
export class FileSystemError extends G1Error {
constructor(message: string, public path: string, details?: any) { super(message, details); }
getTypeCode() { return 'FILE_SYSTEM_ERROR'; }
}
```
`abstract` 메서드로 모든 하위 에러가 식별 코드를 갖게 강제 — 로깅/분기에서 문자열 비교 대신 안정적 코드를 쓴다 [S1].
### "절대 본 흐름을 깨지 않는다"
세션 종료 시 메모리 추출·증류는 *부가 작업* 이다. 실패해도 대화 자체는 정상이어야 하므로 빈 catch 로 삼키되 **왜 삼키는지 주석을 단다**:
```typescript
try { this.extractor.extractFromSession(...); }
catch { /* memory extraction should never break the main flow */ }
```
무분별한 빈 catch 는 안티패턴이지만, "부가 작업 + 의도 주석" 조합은 의도적 견고성이다 [S4].
### 사용자 친화 번역
```typescript
if (msg.includes('timeout')) return {
title: '⏱️ 응답 시간 초과 (Timeout)',
message: 'AI가 답변을 준비하는 데 너무 오래 걸리고 있습니다.',
action: '설정에서 Timeout 시간을 늘리거나, 더 작은 범위로 질문해보세요.',
};
```
기술 메시지(`ECONNREFUSED`)를 그대로 노출하지 않고, *무엇을 하면 되는지* 를 알려준다. 마지막에 일반 fallback 카드로 미분류 에러를 처리 [S2].
### 보상 트랜잭션 (파일 작업의 원자성)
DB 트랜잭션이 없는 파일시스템에서 "여러 파일 변경을 전부 성공 또는 전부 취소" 하려면 직접 백업/복원해야 한다. `begin → record(각 파일) → (성공) commit / (실패) rollback`. rollback 은 `created` 파일은 삭제, `modified` 파일은 원본 내용 복원 [S3].
## ⚖️ 비교 및 선택 기준 (Comparison & decision criteria)
| 항목 (Option) | 장점 | 단점 | 언제 선택 |
|---|---|---|---|
| throw + 상위 catch | 흐름 단순 | 어디서 잡을지 추적 필요 | 계약 위반·복구 불가 상황 |
| 빈 catch (의도 주석) | 본 흐름 보호 | 남용 시 버그 은폐 | 진짜 부가 작업만 |
| 결과 유니온 반환 | 호출부 강제 처리 | 보일러플레이트 | 흔한 실패(파일 append) |
| 커스텀 에러 클래스 | instanceof 분기 + 컨텍스트 | 클래스 정의 비용 | 도메인별 처리 분기 필요 |
## ⚖️ 모순 및 업데이트 (Contradictions & updates)
- **빈 catch 는 기본적으로 위험:** ConnectAI 는 "본 흐름을 깨지 않아야 하는 부가 작업"에 한해 의도 주석과 함께만 허용한다. 검증·핵심 로직의 실패는 절대 조용히 삼키지 않는다.
- **에러 메시지 키워드 매칭의 취약성:** `ErrorTranslator` 는 메시지 문자열에 의존하므로, 라이브러리가 메시지를 바꾸면 매칭이 깨질 수 있다. 가능하면 `getTypeCode()` 같은 안정 식별자 기반 분기가 더 견고하다.
## 🛠️ 적용 사례 (Applied in summary)
- `ConnectAI/src/core/errors.ts` — G1Error 추상 베이스 + 4개 도메인 에러 [S1].
- `ConnectAI/src/core/errorHandler.ts` — ErrorTranslator 사용자 친화 번역 [S2].
- `ConnectAI/src/core/transaction.ts` — begin/record/commit/rollback 보상 트랜잭션 [S3].
- `ConnectAI/src/memory/index.ts` — "본 흐름 보호" 의도적 빈 catch [S4].
## 💻 코드 패턴 (Code patterns)
```typescript
// 1) 컨텍스트를 담는 커스텀 에러 (src/core/errors.ts)
export class APICommunicationError extends G1Error {
constructor(message: string, public engine: string, public status?: number, details?: any) {
super(message, details);
}
getTypeCode() { return 'API_COMMUNICATION_ERROR'; }
}
// 2) catch 의 unknown 을 Error 로 정규화 (src/core/services.ts)
catch (error: any) {
lastError = error instanceof Error ? error : new Error(String(error));
}
// 3) 본 흐름을 깨지 않는 의도적 삼킴 (src/memory/index.ts)
try { distillStaleEpisodes(...); }
catch { /* distillation should never break session end */ }
// 4) 보상 트랜잭션 (src/core/transaction.ts)
tx.begin();
try {
await tx.record(filePath); // 변경 전 백업
fs.writeFileSync(filePath, next);
tx.commit(); // 성공 → 백업 폐기
} catch (e) {
tx.rollback(); // 실패 → 원본 복원
throw e;
}
```
## ✅ 검증 상태 및 신뢰도
- **상태:** draft
- **검증 단계:** applied
- **출처 신뢰도:** A
- **신뢰 점수:** 0.93
- **중복 검사 결과:** 신규 생성 (New discovery)
## 🔗 지식 그래프 (Knowledge Graph)
- **상위/루트:** [[TypeScript 기초와 타입 시스템]]
- **관련 개념:** [[동시성 제어 Lock Queue Transaction]], [[비동기 프로그래밍 Promise async await]], [[코딩 컨벤션과 주석 철학]]
- **참조 맥락:** 로컬 LLM 이 실패를 분류·번역·복구하는 코드를 작성할 때 참조.
## 📚 출처 (Sources)
- [S1] ConnectAI/src/core/errors.ts — G1Error 추상 베이스 + 도메인 에러
- [S2] ConnectAI/src/core/errorHandler.ts — ErrorTranslator
- [S3] ConnectAI/src/core/transaction.ts — 보상 트랜잭션
- [S4] ConnectAI/src/memory/index.ts — 의도적 빈 catch
- [S5] ConnectAI/src/core/services.ts — Error 정규화
## 📝 변경 이력 (Change history)
- 2026-06-13: ConnectAI 코드 분석 기반 초안 생성.