여러 개의 알림을 하나로 묶어서 보내기

강문혁·2일 전
1

겜린더

목록 보기
13/13
post-thumbnail

Github Actions Status

Gamlendar Game Notification Workflow


시작하기 전에

겜린더에는 사용자가 출시 예정인 게임을 달력에 등록하여 출시 당일 알림을 주는 기능이 있습니다.
저는 이 기능을 구현하기 위해 Firebase에서 Topic 이란 기능을 활용했는데요


Firebase Cloud Messaging - Topic

Topic은 주제 별로 알림을 전송할 클라이언트를 분류하여
효율적으로 여러 기기에 알림을 전송할 수 있는 기능입니다.
마치 pub / sub 구조와 비슷하다고 생각이 드네요


문제

초기 구조

어떤 것을 Topic으로 잡아야 할지 고민을 한 결과 게임 정보마다 있는 ID로 주제를 잡으면 될 것이라고 판단하였습니다.

그래서 알림 구조를 만들어보았는데요
과정은 파란색 → 회색 → 빨간색 순서로 봐주시면 됩니다.

예상 결과물에 보면 알 수 있듯이 2024년 12월 8일에 출시하는 게임 4가지가 각각 알림이 나오는 걸 확인할 수 있습니다.

이 방식의 장단점이 있었는데요

장점

  • 개발하기 편하다...
  • DB를 사용할 필요가 없다.

단점

  • 각각 알림이 오기 때문에 중복되는 알림으로 인한 알림 폭탄이 예상된다.

알림 폭탄?

예상 결과물을 보게 되면 겉보기엔 문제가 없어 보입니다.

하지만 만약 게임을 10개 이상 달력에 등록하게 되면

3~4개는 괜찮아도 10개가 한 번에 온다....? 이거 거의 알림 폭탄인데..?

라는 생각과 함께 개선해야한다는 결론에 도달하게 되었습니다.


해결 방안

문제를 해결하기 위해 저는 사용자가 달력에 등록한 정보를 DB에서 불러와 하나의 알림으로 묶어서 보내야겠다는 판단을 하였습니다.

일단 Topic의 기준을 유저 ID로 변경하였고 이를 기반으로 알림 구조를 다시 만들어보았습니다.

플로우를 번호 순서대로 정리를 해봤는데요

순서 정리

1. 로그인

달력에 등록하기 위해선 사용자는 로그인을 해야 합니다.

2. 로그인 성공 시 Access Token 발급

로그인 성공을 하면 Access Token을 발급받는데요 그곳에 유저 id가 존재합니다.
예시로 "id":"00000000"로 하겠습니다.

3. 유저 ID로 FCM Topic 구독

로그인 성공과 동시에 id를 이용해 FCM Topic을 구독하게 됩니다.

4. 게임을 자유롭게 탐색

말 그대로 게임을 자유롭게 탐색을 합니다.

5. 탐색 중 맘에 드는 게임을 달력에 등록 시 MongoDB와 Redis에 저장

게임이 맘에 들었다면 Redis는 HASH 타입으로 저장, MongoDB에는 게임 id가 저장이 됩니다.

Redis에 저장되는 데이터를 활용하기 때문에 Redis를 중점적으로 보자면
날짜 - 유저 ID - 게임1, 게임2 이런식으로 저장이 되는데요

이를 기반으로 실제 결과물을 보게 되면 아래와 같은 이미지가 나오게 됩니다.

날짜와 유저 ID를 기반으로 HASH 타입으로 저장이 되어있고 유저 ID 속에는 유저가 저장한 게임들이 저장됩니다.

6. BOT이 실행되는 날짜 기준으로 게임을 예약한 유저 ID 불러오기

오늘 날짜에 맞춰 달력에 게임을 등록한 유저 ID를 불러옵니다.

7. 유저 ID 별 게임 목록을 하나의 알림으로 그룹화

게임 목록을 불러오고 message_template에 맞게 메시지를 그룹화 시킵니다.

8. 유저 ID Topic 별로 각각 전부 전송

여러 개의 출시 알림을 하나의 알림으로 모아서 전송할 수 있게 됩니다.

개선점

  • 알림 폭탄을 줄일 수 있다.

  • 유저 ID 기반으로 Topic을 구독하였기 때문에 다중 로그인에도 동일한 알림을 전송할 수 있게 되었습니다.

    이전처럼 게임 id를 기반으로 구독하게 되면
    여러 기기에 같은 계정으로 로그인 하더라도 A 기기는 전부 알림이 오지만 추후 B 기기에 같은 계정으로 로그인을 하게 되었을 경우
    알림이 오지 않아 동일한 사용자 경험을 제공할 수 없게 됩니다.

  • MongoDB와 Redis의 역할을 나눠 I/O를 분산하여 MongoDB의 부담을 줄일 수 있었습니다.

    사실 이런 작업은 Redis 없이도 가능합니다. MongoDB Query를 작성해 분류해서 불러온 후 똑같이 그룹화 시켜 알림을 전송하면 되는데요

    하지만 그렇게 하면 쿼리 작업으로 인한 연산이 발생해 안 그래도 느린 자체 서버에 부담을 주기 싫었습니다.

    구조적으로 복잡한 데이터가 아니라고 생각했기 때문에 key-value 스토리지인 Redis를 사용하는 것이 좋다고 판단하였습니다.


요즘 알림 그룹화 시켜주는데 사용하지 않은 이유

요즘 iOS나 Android는 알림을 그룹화 시켜주는 기능이 존재합니다.
근데 이걸 사용하지 않은 이유는 관리하기가 더 힘들 것 같다는 생각이 들었습니다.

안드로이드의 경우 Channel_id로 여러 유형의 알림을 분류하고 그룹화 시켜주는데요. 저는 이미 Topic으로 그 역할을 하고 있다고 생각해 굳이 사용할 필요성을 느끼지 못했습니다.

그래서 channel_id를 하나만 사용하고 있습니다.

그리고 그룹화 시켜줘도 알림은 계속 울린 후에 그룹화가 되기 때문에
저는 알림 1번으로 오늘 어떤 게임이 출시하는지 유저에게 알려주고 싶었습니다.


마치며

겜린더 프로젝트를 하면서 제한된 하드웨어 성능에 최대한 뽕을 뽑아보려고 노력하고 있는데요...
제 스스로 판단하기엔 나름 타당한 근거로 이번 알림 시스템을 잘 개선했다고 생각합니다.

알림 시스템은 여전히 잘 동작하고 있고 앞으로도 안정적으로 운영하려고 최대한 노력해 보려 합니다.

혹시 궁금하신 점이 있다면 언제든 댓글 남겨주세요 열심히 답글 올리겠습니다.

긴 글 읽어주셔서 감사합니다. 🙇‍

profile
흔들리지 말고 나만의 공부를 하자

0개의 댓글