# Kord - 에러 안내 기능 기획 (Error Guidance UX) ## 체인지로그 (Changelog) - **2026-03-27**: 최초 기획서 작성 - **2026-03-27**: 결정 사항 확정 — 에러 코드 사용자 비노출, 서버 로그 전용 상세정보 --- ## 1. 개요 사용자가 봇 인터랙션(슬래시 명령어, 셀렉트 메뉴, 모달 등)을 사용하다가 문제가 발생했을 때, **일관되고 친절한 에러 메시지**를 통해 상황을 안내하고 **재시도 또는 관리자 문의**를 유도하는 시스템입니다. ### 현재 문제 (As-Is) | 문제 | 예시 | |------|------| | 에러 메시지가 하드코딩되어 있음 | `'There was an error while executing this command!'` | | 에러 유형 구분 없이 동일한 메시지 | DB 에러든, 권한 에러든, 사용자 입력 오류든 같은 문구 | | 사용자 행동 유도 없음 | 재시도 버튼, 관리자 알림 등 후속 액션 부재 | | 에러 추적 불가 | 에러 코드나 참조 ID 없음 | | 일부 에러가 조용히 무시됨 | `.catch(() => {})` 패턴으로 유실 | ### 목표 (To-Be) - **에러 유형 분류** 체계화 - **사용자 친화적 Embed** 기반 에러 메시지 - **에러 코드** 부여로 추적성 확보 - **액션 버튼** (재시도 안내, 관리자 문의 유도) - 향후 **다국어(i18n)** 시스템과 자연스럽게 통합할 수 있는 구조 --- ## 2. 에러 유형 분류 (Error Categories) 에러를 발생 원인별로 4단계로 분류합니다. | 카테고리 | 코드 접두사 | 설명 | 사용자 안내 방향 | |----------|-------------|------|------------------| | `USER_INPUT` | `E1xxx` | 잘못된 입력값 (범위 초과, 형식 오류 등) | 올바른 입력 예시 안내, 재입력 유도 | | `PERMISSION` | `E2xxx` | 봇 또는 사용자 권한 부족 | 필요 권한 안내, 서버 관리자 문의 유도 | | `BOT_INTERNAL` | `E3xxx` | 봇 내부 오류 (DB, Redis, 로직 오류) | 잠시 후 재시도 안내, 지속 시 관리자 문의 | | `DISCORD_API` | `E4xxx` | Discord API 오류 (Rate Limit, 서비스 장애 등) | 잠시 후 재시도, Discord 상태 확인 안내 | ### 주요 에러 코드 예시 ``` E1001 유효하지 않은 사용자 한도 값 (User Limit) E1002 채널 이름 형식 오류 E2001 봇에 Manage Channels 권한 없음 E2002 사용자에게 관리자 권한 없음 E2003 채널 소유자만 사용 가능 E3001 데이터베이스 연결/쿼리 실패 E3002 캐시(Redis) 연결 실패 E4001 Discord API Rate Limit E4002 Discord API 50013 (Missing Permissions) E4003 Discord API 일시적 오류 ``` > 에러 코드 체계는 확정이 아니며, 구현 과정에서 필요에 따라 추가·변경됩니다. --- ## 3. 에러 응답 포맷 (Error Response Design) ### 3-1. Embed 기반 에러 메시지 모든 에러 메시지는 **Ephemeral Embed** 형태로 전송합니다. 본인만 보이므로 채널을 어지럽히지 않습니다. ``` ┌─────────────────────────────────────────┐ │ ❌ 작업을 완료할 수 없습니다 │ ← Title (에러 유형별 다름) │ │ │ 채널 이름을 변경할 권한이 부족합니다. │ ← Description (사용자 친화적 설명) │ │ │ 💡 해결 방법 │ ← Field: Resolution │ 서버 관리자에게 봇의 '채널 관리' │ │ 권한을 확인해달라고 요청하세요. │ │ │ └─────────────────────────────────────────┘ ``` > [!IMPORTANT] > **에러 코드(`E2001` 등)는 사용자에게 노출하지 않습니다.** > 에러 코드와 스택 트레이스는 **서버 콘솔 로그에만** 기록되며, 봇 개발자/관리자가 직접 확인합니다. > 사용자에게는 친절한 안내 메시지와 해결 방법만 표시합니다. ### 3-2. 에러 유형별 색상 & 아이콘 | 카테고리 | Embed 색상 | 아이콘 | Title | |----------|-----------|--------|-------| | `USER_INPUT` | `#FFA500` (Orange) | ⚠️ | 입력을 확인해주세요 | | `PERMISSION` | `#FF6B6B` (Red) | 🔒 | 권한이 부족합니다 | | `BOT_INTERNAL` | `#FF4444` (Dark Red) | ❌ | 문제가 발생했습니다 | | `DISCORD_API` | `#7289DA` (Discord Blurple) | 🔄 | 일시적인 문제입니다 | ### 3-3. 에러 메시지 구성 요소 모든 에러 Embed는 아래 구성 요소를 포함합니다: 1. **Title** — 에러 유형에 따른 요약 제목 2. **Description** — 사용자가 이해할 수 있는 구체적 설명 3. **Field: Resolution** — 💡 해결 방법 또는 다음 행동 안내 4. **(선택) Timestamp** — 에러 발생 시각 > 에러 코드는 서버 로그에만 기록됩니다. (e.g. `[ERROR] E2001: Missing Manage Channels permission in guild 123456`) --- ## 4. 기술 설계 (Technical Design) ### 4-1. 핵심 모듈: `BotError` 클래스 + `ErrorReporter` 유틸리티 ``` src/ ├── errors/ │ ├── BotError.ts ← 커스텀 에러 클래스 │ ├── ErrorCodes.ts ← 에러 코드 상수 정의 │ └── ErrorReporter.ts ← Embed 생성 및 응답 전송 유틸리티 ``` #### `BotError` 클래스 ```typescript // 개념 설계 (Conceptual) class BotError extends Error { code: string; // 'E2001' category: ErrorCategory; // 'PERMISSION' userMessage: string; // 사용자에게 보여줄 메시지 resolution?: string; // 💡 해결 방법 } ``` #### `ErrorReporter` 유틸리티 ```typescript // 개념 설계 (Conceptual) class ErrorReporter { // BotError를 받아 Embed를 생성하고 인터랙션에 응답 static async report(interaction, error: BotError): Promise; // 알 수 없는 에러를 BotError로 래핑 static wrap(error: unknown): BotError; } ``` ### 4-2. 사용 패턴 (Usage Pattern) **Before (현재)** ```typescript } catch (error) { logger.error('Button action error', error); if (!interaction.replied) await interaction.reply({ content: 'Failed to execute action.', ephemeral: true }); } ``` **After (개선)** ```typescript } catch (error) { await ErrorReporter.report(interaction, ErrorReporter.wrap(error)); } ``` > `ErrorReporter.wrap()`는 알려진 Discord API 에러 코드(50013 등)를 자동 매핑하고, > 알 수 없는 에러는 `BOT_INTERNAL` 카테고리의 제네릭 에러로 래핑합니다. ### 4-3. 공통 에러 핸들러 래퍼 반복되는 try-catch 패턴을 줄이기 위해 **래퍼 함수**를 도입합니다. ```typescript // 개념 설계 (Conceptual) async function withErrorHandler( interaction: Interaction, fn: () => Promise ): Promise { try { await fn(); } catch (error) { await ErrorReporter.report(interaction, ErrorReporter.wrap(error)); } } ``` ### 4-4. i18n 통합 준비 에러 메시지는 현재 영어 하드코딩으로 시작하되, 향후 i18n 전환을 위해: - 모든 사용자 표시 문자열을 `ErrorCodes.ts`에 **키-값 형태**로 집중 관리 - i18n 구현 시 이 키를 locale 파일의 키로 1:1 매핑하면 전환 완료 --- ## 5. 적용 범위 ### Phase 1 — 인프라 구축 - `BotError` 클래스, `ErrorCodes`, `ErrorReporter` 모듈 생성 - `withErrorHandler` 래퍼 함수 구현 ### Phase 2 — 기존 코드 마이그레이션 - `interactionCreate.ts` 전체 에러 처리 교체 - `voiceSetup.ts` 커맨드 에러 처리 교체 - `VoiceService.ts` 에러 처리 개선 (조용한 실패 → 로깅 + 에러 보고) ### Phase 3 — 테스트 작성 - `BotError` 클래스 유닛 테스트 - `ErrorReporter.wrap()` 에러 매핑 유닛 테스트 - `ErrorReporter.report()` Embed 생성 유닛 테스트 --- ## 6. 검증 계획 (Verification Plan) ### 자동 테스트 (Automated Tests) 1. **BotError 유닛 테스트** — `tests/errors/BotError.test.ts` - BotError 생성, 프로퍼티 검증 - 실행 명령어: `yarn test -- --testPathPattern='tests/errors'` 2. **ErrorReporter 유닛 테스트** — `tests/errors/ErrorReporter.test.ts` - `wrap()`: Discord API 에러(50013 등) 자동 매핑 검증 - `wrap()`: 알 수 없는 에러 → BOT_INTERNAL 래핑 검증 - `report()`: interaction.reply/followUp 호출 검증 (mock) - 실행 명령어: `yarn test -- --testPathPattern='tests/errors'` ### 수동 테스트 (Manual Tests) > Discord 봇 인터랙션은 자동화 테스트의 한계가 있으므로 사용자 수동 확인이 필요합니다. 1. **봇을 개발 서버에서 실행** (`yarn run dev`) 2. **정상 동작 확인**: `/voice-setup create` 명령어가 정상적으로 작동하는지 확인 3. **에러 유발 테스트**: - 봇에서 Manage Channels 권한을 제거한 뒤 `/voice-setup create` 실행 → 권한 에러 Embed 확인 - 임시 음성 채널에서 User Limit에 `abc` 입력 → 입력 오류 Embed 확인 4. **Embed 디자인 확인**: 에러 메시지에 올바른 색상, 아이콘, 에러 코드가 표시되는지 확인 --- ## 7. 결정 사항 (Decisions — 확정됨) 1. ✅ **에러 코드 비노출** — 사용자에게는 친절한 메시지만 표시. 에러 코드는 서버 로그에만 기록. 2. ✅ **감사 채널 후순위** — 감사 채널 기능 구현 이후 연동. 현재는 서버 콘솔 로그만. 3. ✅ **스택 트레이스 콘솔 전용** — 사용자 Embed에는 항상 친절한 메시지만, 서버 콘솔에만 상세 정보 기록.