--- 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", "astraai"] raw_sources: ["AstraAI/src/core/errors.ts", "AstraAI/src/core/errorHandler.ts", "AstraAI/src/core/transaction.ts", "AstraAI/src/memory/index.ts", "AstraAI/src/core/services.ts"] applied_in: ["AstraAI"] github_commit: "" --- # [[에러 처리와 커스텀 에러]] ## 🎯 한 줄 통찰 (One-line insight) 견고한 코드는 "실패를 *예측해서 분류하고*, 사용자에게는 친절히 번역하며, 부가 작업의 실패가 본류를 망가뜨리지 않게" 만든다 — AstraAI 는 커스텀 에러 계층 + 사용자 친화 번역기 + "절대 본 흐름을 깨지 않는 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 는 기본적으로 위험:** AstraAI 는 "본 흐름을 깨지 않아야 하는 부가 작업"에 한해 의도 주석과 함께만 허용한다. 검증·핵심 로직의 실패는 절대 조용히 삼키지 않는다. - **에러 메시지 키워드 매칭의 취약성:** `ErrorTranslator` 는 메시지 문자열에 의존하므로, 라이브러리가 메시지를 바꾸면 매칭이 깨질 수 있다. 가능하면 `getTypeCode()` 같은 안정 식별자 기반 분기가 더 견고하다. ## 🛠️ 적용 사례 (Applied in summary) - `AstraAI/src/core/errors.ts` — G1Error 추상 베이스 + 4개 도메인 에러 [S1]. - `AstraAI/src/core/errorHandler.ts` — ErrorTranslator 사용자 친화 번역 [S2]. - `AstraAI/src/core/transaction.ts` — begin/record/commit/rollback 보상 트랜잭션 [S3]. - `AstraAI/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] AstraAI/src/core/errors.ts — G1Error 추상 베이스 + 도메인 에러 - [S2] AstraAI/src/core/errorHandler.ts — ErrorTranslator - [S3] AstraAI/src/core/transaction.ts — 보상 트랜잭션 - [S4] AstraAI/src/memory/index.ts — 의도적 빈 catch - [S5] AstraAI/src/core/services.ts — Error 정규화 ## 📝 변경 이력 (Change history) - 2026-06-13: AstraAI 코드 분석 기반 초안 생성.