Files
sam-docs/features/notification-settings/README.md
김보곤 ac1eecb71d docs: [notification-settings] API soundType 완료 상태 반영
- Gap 분석 → 연동 현황으로 업데이트 (API 완료, React 대기)
- 저장/조회 흐름 코드 추가
- React 구현 요청서 링크 추가
2026-03-18 11:26:31 +09:00

20 KiB

서비스 알림설정 (Notification Settings)

작성일: 2026-03-18 상태: API 구현 완료 (React soundType 연동 대기) 대상: API (sam/api) + React (sam/react)


1. 개요

1.1 목적

사용자가 알림 유형별로 수신 채널(푸시/이메일/SMS/인앱/카카오)과 알림음을 개별 설정할 수 있는 기능이다.

1.2 핵심 원칙

  • 그룹 기반 UI: 알림 유형을 8개 그룹으로 분류하여 React UI에서 카테고리별 관리
  • 채널별 ON/OFF: 푸시, 이메일, SMS, 인앱, 카카오톡 5개 채널 독립 제어
  • 알림음 선택: 기본음 / SAM 보이스 / 무음 3종 선택
  • 멀티테넌트 격리: 모든 설정은 tenant_id 기반 격리
  • 기본값 정책: 설정 미저장 시 알림 유형별 기본값 자동 적용

2. 테이블 구조

2.1 ERD 개요

User (1) ─── (N) notification_settings          # 채널별 ON/OFF
User (1) ─── (N) push_notification_settings      # 푸시 전용 (알림음/진동/미리보기)
User (1) ─── (N) notification_setting_group_states # 그룹 전체 ON/OFF
User (1) ─── (N) push_device_tokens              # FCM 토큰

Tenant (1) ── (N) notification_setting_groups    # 그룹 메타데이터
Group  (1) ── (N) notification_setting_group_items # 그룹-알림유형 매핑

2.2 notification_settings

사용자별 알림 채널 ON/OFF 설정.

컬럼 타입 설명
id bigint PK
tenant_id bigint FK 테넌트
user_id bigint FK 사용자
notification_type varchar(50) 알림 유형 (approval, order 등)
push_enabled boolean 푸시 (기본: true)
email_enabled boolean 이메일 (기본: false)
sms_enabled boolean SMS (기본: false)
in_app_enabled boolean 인앱 (기본: true)
kakao_enabled boolean 카카오 알림톡 (기본: false)
settings json 추가 설정 (우선순위, 알림 시간대 등)

UNIQUE: (tenant_id, user_id, notification_type)

2.3 push_notification_settings

모바일 앱 푸시 전용 고급 설정 (알림음, 진동, 미리보기).

컬럼 타입 설명
id bigint PK
tenant_id bigint FK 테넌트
user_id bigint FK 사용자
notification_type varchar(50) 알림 유형
is_enabled boolean 푸시 알림 활성화 (기본: true)
sound varchar(100) 알림음 파일명 (기본: 'default')
vibrate boolean 진동 (기본: true)
show_preview boolean 미리보기 (기본: true)

UNIQUE: (tenant_id, user_id, notification_type)

2.4 notification_setting_groups

알림을 UI에서 그룹으로 표시하기 위한 메타데이터 (테넌트별 커스터마이징 가능).

컬럼 타입 설명
id bigint PK
tenant_id bigint FK 테넌트
code varchar(50) 그룹 코드
name varchar(100) 그룹명
sort_order smallint 정렬 순서
is_active boolean 활성화 여부

UNIQUE: (tenant_id, code)

2.5 notification_setting_group_items

그룹-알림유형 매핑 (어떤 알림 타입이 어떤 그룹에 속하는지).

컬럼 타입 설명
id bigint PK
group_id bigint FK 그룹
notification_type varchar(50) 알림 타입
label varchar(100) 항목 라벨
sort_order smallint 정렬 순서

UNIQUE: (group_id, notification_type)

2.6 notification_setting_group_states

사용자가 그룹 전체를 ON/OFF 할 수 있도록 그룹별 활성화 상태 저장.

컬럼 타입 설명
id bigint PK
tenant_id bigint FK 테넌트
user_id bigint FK 사용자
group_code varchar(50) 그룹 코드
enabled boolean 그룹 전체 활성화 (기본: true)

UNIQUE: (tenant_id, user_id, group_code)


3. 알림 유형 분류

3.1 알림 채널 설정 유형 (NotificationSetting — 8가지)

상수 한글명 기본 활성 채널
TYPE_APPROVAL approval 전자결재 푸시, 인앱
TYPE_ORDER order 수주 푸시, 인앱
TYPE_DEPOSIT deposit 입금 푸시, 인앱
TYPE_WITHDRAWAL withdrawal 출금 푸시, 인앱
TYPE_NOTICE notice 공지사항 푸시, 인앱
TYPE_SYSTEM system 시스템 푸시, 인앱
TYPE_MARKETING marketing 마케팅 모두 OFF
TYPE_SECURITY security 보안 푸시, 이메일, 인앱

3.2 그룹 기반 세부 알림 유형 (20가지)

그룹 코드 그룹명 포함 알림 유형 항목 수
notice 공지 알림 notice, event 2
schedule 일정 알림 vat_report, income_tax_report 2
vendor 거래처 알림 new_vendor, credit_rating 2
attendance 근태 알림 annual_leave, clock_in, late, absent 4
order 수주/발주 알림 sales_order, purchase_order 2
approval 전자결재 알림 approval_request, draft_approved, draft_rejected, draft_completed 4
production 생산 알림 safety_stock, production_complete 2
collection 채권/지출 알림 bad_debt, expected_expense 2

3.3 snake_case ↔ camelCase 매핑

React(camelCase) ↔ API(snake_case) 변환이 NotificationSettingGroup::CAMEL_CASE_MAP에 정의:

vat_report ↔ vatReport          new_vendor ↔ newVendor
income_tax_report ↔ incomeTaxReport    credit_rating ↔ creditRating
annual_leave ↔ annualLeave      clock_in ↔ clockIn
sales_order ↔ salesOrder        purchase_order ↔ purchaseOrder
approval_request ↔ approvalRequest    draft_approved ↔ draftApproved
draft_rejected ↔ draftRejected  draft_completed ↔ draftCompleted
safety_stock ↔ safetyStock      production_complete ↔ productionComplete
bad_debt ↔ badDebt              expected_expense ↔ expectedExpense

4. API 엔드포인트

4.1 그룹 기반 설정 (React UI 연동)

Method Path 설명 인증
GET /api/v1/settings/notifications 그룹 기반 알림 설정 조회 auth:sanctum
PUT /api/v1/settings/notifications 그룹 기반 알림 설정 업데이트 auth:sanctum

컨트롤러: NotificationSettingController::indexGrouped(), updateGrouped()

GET 응답 구조

{
  "success": true,
  "data": {
    "notice": {
      "enabled": true,
      "notice": { "enabled": true, "email": false },
      "event": { "enabled": true, "email": false }
    },
    "approval": {
      "enabled": true,
      "approvalRequest": { "enabled": true, "email": true },
      "draftApproved": { "enabled": true, "email": false },
      "draftRejected": { "enabled": true, "email": false },
      "draftCompleted": { "enabled": true, "email": false }
    }
  }
}

PUT 요청 구조

{
  "notice": {
    "enabled": true,
    "notice": { "enabled": true, "email": false },
    "event": { "enabled": true, "email": false }
  }
}

4.2 플랫 구조 설정 (개별 타입)

Method Path 설명 인증
GET /api/v1/users/me/notification-settings 알림 설정 조회 auth:sanctum
PUT /api/v1/users/me/notification-settings 단일 타입 업데이트 auth:sanctum
PUT /api/v1/users/me/notification-settings/bulk 일괄 업데이트 auth:sanctum

컨트롤러: NotificationSettingController::index(), update(), bulkUpdate()

4.3 푸시 알림 전용 설정

Method Path 설명 인증
POST /api/v1/push/register-token FCM 토큰 등록/갱신 auth:sanctum
POST /api/v1/push/unregister-token FCM 토큰 비활성화 auth:sanctum
GET /api/v1/push/tokens 등록된 토큰 목록 auth:sanctum
GET /api/v1/push/settings 푸시 설정 조회 auth:sanctum
PUT /api/v1/push/settings 푸시 설정 업데이트 auth:sanctum
GET /api/v1/push/notification-types 알림 유형/알림음 목록 auth:sanctum

컨트롤러: PushNotificationController


5. 알림음 시스템

5.1 알림음 옵션 (React UI)

export const SOUND_OPTIONS = [
  { value: 'default', label: '기본 알림음' },
  { value: 'sam_voice', label: 'SAM 보이스' },
  { value: 'mute', label: '무음' }
]

React types.ts에 정의됨: soundType: 'default' | 'sam_voice' | 'mute'

5.2 음원 파일 현황

물리적 파일 위치

위치 파일 크기 상태
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

앱(Android) 알림음 채널

앱에서는 Android NotificationChannel로 채널별 알림음이 동작한다.

채널 ID 알림음 파일 용도
push_default res/raw/push_default.wav 일반 알림
push_urgent res/raw/push_urgent.wav 긴급 알림 (신규업체)
push_payment res/raw/push_payment.wav 결제 알림
push_sales_order res/raw/push_sales_order.wav 수주 알림
push_purchase_order res/raw/push_purchase_order.wav 발주 알림
push_contract res/raw/push_contract.wav 계약 알림

5.3 FCM 채널 매핑 (config/fcm.php)

'channels' => [
    'default'          => 'push_default',
    'vendor_register'  => 'push_vendor_register',
    'approval_request' => 'push_approval_request',
    'income'           => 'push_income',
    'sales_order'      => 'push_sales_order',
    'purchase_order'   => 'push_purchase_order',
    'contract'         => 'push_contract',
],

5.4 FcmSender 사운드 매핑

FcmSender::getSoundForChannel():

return match ($channelId) {
    'push_vendor_register'  => 'push_vendor_register',
    'push_approval_request' => 'push_approval_request',
    'push_income'           => 'push_income',
    'push_sales_order'      => 'push_sales_order',
    'push_purchase_order'   => 'push_purchase_order',
    'push_contract'         => 'push_contract',
    default                 => 'push_default',
};

5.5 PushNotificationSetting 알림음 상수

const SOUND_DEFAULT    = 'default';
const SOUND_DEPOSIT    = 'deposit.wav';
const SOUND_WITHDRAWAL = 'withdrawal.wav';
const SOUND_ORDER      = 'order.wav';
const SOUND_APPROVAL   = 'approval.wav';
const SOUND_URGENT     = 'urgent.wav';

6. 알림 발송 흐름

6.1 비즈니스 이벤트 발송

비즈니스 이벤트 (예: 수주 생성)
    ↓
PushNotificationService::notifySalesOrder()
    ↓
PushNotificationService::sendByEvent()
    ├── event → channelId 매핑
    ├── PushDeviceToken 조회 (tenant_id, is_active=true)
    └── FcmSender::sendToMany()
        ├── 토큰 chunk 분할 (200개/배치)
        ├── FCM HTTP v1 API 발송
        │   ├── android: channel_id + sound 파일명
        │   └── apns: sound = 'default' (고정)
        └── FcmBatchResult 반환

6.2 사용자별 타겟 발송

결재 이벤트 (예: 결재요청)
    ↓
TodayIssueObserverService::handleApprovalStepChange()
    ├── target_user_id = step->user_id (결재자)
    ├── TodayIssue 생성 (target_user_id 포함)
    └── sendFcmNotification()
        ├── getEnabledUserTokens(tenantId, type, targetUserId)
        │   ├── targetUserId 있음 → 해당 사용자 토큰만 조회
        │   └── targetUserId 없음 → 테넌트 전체 토큰 조회
        └── 알림 설정 확인 후 발송

6.3 발송 대상 정책

이슈 타입 발송 대상 비고
결재요청 결재자 (step.user_id) 타겟 발송
기안 승인/반려/완료 기안자 (approval.drafter_id) 타겟 발송
수주등록 테넌트 전체 브로드캐스트
입금/출금 테넌트 전체 브로드캐스트
신규업체 등록 테넌트 전체 브로드캐스트
안전재고/추심 테넌트 전체 브로드캐스트

7. React 프론트엔드

7.1 컴포넌트 구조

settings/notification-settings/page.tsx  ← 페이지
  └── NotificationSettings/index.tsx     ← 메인 컴포넌트
      ├── 섹션별 카드 (8개 그룹)
      │   ├── 그룹 토글 (전체 ON/OFF)
      │   └── 개별 항목 (NotificationItemRow)
      │       ├── 알림 ON/OFF 토글
      │       ├── 알림음 선택 (Select + Play 버튼)
      │       └── 이메일 체크박스
      └── ItemSettingsDialog.tsx         ← 항목 표시/숨김 모달

7.2 Server Action (API 호출)

함수 엔드포인트 설명
getNotificationSettings() GET /api/v1/settings/notifications 설정 조회
saveNotificationSettings() PUT /api/v1/settings/notifications 설정 저장

데이터 변환:

  • transformApiToFrontend() — API 응답을 React 타입으로 변환 (soundType 기본값 병합)
  • transformFrontendToApi() — React 구조를 API 형식으로 변환

7.3 항목 표시/숨김 (ItemVisibility)

  • localStorage에 저장 (서버 X)
  • 사용자가 불필요한 알림 항목을 UI에서 숨길 수 있음
  • 알림 설정 자체는 변경하지 않음 (UI 커스터마이징만)

8. 서비스 계층

8.1 NotificationSettingService

메서드 설명
getSettings() 사용자의 알림 설정 조회 (기본값 포함)
updateSetting() 단일 알림 타입 설정 업데이트
bulkUpdateSettings() 일괄 업데이트 (트랜잭션)
initializeDefaultSettings() 새 사용자 기본 설정 초기화
isChannelEnabled() 특정 채널 활성화 여부 확인
getGroupedSettings() 그룹 기반 조회 (React 구조)
updateGroupedSettings() 그룹 기반 업데이트 (React 구조)
initializeGroupsIfNeeded() 테넌트 그룹 정의 자동 초기화

8.2 PushNotificationService

메서드 설명
registerToken() FCM 토큰 등록/갱신
unregisterToken() 토큰 비활성화
getSettings() 푸시 설정 조회
updateSettings() 푸시 설정 업데이트
sendByEvent() 이벤트 기반 푸시 발송
notifyNewClient() 신규 거래처 알림
notifyPayment() 결제 알림
notifySalesOrder() 수주 알림

8.3 FcmSender

메서드 설명
sendToToken() 단일 토큰 발송
sendToMany() 대량 발송 (chunk 200개, delay 100ms)
getSoundForChannel() 채널 ID → 사운드 파일명 매핑
getAccessToken() OAuth2 토큰 발급 (캐싱)

8.4 AdminFcmService

MNG 관리자가 보내는 수동 푸시 알림 관리.

메서드 설명
send() 대상 필터링 → 발송 → 이력 저장
previewCount() 발송 대상 수 미리보기
getHistory() 발송 이력 조회

9. soundType 연동 현황

9.1 현재 상태 (2026-03-18)

항목 API React 상태
soundType 저장 settings.sound_type JSON에 저장 PUT 요청 시 전송 완료
soundType 조회 응답에 soundType 포함 기본값 병합 완료
soundType 검증 in:default,sam_voice,mute 타입 정의 완료
soundType UI 드롭다운 + Play 버튼 완료
미리듣기 실제 재생 Mock (토스트만) React 작업 대기
음원 파일 서빙 빈 placeholder React 작업 대기

9.2 API 구현 완료 내용

저장 흐름:

React PUT → { soundType: "sam_voice" }
  → NotificationSettingService::updateGroupedSettings()
    → notification_settings.settings JSON = { "sound_type": "sam_voice" }

조회 흐름:

NotificationSettingService::getGroupedSettings()
  → notification_settings.settings['sound_type'] 읽기
    → 미저장 시 기본값 'default'
      → React 응답: { enabled, email, soundType }

9.3 React 구현 대기 항목

요청 문서: docs/plans/notification-sound-react-request.md

  1. 음원 파일 배치mng/public/sounds/default.wavreact/public/sounds/ 복사
  2. 미리듣기 실제 재생playPreviewSound()에서 Audio API 사용
  3. types.ts 주석 정리 — "백엔드 API 수정 필요" 블록 제거

10. 관련 파일

API (sam/api)

파일 설명
app/Models/NotificationSetting.php 알림 채널 설정 모델
app/Models/PushNotificationSetting.php 푸시 전용 설정 모델
app/Models/NotificationSettingGroup.php 알림 그룹 모델 (DEFAULT_GROUPS, CAMEL_CASE_MAP)
app/Models/NotificationSettingGroupItem.php 그룹 아이템 모델
app/Models/NotificationSettingGroupState.php 그룹 상태 모델
app/Models/PushDeviceToken.php FCM 토큰 모델
app/Http/Controllers/Api/V1/NotificationSettingController.php 알림 설정 컨트롤러
app/Http/Controllers/Api/V1/PushNotificationController.php 푸시 설정 컨트롤러
app/Services/NotificationSettingService.php 알림 설정 서비스
app/Services/PushNotificationService.php 푸시 알림 서비스
app/Services/Fcm/FcmSender.php FCM 발송 서비스
app/Services/AdminFcmService.php 관리자 FCM 서비스
app/Services/TodayIssueObserverService.php 이슈 알림 발송
config/fcm.php FCM 채널 설정

React (sam/react)

파일 설명
src/components/settings/NotificationSettings/types.ts 타입 정의 (soundType 포함)
src/components/settings/NotificationSettings/index.tsx 메인 UI 컴포넌트
src/components/settings/NotificationSettings/actions.ts Server Action (API 호출)
src/components/settings/NotificationSettings/ItemSettingsDialog.tsx 항목 표시/숨김 모달
src/app/[locale]/(protected)/settings/notification-settings/page.tsx 페이지
src/lib/capacitor/fcm.ts FCM 핸들러 (sound_key 참조)
public/sounds/ 알림음 파일 (현재 빈 placeholder)

MNG (sam/mng)

파일 설명
app/Http/Controllers/FcmController.php FCM 테스트 발송
app/Services/FcmApiService.php MNG → API FCM 발송
resources/views/fcm/send.blade.php 테스트 발송 UI
public/sounds/default.wav 실제 기본 알림음 (788KB)
public/sounds/push_notification.wav 실제 푸시 알림음 (788KB)

관련 문서

문서 설명
docs/plans/notification-sound-react-request.md soundType React 구현 요청서 (프론트 전달용)
docs/dev/dev_plans/flow-tests/notification-settings-flow.json 플로우 테스트 시나리오
docs/dev/dev_plans/archive/notification-sound-system-plan.md 알림음 시스템 구현 계획 (완료)
docs/dev/dev_plans/archive/fcm-user-targeted-notification-plan.md FCM 사용자별 발송 계획 (완료)

최종 업데이트: 2026-03-18