작성일: 2025-12-06 버전: 1.0 목적: 추후 개발할 기능들에 대한 구체적인 기획 및 요구사항 정의
목적: 각 기도 제목마다 누가 기도했는지 표시하여 "함께" 기도하는 경험을 강화하고, 기도가 실제로 응원받고 있다는 느낌을 제공합니다.
핵심 가치: 응원하는 공동체, 투명성, 감사
As a 기도 제목 작성자
I want to 누가 내 기도 제목을 위해 기도했는지 보고 싶다
So that 내 기도가 응원받고 있다는 것을 느끼고 감사할 수 있다
As a 기도방 멤버
I want to 내가 기도한 제목들과 기록을 확인하고 싶다
So that 지속적으로 기도 생활을 이어갈 수 있다
기능:
- 사용자가 "기도 완료" 버튼을 누르면 기도 기록이 저장됨
- 기록 정보: 누가(userId), 언제(timestamp), 어떤 기도 제목(prayerId)
데이터 구조:
interface PrayerLog {
id: string;
prayerId: string; // 기도 제목 ID
userId: string; // 기도한 사람 ID
userName: string; // 기도한 사람 이름
prayedAt: Date; // 기도한 시간
roomId: string; // 기도방 ID
}위치: 기도 상세 화면
표시 방식:
Option A (간단):
✅ 3명이 기도했어요
🙏 철수, 영희, 민수
Option B (상세):
✅ 5명이 기도했어요
👤 철수 (방금 전)
👤 영희 (1시간 전)
👤 민수 (2시간 전)
👤 지영 (어제)
👤 현우 (2일 전)
Option C (아바타):
✅ 5명이 기도했어요
[프로필사진들 겹쳐서 표시]
권장: Option A로 시작 → Option C로 발전 (프로필 사진 기능 추가 후)
표시 정보:
- 총 기도한 사람 수
- 중복 제거 여부: 중복 제거 (한 사람이 여러 번 기도해도 1명으로 카운트)
- 또는: 총 기도 횟수와 기도한 사람 수 모두 표시
예시:
✅ 5명이 총 12번 기도했어요
🙏 철수, 영희, 민수, 지영, 현우
조회 옵션:
- 전체 기록: 이 기도 제목을 위해 기도한 모든 사람
- 최근 기록: 최근 24시간/7일 내 기도한 사람
- 나의 기록: 내가 언제 기도했는지
정렬 기준:
- 최근 기도한 순서
- 이름 가나다순
┌─────────────────────────────────┐
│ 건강을 위한 기도 │
│ 작성자: 철수 · 2일 전 │
│ ✅ 5명이 기도했어요 │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 건강을 위한 기도 │
│ 작성자: 철수 · 2025-12-01 │
├─────────────────────────────────┤
│ │
│ 💬 기도 내용 │
│ - 영희님을 위해: 감기가... │
│ - 민수님을 위해: 건강검진... │
│ │
├─────────────────────────────────┤
│ ✅ 5명이 기도했어요 │
│ │
│ 🙏 기도한 사람들 │
│ 👤 철수 (방금 전) │
│ 👤 영희 (1시간 전) │
│ 👤 민수 (2시간 전) │
│ 👤 지영 (어제) │
│ 👤 현우 (2일 전) │
│ │
│ [더 보기 v] │
│ │
├─────────────────────────────────┤
│ [기도 알림] │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 기도한 사람들 │
├─────────────────────────────────┤
│ │
│ 📊 총 5명이 12번 기도했어요 │
│ │
│ 👤 철수님 │
│ 3번 기도 · 마지막: 방금 전 │
│ │
│ 👤 영희님 │
│ 2번 기도 · 마지막: 1시간 전 │
│ │
│ 👤 민수님 │
│ 4번 기도 · 마지막: 2시간 전 │
│ │
│ 👤 지영님 │
│ 2번 기도 · 마지막: 어제 │
│ │
│ 👤 현우님 │
│ 1번 기도 · 마지막: 2일 전 │
│ │
│ [닫기] │
└─────────────────────────────────┘
POST /v1/prayers/{prayerId}/logs
Request Body:
{
"prayedAt": "2025-12-06T10:30:00Z"
}
Response:
{
"success": true,
"data": {
"logId": "log_123",
"prayerId": "prayer_456",
"userId": "user_789",
"prayedAt": "2025-12-06T10:30:00Z"
}
}
GET /v1/prayers/{prayerId}/logs?limit=10&offset=0
Response:
{
"success": true,
"data": {
"totalCount": 12,
"uniqueUserCount": 5,
"logs": [
{
"userId": "user_1",
"userName": "철수",
"prayedAt": "2025-12-06T10:30:00Z",
"prayCount": 3 // 이 사람이 이 기도 제목을 위해 기도한 총 횟수
},
{
"userId": "user_2",
"userName": "영희",
"prayedAt": "2025-12-06T09:00:00Z",
"prayCount": 2
}
]
}
}
GET /v1/users/me/prayer-logs?roomId={roomId}&startDate=2025-12-01
Response:
{
"success": true,
"data": {
"totalPrayerCount": 25,
"logs": [
{
"prayerId": "prayer_1",
"prayerTitle": "건강을 위한 기도",
"prayedAt": "2025-12-06T10:30:00Z",
"roomName": "가족 기도방"
}
]
}
}
CREATE TABLE prayer_logs (
id VARCHAR(36) PRIMARY KEY,
prayer_id VARCHAR(36) NOT NULL,
user_id VARCHAR(36) NOT NULL,
room_id VARCHAR(36) NOT NULL,
prayed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_prayer_id (prayer_id),
INDEX idx_user_id (user_id),
INDEX idx_room_id (room_id),
INDEX idx_prayed_at (prayed_at),
FOREIGN KEY (prayer_id) REFERENCES prayers(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE
);-
기도 기록 생성:
- 기도방 멤버만 기도 기록 생성 가능
- 하루에 같은 기도 제목을 여러 번 기도 가능
- 기도 기록은 삭제 불가 (통계 유지)
-
기도 기록 조회:
- 기도방 멤버만 조회 가능
- 기도한 사람의 이름만 표시 (프라이버시)
- 기도 횟수는 공개
-
알림 연동:
- 기도 완료 알림을 보낼 때 자동으로 기도 기록 생성
- 알림 없이 조용히 기도 기록만 남기는 옵션 제공 (선택)
-
기도 제목이 삭제된 경우:
- 기도 기록은 유지 (통계 목적)
- 삭제된 기도 제목 표시: "삭제된 기도 제목"
-
사용자가 기도방을 나간 경우:
- 기존 기도 기록은 유지
- 이름은 표시하되 프로필 접근 불가
-
네트워크 오류:
- 로컬에 임시 저장 후 나중에 동기화
- 중복 방지 로직 필요
-
Phase 1 (MVP):
- 기도 기록 저장
- 기도한 사람 수 표시 (숫자만)
- 기도한 사람 이름 목록 표시 (최대 5명)
-
Phase 2:
- 기도 횟수 집계
- 기도한 시간 표시
- 더보기 기능
-
Phase 3:
- 내 기도 기록 조회
- 통계 및 분석
목적: 연속으로 기도한 날짜를 표시하여 지속적인 기도 생활을 동기부여하고, 기도 습관 형성을 돕습니다.
핵심 가치: 지속성, 성장, 동기부여
As a 사용자
I want to 내가 며칠 연속으로 기도했는지 보고 싶다
So that 기도 습관을 유지하고 동기부여를 받을 수 있다
As a 기도방 멤버
I want to 우리 기도방의 연속 기도 기록을 보고 싶다
So that 함께 기도하는 공동체의 성장을 느낄 수 있다
정의:
- "연속 기도": 매일 최소 1번 이상 기도한 경우
- 하루 기준: 자정(00:00) ~ 23:59
- 시간대: 사용자 로컬 타임존 기준
계산 방식:
- 오늘 기도함: 연속 기록 +1
- 오늘 기도 안 함: 연속 기록 유지 (내일까지 기회)
- 1일 건너뜀: 연속 기록 초기화 (0으로 리셋)
예시:
12/1 (토): 기도 → 1일 연속 🔥
12/2 (일): 기도 → 2일 연속 🔥🔥
12/3 (월): 기도 안 함 → 2일 연속 유지
12/4 (화): 기도 안 함 → 0일 연속 (초기화)
12/5 (수): 기도 → 1일 연속 🔥
표시 위치:
- 마이페이지 상단
- 기도 완료 후 축하 화면
표시 정보:
🔥 현재 연속 기도: 7일
📅 최장 연속 기록: 21일
📊 총 기도 일수: 45일 (지난 60일 중)
아이콘:
- 1-6일: 🔥
- 7-13일: 🔥🔥
- 14-20일: 🔥🔥🔥
- 21일 이상: 🔥🔥🔥🔥
정의: 기도방 멤버 중 최소 1명이 기도한 날짜를 기준으로 계산
표시 위치:
- 기도방 상세 화면 상단
표시 정보:
✨ 우리 기도방: 15일 연속 기도중!
📅 최장 기록: 30일
배지 종류:
🥉 청동: 7일 연속
🥈 은메달: 14일 연속
🥇 금메달: 21일 연속
💎 다이아몬드: 30일 연속
👑 왕관: 50일 연속
⭐ 별: 100일 연속
획득 시:
- 축하 팝업 표시
- (선택) 기도방 멤버들에게 알림
┌─────────────────────────────────┐
│ 마이페이지 │
├─────────────────────────────────┤
│ │
│ 👤 철수님 │
│ │
│ ┌─────────────────────────────┐ │
│ │ 🔥 기도 연속 기록 │ │
│ │ │ │
│ │ 현재: 7일 연속 │ │
│ │ 최장: 21일 연속 │ │
│ │ │ │
│ │ 🥉 청동 배지 획득! │ │
│ │ │ │
│ │ [상세 보기] │ │
│ └─────────────────────────────┘ │
│ │
│ 📋 프로필 수정 │
│ 🔒 비밀번호 변경 │
│ ... │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ │
│ 🙏 │
│ 기도를 완료했어요! │
│ │
│ 🔥 7일 연속 기도중 │
│ │
│ ┌─────────────────────────────┐ │
│ │ 🎉 축하합니다! │ │
│ │ 청동 배지를 획득했어요! │ │
│ │ 🥉 │ │
│ └─────────────────────────────┘ │
│ │
│ 알림을 보내시겠어요? │
│ │
│ [아니요] [알림 보내기] │
│ │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 기도 연속 기록 │
├─────────────────────────────────┤
│ │
│ 🔥 현재 연속: 7일 │
│ 📅 최장 연속: 21일 │
│ 📊 총 기도 일수: 45일 │
│ (지난 60일 중) │
│ │
├─────────────────────────────────┤
│ │
│ 📅 기도 캘린더 │
│ │
│ 월 화 수 목 금 토 일 │
│ [✓] [✓] [✓] [✓] [✓] [✓] [✓] │
│ [✓] [✓] [ ] [✓] [✓] [✓] [✓] │
│ [✓] [ ] [ ] [✓] [✓] [✓] [✓] │
│ │
│ ✓ = 기도한 날 │
│ │
├─────────────────────────────────┤
│ │
│ 🏆 획득한 배지 │
│ │
│ 🥉 청동 (7일) ✅ │
│ 🥈 은메달 (14일) ❌ │
│ 🥇 금메달 (21일) ❌ │
│ 💎 다이아몬드 (30일) ❌ │
│ │
└─────────────────────────────────┘
GET /v1/users/me/streak
Response:
{
"success": true,
"data": {
"currentStreak": 7, // 현재 연속 일수
"longestStreak": 21, // 최장 연속 일수
"totalPrayerDays": 45, // 총 기도 일수
"lastPrayedAt": "2025-12-06T10:30:00Z",
"badges": [
{
"type": "BRONZE",
"name": "청동",
"requiredDays": 7,
"achieved": true,
"achievedAt": "2025-12-05T08:00:00Z"
}
]
}
}
GET /v1/rooms/{roomId}/streak
Response:
{
"success": true,
"data": {
"currentStreak": 15,
"longestStreak": 30,
"totalPrayerDays": 60
}
}
GET /v1/users/me/prayer-calendar?year=2025&month=12
Response:
{
"success": true,
"data": {
"year": 2025,
"month": 12,
"days": [
{
"date": "2025-12-01",
"prayerCount": 3,
"prayed": true
},
{
"date": "2025-12-02",
"prayerCount": 0,
"prayed": false
}
]
}
}
-- 사용자별 연속 기록 (캐시 테이블)
CREATE TABLE user_streaks (
user_id VARCHAR(36) PRIMARY KEY,
current_streak INT NOT NULL DEFAULT 0,
longest_streak INT NOT NULL DEFAULT 0,
last_prayed_date DATE,
total_prayer_days INT NOT NULL DEFAULT 0,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 기도방별 연속 기록 (캐시 테이블)
CREATE TABLE room_streaks (
room_id VARCHAR(36) PRIMARY KEY,
current_streak INT NOT NULL DEFAULT 0,
longest_streak INT NOT NULL DEFAULT 0,
last_prayed_date DATE,
total_prayer_days INT NOT NULL DEFAULT 0,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE
);
-- 배지 획득 기록
CREATE TABLE user_badges (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
badge_type ENUM('BRONZE', 'SILVER', 'GOLD', 'DIAMOND', 'CROWN', 'STAR') NOT NULL,
achieved_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
UNIQUE KEY unique_user_badge (user_id, badge_type),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);-
연속 기록 계산:
- 매일 자정(00:00)에 전날 기도하지 않은 사용자의 연속 기록 확인
- 2일 연속 기도하지 않으면 연속 기록 초기화
- 기도 로그 테이블에서 실시간 계산
-
배지 획득:
- 연속 기록 달성 시 자동으로 배지 부여
- 한 번 획득한 배지는 연속 기록이 끊겨도 유지
- 배지 획득 시 축하 팝업 표시
-
기도 인정 기준:
- "기도 알림" 버튼 클릭 시 기도로 인정
- 하루에 여러 번 기도해도 1일로 계산
- 어떤 기도방에서든 기도하면 개인 연속 기록에 반영
-
기도방 연속 기록:
- 멤버 중 1명이라도 기도하면 카운트
- 모든 멤버가 기도하지 않은 날이 2일 연속이면 초기화
-
시간대 변경:
- 사용자의 로컬 타임존 기준으로 계산
- 여행 등으로 시간대 변경 시에도 정확히 계산
-
데이터 불일치:
- 캐시 테이블(user_streaks)과 실제 기도 로그 불일치 시 재계산
- 매일 자정에 일괄 검증 및 수정
-
서버 오류:
- 연속 기록 계산 실패 시 기존 값 유지
- 다음 계산 주기에 재시도
-
Phase 1 (MVP):
- 개인 연속 기록 계산 및 표시
- 기본 배지 시스템 (청동, 은메달, 금메달)
-
Phase 2:
- 기도 캘린더
- 기도방 연속 기록
-
Phase 3:
- 추가 배지 (다이아몬드, 왕관, 별)
- 배지 획득 알림 및 공유 기능
목적: 사용자 자신의 기도 생활을 한눈에 파악하고, 기도 패턴을 분석하여 더 나은 기도 생활을 돕습니다.
핵심 가치: 자기 성찰, 성장, 동기부여
As a 사용자
I want to 내 기도 생활을 통계로 보고 싶다
So that 나의 기도 습관을 파악하고 개선할 수 있다
As a 사용자
I want to 어떤 기도방에서 얼마나 활동했는지 보고 싶다
So that 더 관심을 가져야 할 기도방을 알 수 있다
기본 통계:
- 총 기도 횟수 (전체 기간)
- 이번 주 기도 횟수
- 이번 달 기도 횟수
- 평균 하루 기도 횟수 (지난 30일)
연속 기록:
- 현재 연속 기도 일수
- 최장 연속 기도 일수
- 총 기도한 일수
기도방별 통계:
- 가장 활발한 기도방
- 기도방별 기도 횟수
- 기도방별 기도 비율
기도 시간대 분석:
- 주로 기도하는 시간대 (아침/점심/저녁/밤)
- 시간대별 기도 횟수 분포
주간/월간 트렌드:
- 지난 7일간 기도 횟수 그래프
- 지난 30일간 기도 횟수 그래프
- 전월 대비 증감률
기간 옵션:
- 오늘
- 이번 주
- 이번 달
- 지난 30일
- 지난 3개월
- 전체
표시 정보:
최근 기도 활동:
- 12/6 10:30 - "건강을 위한 기도" (가족 기도방)
- 12/6 08:00 - "시험 합격을 위한 기도" (친구 기도방)
- 12/5 21:00 - "새 직장을 위한 기도" (개인 기도방)
┌─────────────────────────────────┐
│ 나의 기도 대시보드 │
├─────────────────────────────────┤
│ │
│ 📊 이번 달 통계 │
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 25 │ │ 7 │ │ 3.2 │ │
│ │기도 │ │ 일 │ │평균 │ │
│ └─────┘ └─────┘ └─────┘ │
│ 총 횟수 연속 하루 │
│ │
├─────────────────────────────────┤
│ │
│ 📈 주간 기도 트렌드 │
│ │
│ 기도 │
│ 횟수 │
│ 5│ ┌─┐ │
│ 4│ │ │ ┌─┐ │
│ 3│ ┌─┐ │ │ ┌─┐│ │ │
│ 2│ │ │ │ │ │ ││ │┌─┐ │
│ 1│ │ │ │ │ │ ││ ││ │┌─┐ │
│ 0└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─> │
│ 월 화 수 목 금 토 일 │
│ │
├─────────────────────────────────┤
│ │
│ 🏠 기도방별 활동 │
│ │
│ 가족 기도방 12번 (48%) │
│ ████████████░░░░░░░░░░░░░ │
│ │
│ 친구 기도방 8번 (32%) │
│ ████████░░░░░░░░░░░░░░░░ │
│ │
│ 교회 기도방 5번 (20%) │
│ █████░░░░░░░░░░░░░░░░░░░ │
│ │
├─────────────────────────────────┤
│ │
│ 🕐 주로 기도하는 시간 │
│ │
│ ┌─────────────────────────────┐ │
│ │ 아침 (6-12시) 30% │ │
│ │ 점심 (12-18시) 20% │ │
│ │ 저녁 (18-22시) 40% ⭐ │ │
│ │ 밤 (22-6시) 10% │ │
│ └─────────────────────────────┘ │
│ │
├─────────────────────────────────┤
│ │
│ 📜 최근 기도 활동 │
│ │
│ 12/6 10:30 │
│ 건강을 위한 기도 (가족 기도방) │
│ │
│ 12/6 08:00 │
│ 시험 합격 (친구 기도방) │
│ │
│ 12/5 21:00 │
│ 새 직장 (개인 기도방) │
│ │
│ [더 보기] │
│ │
└─────────────────────────────────┘
옵션 1: 마이페이지에 새 탭 추가
[기도방 목록] [마이페이지]
↓
[프로필] [통계]
옵션 2: 마이페이지 상단에 카드 형태로 배치
마이페이지
├─ 나의 기도 통계 (카드)
├─ 프로필 수정
├─ 비밀번호 변경
└─ ...
권장: 옵션 2 (접근성 좋음)
┌─────────────────────────────────┐
│ 상세 통계 (12월) │
├─────────────────────────────────┤
│ │
│ 기간 선택: │
│ [오늘] [이번주] [이번달] [전체] │
│ │
├─────────────────────────────────┤
│ │
│ 📊 전체 통계 │
│ │
│ 총 기도 횟수: 128번 │
│ 평균 하루: 4.1번 │
│ 가장 많이 기도한 날: 12/15 (8번)│
│ │
├─────────────────────────────────┤
│ │
│ 📈 월간 트렌드 │
│ │
│ [그래프 표시] │
│ │
│ 전월 대비: +15% ⬆️ │
│ │
├─────────────────────────────────┤
│ │
│ 🏆 이번 달 성과 │
│ │
│ ✅ 청동 배지 획득 (7일 연속) │
│ ✅ 누적 100번 기도 달성 │
│ ✅ 5개 기도방에서 활동 │
│ │
└─────────────────────────────────┘
GET /v1/users/me/dashboard?period=month
Request Parameters:
- period: today | week | month | last30days | last3months | all
Response:
{
"success": true,
"data": {
"period": "month",
"summary": {
"totalPrayerCount": 25,
"averageDailyPrayers": 3.2,
"currentStreak": 7,
"longestStreak": 21
},
"weeklyTrend": [
{ "date": "2025-12-01", "count": 3 },
{ "date": "2025-12-02", "count": 4 },
{ "date": "2025-12-03", "count": 2 }
],
"roomStats": [
{
"roomId": "room_1",
"roomName": "가족 기도방",
"prayerCount": 12,
"percentage": 48
}
],
"timeDistribution": {
"morning": 30, // 6-12시
"afternoon": 20, // 12-18시
"evening": 40, // 18-22시
"night": 10 // 22-6시
},
"recentActivities": [
{
"prayerId": "prayer_1",
"prayerTitle": "건강을 위한 기도",
"roomName": "가족 기도방",
"prayedAt": "2025-12-06T10:30:00Z"
}
]
}
}
GET /v1/users/me/dashboard/comparison?currentMonth=2025-12&previousMonth=2025-11
Response:
{
"success": true,
"data": {
"current": {
"month": "2025-12",
"totalPrayers": 25,
"activeDays": 15
},
"previous": {
"month": "2025-11",
"totalPrayers": 20,
"activeDays": 12
},
"change": {
"prayerCountChange": 5,
"prayerCountChangePercent": 25,
"activeDaysChange": 3,
"activeDaysChangePercent": 25
}
}
}
-- 사용자별 일일 통계 (집계 테이블)
CREATE TABLE user_daily_stats (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
date DATE NOT NULL,
prayer_count INT NOT NULL DEFAULT 0,
room_count INT NOT NULL DEFAULT 0, -- 활동한 기도방 수
morning_count INT NOT NULL DEFAULT 0,
afternoon_count INT NOT NULL DEFAULT 0,
evening_count INT NOT NULL DEFAULT 0,
night_count INT NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_user_date (user_id, date),
INDEX idx_user_id (user_id),
INDEX idx_date (date),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 사용자별 월간 통계 (집계 테이블)
CREATE TABLE user_monthly_stats (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
year INT NOT NULL,
month INT NOT NULL,
total_prayer_count INT NOT NULL DEFAULT 0,
active_days INT NOT NULL DEFAULT 0,
average_daily_prayers DECIMAL(4, 2) NOT NULL DEFAULT 0,
most_active_room_id VARCHAR(36),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_user_month (user_id, year, month),
INDEX idx_user_id (user_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (most_active_room_id) REFERENCES rooms(id) ON DELETE SET NULL
);-
데이터 집계:
- 매일 자정에 전날 데이터 집계하여
user_daily_stats저장 - 매월 1일에 전월 데이터 집계하여
user_monthly_stats저장 - 실시간 데이터는
prayer_logs테이블에서 조회
- 매일 자정에 전날 데이터 집계하여
-
통계 기간:
- 기본 기간: 이번 달
- 최대 조회 기간: 1년
- 전체 기간은 월간 통계 테이블에서 집계
-
프라이버시:
- 개인 대시보드는 본인만 조회 가능
- 다른 사용자의 통계는 공개하지 않음
-
성능 최적화:
- 일일/월간 집계 테이블 활용
- 캐싱 적용 (1시간 TTL)
-
데이터 없음:
- 기도 기록이 없는 경우 빈 상태 화면 표시
- 안내 메시지: "첫 기도를 시작해보세요!"
-
집계 오류:
- 집계 실패 시 실시간 계산으로 대체
- 다음 집계 주기에 재시도
-
대용량 데이터:
- 페이지네이션 적용 (최근 활동)
- 그래프는 최대 90일로 제한
-
Phase 1 (MVP):
- 기본 통계 (총 횟수, 연속 기록)
- 주간 트렌드 그래프
- 기도방별 통계
-
Phase 2:
- 시간대별 분석
- 월간 비교
- 최근 활동 타임라인
-
Phase 3:
- 월간 리포트 생성
- 목표 설정 및 달성률
- 개인화된 인사이트 제공
목적: 특정 날짜까지 기도할 제목을 설정하고, 기간이 지나면 자동으로 정리하여 기도 제목 관리를 용이하게 합니다.
핵심 가치: 관리 편의성, 정리정돈, 목표 지향
As a 기도 제목 작성자
I want to 기도 제목에 만료일을 설정하고 싶다
So that 특정 기간까지만 기도할 제목을 자동으로 관리할 수 있다
As a 기도 제목 작성자
I want to 만료된 기도 제목을 따로 보관하고 싶다
So that 나중에 기도 응답 여부를 확인할 수 있다
설정 옵션:
- 만료일 없음 (기본값)
- 오늘
- 내일
- 1주일 후
- 1개월 후
- 사용자 지정 날짜
설정 위치:
- 기도 제목 작성 시
- 기도 제목 수정 시
입력 방식:
- 캘린더 UI
- 날짜 선택 피커
만료 시점:
- 설정한 날짜의 23:59:59에 만료
- 또는: 다음 날 00:00:00에 만료 (권장)
만료 후 처리:
Option A (아카이브):
- "완료된 기도" 섹션으로 자동 이동
- 조회는 가능하지만 수정 불가
- 기도 알림은 불가
Option B (숨김):
- 기본 목록에서 숨김
- "만료된 기도" 필터로 조회 가능
- 복원 가능
Option C (삭제):
- 자동 삭제 (권장하지 않음)
권장: Option A (아카이브 방식)
알림 시점:
- 만료 1일 전
- 만료 당일 아침 (08:00)
- 만료 직후
알림 내용:
1일 전: "내일 만료되는 기도 제목이 있어요: '시험 합격을 위한 기도'"
당일: "오늘 만료되는 기도 제목이 있어요: '시험 합격을 위한 기도'"
만료 후: "'시험 합격을 위한 기도'가 만료되었어요. 응답받으셨나요?"
알림 수신자:
- 기도 제목 작성자만
기도 목록 화면:
┌─────────────────────────────────┐
│ 시험 합격을 위한 기도 │
│ 작성자: 철수 · D-3 ⏰ │
└─────────────────────────────────┘
기도 상세 화면:
시험 합격을 위한 기도
작성자: 철수 · 2025-12-01
⏰ 만료일: 2025-12-10 (D-3)
만료 임박 표시:
- D-7 이상: 일반 표시
- D-3 ~ D-6: 노란색 표시 🟡
- D-0 ~ D-2: 빨간색 표시 🔴
- 만료됨: 회색 표시 ⚪
만료 시 선택:
┌─────────────────────────────────┐
│ 기도 제목이 만료되었어요 │
├─────────────────────────────────┤
│ │
│ '시험 합격을 위한 기도' │
│ │
│ 기도 응답을 받으셨나요? │
│ │
│ [응답받음 ✅] │
│ [아직 기다리는 중] │
│ [나중에 답변] │
│ │
└─────────────────────────────────┘
응답 기록 시:
- 응답받음: "응답받음" 배지 표시
- 아직 기다리는 중: 만료일 연장 옵션 제공
- 나중에 답변: 나중에 다시 물어봄
위치: 기도방 상세 화면
필터:
[진행중] [완료됨] [전체]
완료된 기도 목록:
┌─────────────────────────────────┐
│ 완료된 기도 제목 │
├─────────────────────────────────┤
│ │
│ ✅ 시험 합격을 위한 기도 │
│ 응답받음 · 12/10 완료 │
│ │
│ ⏰ 건강을 위한 기도 │
│ 기다리는 중 · 12/05 완료 │
│ │
│ 📌 새 직장을 위한 기도 │
│ 만료됨 · 11/30 완료 │
│ │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 기도 제목 작성하기 │
├─────────────────────────────────┤
│ │
│ 제목 │
│ ┌─────────────────────────────┐ │
│ │ 시험 합격을 위한 기도 │ │
│ └─────────────────────────────┘ │
│ │
│ ⏰ 만료일 설정 (선택) │
│ ┌─────────────────────────────┐ │
│ │ 만료일 없음 [변경] ▼ │ │
│ └─────────────────────────────┘ │
│ │
│ [변경] 클릭 시: │
│ ┌─────────────────────────────┐ │
│ │ ○ 만료일 없음 │ │
│ │ ○ 오늘 │ │
│ │ ○ 내일 │ │
│ │ ○ 1주일 후 │ │
│ │ ○ 1개월 후 │ │
│ │ ○ 사용자 지정 📅 │ │
│ └─────────────────────────────┘ │
│ │
│ 기도할 멤버 선택 │
│ ... │
│ │
│ [취소] [저장] │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 만료일 선택 │
├─────────────────────────────────┤
│ │
│ 2025년 12월 │
│ < ─────────────────── > │
│ │
│ 일 월 화 수 목 금 토 │
│ 1 2 3 4 5 6 7 │
│ 8 9 10 11 12 13 14 │
│ 15 16 17 18 19 20 21 │
│ 22 23 24 25 26 27 28 │
│ 29 30 31 │
│ │
│ 선택된 날짜: 2025-12-10 │
│ (오늘로부터 3일 후) │
│ │
│ [취소] [확인] │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 🔔 알림 │
├─────────────────────────────────┤
│ │
│ ⏰ 내일 만료되는 기도 제목 │
│ │
│ '시험 합격을 위한 기도'가 │
│ 내일(12/10) 만료됩니다. │
│ │
│ 만료일을 연장하시겠어요? │
│ │
│ [아니요] [연장하기] │
│ │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 🙏 기도 응답 기록 │
├─────────────────────────────────┤
│ │
│ '시험 합격을 위한 기도'가 │
│ 만료되었습니다. │
│ │
│ 기도 응답을 받으셨나요? │
│ │
│ ┌─────────────────────────────┐ │
│ │ ✅ 응답받음 │ │
│ │ 감사와 간증을 남겨주세요 │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ ⏰ 아직 기다리는 중 │ │
│ │ 만료일을 연장할 수 있어요 │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ 📝 나중에 답변 │ │
│ │ 다음에 다시 물어볼게요 │ │
│ └─────────────────────────────┘ │
│ │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 가족 기도방 │
├─────────────────────────────────┤
│ │
│ [진행중] [완료됨] [전체] │
│ ▔▔▔▔▔ │
│ │
│ ✅ 시험 합격을 위한 기도 │
│ 응답받음 · 12/10 완료 │
│ ─────────────────────── │
│ "합격했습니다! 감사해요 🙏" │
│ │
│ ⏰ 건강을 위한 기도 │
│ 기다리는 중 · 12/05 완료 │
│ │
│ 📌 새 직장을 위한 기도 │
│ 답변 없음 · 11/30 완료 │
│ │
└─────────────────────────────────┘
PATCH /v1/prayers/{prayerId}/expiration
Request Body:
{
"expiresAt": "2025-12-10T23:59:59Z" // null이면 만료일 제거
}
Response:
{
"success": true,
"data": {
"prayerId": "prayer_123",
"expiresAt": "2025-12-10T23:59:59Z",
"daysRemaining": 3
}
}
POST /v1/prayers/{prayerId}/expire
Request Body:
{
"answerStatus": "ANSWERED" | "WAITING" | "NO_ANSWER",
"testimony": "합격했습니다! 감사해요" // answerStatus가 ANSWERED일 때만
}
Response:
{
"success": true,
"data": {
"prayerId": "prayer_123",
"status": "EXPIRED",
"answerStatus": "ANSWERED",
"expiredAt": "2025-12-10T23:59:59Z"
}
}
GET /v1/rooms/{roomId}/prayers?status=expired&limit=20&offset=0
Request Parameters:
- status: active | expired | all
- limit: 페이지 크기 (기본 20)
- offset: 오프셋 (기본 0)
Response:
{
"success": true,
"data": {
"total": 15,
"prayers": [
{
"prayerId": "prayer_123",
"title": "시험 합격을 위한 기도",
"authorName": "철수",
"expiresAt": "2025-12-10T23:59:59Z",
"expiredAt": "2025-12-10T23:59:59Z",
"status": "EXPIRED",
"answerStatus": "ANSWERED",
"testimony": "합격했습니다!"
}
]
}
}
GET /v1/users/me/prayers/expiring-soon?days=3
Request Parameters:
- days: 며칠 이내 만료 (기본 3일)
Response:
{
"success": true,
"data": {
"prayers": [
{
"prayerId": "prayer_123",
"title": "시험 합격을 위한 기도",
"expiresAt": "2025-12-10T23:59:59Z",
"daysRemaining": 2,
"roomName": "가족 기도방"
}
]
}
}
-- prayers 테이블에 컬럼 추가
ALTER TABLE prayers ADD COLUMN expires_at TIMESTAMP NULL;
ALTER TABLE prayers ADD COLUMN status ENUM('ACTIVE', 'EXPIRED') NOT NULL DEFAULT 'ACTIVE';
ALTER TABLE prayers ADD COLUMN answer_status ENUM('ANSWERED', 'WAITING', 'NO_ANSWER') NULL;
ALTER TABLE prayers ADD COLUMN testimony TEXT NULL;
ALTER TABLE prayers ADD COLUMN expired_at TIMESTAMP NULL;
-- 인덱스 추가
CREATE INDEX idx_expires_at ON prayers(expires_at);
CREATE INDEX idx_status ON prayers(status);
-- 만료 알림 기록 테이블
CREATE TABLE prayer_expiration_notifications (
id VARCHAR(36) PRIMARY KEY,
prayer_id VARCHAR(36) NOT NULL,
user_id VARCHAR(36) NOT NULL,
notification_type ENUM('ONE_DAY_BEFORE', 'EXPIRATION_DAY', 'EXPIRED') NOT NULL,
sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_prayer_id (prayer_id),
INDEX idx_user_id (user_id),
FOREIGN KEY (prayer_id) REFERENCES prayers(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);-
만료일 설정:
- 작성자만 만료일 설정/수정 가능
- 과거 날짜는 설정 불가
- 만료일은 언제든 변경/제거 가능
-
만료 처리:
- 매일 자정(00:00)에 배치 작업으로 만료 처리
- 만료된 기도는
status = 'EXPIRED'로 변경 - 만료된 기도는 기본 목록에서 숨김
-
만료 알림:
- 만료 1일 전, 당일, 만료 후 총 3번 알림
- 각 알림은 한 번만 발송 (중복 방지)
- 알림 수신 여부는 기도방 알림 설정 따름
-
기도 응답 기록:
- 만료 후 7일 이내에 응답 기록 가능
- 응답 기록은 선택 사항 (강제 아님)
- 응답 기록 후에도 수정 가능
-
완료된 기도 조회:
- 기도방 멤버만 조회 가능
- 삭제되지 않고 아카이브로 보관
- 만료 후 1년 이상 지나면 자동 삭제 (선택)
-
만료일 연장:
- 만료 후에도 연장 가능
- 연장 시
status = 'ACTIVE'로 복원
-
기도방 나가기:
- 기도방을 나가도 내가 작성한 기도 제목의 만료 알림은 받음
-
네트워크 오류:
- 만료 처리 실패 시 다음 배치 작업에서 재시도
- 알림 발송 실패 시 재시도 (최대 3번)
-
사용자 삭제:
- 사용자 탈퇴 시 작성한 기도 제목도 삭제
- 또는: 작성자 이름을 "알 수 없음"으로 변경하고 보관
-
Phase 1 (MVP):
- 만료일 설정 UI
- 만료 처리 배치 작업
- 만료된 기도 필터
-
Phase 2:
- 만료 알림
- 만료일 연장 기능
- D-Day 표시
-
Phase 3:
- 기도 응답 기록
- 간증 작성 및 공유
- 완료된 기도 통계
Phase 1 (4주):
- Week 1-2: 함께 기도한 사람 표시
- Week 3-4: 기도 제목 자동 만료
Phase 2 (6주):
- Week 5-7: 기도 연속 기록
- Week 8-10: 개인 기도 대시보드
Phase 3 (4주):
- Week 11-12: 통합 테스트 및 버그 수정
- Week 13-14: 배포 및 모니터링
높음:
- 함께 기도한 사람 표시 (Phase 1)
- 기도 제목 자동 만료 (Phase 1)
중간: 3. 기도 연속 기록 (Phase 1-2)
낮음: 4. 개인 기도 대시보드 (Phase 2-3)
함께 기도한 사람 표시
└─> 기도 연속 기록 (기도 로그 활용)
└─> 개인 기도 대시보드 (연속 기록 표시)
기도 제목 자동 만료
└─> 독립적 (다른 기능과 의존성 없음)
- UI 라이브러리: React Native 기존 컴포넌트 활용
- 차트:
react-native-chart-kit또는victory-native(대시보드용) - 캘린더:
react-native-calendars(만료일 선택용) - 날짜 처리:
date-fns(이미 사용 중인지 확인)
-
배치 작업: Cron 또는 Node.js 스케줄러
- 만료 처리: 매일 00:00
- 통계 집계: 매일 00:30
- 알림 발송: 매일 08:00
-
캐싱: Redis (대시보드 데이터 캐싱)
- 푸시 알림: Firebase Cloud Messaging (FCM)
- 만료 알림
- 배지 획득 알림 (선택)
- 데이터 집계: 일일/월간 통계 테이블 활용
- 페이지네이션: 목록 조회 시 필수
- 캐싱: 대시보드 데이터 1시간 캐시
- 인덱스: 날짜, 사용자ID 등 자주 조회하는 컬럼에 인덱스 추가
- 연속 기록 계산 로직
- 만료일 처리 로직
- 통계 집계 로직
- API 엔드포인트
- 배치 작업
- 알림 발송
- 기도 작성 → 기도 완료 → 기록 확인
- 만료일 설정 → 만료 처리 → 응답 기록
- 대시보드 조회
목적: 일정 기간 동안 앱에 접속하지 않거나 기도하지 않은 사용자에게 알림을 보내어 다시 기도 생활에 참여하도록 유도합니다.
핵심 가치: 지속적인 참여, 습관 형성, 공동체 유지
As a 사용자
I want to 한동안 기도하지 않았을 때 알림을 받고 싶다
So that 기도 습관을 잃지 않고 지속할 수 있다
As a 기도방 운영자
I want to 비활성 멤버들이 다시 참여하도록 유도하고 싶다
So that 기도방이 활성화되고 공동체가 유지될 수 있다
트리거 조건:
조건 A: 마지막 접속 후 2주 경과
조건 B: 마지막 기도 완료 후 2주 경과
→ 둘 중 하나라도 만족 시 알림 발송
제외 조건:
- 앱을 삭제한 사용자 (FCM 토큰 무효화)
- 알림을 끈 사용자
- 이미 재참여 알림을 받은 후 7일 이내인 사용자 (중복 방지)
메시지 종류:
Option A (단순):
제목: "기도 시간이에요 🙏"
내용: "오랜만이에요! 함께 기도해요"
Option B (통계 포함):
제목: "기도 시간이에요 🙏"
내용: "2주간 기도방에서 15개의 새 기도 제목이 올라왔어요"
Option C (개인화):
제목: "기도 시간이에요 🙏"
내용: "철수님, 가족 기도방에서 기다리고 있어요"
Option D (연속 기록):
제목: "7일 연속 기록이 끊어질 뻔했어요 😢"
내용: "다시 시작해볼까요?"
권장: Option B (통계 포함) - 구체적인 정보로 참여 동기 부여
발송 시각:
- 매일 오전 8시 (사용자 로컬 시간대 기준)
- 또는: 사용자가 가장 활발했던 시간대 (개인화)
발송 주기:
1차: 마지막 활동 후 14일
2차: 1차 알림 후 7일 (여전히 비활성 시)
3차: 2차 알림 후 7일 (여전히 비활성 시)
→ 이후 발송 중단 (스팸 방지)
추적 데이터:
interface UserActivity {
userId: string;
lastLoginAt: Date; // 마지막 접속 시각
lastPrayerAt: Date; // 마지막 기도 완료 시각
lastNotificationSentAt: Date; // 마지막 재참여 알림 발송 시각
notificationCount: number; // 재참여 알림 발송 횟수 (최대 3회)
isActive: boolean; // 활성 상태
}활동 업데이트:
- 앱 접속 시
lastLoginAt업데이트 - 기도 완료 시
lastPrayerAt업데이트 - 알림 발송 시
lastNotificationSentAt,notificationCount업데이트
딥링크 설정:
pray-together://reengagement
→ 앱 실행 후 기도방 목록 화면으로 이동
→ (선택) 웰컴백 메시지 표시
웰컴백 메시지:
┌─────────────────────────────────┐
│ │
│ 🙏 │
│ 다시 오신 것을 환영해요! │
│ │
│ 2주간 기도방에서 일어난 일: │
│ • 15개의 새 기도 제목 │
│ • 38번의 기도 완료 │
│ • 3명의 새 멤버 │
│ │
│ [시작하기] │
│ │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 🙏 기도함께 │
├─────────────────────────────────┤
│ 기도 시간이에요 🙏 │
│ │
│ 2주간 기도방에서 15개의 새 │
│ 기도 제목이 올라왔어요 │
│ │
│ 5분 전 │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ │
│ 🙏 │
│ 다시 오신 것을 환영해요! │
│ │
├─────────────────────────────────┤
│ │
│ 📊 지난 2주간 활동 │
│ │
│ 가족 기도방 │
│ • 8개의 새 기도 제목 │
│ • 철수님, 영희님이 기도했어요 │
│ │
│ 친구 기도방 │
│ • 5개의 새 기도 제목 │
│ • 민수님이 새로 참여했어요 │
│ │
│ 교회 기도방 │
│ • 2개의 새 기도 제목 │
│ │
├─────────────────────────────────┤
│ │
│ 🔥 연속 기도 기록이 │
│ 초기화되었어요 │
│ │
│ 다시 시작해볼까요? │
│ │
│ [기도하러 가기] │
│ │
└─────────────────────────────────┘
마이페이지 > 알림 설정:
┌─────────────────────────────────┐
│ 알림 설정 │
├─────────────────────────────────┤
│ │
│ 기도 알림 [ON ] │
│ 새 기도 제목 [ON ] │
│ │
│ 재참여 알림 [ON ] │
│ 한동안 기도하지 않을 때 │
│ 알림을 받습니다 │
│ │
│ 기도방 활동 알림 [ON ] │
│ ... │
│ │
└─────────────────────────────────┘
POST /v1/users/me/activity
Request Body:
{
"activityType": "LOGIN" | "PRAYER_COMPLETE"
}
Response:
{
"success": true,
"data": {
"userId": "user_123",
"lastLoginAt": "2025-12-14T08:00:00Z",
"lastPrayerAt": "2025-12-14T08:05:00Z"
}
}
GET /v1/admin/users/inactive?days=14
Request Parameters:
- days: 비활성 기준 일수 (기본 14일)
Response:
{
"success": true,
"data": {
"total": 50,
"users": [
{
"userId": "user_123",
"fcmToken": "fcm_token_xxx",
"lastLoginAt": "2025-11-30T10:00:00Z",
"lastPrayerAt": "2025-11-30T10:05:00Z",
"lastNotificationSentAt": null,
"notificationCount": 0,
"preferredLanguage": "ko",
"timezone": "Asia/Seoul"
}
]
}
}
POST /v1/users/{userId}/reengagement-notifications
Request Body:
{
"sentAt": "2025-12-14T08:00:00Z",
"messageType": "FIRST_REMINDER" | "SECOND_REMINDER" | "THIRD_REMINDER"
}
Response:
{
"success": true,
"data": {
"notificationId": "notif_123",
"userId": "user_123",
"sentAt": "2025-12-14T08:00:00Z",
"notificationCount": 1
}
}
GET /v1/users/me/welcome-back-stats?since=2025-11-30
Request Parameters:
- since: 통계 시작 날짜
Response:
{
"success": true,
"data": {
"period": {
"from": "2025-11-30T00:00:00Z",
"to": "2025-12-14T00:00:00Z",
"days": 14
},
"rooms": [
{
"roomId": "room_1",
"roomName": "가족 기도방",
"newPrayersCount": 8,
"totalPrayersCount": 38,
"newMembersCount": 0,
"activeMembersNames": ["철수", "영희"]
}
],
"totalNewPrayers": 15,
"totalNewMembers": 1
}
}
-- users 테이블에 컬럼 추가
ALTER TABLE users ADD COLUMN last_login_at TIMESTAMP NULL;
ALTER TABLE users ADD COLUMN last_prayer_at TIMESTAMP NULL;
-- 인덱스 추가
CREATE INDEX idx_last_login_at ON users(last_login_at);
CREATE INDEX idx_last_prayer_at ON users(last_prayer_at);
-- 재참여 알림 발송 기록 테이블
CREATE TABLE reengagement_notifications (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
message_type ENUM('FIRST_REMINDER', 'SECOND_REMINDER', 'THIRD_REMINDER') NOT NULL,
sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
clicked_at TIMESTAMP NULL,
INDEX idx_user_id (user_id),
INDEX idx_sent_at (sent_at),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- FCM 토큰 테이블 (기존에 없다면)
CREATE TABLE fcm_tokens (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
token VARCHAR(255) NOT NULL,
device_type ENUM('IOS', 'ANDROID') NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
UNIQUE KEY unique_token (token),
INDEX idx_user_id (user_id),
INDEX idx_is_active (is_active),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);-
활동 추적:
- 앱 실행 시
last_login_at자동 업데이트 - 기도 완료 버튼 클릭 시
last_prayer_at자동 업데이트 - 두 시각 중 더 최근 것을 기준으로 비활성 판단
- 앱 실행 시
-
알림 발송 조건:
- 마지막 활동 후 정확히 14일째 되는 날 발송
- 이미 3회 발송했다면 더 이상 발송 안 함
- 마지막 알림 발송 후 7일 이내면 발송 안 함
- FCM 토큰이 유효한 사용자만 발송
-
알림 설정 존중:
- 사용자가 재참여 알림을 꺼놨다면 발송 안 함
- 전체 알림을 꺼놨다면 발송 안 함
- 방해 금지 시간대에는 발송 안 함
-
FCM 토큰 관리:
- NotRegistered 에러 발생 시 토큰 비활성화
- InvalidRegistration 에러 발생 시 토큰 삭제
- 사용자 재로그인 시 토큰 갱신
-
재활성화 처리:
- 알림 클릭 후 앱 접속 시
notificationCount초기화 - 스스로 돌아온 경우도
notificationCount초기화
- 알림 클릭 후 앱 접속 시
// 매일 오전 8시 실행 (Cron: 0 8 * * *)
async function sendReengagementNotifications() {
const fourteenDaysAgo = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000);
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
// 비활성 사용자 조회
const inactiveUsers = await db.query(`
SELECT u.id, u.last_login_at, u.last_prayer_at, f.token as fcm_token
FROM users u
INNER JOIN fcm_tokens f ON u.id = f.user_id AND f.is_active = TRUE
LEFT JOIN reengagement_notifications rn ON u.id = rn.user_id
WHERE (u.last_login_at < ? OR u.last_prayer_at < ?)
AND (rn.sent_at IS NULL OR rn.sent_at < ?)
AND (
SELECT COUNT(*)
FROM reengagement_notifications
WHERE user_id = u.id
) < 3
`, [fourteenDaysAgo, fourteenDaysAgo, sevenDaysAgo]);
// 사용자별로 통계 조회 및 푸시 발송
for (const user of inactiveUsers) {
try {
// 웰컴백 통계 조회
const stats = await getWelcomeBackStats(user.id);
// FCM 푸시 발송
await admin.messaging().send({
token: user.fcm_token,
notification: {
title: "기도 시간이에요 🙏",
body: `2주간 기도방에서 ${stats.totalNewPrayers}개의 새 기도 제목이 올라왔어요`
},
data: {
type: "REENGAGEMENT",
deeplink: "pray-together://reengagement"
}
});
// 알림 발송 기록
await recordNotification(user.id);
} catch (error) {
if (error.code === 'messaging/registration-token-not-registered') {
// FCM 토큰 비활성화
await deactivateFcmToken(user.fcm_token);
}
console.error(`Failed to send notification to user ${user.id}:`, error);
}
}
}// 앱 실행 시 (App.tsx)
useEffect(() => {
updateUserActivity('LOGIN');
}, []);
// 기도 완료 시
const handlePrayerComplete = async () => {
await updateUserActivity('PRAYER_COMPLETE');
// ... 기존 로직
};-
FCM 토큰 관련:
- 토큰이 없는 사용자: 알림 발송 건너뜀
- 토큰 무효화: 자동으로 비활성화 후 다음 로그인 시 재등록
- 여러 기기: 모든 활성 토큰에 발송
-
시간대 처리:
- 사용자의 로컬 시간대 기준으로 오전 8시 계산
- 시간대 정보 없으면 서버 시간대 사용
-
통계 조회 실패:
- 통계 조회 실패 시 기본 메시지로 발송
- "오랜만이에요! 함께 기도해요"
-
네트워크 오류:
- 발송 실패 시 재시도 (최대 3번)
- 계속 실패 시 로그 기록 후 건너뜀
-
사용자 탈퇴:
- 탈퇴한 사용자는 자동으로 제외
- FCM 토큰도 함께 삭제
- 알림 발송 수 (일별)
- 알림 클릭율
- 재활성화율 (알림 후 7일 내 활동)
- 발송 실패 수 (토큰 무효화 등)
┌─────────────────────────────────┐
│ 재참여 알림 현황 │
├─────────────────────────────────┤
│ │
│ 📊 이번 주 통계 │
│ │
│ 발송 수: 150건 │
│ 클릭율: 35% (53건) │
│ 재활성화율: 28% (42건) │
│ 실패율: 5% (8건) │
│ │
├─────────────────────────────────┤
│ │
│ 📈 주간 트렌드 │
│ [그래프] │
│ │
└─────────────────────────────────┘
-
Phase 1 (MVP):
- 사용자 활동 추적 (
last_login_at,last_prayer_at) - 재참여 알림 배치 작업
- 기본 푸시 알림 발송
- 사용자 활동 추적 (
-
Phase 2:
- 웰컴백 통계 API
- 개인화된 메시지
- 알림 클릭 딥링크
-
Phase 3:
- 웰컴백 화면 UI
- 분석 및 모니터링 대시보드
- A/B 테스트 (메시지 최적화)
테스트 항목:
- 메시지 톤앤매너 (친근함 vs 격식)
- 통계 포함 여부
- 발송 시각 (아침 vs 저녁)
- 발송 주기 (2주 vs 1주)
측정 지표:
- 클릭율 (CTR)
- 재활성화율
- 7일 리텐션
Phase 1 (5주):
- Week 1-2: 함께 기도한 사람 표시
- Week 3-4: 기도 제목 자동 만료
- Week 5: 재참여 유도 알림 (MVP)
Phase 2 (6주):
- Week 6-8: 기도 연속 기록
- Week 9-11: 개인 기도 대시보드
Phase 3 (4주):
- Week 12-13: 통합 테스트 및 버그 수정
- Week 14-15: 배포 및 모니터링
높음:
- 함께 기도한 사람 표시 (Phase 1)
- 기도 제목 자동 만료 (Phase 1)
- 재참여 유도 알림 (Phase 1) ⭐ NEW
중간: 4. 기도 연속 기록 (Phase 1-2)
낮음: 5. 개인 기도 대시보드 (Phase 2-3)
함께 기도한 사람 표시
└─> 기도 연속 기록 (기도 로그 활용)
└─> 개인 기도 대시보드 (연속 기록 표시)
기도 제목 자동 만료
└─> 독립적
재참여 유도 알림
└─> 기도 로그 활용 (last_prayer_at)
└─> FCM 토큰 관리 (기존)
- UI 라이브러리: React Native 기존 컴포넌트 활용
- 차트:
react-native-chart-kit또는victory-native(대시보드용) - 캘린더:
react-native-calendars(만료일 선택용) - 날짜 처리:
date-fns(이미 사용 중) - 딥링크:
expo-linking(재참여 알림용) ⭐ NEW
-
배치 작업: Cron 또는 Node.js 스케줄러
- 만료 처리: 매일 00:00
- 통계 집계: 매일 00:30
- 재참여 알림: 매일 08:00 ⭐ NEW
-
캐싱: Redis (대시보드 데이터 캐싱)
-
푸시 알림: Firebase Cloud Messaging (FCM)
- 만료 알림
- 재참여 알림 ⭐ NEW
- 배지 획득 알림 (선택)
-
딥링크: Custom URL Scheme ⭐ NEW
pray-together://reengagementpray-together://room/{roomId}
- 데이터 집계: 일일/월간 통계 테이블 활용
- 페이지네이션: 목록 조회 시 필수
- 캐싱: 대시보드 데이터 1시간 캐시
- 인덱스: 날짜, 사용자ID 등 자주 조회하는 컬럼에 인덱스 추가
- 배치 크기: 재참여 알림 발송 시 100명씩 처리 ⭐ NEW
- 연속 기록 계산 로직
- 만료일 처리 로직
- 통계 집계 로직
- 재참여 조건 판단 로직 ⭐ NEW
- FCM 토큰 무효화 처리 ⭐ NEW
- API 엔드포인트
- 배치 작업
- 알림 발송
- 딥링크 동작 ⭐ NEW
- 기도 작성 → 기도 완료 → 기록 확인
- 만료일 설정 → 만료 처리 → 응답 기록
- 대시보드 조회
- 14일 비활성 → 알림 수신 → 앱 재접속 ⭐ NEW
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 1.0 | 2025-12-06 | AI | 초안 작성 |
| 1.1 | 2025-12-14 | AI | 재참여 유도 알림 기능 추가 |
문서 끝