알림 기능, 생각보다 쉬웠다
할 일 시작 전에 알림을 보내는 기능. 어려울 줄 알았는데 계산된 필드 하나로 끝났습니다.
알림 기능, 생각보다 쉬웠다
Fecit에서 할 일에 시작 시간을 설정할 수 있습니다. 오후 2시에 회의, 저녁 7시에 운동. 그런데 시작 시간을 설정해놓고 까먹으면 의미가 없습니다. 알림이 필요했습니다.
알림. 이 단어만 들으면 복잡한 게 떠오릅니다. 스케줄러, 타임존 처리, 반복 알림, 로컬 알림과 푸시 알림의 차이. 솔직히 좀 미루고 있었습니다.
그런데 막상 해보니까 의외로 간단했습니다.
핵심 아이디어: 계산된 필드
처음에는 매분마다 실행되는 배치 작업에서 “현재 시간 + offset”에 해당하는 task를 찾으려고 했습니다. 5분 전, 15분 전, 30분 전, 1시간 전… offset마다 쿼리를 날려야 했습니다.
그런데 생각해보니까 더 간단한 방법이 있었습니다.
reminder_at = start_date - reminder_offset
task에 reminder_offset(몇 분 전)을 저장할 때, reminder_at(실제 알림 시간)을 미리 계산해서 같이 저장합니다. 그러면 배치 작업은 이 한 줄이면 됩니다:
task_record_collection.find({
'reminder_at': {'$gte': now, '$lt': now_end},
'state': TaskState.REGISTERED,
})
offset 종류가 몇 개든 상관없습니다. 새 옵션을 추가해도 쿼리는 그대로입니다. 인덱스도 reminder_at 하나만 걸면 됩니다.
start_date가 바뀌면?
reminder_at은 start_date와 reminder_offset 두 값에서 계산됩니다. 둘 중 하나가 바뀌면 reminder_at도 재계산해야 합니다.
reminder_offset변경 시:start_date를 읽어서 재계산start_date변경 시:reminder_offset을 읽어서 재계산- 둘 중 하나가 없으면:
reminder_at = null(알림 안 감)
이것만 지키면 항상 정확합니다.
UI
알림 설정 UI는 시작 시간이 있을 때만 보입니다. 시작 시간이 없으면 알림도 의미가 없으니까요.
옵션은 간단합니다:
- 시작 시간
- 시작 시간 5분 전
- 시작 시간 15분 전
- 시작 시간 30분 전
- 시작 시간 1시간 전
- 시작 시간 하루 전
선택하면 서버에 저장되고, reminder_at이 자동으로 계산됩니다. 해제하고 싶으면 옆의 X 버튼을 누르면 됩니다. 시작 시간 옆의 X 버튼과 같은 패턴입니다.
돌아보면
어려울 거라고 지레 겁먹었던 것 같습니다. 핵심은 “쿼리를 단순하게 만들기 위해 계산된 필드를 미리 저장한다”는 것뿐이었습니다. 나머지는 기존 패턴을 따라가면 됐습니다.
미루지 말고 해볼걸.