Skip to main content
← 블로그

Weekly Routine 알림이 오지 않았던 이유

VauDium ·

Daily는 되는데 Weekly만 안 됐습니다. 원인은 빈 칸 하나였습니다.

Weekly Routine 알림이 오지 않았던 이유

Fecit에는 루틴 기능이 있습니다. “매일 아침 8시에 약 먹기”나 “매주 월요일 9시에 회의 준비”처럼, 정해진 시간에 자동으로 태스크를 만들고 알림을 보내주는 기능입니다.

어느 날, 매주 반복되는 루틴의 알림이 오지 않는 걸 발견했습니다. 매일 반복되는 건 잘 옵니다. 매주 반복되는 것만 안 됩니다.

시스템은 돌아가고 있었다

서버는 정상이었습니다. 매분마다 “지금 이 시각에 울려야 하는 루틴이 있나?” 확인하는 Background Job도 멈추지 않고 돌아가고 있었습니다. 매일 루틴은 잘 작동하니 알림 시스템 자체가 죽은 건 아닙니다. 푸시 알림 서비스도 정상입니다.

그러면 매일 루틴과 매주 루틴의 차이는 뭘까. 딱 하나입니다. 매주 루틴에는 요일 조건이 추가됩니다. “분이 맞고, 요일도 맞으면 알림을 보내라.” 분 매칭은 매일 루틴에서 검증됐으니, 요일 매칭에서 뭔가 어긋나고 있다는 뜻입니다.

데이터베이스를 열어 보니

루틴 데이터를 직접 열어 봤습니다. 매주 루틴 15개의 요일 필드를 확인했습니다.

전부 비어 있었습니다.

월요일이어야 할 것이 빈 칸. 수요일이어야 할 것도 빈 칸. 토요일도, 목요일도 — 15개 전부 None.

요일이 비어 있으면 “오늘이 월요일인가?” 같은 조건에 절대 매칭되지 않습니다. 매분 확인해도, 매시간 확인해도, 빈 칸은 어떤 요일과도 같지 않습니다. 매주 루틴이 단 한 번도 발동하지 않았던 이유가 여기 있었습니다.

왜 비어 있었나

루틴을 등록할 때, 앱이 서버에 요일 정보를 보내도록 되어 있었습니다. “월요일”이라는 값을 앱이 보내면 서버가 그대로 저장합니다. 서버는 앱이 보내주는 값을 신뢰했습니다.

문제는 앱의 특정 등록 경로에서 요일 값이 누락되고 있었다는 겁니다. 값이 전달되지 않으니 서버는 빈 칸으로 저장했고, 에러도 나지 않았고, 아무도 눈치채지 못했습니다.

매일 루틴은 요일이 필요 없으니 이 문제가 드러나지 않았습니다. 매주 루틴을 등록하고, 해당 요일이 지나가고, 알림이 안 오는 걸 확인하기 전까지는 발견할 수 없는 종류의 버그였습니다.

고치면서 발견한 두 번째 문제

빈 칸을 채우면 끝일 줄 알았습니다. 그런데 “어떤 값으로 채워야 하지?”를 생각하는 순간, 타임존 문제가 보였습니다.

서버의 Background Job은 세계 표준시(UTC) 기준으로 동작합니다. 매분 현재 UTC 시각을 계산하고, 그 시각의 요일을 구해서 데이터베이스에 물어봅니다.

한국은 UTC보다 9시간 빠릅니다. 한국 시간 월요일 아침 8시는 UTC로는 일요일 밤 11시입니다. Job이 “오늘 일요일인 매주 루틴 있나?”라고 물으면, 한국 사용자가 “월요일”로 등록한 루틴은 매칭되지 않습니다.

다행히 시작 시각은 이미 UTC로 저장되고 있었습니다. 앱이 시각을 보낼 때 자동으로 UTC로 변환됩니다. 한국 월요일 아침 8시는 UTC 일요일 밤 11시로 저장됩니다. 분도 UTC 기준으로 저장되고, Job도 UTC로 물어보니 분 매칭은 정확했던 겁니다.

하지만 요일만은 앱이 보내는 로컬 값을 쓰고 있었습니다. 설사 값이 들어갔더라도, 한국 시간 자정~오전 9시 사이에 설정된 루틴은 UTC와 로컬의 요일이 달라서 어긋났을 겁니다.

수정: 서버가 직접 계산한다

두 가지를 바꿨습니다.

첫째, 앱이 보내는 요일 값에 의존하지 않기로 했습니다. 서버는 이미 루틴의 시작 시각을 UTC로 가지고 있습니다. 그 시각에서 요일을 직접 계산하면 됩니다. 앱이 뭘 보내든, 서버가 가진 정보로 요일을 결정합니다. UTC 시각에서 UTC 요일을 구하니 Job의 UTC 요일과 정확하게 일치합니다.

둘째, 이미 등록된 15개의 매주 루틴을 고쳐야 했습니다. 마이그레이션 스크립트를 만들어서 각 루틴의 시작 시각에서 UTC 요일을 계산하고, 빈 칸을 메웠습니다.

스크립트를 실행하니 15개 루틴이 모두 업데이트됐습니다. 빈 칸이 월요일(0), 토요일(5), 목요일(3)… 제자리를 찾아갔습니다. 다음 주기부터 알림이 정상적으로 도착하기 시작했습니다.

되돌아 보면

원인은 단순했습니다. 데이터베이스의 빈 칸 하나. 하지만 이걸 찾기까지 알림 시스템 전체를 따라가야 했습니다. 앱에서 서버로, 서버에서 Job으로, Job에서 다시 서버로, 서버에서 푸시 알림 서비스로.

매일 루틴이 잘 작동하고 있었기 때문에 더 찾기 어려웠습니다. 시스템이 “거의” 정상일 때, “약간” 안 되는 부분을 찾는 게 가장 까다롭습니다. 서버가 돌아가고 있고, Job이 매분 실행되고 있고, 쿼리 문법도 맞는데 — 그런데 결과가 없습니다.

교훈이 두 가지 있습니다.

하나는, 서버가 계산할 수 있는 값은 서버가 직접 계산하라는 것. 앱이 보내주길 기다리면 이번처럼 값이 안 올 수도 있고, 다음번에는 타임존이 다른 값이 올 수도 있습니다.

다른 하나는, “되는 것”에 가려진 “안 되는 것”을 주의하라는 것. 매일 루틴이 완벽하게 동작했기 때문에, 매주 루틴의 문제를 알아차리는 데 시간이 걸렸습니다. 전체가 고장나면 금방 알아챕니다. 일부만 고장나면 한참 걸립니다.