feat: Add Audit Channel Plan and update the Feature Roadmap.
This commit is contained in:
parent
b785a276f8
commit
f6feb9b83e
|
|
@ -0,0 +1,215 @@
|
|||
# 감사 채널 기획서 (Audit Log Channel Plan)
|
||||
|
||||
## 체인지로그 (Changelog)
|
||||
- **2026-03-27**: 최초 작성
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요 (Overview)
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목표** | 관리자가 지정한 텍스트 채널에 봇의 주요 이벤트·문제 상황·서비스 상태 변동을 자동으로 기록 |
|
||||
| **등록 방식** | 슬래시 명령어 (`/audit-channel set`, `/audit-channel clear`) |
|
||||
| **대상** | 서버(Guild) 관리자 전용 |
|
||||
| **응답 형태** | 지정 채널로 Embed 전송 (로그 메시지) |
|
||||
|
||||
### 설계 원칙
|
||||
|
||||
- **단일 채널 원칙**: 서버당 최대 1개의 감사 채널 지정 (명확한 운영 동선)
|
||||
- **Severity 분류**: `INFO` / `WARN` / `ERROR` 3단계 심각도로 로그를 구분하여 필요 시 채널을 분리 운영 가능한 여지 확보
|
||||
- **Category 필터링**: 기능(예: VOICE, PERMISSION 등)별로 카테고리를 나누어, 관리자가 특정 주제의 알림을 선택적으로 비활성화(Mute)할 수 있는 플래그 제공
|
||||
- **비동기 큐 처리**: 대량 이벤트 발생 시 Rate Limit 초과를 방지하기 위해 Promise 큐 또는 쓰로틀링 적용
|
||||
- **조용한 실패 (Silent Fail)**: 감사 채널 전송 실패가 메인 기능을 중단시키지 않도록 격리
|
||||
|
||||
---
|
||||
|
||||
## 2. 로그 기록 대상 (Log Event Catalog)
|
||||
|
||||
> 아래는 초기 구현 대상 이벤트이며, 향후 기능 추가 시 이 테이블에 **행을 추가**하여 확장합니다.
|
||||
|
||||
| 카테고리 | Severity | 이벤트 트리거 | 설명 |
|
||||
| :---: | :---: | :--- | :--- |
|
||||
| **SYSTEM** | **INFO** | 봇 온라인 (`ready`) | 봇이 시작·재시작된 시점 |
|
||||
| **VOICE** | **INFO** | 임시 채널 생성 | 생성기 채널 입장으로 임시 음성 채널 생성 |
|
||||
| **VOICE** | **INFO** | 임시 채널 삭제 | 조건 충족으로 임시 음성 채널 삭제 |
|
||||
| **PERMISSION**| **WARN** | 권한 오버라이드 감지 | `/audit-permissions` 실행 시 ⚠️ 항목 발생 |
|
||||
| **INVITE** | **WARN** | 초대 추적 실패 | 초대 정보를 불러올 수 없어 추적 중단 |
|
||||
| **PERMISSION**| **ERROR** | 권한 부족으로 기능 실패 | `PermissionDenied` 에러 발생 시 |
|
||||
| **MIMIC** | **ERROR** | 웹훅 생성/전송 실패 | Mimic 기능 수행 불가 |
|
||||
| **SYSTEM** | **ERROR** | DB 연결 오류 | Prisma 쿼리 실패 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 등록 흐름 (Registration Flow)
|
||||
|
||||
### 3.1. 채널 설정 (`/audit-channel set`)
|
||||
|
||||
```
|
||||
1. 명령어 실행: /audit-channel set channel:#로그채널
|
||||
2. 권한 검증: 봇이 대상 채널에 Send Messages + Embed Links 보유 여부 확인
|
||||
├─ 권한 없음 → ❌ "봇에게 해당 채널의 Send Messages 권한을 부여해주세요." (Ephemeral)
|
||||
└─ 권한 있음 → DB UpsertC (AuditChannel 생성/갱신) → ✅ 확인 Embed 전송
|
||||
3. 확인 메시지: 설정된 채널에 INFO 레벨 테스트 로그 1건 발송
|
||||
```
|
||||
|
||||
### 3.2. 채널 해제 (`/audit-channel clear`)
|
||||
|
||||
```
|
||||
1. 명령어 실행: /audit-channel clear
|
||||
2. DB에서 guildId 기준 AuditChannel 레코드 삭제
|
||||
3. Ephemeral: "감사 채널 설정이 해제되었습니다."
|
||||
```
|
||||
|
||||
### 3.3. 현재 설정 확인 (`/audit-channel status`)
|
||||
|
||||
```
|
||||
1. DB에서 현재 guildId의 AuditChannel 조회
|
||||
2. 설정된 경우: "현재 감사 채널: #채널명" (채널 멘션 포함)
|
||||
3. 미설정 경우: "설정된 감사 채널이 없습니다."
|
||||
```
|
||||
|
||||
### 3.4. 카테고리 필터 설정 (`/audit-channel filter`)
|
||||
|
||||
```
|
||||
1. 명령어 실행: /audit-channel filter category:VOICE state:Disable
|
||||
2. DB의 AuditChannel에서 disabledCategories 배열에 'VOICE' 추가 또는 제거
|
||||
3. Ephemeral: "VOICE 카테고리의 감사 로그 수신이 비활성화되었습니다."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. UI 설계 (Embed 구성)
|
||||
|
||||
### 로그 Embed 형식
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ [Kord Audit Log] │
|
||||
│ 🔴 ERROR · 2026-03-27 15:30:22 KST │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 권한 부족으로 임시 채널 삭제 실패 │
|
||||
│ │
|
||||
│ 채널: #temp-gaming-🎮 │
|
||||
│ 사유: Manage Channels 권한 없음 │
|
||||
│ 코드: ERR_PERM_001 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
| Severity | 색상 (Embed Color) | 아이콘 |
|
||||
|:---:|---|---|
|
||||
| INFO | `#5865F2` (Discord Blurple) | 🔵 |
|
||||
| WARN | `#FEE75C` (Discord Yellow) | 🟡 |
|
||||
| ERROR | `#ED4245` (Discord Red) | 🔴 |
|
||||
|
||||
### Embed 공통 필드
|
||||
|
||||
| 필드 | 내용 |
|
||||
|------|------|
|
||||
| `title` | `[Kord Audit Log - 카테고리명]` |
|
||||
| `description` | 이벤트 요약 메시지 |
|
||||
| `color` | Severity에 따른 색상 |
|
||||
| `timestamp` | 이벤트 발생 시각 |
|
||||
| `footer` | Severity 아이콘 + 레벨 텍스트 (예: `🔴 ERROR`) |
|
||||
| `fields` | 이벤트별 상황 정보 (채널명, 에러 코드 등 선택적 추가) |
|
||||
|
||||
---
|
||||
|
||||
## 5. DB 스키마 설계 (Database Schema)
|
||||
|
||||
### `AuditChannel` 모델
|
||||
|
||||
```prisma
|
||||
model AuditChannel {
|
||||
guildId String @id // 서버당 1개 보장
|
||||
channelId String // 전송 대상 채널 ID
|
||||
disabledCategories String[] @default([]) // 전송을 무시할 로그 카테고리 목록 (예: ["VOICE", "INVITE"])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> `guildId`를 `@id`로 설정하여 **서버당 1개의 감사 채널**만 유지합니다. 채널 변경 시 Upsert로 처리합니다.
|
||||
|
||||
---
|
||||
|
||||
## 6. 기술 설계 (Technical Design)
|
||||
|
||||
### 서비스 구조
|
||||
|
||||
```typescript
|
||||
// src/services/AuditLogService.ts
|
||||
|
||||
export type AuditSeverity = 'INFO' | 'WARN' | 'ERROR';
|
||||
export type AuditCategory = 'SYSTEM' | 'VOICE' | 'PERMISSION' | 'INVITE' | 'MIMIC';
|
||||
|
||||
export interface AuditLogPayload {
|
||||
category: AuditCategory;
|
||||
severity: AuditSeverity;
|
||||
title: string;
|
||||
description: string;
|
||||
fields?: { name: string; value: string; inline?: boolean }[];
|
||||
errorCode?: string; // 에러 추적용 코드 (옵션)
|
||||
}
|
||||
|
||||
class AuditLogService {
|
||||
// 로그 Embed 전송 (payload.category가 disabledCategories에 포함 시 무시)
|
||||
async log(guild: Guild, payload: AuditLogPayload): Promise<void>;
|
||||
|
||||
// 채널 설정 (Upsert)
|
||||
async setChannel(guildId: string, channelId: string): Promise<void>;
|
||||
|
||||
// 채널 설정 해제
|
||||
async clearChannel(guildId: string): Promise<void>;
|
||||
|
||||
// 현재 채널 조회 및 필터(disabledCategories) 업데이트 제공
|
||||
async getChannel(guildId: string): Promise<AuditChannel | null>;
|
||||
}
|
||||
```
|
||||
|
||||
### Rate Limit 대응 전략
|
||||
|
||||
대량 이벤트 발생 (예: 서버 재시작, 권한 일괄 감지)으로 Discord Rate Limit(`429 Too Many Requests`)에 노출될 수 있습니다.
|
||||
|
||||
- **Phase 1 (기본)**: 각 로그를 독립된 `try/catch`로 격리 → 전송 실패 시 서버 콘솔 로그만 기록
|
||||
- **Phase 2 (옵션)**: 제한 시간 내 동일 Severity 로그를 묶어 1건의 Embed로 병합하는 배치 큐 도입
|
||||
|
||||
### 기존 에러 핸들러와의 연동
|
||||
|
||||
`src/errors/` 의 에러 핸들링 유틸리티에서 `AuditLogService.log()`를 callsite에서 호출하는 방식으로 연동합니다.
|
||||
|
||||
```typescript
|
||||
// 예시: 에러 발생 지점에서 감사 로그 전송
|
||||
await auditLogService.log(guild, {
|
||||
category: 'PERMISSION',
|
||||
severity: 'ERROR',
|
||||
title: '권한 부족으로 기능 실패',
|
||||
description: '임시 음성 채널 삭제 불가',
|
||||
fields: [{ name: '사유', value: 'Missing Permissions: MANAGE_CHANNELS' }],
|
||||
errorCode: 'ERR_PERM_001',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 구현 단계 (Phased Implementation)
|
||||
|
||||
| 단계 | 내용 | 세부 작업 |
|
||||
|------|------|-----------|
|
||||
| **Phase 1** | 인프라 구축 | `AuditChannel` Prisma 모델 추가 + 마이그레이션 |
|
||||
| **Phase 1** | 서비스 구현 | `AuditLogService` 구현 (log / setChannel / clearChannel / getChannel) |
|
||||
| **Phase 1** | 명령어 구현 | `/audit-channel set \| clear \| status \| filter` 슬래시 커맨드 |
|
||||
| **Phase 2** | 이벤트 연동 | 각 서비스(VoiceService 등)의 주요 이벤트 발생 시점에 `auditLogService.log()` 호출 추가 |
|
||||
| **Phase 2** | Rate Limit 대응 | 배치 큐 또는 쓰로틀링 로직 도입 (필요 시) |
|
||||
| **Phase 3** | i18n 연동 | 로그 메시지 텍스트를 i18n 키로 전환 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 관련 문서 (References)
|
||||
|
||||
| 문서 | 링크 |
|
||||
|------|------|
|
||||
| 기능 로드맵 | [`Feature_Roadmap.md`](./Feature_Roadmap.md) |
|
||||
| 권한 감사 기획서 | [`Permission_Audit_Plan.md`](./Permission_Audit_Plan.md) |
|
||||
| 에러 안내 기획서 | `Docs/Plans/Error_Guidance_Plan.md` |
|
||||
|
|
@ -57,14 +57,14 @@
|
|||
|
||||
---
|
||||
|
||||
### 3. ⬜ 감사 채널 (Audit Log Channel)
|
||||
### 3. 📝 감사 채널 (Audit Log Channel)
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **목표** | 관리자가 지정한 텍스트 채널에 봇의 이벤트·문제 상황·서비스 상태 변동을 자동 기록 |
|
||||
| **등록 방식** | 관리자가 명령어로 채널 생성 또는 기존 채널 등록 |
|
||||
| **기록 대상** | 에러 발생, 봇 재시작, 기능 status 변동, 권한 이슈 감지 등 |
|
||||
| **기획서** | `Docs/Plans/Audit_Channel_Plan.md` *(미작성)* |
|
||||
| **기획서** | [`Audit_Channel_Plan.md`](./Audit_Channel_Plan.md) |
|
||||
|
||||
**핵심 고려사항**
|
||||
- 감사 로그 메시지 분류 체계 (severity: info / warn / error)
|
||||
|
|
|
|||
Loading…
Reference in New Issue