Skip to content

Commit 56bca19

Browse files
authored
[DABOM-453] v23.1 family_quota 분리 및 family redis 키 suffix 적용
[DABOM-453] v23.1 family_quota 분리 및 family redis 키 suffix 적용
2 parents 0e3e696 + 78aa68b commit 56bca19

4 files changed

Lines changed: 185 additions & 101 deletions

File tree

ARCHITECTURE.md

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# 실시간 가족 데이터 통합 관리 시스템 - 아키텍처 설계서
22

3-
> **문서 버전**: v23.0
3+
> **문서 버전**: v23.1
44
> **작성일**: 2026-03-15
55
> **작성자**: DABOM 팀
6-
> **변경 이력**: v22.0 - API_SPECIFICATION v22.2 Major 버전 동기화: 엔드포인트 총 60개 반영 | v21.0 - API_SPECIFICATION v21.6 동기화: 도메인 리네이밍 (NEGOTIATIONS→APPEALS, REPORTS→RECAPS), 엔드포인트 경로 갱신 6건, 알림 subType APPEAL_*/EMERGENCY_APPROVED 전환, 배치 흐름 FamilyRecapBatchJob/communication_score 갱신, SSE 경로 /families/usage/sse 반영 | v11.0 - 2차기획서 Phase 2 기능 반영: api-core 도메인 확장, 알림 subType 확장, E2E 플로우 추가, 배치 설계 추가 | v10.2 - POLICY 테이블 is_activate → is_active 리네이밍 버전 동기화 | v10.0 - web-core 서브도메인 분리: web-service (www.dabom.site) + web-admin (admin.dabom.site), 모노레포 구조 반영 | v9.0 - api-spec 최종 동기화: 도메인 구조 변경 (7→5도메인), 엔드포인트 URL/메서드/응답 구조 변경 | v8.0 - 전체 문서 버전 통일 (공유 Major + 독립 Minor 체계 도입) | v7.0 - simulator-traffic → simulator-usage 리네이밍, 기술 스택 Go 확정 | v6.0 - ERD v6.0 동기화: customerId 네이밍, POLICY 스키마 반영 | v5.0 - ERD v5.0 동기화: daily→monthly 전환, Redis 키/Lua Script 업데이트, 관리자 전용 인증 API 추가, CUSTOMER/ADMIN 분리 반영; v4.0 - API 엔드포인트 전면 재구성 (api-spec.csv 기반, /api/v1 prefix 제거, 도메인별 그룹핑, JWT familyId 추론, REST 알림 API 추가)
6+
> **변경 이력**: v23.1 - `family`의 월별 상태를 `family_quota`로 분리하고, Family Redis 키(`info`, `remaining`, `alert`)에 `{yyyyMM}` suffix를 적용하는 구조로 동기화 | v22.0 - API_SPECIFICATION v22.2 Major 버전 동기화: 엔드포인트 총 60개 반영 | v21.0 - API_SPECIFICATION v21.6 동기화: 도메인 리네이밍 (NEGOTIATIONS→APPEALS, REPORTS→RECAPS), 엔드포인트 경로 갱신 6건, 알림 subType APPEAL_*/EMERGENCY_APPROVED 전환, 배치 흐름 FamilyRecapBatchJob/communication_score 갱신, SSE 경로 /families/usage/sse 반영 | v11.0 - 2차기획서 Phase 2 기능 반영: api-core 도메인 확장, 알림 subType 확장, E2E 플로우 추가, 배치 설계 추가 | v10.2 - POLICY 테이블 is_activate → is_active 리네이밍 버전 동기화 | v10.0 - web-core 서브도메인 분리: web-service (www.dabom.site) + web-admin (admin.dabom.site), 모노레포 구조 반영 | v9.0 - api-spec 최종 동기화: 도메인 구조 변경 (7→5도메인), 엔드포인트 URL/메서드/응답 구조 변경 | v8.0 - 전체 문서 버전 통일 (공유 Major + 독립 Minor 체계 도입) | v7.0 - simulator-traffic → simulator-usage 리네이밍, 기술 스택 Go 확정 | v6.0 - ERD v6.0 동기화: customerId 네이밍, POLICY 스키마 반영 | v5.0 - ERD v5.0 동기화: daily→monthly 전환, Redis 키/Lua Script 업데이트, 관리자 전용 인증 API 추가, CUSTOMER/ADMIN 분리 반영; v4.0 - API 엔드포인트 전면 재구성 (api-spec.csv 기반, /api/v1 prefix 제거, 도메인별 그룹핑, JWT familyId 추론, REST 알림 API 추가)
77
88
---
99

@@ -523,27 +523,25 @@ flowchart LR
523523

524524
| Key 패턴 | 타입 | 설명 | TTL |
525525
|----------|------|------|-----|
526-
| `family:{fid}:info` | Hash | 가족 기본 정보 (name, total_quota, created_at) | 영구 |
527-
| `family:{fid}:remaining` | String | 가족 공용 실시간 잔여량 (DECRBY 대상) | 월초 리셋 |
526+
| `family:{fid}:info:{yyyyMM}` | Hash | 가족 월별 메타 정보 (name, total_quota) | 해당 월 만료 시 |
527+
| `family:{fid}:remaining:{yyyyMM}` | String | 가족 월별 실시간 잔여량 (DECRBY 대상) | 해당 월 만료 시 |
528528

529529
```bash
530-
HSET family:100:info name "HappyFamily" total_quota "107374182400" created_at "1707462000"
531-
SET family:100:remaining "12884901888"
530+
HSET family:100:info:202603 name "HappyFamily" total_quota "107374182400"
531+
SET family:100:remaining:202603 "12884901888"
532532
```
533533

534-
#### 5.1.2 사용자 사용량 (기간별 집계)
534+
#### 5.1.2 사용자 월 사용량
535535

536-
정책 확장을 위해 사용량을 **기간별(Period)**로 분리 저장. Lua Script는 정책에 설정된 기간의 Key를 동적으로 참조합니다.
536+
현재 월별 고객 사용량 키는 월 suffix를 포함한 단일 패턴으로 고정합니다.
537537

538538
| Key 패턴 | 타입 | 설명 | TTL |
539539
|----------|------|------|-----|
540-
| `family:{fid}:customer:{cid}:usage:{period}` | String | 기간별 누적 사용량 | 해당 기간 만료 시 |
540+
| `family:{fid}:customer:{cid}:usage:monthly:{yyyyMM}` | String | 고객 월 누적 사용량 | 해당 만료 시 |
541541

542542
```bash
543-
# 월별 사용량 (이번 달 5GB 사용)
544-
SET family:100:customer:1:usage:monthly "5368709120"
545-
# 일일 사용량 (오늘 500MB 사용)
546-
SET family:100:customer:1:usage:daily "524288000"
543+
# 월별 사용량 (2026년 3월 5GB 사용)
544+
SET family:100:customer:1:usage:monthly:202603 "5368709120"
547545
```
548546

549547
#### 5.1.3 런타임 제약 (Runtime Constraints) - 핵심 설계
@@ -577,7 +575,7 @@ HMSET family:100:customer:1:constraints \
577575
| Key 패턴 | 타입 | 설명 | TTL |
578576
|----------|------|------|-----|
579577
| `event:dedup:{uuid}` | String | Kafka 이벤트 중복 처리 방지 | 1시간 |
580-
| `family:{fid}:alert:{type}:{value}` | String | 알림 중복 발송 방지 | 월초 리셋 |
578+
| `family:{fid}:alert:THRESHOLD:{threshold}:{yyyyMM}` | String | 월별 임계치 알림 중복 발송 방지 | 해당 월 만료 시 |
581579

582580
#### 5.1.5 정책 메타데이터 (API용)
583581

@@ -712,7 +710,7 @@ sequenceDiagram
712710
KF->>AN: notification-events 수신
713711
714712
AN->>RD: 알림 발송 여부 확인
715-
Note over AN,RD: family:{familyId}:alert:50
713+
Note over AN,RD: family:{familyId}:alert:THRESHOLD:50:{yyyyMM}
716714
717715
alt 미발송
718716
RD-->>AN: 없음
@@ -806,8 +804,8 @@ constraints Hash를 순회하며 BLOCK/LIMIT/THROTTLE 등 다양한 정책을 **
806804

807805
| 구분 | Key/Arg | 설명 |
808806
|------|---------|------|
809-
| `KEYS[1]` | `family:{fid}:remaining` | 가족 잔여 데이터량 |
810-
| `KEYS[2]` | `family:{fid}:customer:{cid}:usage` | 기간별 사용량 Key Prefix |
807+
| `KEYS[1]` | `family:{fid}:remaining:{yyyyMM}` | 가족 월별 잔여 데이터량 |
808+
| `KEYS[2]` | `family:{fid}:customer:{cid}:usage:monthly:{yyyyMM}` | 고객 월별 사용량 Key |
811809
| `KEYS[3]` | `family:{fid}:customer:{cid}:constraints` | 제약 조건 Hash |
812810
| `ARGV[1]` | `request_bytes` | 요청 데이터량 |
813811
| `ARGV[2]` | `app_id` | 현재 실행 앱 패키지명 |
@@ -1338,7 +1336,8 @@ Spring Scheduler (매월 1일 00:20 트리거)
13381336
Spring Scheduler (매월 1일 00:01 트리거)
13391337
→ [Acquire Lock]
13401338
→ [Reset Family Month] DB: family.family_month 갱신
1341-
→ [Reset Family Redis Keys] Redis: remaining + alert 임계치 키 삭제
1339+
→ [Precreate Next Family Quota] MySQL: 다음 달 family_quota 선생성
1340+
→ [Reset Family Redis Keys] Redis: 전월 info + remaining + alert 임계치 키 삭제
13421341
→ [Reset Customer Monthly Usage] Redis: 전월 suffix 구성원 월사용량 키 삭제
13431342
→ [Release Lock]
13441343
```
@@ -1357,8 +1356,8 @@ Spring Scheduler (매월 1일 00:01 트리거)
13571356
```
13581357
Spring Scheduler (매일 03:00 트리거)
13591358
→ [Acquire Lock]
1360-
→ [Invalidate Family Keys] Redis: family:{id}:info, remaining 삭제
1361-
→ [Invalidate Customer Monthly Usage] Redis: 구성원 월사용량 키 삭제
1359+
→ [Invalidate Family Keys] Redis: family:{id}:info:{yyyyMM}, remaining:{yyyyMM} 삭제
1360+
→ [Invalidate Customer Monthly Usage] Redis: 구성원 월사용량 suffix 키 삭제
13621361
→ [Release Lock]
13631362
```
13641363

BACK_SPECIFICATION.md

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
| 버전 | 날짜 | 변경 내용 |
66
|------|------|-----------|
7+
| v23.1 | 2026-03-15 | `family` 월별 상태를 `family_quota`로 분리하고 Family Redis 키(`info`, `remaining`, `alert`)에 `{yyyyMM}` suffix 규칙 반영 |
78
| v23.0 | 2026-03-15 | Major 버전 동기화 |
89
| v22.0 | 2026-03-12 | API_SPECIFICATION v22.2 동기화: UPLOADS 도메인 추가 (upload 패키지, `POST /uploads/images`, UploadType enum), 보상 템플릿 상세 조회 엔드포인트 추가 (`GET /admin/rewards/templates/{id}`) |
910
| v21.0 | 2026-03-12 | API_SPECIFICATION v21.6 동기화: 도메인 리네이밍 (negotiation→appeal, report→recap), 엔티티/Enum 갱신, 엔드포인트 경로 갱신, 알림 타입 APPEAL_* 전환, admin 로그인/리프레시 권한 수정 |
@@ -354,6 +355,20 @@ CREATE INDEX idx_notif_family ON notification_log(family_id, sent_at DESC);
354355

355356
**UNIQUE 제약**: `(family_id, customer_id, current_month)` + `deleted_at`
356357

358+
### FamilyQuota 엔티티
359+
360+
| 필드 | 타입 | 설명 |
361+
|------|------|------|
362+
| `id` | Long | PK (auto-increment) |
363+
| `familyId` | Long | 가족 ID |
364+
| `currentMonth` | LocalDate | 기준 월 (yyyy-MM-01) |
365+
| `totalQuotaBytes` | Long | 월별 총 할당량 스냅샷 |
366+
| `usedBytes` | Long | 월별 총 사용량 |
367+
368+
**UNIQUE 제약**: `(family_id, current_month)` + `deleted_at`
369+
370+
> 가족 월별 총량 조회의 Source of Truth는 `family`가 아니라 `family_quota`다.
371+
357372
---
358373

359374
## 인증 & 인가
@@ -715,11 +730,12 @@ usage-persist (Topic) → UsagePersistKafkaConsumer → UsagePersistService.pers
715730

716731
| 키 패턴 | 타입 | 설명 |
717732
|---------|------|------|
718-
| `family:{familyId}:info` | Hash | 가족 메타데이터 (`total_quota` 등) |
719-
| `family:{familyId}:remaining` | String | 가족 잔여 데이터량 (bytes) |
720-
| `family:{familyId}:customer:{customerId}:usage:monthly` | String | 고객 월간 사용량 (bytes) |
733+
734+
| `family:{familyId}:info:{yyyyMM}` | Hash | 가족 월별 메타 정보 (name, total_quota 등) |
735+
| `family:{familyId}:remaining:{yyyyMM}` | String | 가족 월별 잔여 데이터량 (bytes) |
736+
| `family:{familyId}:customer:{customerId}:usage:monthly:{yyyyMM}` | String | 고객 월간 사용량 (bytes) |
721737
| `family:{familyId}:customer:{customerId}:constraints` | Hash | 고객별 정책 제약 조건 |
722-
| `family:{familyId}:alerts` | Hash | 임계치 경고 발송 이력 |
738+
| `family:{familyId}:alert:THRESHOLD:{threshold}:{yyyyMM}` | String | 월별 임계치 경고 발송 이력 |
723739
| `event:dedup:policy:{eventId}:{customerId}` | String | 정책 이벤트 중복 방지 (TTL: 1시간) |
724740
| `event:dedup:usage-persist:{originEventId}` | String | 사용량 DB 저장 중복 방지 (TTL: 10분) |
725741

@@ -793,7 +809,7 @@ usage-persist (Topic) → UsagePersistKafkaConsumer → UsagePersistService.pers
793809

794810
### 중복 발송 방지
795811

796-
- **임계치 알림**: 50%, 30%, 10% 각각 한 번만 발송 (Redis `alerts` Hash)
812+
- **임계치 알림**: 50%, 30%, 10% 각각 한 번만 발송 (Redis `family:{familyId}:alert:THRESHOLD:{threshold}:{yyyyMM}` String key 존재 여부로 중복 방지)
797813
- **정책 이벤트**: `event:dedup:policy:{eventId}:{customerId}` (TTL 1시간)
798814
- **DB 저장 이벤트**: `event:dedup:usage-persist:{originEventId}` (TTL 10분)
799815

0 commit comments

Comments
 (0)