푸시알림 설계 - depromeet/Took-BE GitHub Wiki
FCM + 스케쥴러 통해 특정 시간에 알림 전송하는 플로우로 구현 예정
역할 정의
- 이벤트 + 비동기 : 알림 예약 역할
- 스케쥴러 : 알림 전송 트리거 역할
- FCM : 알림 전송 역할
푸시알림 종류
- 흥미로운 알림
- 명함 받은 시점에 예약 → 특정 시점(스케쥴러)에 전송
- 한줄메모 알림
- 명함 받은 시점에 예약 → 특정 시점(스케쥴러)에 전송
- 시스템 알림 (사용자 전체 대상)
- 기능 추가되는 시점에 tokens 방식으로 전체 알림 전송
알림 전송까지의 플로우
- [명함 받기]
POST /api/card/receive
명함을 받음 → ReceivedCard 테이블에 (user, card) 저장 - [알림 예약] 이벤트 + 비동기 Notification 테이블에 (user, received_card, type, willSendAt, createdAt) 저장
- type 설정 기준
- 타입 “흥미-공통점” = 관심도메인, 세부직군, 소속정보 중 둘 이상이 같은 경우
- 타입 “흥미-상세-관심도메인” = 관심도메인 하나만 같은 경우
- 타입 “흥미-상세-세부직군” = 세부직군 하나만 같은 경우
- 타입 “흥미-상세-소속정보” = 소속정보 하나만 같은 경우
- 타입 “한줄메모” = 관심도메인, 세부직군, 소속정보 모두 다른 경우
- willSendAt 설정 기준
- 받은 시각이 00시 ~ 06시라면 → 오늘 10시에 전송
- 받은 시각이 06시 ~ 21시라면 → 오늘 22시에 전송
- 받은 시각이 21시 ~ 00시라면 → 내일 10시에 전송
- type 설정 기준
- [알림 전송] 스케쥴러 돌려서 특정 시간에 알림 전송
- 확장 가능성 고려
- 기존: 10:00, 22:00 알림 발송
- 확장: 하루 1번 알림 수신 & 사용자가 시간 설정 가능 (1시간 단위로 합의)
- 10:00
-
Notification에서 willSendAt이 10:00인 Notification 조회
-
Notification의 User의 UserDevice 대상으로 푸시알림 허용인 디바이스에만 FCM 알림 전송 (type에 따라 내용 결정)
타입 "흥미-상세-ㅇㅇㅇ" : 방금 나와 ‘ㅇㅇㅇ’이(가) 같은 사람을 발견했어요! 어떤 사람인지 살펴보세요 타입 "흥미-공통점" : 방금 나와 공통점이 같은 사람을 발견했어요! 어떤 사람인지 살펴보세요 타입 "한줄메모" : 오늘 공유한 명함을 특별하게 만들어 볼까요?\n다음 만남이 훨씬 자연스러워질 거예요
-
FCM 알림 전송 시점을 Notification sendAt 필드에 저장
-
- 22:00
-
Notification에서 willSendAt이 22:00인 Notification 조회
-
Notification의 User의 UserDevice 대상으로 푸시알림 허용인 디바이스에만 FCM 알림 전송 (type에 따라 내용 결정)
타입 "흥미-상세-ㅇㅇㅇ" : 방금 나와 ‘ㅇㅇㅇ’이(가) 같은 사람을 발견했어요! 어떤 사람인지 살펴보세요 타입 "흥미-공통점" : 방금 나와 공통점이 같은 사람을 발견했어요! 어떤 사람인지 살펴보세요 타입 "한줄메모" : 오늘 공유한 명함을 특별하게 만들어 볼까요?\n다음 만남이 훨씬 자연스러워질 거예요
-
FCM 알림 전송 시점을 Notification sendAt 필드에 저장
-
- 확장 가능성 고려
흥미로운 명함 페이지 조회 방법
플로우
-
사용자가 페이지 조회
-
현재 시간에 따라 sendAt 기반으로 조회
ex) 오늘 19:00라면 → 전날 22시 알림 + 당일 10시 알림
ex) 오늘 23:00라면 → 전날 22시 알림 + 당일 10시 알림 + 당일 22시 알림
흥미로운 명함 정보 제거 시점
- 23:59
- 전날 22:00에 전송한 흥미로운명함정보 제거
- 오늘 10:00에 전송한 흥미로운명함정보 제거
알림 내역
- Notification에서 User 기반으로 30일간 명함 조회
테이블 설계
Notification
- id
- PK
- user
- user 저장하고 나중에 user의 device 조회해서 보내는 게 나을 듯
- receivedCard
- (받은명함Id, 받은명함주인Id)
- type
- 흥미-공통점, 흥미-상세-관심도메인, 흥미-상세-세부직군, 흥미-상세-소속정보, 한줄메모
- willSendAt
- 알림이 전송될 시점
- sendAt
- 실제 알림이 전송된 시점
- createdAt
- 알림을 예약한 시점
- updatedAt
고민포인트
[흥미로운 명함 페이지] 관리 장소
- notification + received_card
- 별도 테이블
결론: 기존 테이블 재활용!
- 하루이틀 조회될 데이터를 별도 테이블로 관리하는 것은 리소스 낭비라고 생각함
[전송 예정 시점] 알림 예약할 때 vs 스케쥴러 돌릴 때 ?
-
알림 예약할 때
notification 저장할 때 10:00인지 22:00인지 미리 계산해서 wil_send_at 필드에 저장
→ 나중에 사용자 지정 시점으로 기획 변경되면 사용자 지정 시점(user의 필드)을 저장
- 장점: 스케쥴러 알림 전송이 빠름
- 단점: 알림 예약이 빠름 (그런데 비동기라 크게 상관 없음) & send_at과 약간의 중복
-
스케쥴러 돌릴 때
10:00와 22:00에 스케쥴러 돌릴 때 createdAt 범위로 조회하기
→ 나중에 사용자 지정 시점으로 기획 변경되면 1시간에 한번씩 사용자 지정 시점을 user와의 join을 통해 조회
- 장점: 알림 예약이 빠름
- 단점: 스케쥴러 알림 전송이 느림
결론: 알림 예약할 때!
- 스케쥴러에게는 오직! 알림을 전송하는 역할만 부여하자.
- 사용자 지정 시점으로 기획이 변경되면, 스케쥴러가 userRepository에 대한 의존성 가짐
- 알림 예약은 비동기이므로 예약 속도에 영향을 끼치지 않음
나중에 Spring Batch 도입할래!!!
알림 설정 페이지