2026년 10월 25일
뮤즈캣 알림 시스템의 구조와 작동 원리
계정 알림함과 기기별 푸시를 연결하는 공통 시스템 구축.
도입
소통과 알림
#공지
eric

오락실 공식 계정을 도입하면서 운영자가 새 소식을 제때 알아야 한다는 필요가 뚜렷해졌다. 관리 화면에 고장신고가 표시되더라도 새 신고를 찾으려고 계속 열어 봐야 한다면 업무를 돕는 데 한계가 있다. 문의에 답장이 도착했거나 관리 설정을 확인해야 할 때에도 비슷한 문제가 생긴다.

신고 화면에 푸시만 추가하는 방식으로 시작하면 이후 문의, DM과 업데이트 공지를 만들 때 같은 수신자 검사와 기기 등록을 반복하게 된다. 뮤즈캣의 현재 Preview는 계정이 확인할 알림을 보관하고, 필요할 때 동의한 기기로 전달하는 공통 시스템을 사용한다. 공식 계정에서 출발한 필요를 여러 기능이 어떻게 함께 사용하는지 살펴본다.

알림의 종류와 계정 알림함

알림함은 서비스 안에서 나에게 알려 줄 일이 생겼을 때 다시 찾아볼 수 있는 계정별 목록이다. 운영자에게는 새 고장신고가, 신고 작성자에게는 다른 이용자의 반응이, 문의 작성자에게는 답변이 필요하다. 개인 메시지와 서비스 공지도 알려 줄 대상이 있다. 내용과 수신자는 다르지만 각 사건을 보관하고 확인하는 과정을 공통으로 사용할 수 있다.

Mermaid
flowchart LR
    accTitle: 여러 기능이 공유하는 알림 기반
    accDescr: 공식 계정의 운영 업무와 신고, 문의, 개인 메시지, 공지가 공통 알림 기반에 연결된다. 블로그 발행은 아직 연결하지 않은 확장 대상이다.
    O["공식 계정의 운영 업무"] --> F["고장 신고와 문의"]
    F --> N["공통 알림 처리"]
    D["개인 메시지"] --> N
    A["업데이트 스토리 발행"] --> N
    B["블로그 새 글"] -. "향후 연결 대상" .-> N
    N --> I["계정 알림함"]
    N --> P["동의한 기기의 푸시"]

현재는 공식 계정의 설정 안내, 신고와 반응, 문의 접수와 답변, 개인 메시지와 공유 문의, 발행한 업데이트 스토리가 연결되어 있다. 기존 문의 알림도 읽을 수 있게 유지한다. 아래 표와 그림은 이 범위를 나타내며, Markdown 블로그 글의 자동 발행 알림은 아직 구현되어 있지 않다. 블로그 연결에는 별도의 사건과 수신 정책을 구현해야 한다.

현재 연결된 사건전달하는 이유
공식 계정의 관리 설정 안내현재 공식 계정이 관리 도구의 설정을 확인하도록 안내한다.
새 고장 신고와 신고 반응현재 담당자에게 신고를 알리고, 작성자에게 다른 이용자의 반응을 전달한다.
문의 접수와 답변문의 작성자와 현재 담당자가 대화를 이어가도록 한다. 기존 문의 알림도 읽을 수 있게 유지한다.
개인 메시지와 공유 문의의 새 메시지대화 참여자 가운데 해당 메시지를 알려야 할 사람에게 전달한다.
발행한 업데이트 스토리공개한 언어의 공지를 계정 알림함과 해당 언어의 기기에 연결한다.

수신 대상과 현재 접근 권한

알림을 만들기 전에 먼저 누가 해당 내용을 받아야 하는지 정해야 한다. 예를 들어 오락실에 일반 문의가 들어오면 현재 공식 담당자에게 알려야 하지만, 조회 권한이 있는 플랫폼 직원 모두를 매번 부를 필요는 없다. 뮤즈캣 담당자를 부를 때는 명시적인 멘션을 사용하며, 답장을 보낸 자신에게 같은 메시지를 다시 알리지 않는다.

서버는 계정 상태와 역할, 오락실 소유 관계, 대화 접근 자격과 사건별 수신 규칙을 적용한다. 기기 등록과 수신 설정이 있으면 푸시 대기도 만들고, 그렇지 않아도 알림함에는 계정의 알림을 남길 수 있다. 푸시 설정을 바꿨다고 문의를 읽을 자격까지 바꾸지는 않는다.

Mermaid
flowchart TD
    accTitle: 사건에서 수신자와 전달 경로를 정하는 과정
    accDescr: 서버가 사건별 수신 자격을 판단해 계정 알림을 만들고, 별도의 기기 등록과 수신 설정이 있는 경우에만 푸시 대기를 만든다.
    E["서비스 안에서 사건 발생"] --> R{"현재 수신 자격이 있는가"}
    R -->|"아니오"| X["그 계정의 새 알림을 만들지 않음"]
    R -->|"예"| I["계정 알림 저장"]
    I --> V["알림함에서 확인 가능"]
    I --> C{"동의한 기기와 수신 설정이 있는가"}
    C -->|"예"| Q["기기별 전달 대기"]
    C -->|"아니오"| W["알림함 기록만 유지"]

이미 만들어진 알림을 읽을 때에도 원본 신고·문의·대화의 현재 접근 권한을 확인한다. 담당자가 교체됐거나 계정의 권한이 사라졌다면 예전에 수신자였다는 이유로 내용을 계속 보여 주지 않는다. 대기 중인 푸시에도 이후의 상황을 확인할 별도 절차가 필요하다.

알림 저장과 outbox의 처리 과정

외부 푸시 서비스가 느리더라도 신고 등록을 오래 기다리게 할 필요는 없다. 예를 들어 신고는 저장됐는데 전송만 실패한 경우를 생각하면, 원래 작업을 취소할지 알림만 다시 보낼지 구분할 방법이 필요하다. 이 때문에 외부로 보낼 일을 먼저 저장하는 outbox 방식을 사용한다.

계정 알림은 user_notification, 기기 등록은 user_notification_device, 기기별 전달 대기는 user_notification_delivery에 저장한다. 신고나 메시지의 처리에서는 원본과 알림, 해당 기기의 전달 대기까지 하나의 트랜잭션으로 확정한다. 실제 외부 전송은 확정 후 별도 작업자가 수행하므로 외부 지연이 원본의 저장을 붙잡지 않는다.

Mermaid
sequenceDiagram
    accTitle: 도메인 작업과 알릴 일을 함께 확정하기
    accDescr: 신고나 메시지의 트랜잭션에서 원본과 계정 알림 및 기기별 outbox를 함께 저장하고, 확정 후 별도 작업자가 외부 푸시를 전송한다.
    participant U as 요청 화면
    participant D as 신고 또는 메시지 처리
    participant S as 데이터베이스
    participant W as 전달 작업자
    participant P as 외부 푸시 서비스
    U->>D: 신고 또는 메시지 등록
    D->>S: 트랜잭션 시작
    D->>S: 원본과 계정 알림 저장
    D->>S: 해당 기기의 outbox 저장
    D->>S: 함께 확정
    D-->>U: 원래 요청의 저장 결과 반환
    W->>S: 확정된 전달 대기 조회
    W->>P: 트랜잭션 밖에서 전송
    W->>S: 완료 또는 다음 시도 기록

같은 사건을 다시 처리할 가능성도 고려한다. 계정과 event_key의 조합으로 알림 중복을 막고, 알림과 기기의 조합으로 전달 대기 중복을 막는다. 원본 신고·메시지·발행물에 대한 참조는 이후 조회 권한 검사와 목적지 이동에 사용한다.

많은 계정으로 배포하는 공지는 발행 요청 안에서 모두 처리하지 않는다. 발행 상태를 저장한 뒤 배포 작업이 수신자를 묶음으로 나누어 처리하고, 각 묶음의 진행 위치와 알림·기기 대기를 함께 확정한다. 중단 후 같은 묶음을 다시 읽더라도 이미 읽은 알림을 초기화하거나 같은 발행물을 새 사건으로 만들지 않도록 했다.

알림 열기와 기기 푸시 설정

알림함은 브라우저 푸시를 사용하지 않아도 확인할 수 있다. 한 기기의 전송 실패가 계정의 읽음이나 다른 기기의 전달을 되돌리지 않도록 각각 관리한다. 화면은 주기 조회와 메시징 변경 신호로 목록을 갱신하고, 이전에 더 불러온 항목과 새 결과를 알림 ID로 합친다. 현재 접근 가능한 원본을 기준으로 목록과 미확인 수를 계산한다.

항목을 선택하면 읽음 저장의 성공을 확인한 뒤 대화를 열거나 원본 화면으로 이동한다. 목적지에서도 접근 권한을 확인한다. 읽음 저장에 실패한 경우에는 오류와 재시도를 제공하므로 이동만 하고 읽음이 저장되지 않는 상황을 구분할 수 있다.

Mermaid
flowchart TD
    accTitle: 알림 목록에서 원본 화면으로 이동하기
    accDescr: 인증된 계정의 알림을 현재 원본 권한과 언어로 조회하고, 읽음 저장 성공 후 대화나 원본 화면으로 이동한다. 목적지에서도 접근 권한을 다시 검사한다.
    L["알림함 조회 또는 갱신"] --> V["현재 계정과 원본 권한 확인"]
    V --> C["언어에 맞는 목록과 미확인 수"]
    C --> T["알림 항목 선택"]
    T --> R{"읽음 저장 성공"}
    R -->|"아니오"| E["오류 표시와 재시도"]
    R -->|"예"| O["대화 열기 또는 원본으로 이동"]
    O --> A["목적지에서 현재 접근 권한 재확인"]

푸시는 이용자가 명시적으로 켤 때 브라우저 권한을 요청하고 기기를 등록한다. 알림함을 여는 일 자체로 권한 요청을 띄우지는 않는다. 서버는 endpoint를 현재 계정에 연결해 계정을 바꾼 브라우저에 이전 계정의 구독 소유가 남지 않도록 한다.

기기에는 언어도 저장한다. 화면 언어가 바뀌면 이미 동의한 현재 계정의 등록만 갱신하며, 이 동작으로 새 기기를 등록하거나 푸시를 켜지 않는다. 공지는 발행된 언어의 기기로 보내고, 비공개 문의와 DM은 잠금 화면에 원문이 드러나지 않도록 일반적인 도착 안내를 사용한다.

전송 전 재검사와 실패 재시도

전송을 기다리는 사이에도 신고가 해결되거나 담당자가 바뀔 수 있다. 이용자가 기기를 해제하거나 공지를 보관하는 경우도 있다. 실제 전송을 맡는 Backend Full은 원본 상태, 수신자의 접근 권한과 메시지 수신 규칙, 기기의 소유자와 설정을 다시 확인한다. 더 이상 유효하지 않으면 외부로 보내지 않고 종료한다.

일시적인 실패에는 시도 횟수와 다음 실행 시각을 남기며 지수적으로 지연을 늘려 최대 다섯 번 시도한다. 제공자가 404나 410으로 만료를 알리면 해당 계정의 기기 등록과 연결된 대기를 정리한다. 이 과정으로 불안정한 외부 연결을 원래 신고나 메시지의 저장과 분리해 복구할 수 있다.

Mermaid
flowchart TD
    accTitle: 기기 전달의 재검사와 실패 복구
    accDescr: 현재 원본과 수신자 및 기기를 다시 검사하고 유효한 전달만 전송한다. 일시 실패는 한도 내에서 재시도하고, 만료된 기기는 정리한다.
    Q["확정된 전달 대기"] --> V{"원본과 수신자, 기기가 아직 유효한가"}
    V -->|"아니오"| X["전송 없이 종료"]
    V -->|"예"| S["기기 언어로 푸시 전송"]
    S -->|"제공자 수락"| D["전달 완료 기록"]
    S -->|"일시 실패"| R["횟수 한도와 지연을 두고 재시도"]
    R --> V
    S -->|"기기 만료"| C["기기 등록과 남은 대기 정리"]

제공자가 요청을 수락했어도 운영체제가 실제 알림을 표시했다고 확정할 수는 없다. 전송 직후 완료를 기록하기 전에 작업자가 중단되면 재전송이 가능하며, 같은 알림 ID로 만든 push tag는 반복 표시를 합치는 데 사용한다. 이 한계까지 고려한 공통 기반 위에서 문의와 공지가 동작한다. 앞으로 블로그를 연결할 때에도 발행 사건, 식별자와 수신 정책을 실제로 추가해야 한다.

관련 글과 자료: API 경계의 변화

뮤즈캣
리듬게이머의 최고의 파트너