From aa6c05e6b57c2087c8cfe7adc9790d17a161c825 Mon Sep 17 00:00:00 2001 From: caadiq Date: Mon, 2 Mar 2026 17:08:35 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20=ED=99=9C=EB=8F=99=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EB=AC=B8=EC=84=9C=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit api.md에 GET /admin/logs 명세 추가, architecture.md에 logs 테이블/파일 추가, development.md에 로그 시스템 가이드 추가, logs.md를 실제 구현 결과에 맞게 갱신. Co-Authored-By: Claude Opus 4.6 --- docs/api.md | 50 ++++++++++++++++++++ docs/architecture.md | 12 ++++- docs/development.md | 28 +++++++++++ docs/logs.md | 109 +++++++++---------------------------------- 4 files changed, 109 insertions(+), 90 deletions(-) diff --git a/docs/api.md b/docs/api.md index 6a0f75d..f60b9a9 100644 --- a/docs/api.md +++ b/docs/api.md @@ -522,6 +522,56 @@ X 일정 저장 --- +## 관리자 - 활동 로그 (인증 필요) + +### GET /admin/logs +활동 로그 목록 조회 + +**Query Parameters:** +- `page` - 페이지 번호 (기본 1) +- `limit` - 페이지당 개수 (기본 50, 최대 100) +- `category` - 카테고리 필터 (콤마 구분: album, schedule, member, bot, category, dict, concert, sync) +- `actor` - 행위자 필터 (admin 또는 bot) +- `search` - summary 텍스트 검색 +- `from` - 시작 날짜 (YYYY-MM-DD) +- `to` - 종료 날짜 (YYYY-MM-DD) + +**응답:** +```json +{ + "logs": [ + { + "id": 1, + "actor": "admin", + "action": "create", + "category": "album", + "target_type": "album", + "target_id": 12, + "summary": "앨범 생성: Unlock My World", + "details": null, + "created_at": "2026-03-02 14:30:00" + } + ], + "total": 150, + "page": 1, + "limit": 50, + "totalPages": 3 +} +``` + +**actor 값:** +- `"admin"` - 관리자 수동 작업 +- `"youtube-{id}"` - YouTube 봇 (예: youtube-3) +- `"x-{id}"` - X 봇 (예: x-1) + +**action 값:** +- `create`, `update`, `delete`, `upload` - CRUD 작업 +- `start`, `stop` - 봇 시작/정지 +- `sync_complete` - 봇 동기화 완료 +- `error` - 봇 동기화 에러 + +--- + ## 헬스 체크 ### GET /health diff --git a/docs/architecture.md b/docs/architecture.md index 6ecb5e9..d7cea7d 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -21,7 +21,8 @@ fromis_9/ │ │ │ │ ├── youtube-bots.js # YouTube 봇 CRUD │ │ │ │ ├── x-bots.js # X 봇 CRUD │ │ │ │ ├── youtube.js # YouTube 일정 관리 -│ │ │ │ └── x.js # X 일정 관리 +│ │ │ │ ├── x.js # X 일정 관리 +│ │ │ │ └── logs.js # 활동 로그 조회 │ │ │ ├── albums/ │ │ │ │ ├── index.js # 앨범 CRUD │ │ │ │ ├── photos.js # 앨범 사진 관리 @@ -46,6 +47,7 @@ fromis_9/ │ │ │ ├── cache.js # Redis 캐시 헬퍼 (SCAN 사용) │ │ │ ├── date.js # 날짜 유틸 (KST 변환) │ │ │ ├── error.js # 에러 응답 헬퍼 +│ │ │ ├── log.js # 활동 로그 유틸 (fire-and-forget) │ │ │ ├── logger.js # 로깅 유틸 │ │ │ └── transaction.js # DB 트랜잭션 래퍼 │ │ ├── app.js # Fastify 앱 설정 @@ -69,6 +71,7 @@ fromis_9/ │ │ │ ├── categories.js │ │ │ ├── stats.js │ │ │ ├── bots.js +│ │ │ ├── logs.js │ │ │ ├── auth.js │ │ │ └── suggestions.js │ │ │ @@ -204,6 +207,8 @@ fromis_9/ │ │ │ │ │ ├── Albums.jsx │ │ │ │ │ ├── AlbumForm.jsx │ │ │ │ │ └── AlbumPhotos.jsx +│ │ │ │ ├── logs/ +│ │ │ │ │ └── Logs.jsx │ │ │ │ └── schedules/ │ │ │ │ ├── Schedules.jsx │ │ │ │ ├── ScheduleForm.jsx @@ -286,7 +291,7 @@ fromis_9/ ## 데이터베이스 -### 테이블 목록 (27개) +### 테이블 목록 (28개) #### 사용자/인증 - `admin_users` - 관리자 계정 @@ -322,6 +327,9 @@ fromis_9/ - `bot_youtube` - YouTube 봇 설정 (채널 정보, 동기화 간격, 필터 등, video_id UNIQUE) - `bot_x` - X 봇 설정 (username, 프로필, 동기화 간격, 텍스트 필터, 리트윗 포함, YouTube 추출) +#### 활동 로그 +- `logs` - 관리자/봇 활동 로그 (actor, action, category, summary 등) + #### 이미지 - `images` - 이미지 메타데이터 (3개 해상도 URL) diff --git a/docs/development.md b/docs/development.md index f11b60e..920171b 100644 --- a/docs/development.md +++ b/docs/development.md @@ -283,6 +283,34 @@ queryClient.invalidateQueries(); --- +## 활동 로그 시스템 + +관리자/봇의 모든 활동을 `logs` 테이블에 기록하고 관리자 페이지에서 조회. + +### 로그 기록 방법 + +```js +import { logActivity } from '../utils/log.js'; + +// fire-and-forget: 로그 실패가 비즈니스 로직에 영향 주지 않음 +logActivity(db, { + actor: 'admin', // "admin" 또는 봇 ID ("youtube-3", "x-1") + action: 'create', // create, update, delete, upload, start, stop, sync_complete, error + category: 'album', // album, schedule, member, bot, category, dict, concert, sync + targetType: 'album', // 대상 타입 (optional) + targetId: 12, // 대상 DB ID (optional) + summary: '앨범 생성: 제목', // 한 줄 요약 + details: { key: 'value' }, // 추가 정보 JSON (optional) +}); +``` + +### 로그 대상 +- **관리자 라우트**: 앨범/일정/멤버/봇/카테고리/사전/콘서트 CRUD +- **봇 스케줄러**: 동기화 완료(addedCount > 0), 동기화 에러 +- **봇 서비스**: YouTube 영상 추가, X 트윗 추가 + +--- + ## 유용한 명령어 ```bash diff --git a/docs/logs.md b/docs/logs.md index bf202e5..d3e8854 100644 --- a/docs/logs.md +++ b/docs/logs.md @@ -9,10 +9,10 @@ ## DB 테이블 -### `activity_logs` +### `logs` ```sql -CREATE TABLE activity_logs ( +CREATE TABLE logs ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, actor VARCHAR(50) NOT NULL, -- "admin" 또는 봇 ID ("youtube-3", "x-1" 등) action VARCHAR(50) NOT NULL, -- create, update, delete, start, stop, sync_complete, error 등 @@ -33,7 +33,7 @@ CREATE TABLE activity_logs ( | 컬럼 | 설명 | 예시 | |------|------|------| | `actor` | 행위자 | `"admin"`, `"youtube-3"`, `"x-1"`, `"meilisearch"` | -| `action` | 행동 유형 | `create`, `update`, `delete`, `upload`, `start`, `stop`, `sync_complete`, `error`, `reorder` | +| `action` | 행동 유형 | `create`, `update`, `delete`, `upload`, `start`, `stop`, `sync_complete`, `error` | | `category` | 대분류 | `album`, `schedule`, `member`, `bot`, `category`, `dict`, `concert`, `sync` | | `target_type` | 대상 타입 | `youtube_schedule`, `x_schedule`, `album`, `photo`, `teaser`, `member`, `youtube_bot`, `x_bot`, `category`, `concert` | | `target_id` | 대상 DB ID | 해당 레코드의 PK | @@ -46,10 +46,12 @@ CREATE TABLE activity_logs ( ### 로그 유틸리티 -**파일:** `backend/src/utils/activityLog.js` +**파일:** `backend/src/utils/log.js` ```javascript -logActivity(db, { actor, action, category, targetType, targetId, summary, details }) +import { logActivity } from '../utils/log.js'; + +logActivity(db, { actor, action, category, targetType, targetId, summary, details }); ``` - fire-and-forget: 로그 실패가 비즈니스 로직에 영향 주지 않도록 try/catch 감싸기 @@ -57,7 +59,7 @@ logActivity(db, { actor, action, category, targetType, targetId, summary, detail ### API 엔드포인트 -**GET /api/admin/activity-logs** — 로그 목록 조회 (인증 필수) +**GET /api/admin/logs** — 로그 목록 조회 (인증 필수) | 파라미터 | 타입 | 기본값 | 설명 | |----------|------|--------|------| @@ -69,29 +71,6 @@ logActivity(db, { actor, action, category, targetType, targetId, summary, detail | `from` | string | - | 시작 날짜 (YYYY-MM-DD) | | `to` | string | - | 종료 날짜 (YYYY-MM-DD) | -**응답:** -```json -{ - "logs": [ - { - "id": 1, - "actor": "admin", - "action": "create", - "category": "schedule", - "target_type": "youtube_schedule", - "target_id": 456, - "summary": "YouTube 일정 생성: fromis_9 영상 제목", - "details": { "videoId": "abc123", "channelName": "채널명" }, - "created_at": "2026-03-01T14:30:00" - } - ], - "total": 1234, - "page": 1, - "limit": 50, - "totalPages": 25 -} -``` - ### 로그 삽입 대상 #### 관리자 수동 작업 @@ -108,7 +87,7 @@ logActivity(db, { actor, action, category, targetType, targetId, summary, detail | `routes/albums/photos.js` | 사진 업로드/삭제 | | `routes/albums/teasers.js` | 티저 삭제 | | `routes/members/index.js` | 멤버 수정 | -| `routes/schedules/index.js` | 일정 삭제, 카테고리 CRUD, 순서변경 | +| `routes/schedules/index.js` | 일정 삭제, 카테고리 CRUD | | `routes/schedules/suggestions.js` | 사전 저장 | #### 봇 자동 작업 @@ -125,73 +104,27 @@ logActivity(db, { actor, action, category, targetType, targetId, summary, detail ## 프론트엔드 구현 +### 파일 구조 + +| 파일 | 내용 | +|------|------| +| `frontend/src/api/admin/logs.js` | API 클라이언트 | +| `frontend/src/pages/pc/admin/logs/Logs.jsx` | 로그 페이지 컴포넌트 | + ### 로그 페이지 **경로:** `/admin/logs` **UI 구성:** -- 필터 바: 카테고리 칩, 행위자 드롭다운, 기간 선택, 텍스트 검색 -- 로그 테이블: 시간, 행위자(아이콘), 액션 뱃지(색상별), summary -- 페이지네이션 +- 필터 바: 카테고리 칩, 행위자 드롭다운(애니메이션), 기간 선택(커스텀 DatePicker), 텍스트 검색(300ms 디바운스) +- 로그 테이블: 시간, 행위자(아이콘), 액션 뱃지(색상별), 카테고리, summary +- 서버 사이드 페이지네이션 (keepPreviousData로 깜빡임 방지) **액션 뱃지 색상:** | 액션 | 색상 | |------|------| | create / upload | 초록 | -| update / reorder | 파랑 | -| delete | 빨강 | +| update | 파랑 | +| delete / error | 빨강 | | sync_complete | 보라 | -| error | 빨강 | | start / stop | 노랑 | - -### 대시보드 메뉴 - -대시보드 menuItems에 활동 로그 항목 추가 (ScrollText 아이콘) - ---- - -## 수정 파일 목록 - -### 신규 생성 (4개) - -| 파일 | 내용 | -|------|------| -| `backend/src/utils/activityLog.js` | logActivity 유틸리티 함수 | -| `backend/src/routes/admin/activity-logs.js` | API 엔드포인트 | -| `frontend/src/api/admin/activityLogs.js` | API 클라이언트 | -| `frontend/src/pages/pc/admin/logs/ActivityLogs.jsx` | 로그 페이지 컴포넌트 | - -### 수정 (백엔드 15개 + 프론트엔드 3개) - -| 파일 | 변경 | -|------|------| -| `backend/src/routes/index.js` | 라우트 등록 | -| `backend/src/routes/admin/youtube.js` | logActivity 호출 | -| `backend/src/routes/admin/x.js` | logActivity 호출 | -| `backend/src/routes/admin/concert.js` | logActivity 호출 | -| `backend/src/routes/admin/youtube-bots.js` | logActivity 호출 | -| `backend/src/routes/admin/x-bots.js` | logActivity 호출 | -| `backend/src/routes/admin/bots.js` | logActivity 호출 | -| `backend/src/routes/albums/index.js` | logActivity 호출 | -| `backend/src/routes/albums/photos.js` | logActivity 호출 | -| `backend/src/routes/albums/teasers.js` | logActivity 호출 | -| `backend/src/routes/members/index.js` | logActivity 호출 | -| `backend/src/routes/schedules/index.js` | logActivity 호출 | -| `backend/src/routes/schedules/suggestions.js` | logActivity 호출 | -| `backend/src/plugins/scheduler.js` | 동기화 로그 | -| `backend/src/services/youtube/index.js` | 영상 추가 로그 | -| `backend/src/services/x/index.js` | 트윗 추가 로그 | -| `frontend/src/api/admin/index.js` | export 추가 | -| `frontend/src/routes/pc/admin/index.jsx` | 라우트 등록 | -| `frontend/src/pages/pc/admin/dashboard/Dashboard.jsx` | 메뉴 추가 | - ---- - -## 구현 순서 - -1. DB 테이블 생성 -2. 백엔드: logActivity 유틸리티 + API 엔드포인트 + 라우트 등록 -3. 백엔드: 각 라우트/서비스에 logActivity 호출 추가 -4. 프론트엔드: API 클라이언트 + 로그 페이지 + 라우트/대시보드 연결 -5. 서버 재빌드 + 테스트 -6. 문서 업데이트