243 lines
9.5 KiB
Markdown
243 lines
9.5 KiB
Markdown
# 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, 로직 오류 등) | 잠시 후 재시도 안내, 지속 시 관리자 문의 |
|
|
| `DISCORD_API` | `E4xxx` | Discord API 오류 (Rate Limit, 서비스 장애 등) | 잠시 후 재시도, Discord 상태 확인 안내 |
|
|
|
|
### 주요 에러 코드 예시
|
|
|
|
```
|
|
E1001 유효하지 않은 사용자 한도 값 (User Limit)
|
|
E1002 채널 이름 형식 오류
|
|
E2001 봇에 Manage Channels 권한 없음
|
|
E2002 사용자에게 관리자 권한 없음
|
|
E2003 채널 소유자만 사용 가능
|
|
E3001 데이터베이스 연결/쿼리 실패
|
|
E3002 내부 캐시/상태 계층 오류
|
|
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<void>;
|
|
|
|
// 알 수 없는 에러를 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<void>
|
|
): Promise<void> {
|
|
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에는 항상 친절한 메시지만, 서버 콘솔에만 상세 정보 기록.
|