From 950626f06dc3af8ca3b4534cfc37df77bd98c9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Wed, 18 Mar 2026 12:33:32 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[notification]=20API=20=EC=9D=8C?= =?UTF-8?q?=EC=9B=90=20=EC=84=9C=EB=B9=99=20=EB=B0=A9=EC=8B=9D=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=AC=B8=EC=84=9C=20=EC=A0=84=EB=A9=B4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 음원 파일을 API에서 서빙 (React public/ 불필요) - _soundUrls 응답 필드 문서화 - React 구현 요청서: _soundUrls 활용 미리듣기 코드로 변경 - 환경별 음원 URL 테이블 추가 --- features/notification-settings/README.md | 17 ++- plans/notification-sound-react-request.md | 130 +++++++++++++++------- 2 files changed, 98 insertions(+), 49 deletions(-) diff --git a/features/notification-settings/README.md b/features/notification-settings/README.md index b637658..5036f1a 100644 --- a/features/notification-settings/README.md +++ b/features/notification-settings/README.md @@ -252,10 +252,12 @@ export const SOUND_OPTIONS = [ | 위치 | 파일 | 크기 | 상태 | |------|------|------|------| -| `mng/public/sounds/default.wav` | 기본 알림음 | 788KB | ✅ 실제 파일 | +| `api/public/sounds/default.wav` | 기본 알림음 | 788KB | ✅ API에서 서빙 | +| `api/public/sounds/sam_voice.wav` | SAM 보이스 | 788KB | ✅ API에서 서빙 (임시로 기본음 동일) | +| `mng/public/sounds/default.wav` | 기본 알림음 | 788KB | ✅ 실제 파일 (원본) | | `mng/public/sounds/push_notification.wav` | 푸시 알림음 | 788KB | ✅ 실제 파일 | -| `react/public/sounds/default.wav` | 기본 알림음 | 0 bytes | ❌ 빈 placeholder | -| `react/public/sounds/push_notification.wav` | 푸시 알림음 | 0 bytes | ❌ 빈 placeholder | + +> React에는 음원 파일을 두지 않는다. API 서버가 정적 파일로 서빙하며, 응답의 `_soundUrls`로 URL을 제공한다. #### 앱(Android) 알림음 채널 @@ -455,8 +457,9 @@ MNG 관리자가 보내는 수동 푸시 알림 관리. | `soundType` 조회 | ✅ 응답에 `soundType` 포함 | ✅ 기본값 병합 | **완료** | | `soundType` 검증 | ✅ `in:default,sam_voice,mute` | ✅ 타입 정의 | **완료** | | `soundType` UI | — | ✅ 드롭다운 + Play 버튼 | **완료** | +| 음원 파일 서빙 | ✅ `api/public/sounds/` | — | **API에서 서빙** | +| `_soundUrls` 응답 | ✅ GET 응답에 URL 맵 포함 | — | **완료** | | 미리듣기 실제 재생 | — | ❌ Mock (토스트만) | **React 작업 대기** | -| 음원 파일 서빙 | — | ❌ 빈 placeholder | **React 작업 대기** | ### 9.2 API 구현 완료 내용 @@ -479,10 +482,12 @@ NotificationSettingService::getGroupedSettings() > **요청 문서**: `docs/plans/notification-sound-react-request.md` -1. **음원 파일 배치** — `mng/public/sounds/default.wav` → `react/public/sounds/` 복사 -2. **미리듣기 실제 재생** — `playPreviewSound()`에서 `Audio` API 사용 +1. **미리듣기 실제 재생** — `playPreviewSound()`에서 `_soundUrls`의 URL로 `Audio` API 재생 +2. **`_soundUrls` 수신** — `actions.ts`에서 API 응답의 `_soundUrls` 추출 3. **types.ts 주석 정리** — "백엔드 API 수정 필요" 블록 제거 +> 음원 파일은 API 서버에서 서빙한다. React에 파일 배치 불필요. + --- ## 10. 관련 파일 diff --git a/plans/notification-sound-react-request.md b/plans/notification-sound-react-request.md index 11556ff..e33808b 100644 --- a/plans/notification-sound-react-request.md +++ b/plans/notification-sound-react-request.md @@ -9,18 +9,19 @@ ## 1. 개요 -알림설정 페이지에서 사용자가 선택한 알림음(soundType)을 API에 저장하고, 미리듣기 기능을 실제 음원 재생으로 구현하는 작업이다. +알림설정 페이지에서 사용자가 선택한 알림음(soundType)을 API에 저장/반환하도록 구현 완료했다. +React에서는 미리듣기 기능을 실제 음원 재생으로 변경하면 된다. ### 1.1 현재 상태 | 항목 | 상태 | 설명 | |------|------|------| | **API soundType 저장** | ✅ 완료 | `PUT /api/v1/settings/notifications`에서 soundType 저장 | -| **API soundType 반환** | ✅ 완료 | `GET /api/v1/settings/notifications`에서 soundType 반환 | +| **API soundType 반환** | ✅ 완료 | `GET /api/v1/settings/notifications`에서 soundType + 음원 URL 반환 | +| **API 음원 파일 서빙** | ✅ 완료 | `{API_URL}/sounds/default.wav`, `{API_URL}/sounds/sam_voice.wav` | | **React 타입 정의** | ✅ 이미 있음 | `types.ts`에 `SoundType`, `SOUND_OPTIONS` 정의됨 | | **React UI (드롭다운)** | ✅ 이미 있음 | 알림음 선택 Select + Play 버튼 렌더링 중 | | **React 미리듣기** | ❌ Mock | 토스트만 표시, 실제 재생 없음 | -| **음원 파일** | ❌ 빈 파일 | `public/sounds/default.wav` (0 bytes) | --- @@ -28,30 +29,32 @@ ### 2.1 GET 응답 변경 -기존 응답: -```json -{ - "notice": { - "enabled": true, - "notice": { "enabled": true, "email": false }, - "event": { "enabled": true, "email": false } - } -} -``` +각 알림 항목에 `soundType` 필드가 추가되고, 최상위에 `_soundUrls` 맵이 추가되었다. -변경된 응답 (**soundType 추가**): ```json { "notice": { "enabled": true, "notice": { "enabled": true, "email": false, "soundType": "default" }, "event": { "enabled": true, "email": false, "soundType": "sam_voice" } + }, + "approval": { + "enabled": true, + "approvalRequest": { "enabled": true, "email": true, "soundType": "default" }, + "draftApproved": { "enabled": true, "email": false, "soundType": "sam_voice" }, + "draftRejected": { "enabled": true, "email": false, "soundType": "default" }, + "draftCompleted": { "enabled": true, "email": false, "soundType": "mute" } + }, + "_soundUrls": { + "default": "https://api.dev.codebridge-x.com/sounds/default.wav", + "sam_voice": "https://api.dev.codebridge-x.com/sounds/sam_voice.wav" } } ``` > `soundType` 값: `"default"` | `"sam_voice"` | `"mute"` > 미저장 항목의 기본값: `"default"` +> `_soundUrls`는 미리듣기 재생에 사용할 음원 파일의 절대 URL ### 2.2 PUT 요청 @@ -71,27 +74,13 @@ ## 3. React 구현 필요 항목 -### 3.1 음원 파일 배치 - -`public/sounds/` 폴더에 실제 음원 파일 배치 필요: - -| 파일 | 용도 | 소스 | -|------|------|------| -| `public/sounds/default.wav` | 기본 알림음 | `mng/public/sounds/default.wav` 복사 (788KB) | -| `public/sounds/sam_voice.wav` | SAM 보이스 | 별도 제작 필요 (임시로 default.wav 복사 가능) | - -```bash -# MNG에서 기본 알림음 복사 -cp /home/aweso/sam/mng/public/sounds/default.wav /home/aweso/sam/react/public/sounds/default.wav - -# SAM 보이스 임시 배치 (별도 음원 제작 전까지) -cp /home/aweso/sam/mng/public/sounds/default.wav /home/aweso/sam/react/public/sounds/sam_voice.wav -``` - -### 3.2 미리듣기 실제 재생 구현 +### 3.1 미리듣기 실제 재생 구현 **파일**: `src/components/settings/NotificationSettings/index.tsx` +API 응답의 `_soundUrls`를 사용하여 미리듣기를 실제 음원 재생으로 변경한다. +React `public/sounds/`에 파일을 둘 필요 없이, API가 서빙하는 URL을 직접 사용한다. + **현재 코드** (Mock): ```typescript function playPreviewSound(soundType: SoundType) { @@ -104,11 +93,11 @@ function playPreviewSound(soundType: SoundType) { } ``` -**변경 코드** (실제 재생): +**변경 코드** (실제 재생 — `_soundUrls` 활용): ```typescript let previewAudio: HTMLAudioElement | null = null; -function playPreviewSound(soundType: SoundType) { +function playPreviewSound(soundType: SoundType, soundUrls?: Record) { if (soundType === 'mute') { toast.info('무음으로 설정되어 있습니다.'); return; @@ -120,8 +109,14 @@ function playPreviewSound(soundType: SoundType) { previewAudio = null; } - const soundFile = soundType === 'sam_voice' ? 'sam_voice.wav' : 'default.wav'; - previewAudio = new Audio(`/sounds/${soundFile}`); + const url = soundUrls?.[soundType]; + if (!url) { + const soundName = soundType === 'default' ? '기본 알림음' : 'SAM 보이스'; + toast.info(`${soundName} 미리듣기`); + return; + } + + previewAudio = new Audio(url); previewAudio.play().catch(() => { const soundName = soundType === 'default' ? '기본 알림음' : 'SAM 보이스'; toast.info(`${soundName} 미리듣기`); @@ -129,17 +124,51 @@ function playPreviewSound(soundType: SoundType) { } ``` +**호출 시**: +- API 응답에서 `_soundUrls`를 state로 보관 +- Play 버튼 클릭 시 `playPreviewSound(item.soundType, soundUrls)` 호출 + +### 3.2 _soundUrls 수신 처리 + +**파일**: `src/components/settings/NotificationSettings/actions.ts` + +`transformApiToFrontend()` 함수에서 `_soundUrls`를 별도로 추출하여 반환한다. + +```typescript +// API 응답에서 _soundUrls 추출 +export async function getNotificationSettings(): Promise<{ + success: boolean; + data: NotificationSettings; + soundUrls?: Record; + error?: string; +}> { + const result = await executeServerAction({ + url: `${API_URL}/api/v1/settings/notifications`, + transform: (data: Record) => { + const soundUrls = data._soundUrls as Record | undefined; + // _soundUrls는 그룹 데이터가 아니므로 제거 후 변환 + const { _soundUrls, ...groupData } = data; + return { settings: transformApiToFrontend(groupData), soundUrls }; + }, + errorMessage: '알림 설정 조회에 실패했습니다.', + }); + + // ... 기존 에러 처리 +} +``` + ### 3.3 types.ts 주석 업데이트 **파일**: `src/components/settings/NotificationSettings/types.ts` -상단 주석의 "백엔드 API 수정 필요 사항" 블록을 제거하거나 완료 표시로 변경: +상단 주석의 "백엔드 API 수정 필요 사항" 블록을 완료 표시로 변경: ```typescript /** * 알림 설정 타입 정의 * * API 응답 구조: { enabled, email, soundType } per item * soundType: 'default' | 'sam_voice' | 'mute' + * _soundUrls: { default: URL, sam_voice: URL } — 미리듣기용 음원 URL * * [2026-03-18] soundType API 연동 완료 */ @@ -147,28 +176,43 @@ function playPreviewSound(soundType: SoundType) { --- -## 4. 동작 확인 체크리스트 +## 4. 음원 파일 접근 URL + +API 서버에서 정적 파일로 서빙한다. React에 파일을 둘 필요 없다. + +| 환경 | 기본 알림음 | SAM 보이스 | +|------|-----------|-----------| +| 개발 서버 | `https://api.dev.codebridge-x.com/sounds/default.wav` | `https://api.dev.codebridge-x.com/sounds/sam_voice.wav` | +| 운영 서버 | `https://api.codebridge-x.com/sounds/default.wav` | `https://api.codebridge-x.com/sounds/sam_voice.wav` | +| 로컬 | `http://api.sam.kr/sounds/default.wav` | `http://api.sam.kr/sounds/sam_voice.wav` | + +> `sam_voice.wav`는 현재 `default.wav`와 동일한 파일이다. 별도 음원이 준비되면 API에서 교체한다. + +--- + +## 5. 동작 확인 체크리스트 - [ ] `GET /api/v1/settings/notifications` 응답에 `soundType` 포함 확인 +- [ ] `GET /api/v1/settings/notifications` 응답에 `_soundUrls` 포함 확인 +- [ ] `_soundUrls.default` URL 브라우저에서 직접 열어 소리 재생 확인 - [ ] 알림음 변경 후 저장 → 새로고침 시 선택값 유지 확인 - [ ] 기본 알림음 미리듣기(Play 버튼) → 실제 소리 재생 - [ ] SAM 보이스 미리듣기 → 실제 소리 재생 - [ ] 무음 미리듣기 → "무음으로 설정되어 있습니다" 토스트 -- [ ] `public/sounds/default.wav` 파일 크기 확인 (0이 아닌 실제 파일) --- -## 5. 관련 파일 +## 6. 관련 파일 | 프로젝트 | 파일 | 변경 여부 | |---------|------|----------| | API | `app/Services/NotificationSettingService.php` | ✅ 완료 | | API | `app/Http/Requests/NotificationSetting/UpdateGroupedSettingRequest.php` | ✅ 완료 | -| React | `public/sounds/default.wav` | 🔴 교체 필요 (0 → 788KB) | -| React | `public/sounds/sam_voice.wav` | 🔴 신규 추가 | +| API | `public/sounds/default.wav` (788KB) | ✅ 배포 완료 | +| API | `public/sounds/sam_voice.wav` (788KB) | ✅ 배포 완료 | | React | `src/components/settings/NotificationSettings/index.tsx` | 🔴 수정 필요 | +| React | `src/components/settings/NotificationSettings/actions.ts` | 🔴 수정 필요 (`_soundUrls` 추출) | | React | `src/components/settings/NotificationSettings/types.ts` | 🟡 주석 정리 | -| MNG | `public/sounds/default.wav` | 참조용 (음원 소스) | ---