feat: [notification] 알림 사운드 추가 + 설정 개선
- default.wav, sam_voice.wav 알림 사운드 추가 - 알림 설정 UI 개선 - 테넌트 모듈 분리 계획/멀티테넌트 문서 업데이트
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
# 동적 멀티테넌트 페이지 시스템 설계
|
||||
|
||||
> 작성일: 2026-03-11
|
||||
> 상태: 초안 (백엔드 논의 필요)
|
||||
> 최종 업데이트: 2026-03-18
|
||||
> 상태: 초안 (백엔드 논의 진행 중)
|
||||
> 관련 문서:
|
||||
> - `[VISION-2026-02-19] dynamic-rendering-platform-strategy.md`
|
||||
> - `[PLAN-2026-02-06] multi-tenancy-optimization-roadmap.md`
|
||||
> - `[DESIGN-2026-02-11] dynamic-field-type-extension.md`
|
||||
> - `[ANALYSIS-2026-03-17] tenant-module-separation-dependency-audit.md`
|
||||
> - `[PLAN-2026-03-17] tenant-module-separation-plan.md` — **본 설계의 실행 계획 (Phase 0~3)**
|
||||
|
||||
---
|
||||
|
||||
@@ -761,9 +764,61 @@ DynamicItemForm의 ComputedField → computed 타입으로 범용화
|
||||
|
||||
### 규칙 17: 점진적 마이그레이션 전략
|
||||
|
||||
#### 17-1. 3단계 아키텍처 방향 (2026-03-17 확인)
|
||||
|
||||
```
|
||||
1단계: 현재 → 모듈 분리
|
||||
- 공통 ERP / 테넌트별 모듈 물리적 분리
|
||||
- 선결과제 해소 (아래 17-2 참조)
|
||||
|
||||
2단계: 모듈 분리 → JSON 동적 조립
|
||||
- 테넌트 모듈을 manifest/JSON 기반으로 전환
|
||||
- 동적 페이지 렌더러 도입
|
||||
|
||||
3단계: 최종 — 빈 페이지 셸 + 백엔드 JSON으로 페이지 자동 조립
|
||||
- 이 문서의 최종 목표
|
||||
```
|
||||
|
||||
#### 17-2. 선결과제 (모듈 분리 전 해결 필수)
|
||||
|
||||
| # | 과제 | 내용 | 예상 |
|
||||
|---|------|------|------|
|
||||
| 1 | CEO 대시보드 테넌트 의존성 해소 | 생산/건설 섹션 직접 import → 동적 로딩 전환 | - |
|
||||
| 2 | 공유 컴포넌트 추출 | 결재/영업(공통)이 생산(경동) 코드 직접 import | - |
|
||||
| 3 | 라우트 가드 추가 | 테넌트 미보유 모듈 URL 직접 접근 차단 | - |
|
||||
| 4 | dashboard-invalidation 동적화 | production/construction 도메인 키 하드코딩 제거 | - |
|
||||
|
||||
> 선결과제 해소 예상: 3~4일, 이후 모듈 분리 본작업은 별도 산정
|
||||
|
||||
**핵심 의존성 위반 (공통 → 테넌트 방향, 수정 필요)**:
|
||||
```
|
||||
ApprovalBox → production/InspectionReportModal
|
||||
Sales/production-orders → production/ProductionOrders (actions+types+UI)
|
||||
Sales → router.push("/production/work-orders") 하드코딩
|
||||
CEODashboard → DailyProductionSection, ConstructionSection 직접 import
|
||||
dashboard-invalidation.ts → production/construction 도메인 키
|
||||
```
|
||||
|
||||
**안전한 부분**:
|
||||
- 테넌트 간 교차 의존성 없음 (생산↔건설 = 0)
|
||||
- 건설(주일) 모듈 완전 독립 → 바로 분리 가능
|
||||
- Zustand 스토어, API 프록시, 메뉴 시스템은 무관
|
||||
|
||||
#### 17-3. 테넌트별 페이지 현황 (2026-03-17 분석)
|
||||
|
||||
| 테넌트 | 업종 | 전용 모듈 | 페이지 수 |
|
||||
|--------|------|----------|:---:|
|
||||
| 공통 ERP | 전 업종 | 회계, 인사, 결재, 게시판, 설정, 고객센터 등 | ~165 |
|
||||
| 경동 | 셔터 제조 (MES) | 생산, 품질관리 | ~27 |
|
||||
| 주일 | 건설 시공 | 건설/프로젝트, 입찰, 기성 | ~48 |
|
||||
| (옵션) | - | 차량관리 | ~13 |
|
||||
|
||||
#### 17-4. 마이그레이션 Phase
|
||||
|
||||
| Phase | 범위 | 예상 기간 | 상태 |
|
||||
|-------|------|----------|------|
|
||||
| **Phase 0** | 인프라 구축 | 2-3주 | ⏳ 준비 |
|
||||
| **선결과제** | 의존성 해소 (17-2) | 3-4일 | ⏳ 준비 |
|
||||
| **Phase 0** | 인프라 구축 | 2-3주 | ⏳ |
|
||||
| | - catch-all 라우터 | | |
|
||||
| | - pageConfigStore | | |
|
||||
| | - DynamicListPage/FormPage 렌더러 | | |
|
||||
@@ -776,13 +831,13 @@ DynamicItemForm의 ComputedField → computed 타입으로 범용화
|
||||
| | - 거래처관리, 설비관리 등 | | |
|
||||
| **Phase 3** | 복잡한 비즈니스 페이지 전환 | 6-8주 | ⏳ |
|
||||
| | - 견적, 수주, 생산 등 로직 있는 페이지 | | |
|
||||
| | - 로직 블록 구축 병행 | | |
|
||||
| **Phase 4** | 기존 정적 → 동적 완전 전환 | 지속적 | ⏳ |
|
||||
| | - 남은 하드코딩 페이지 점진적 전환 | | |
|
||||
|
||||
```
|
||||
전환 판단 기준:
|
||||
|
||||
[선행] 선결과제 해소 (의존성 분리) → 선결과제 Phase
|
||||
[쉬움] 순수 CRUD (리스트+폼) → Phase 2에서 전환
|
||||
[보통] CRUD + 단순 계산 → Phase 2~3
|
||||
[어려움] 복잡한 비즈니스 로직 → Phase 3
|
||||
@@ -886,9 +941,10 @@ DynamicItemForm의 ComputedField → computed 타입으로 범용화
|
||||
| 동적 필드 타입 설계 | `claudedocs/architecture/[DESIGN-2026-02-11]` | 4-Level 구조, 14종 필드 |
|
||||
| 동적 필드 구현 현황 | `claudedocs/architecture/[IMPL-2026-02-11]` | Phase 1~3 프론트 구현 완료 |
|
||||
| 백엔드 API 스펙 | `claudedocs/item-master/[API-REQUEST-2026-02-12]` | 동적 필드 타입 백엔드 요청서 |
|
||||
| 테넌트 모듈 의존성 분석 | `claudedocs/architecture/[ANALYSIS-2026-03-17]` | 3테넌트 분리, 선결과제 4개, 의존성 위반 목록 |
|
||||
|
||||
---
|
||||
|
||||
**문서 버전**: 1.2
|
||||
**마지막 업데이트**: 2026-03-11
|
||||
**문서 버전**: 1.3
|
||||
**마지막 업데이트**: 2026-03-18
|
||||
**다음 단계**: 백엔드 회의 → 협의 필요 항목 확정 → v2.0 작성 → `sam-docs/frontend/v2/`에 최종본 등록
|
||||
|
||||
@@ -6,6 +6,39 @@
|
||||
**Estimated Total Effort**: 12-16 working days across 4 phases
|
||||
**Zero Downtime Requirement**: All changes are additive; no page removal until Phase 3
|
||||
|
||||
### 관련 문서 (로드맵 상 위치)
|
||||
|
||||
| 문서 | 역할 | 관계 |
|
||||
|------|------|------|
|
||||
| `[VISION-2026-02-19] dynamic-rendering-platform-strategy.md` | 플랫폼 비전 | 최상위 방향성 |
|
||||
| `[PLAN-2026-02-06] multi-tenancy-optimization-roadmap.md` | 멀티테넌시 로드맵 | 전체 단계 정의 |
|
||||
| `[DESIGN-2026-02-11] dynamic-field-type-extension.md` | 동적 필드 타입 설계 | v3 렌더러 기초 |
|
||||
| `[PLAN-2026-03-11] dynamic-multi-tenant-page-system.md` | **v3 최종 설계 (JSON 동적 페이지)** | **본 계획의 도착점** |
|
||||
| `[ANALYSIS-2026-03-17] tenant-module-separation-dependency-audit.md` | 의존성 감사 | 본 계획의 근거 데이터 |
|
||||
| **본 문서 (Phase 0~3)** | **프론트 모듈 분리 실행 계획** | **v3로 가는 징검다리** |
|
||||
|
||||
```
|
||||
로드맵 전체 흐름:
|
||||
|
||||
[VISION 02-19] 플랫폼 비전
|
||||
│
|
||||
[PLAN 02-06] 멀티테넌시 로드맵
|
||||
│
|
||||
[DESIGN 02-11] 동적 필드 타입
|
||||
│
|
||||
[PLAN 03-11] v3 최종 설계 (JSON 동적 페이지 + JSONB + pageType 렌더러)
|
||||
│
|
||||
[ANALYSIS 03-17] 의존성 감사 ──→ 현재 코드의 모듈 간 결합 6건 발견
|
||||
│
|
||||
[PLAN 03-17] ★ 본 문서 ★ (Phase 0~3: 프론트 모듈 분리)
|
||||
│
|
||||
[v2] 백엔드 page_configs JSONB API → useModules() 연결
|
||||
│
|
||||
[v3] catch-all route + DynamicListPage/FormPage 렌더러
|
||||
│
|
||||
[최종] 테넌트 추가 = 어드민 config 등록 → 코드 변경 0줄
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
Binary file not shown.
BIN
public/sounds/sam_voice.wav
Normal file
BIN
public/sounds/sam_voice.wav
Normal file
Binary file not shown.
@@ -28,14 +28,27 @@ import { SOUND_OPTIONS, DEFAULT_ITEM_VISIBILITY } from './types';
|
||||
import { saveNotificationSettings } from './actions';
|
||||
import { ItemSettingsDialog } from './ItemSettingsDialog';
|
||||
|
||||
// 미리듣기 함수
|
||||
// 미리듣기 - Audio API 재생
|
||||
let previewAudio: HTMLAudioElement | null = null;
|
||||
|
||||
function playPreviewSound(soundType: SoundType) {
|
||||
if (soundType === 'mute') {
|
||||
toast.info('무음으로 설정되어 있습니다.');
|
||||
return;
|
||||
}
|
||||
const soundName = soundType === 'default' ? '기본 알림음' : 'SAM 보이스';
|
||||
toast.info(`${soundName} 미리듣기`);
|
||||
|
||||
// 이전 재생 중지
|
||||
if (previewAudio) {
|
||||
previewAudio.pause();
|
||||
previewAudio = null;
|
||||
}
|
||||
|
||||
const soundFile = soundType === 'sam_voice' ? 'sam_voice.wav' : 'default.wav';
|
||||
previewAudio = new Audio(`/sounds/${soundFile}`);
|
||||
previewAudio.play().catch(() => {
|
||||
const soundName = soundType === 'default' ? '기본 알림음' : 'SAM 보이스';
|
||||
toast.info(`${soundName} 미리듣기 (음원 파일 준비 중)`);
|
||||
});
|
||||
}
|
||||
|
||||
// 알림 항목 컴포넌트
|
||||
|
||||
@@ -1,36 +1,12 @@
|
||||
/**
|
||||
* 알림 설정 타입 정의
|
||||
*
|
||||
* ========================================
|
||||
* [2026-01-05] 백엔드 API 수정 필요 사항
|
||||
* ========================================
|
||||
* API 응답 구조: { enabled, email, soundType } per item
|
||||
* soundType: 'default' | 'sam_voice' | 'mute'
|
||||
*
|
||||
* 1. NotificationItem에 soundType 필드 추가
|
||||
* - 기존: { enabled: boolean, email: boolean }
|
||||
* - 변경: { enabled: boolean, email: boolean, soundType: 'default' | 'sam_voice' | 'mute' }
|
||||
*
|
||||
* 2. OrderNotificationSettings에 approvalRequest 항목 추가
|
||||
* - 기존: { salesOrder, purchaseOrder }
|
||||
* - 변경: { salesOrder, purchaseOrder, approvalRequest }
|
||||
*
|
||||
* 3. API 응답 예시:
|
||||
* {
|
||||
* "notice": {
|
||||
* "enabled": true,
|
||||
* "notice": { "enabled": true, "email": false, "soundType": "default" },
|
||||
* "event": { "enabled": true, "email": true, "soundType": "sam_voice" }
|
||||
* },
|
||||
* "order": {
|
||||
* "enabled": true,
|
||||
* "salesOrder": { ... },
|
||||
* "purchaseOrder": { ... },
|
||||
* "approvalRequest": { "enabled": false, "email": false, "soundType": "default" } // 새로 추가
|
||||
* }
|
||||
* }
|
||||
* ========================================
|
||||
* [2026-03-18] soundType API 연동 완료
|
||||
*/
|
||||
|
||||
// 알림 소리 타입 (NEW: 2026-01-05)
|
||||
export type SoundType = 'default' | 'sam_voice' | 'mute';
|
||||
|
||||
// 알림 소리 옵션
|
||||
|
||||
Reference in New Issue
Block a user