오늘 배운 것

오늘은 알림 기능 개발을 마저 진행해보려고 한다. 지금까지는 서버에서 알림을 보내면 앱의 화면에 알림이 잘 뜨는지를 확인했고, 이제는 잘 뜨는 것을 확인했으니 목적에 맞게 알람을 보내도록 코드를 작성해주면 되겠다. 

 

목적에 맞게 알람을 보내려면 크게 두 가지의 요구사항을 충족해야 한다.

1. foreground 알림의 경우, 아침(오전 8시), 점심(오후 2시), 저녁(오후 8시) 시간을 기본값으로 하고, 각 시간마다 사용자에게 알림을 보낸다. 

2. background 알림의 경우, 사용자가 CRUD 중 CUD에 해당하는 API를 호출한다면 사용자(User)에 해당하는, 현재 알림을 보낸 디바이스가 아닌 다른 디바이스(Device)가 있는지 확인하고, 있다면 해당 디바이스에게 API를 다시 호출하라는 알림을 보낸다. 

 

우선 첫 번째 경우부터 작업해 보자. 

 

지정된 시간마다 알림을 보내야 하니, 처음에 생각난 방법은 cronjob을 이용해서 서버에서 특정 시간마다 배치 로직을 돌려주는 것이었다. 'django cronjob'으로 검색해 보니 django-crontab이라는 라이브러리가 나왔다. 찾아보니 지속적으로 실행해주었으면 하는 Job에 해당하는 함수를 정의해준 뒤, settings.py에 해당 함수를 cronjob 날짜 표시 규칙에 맞게 정의해주면 끝이어서 간단해 보였다. 

 

우선 todos 앱 내부에 jobs.py라는 cronjob에 쓰일 job 함수들을 정의하는 용도의 파일을 만들어 주었다. 그리고 다음과 같이 send_day_alarm 이라는 공통 로직을 만들어 주었다. 

MORNING_ALARM_TITLE = "오늘의 할 일을 확인해보세요"
AFTERNOON_ALARM_TITLE = "지금 할 일을 확인해보세요"
EVENING_ALARM_TITLE = "오늘의 남은 할 일을 확인해보세요"


def send_morninig_alarm():
    send_day_alarm(MORNING_ALARM_TITLE)


def send_afternoon_alarm():
    send_day_alarm(AFTERNOON_ALARM_TITLE)


def send_evening_alarm():
    send_day_alarm(EVENING_ALARM_TITLE)


def send_day_alarm(alarm_title):
    users_prefetch = Prefetch('user__todo_set', queryset=Todo.objects.filter(is_completed=False))
    devices = FCMDevice.objects.all().select_related('user').prefetch_related(users_prefetch)
    try:
        for device in devices:
            todos_queryset = device.user.todo_set.filter(is_completed=False).values_list("content", flat=True)
            todos_list = "\n".join(todos_queryset)
            device.send_message(
                messaging.Message(
                    notification=messaging.Notification(
                        title=alarm_title,
                        body=todos_list,
                    ),
                )
            )
    except Exception:
        pass

 

FCMDevice는 fcm_django에서 send_message() 메소드를 통해 알람을 편하게 보낼 수 있게 하는 전용 모델이다. 이 FCMDevice와 User는 N:1 관계이고, User와 할 일을 나타내는 Todo 모델은 1:N 관계이다. 그리고 여기서 원하는 것은 FCMDevice를 조회하면서 관련 User와 Todo 데이터 모두를 한 번의 쿼리로 조회하는 것이었다. 

 

이를 위해서 django의 Prefetch를 사용했다. django의 select_related와 prefetch_related 안에 Prefetch() 객체를 넣어서 두 번의 join문을 통해 한 번의 쿼리로 User, Todo 데이터를 가진 FCMDevice 쿼리셋을 만들었다. 

 

궁금한 점

1. INSTALLED_APPS에 'django_crontab'을 넣으면 django에서 이를 어떻게 인식하는 것일까?

2. 'python manage.py crontab add' 명령어를 입력하면 settings.py에 입력한 함수 커맨드가 crontab에서 관리해야 할 job으로 등록되는 것으로 이해했다. 이 과정이 어떻게 일어나는지 궁금하다. 

3. Prefetch를 통해서 데이터를 불러오는 것과 그냥 objects.all()를 통해서 데이터를 불러오는 것은 차이가 있을까? 

4. select_related나 prefetch_related 안에 Prefetch() 객체를 넣으면 django에서 이를 어떻게 인식해서 join문으로 DB에 쿼리를 날리는지 궁금하다. 

 

+ Recent posts