Kord/Docs/Plans/Error_Guidance_Plan.md

9.5 KiB

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 클래스

// 개념 설계 (Conceptual)
class BotError extends Error {
  code: string;          // 'E2001'
  category: ErrorCategory;  // 'PERMISSION'
  userMessage: string;   // 사용자에게 보여줄 메시지
  resolution?: string;   // 💡 해결 방법
}

ErrorReporter 유틸리티

// 개념 설계 (Conceptual)
class ErrorReporter {
  // BotError를 받아 Embed를 생성하고 인터랙션에 응답
  static async report(interaction, error: BotError): Promise<void>;

  // 알 수 없는 에러를 BotError로 래핑
  static wrap(error: unknown): BotError;
}

4-2. 사용 패턴 (Usage Pattern)

Before (현재)

} catch (error) {
  logger.error('Button action error', error);
  if (!interaction.replied) await interaction.reply({ content: 'Failed to execute action.', ephemeral: true });
}

After (개선)

} catch (error) {
  await ErrorReporter.report(interaction, ErrorReporter.wrap(error));
}

ErrorReporter.wrap()는 알려진 Discord API 에러 코드(50013 등)를 자동 매핑하고, 알 수 없는 에러는 BOT_INTERNAL 카테고리의 제네릭 에러로 래핑합니다.

4-3. 공통 에러 핸들러 래퍼

반복되는 try-catch 패턴을 줄이기 위해 래퍼 함수를 도입합니다.

// 개념 설계 (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에는 항상 친절한 메시지만, 서버 콘솔에만 상세 정보 기록.