오락실 공식 계정을 도입하면서 운영자가 새 소식을 제때 알아야 한다는 필요가 뚜렷해졌다. 관리 화면에 고장신고가 표시되더라도 새 신고를 찾으려고 계속 열어 봐야 한다면 업무를 돕는 데 한계가 있다. 문의에 답장이 도착했거나 관리 설정을 확인해야 할 때에도 비슷한 문제가 생긴다.
신고 화면에 푸시만 추가하는 방식으로 시작하면 이후 문의, DM과 업데이트 공지를 만들 때 같은 수신자 검사와 기기 등록을 반복하게 된다. 뮤즈캣의 현재 Preview는 계정이 확인할 알림을 보관하고, 필요할 때 동의한 기기로 전달하는 공통 시스템을 사용한다. 공식 계정에서 출발한 필요를 여러 기능이 어떻게 함께 사용하는지 살펴본다.
알림함은 서비스 안에서 나에게 알려 줄 일이 생겼을 때 다시 찾아볼 수 있는 계정별 목록이다. 운영자에게는 새 고장신고가, 신고 작성자에게는 다른 이용자의 반응이, 문의 작성자에게는 답변이 필요하다. 개인 메시지와 서비스 공지도 알려 줄 대상이 있다. 내용과 수신자는 다르지만 각 사건을 보관하고 확인하는 과정을 공통으로 사용할 수 있다.
현재는 공식 계정의 설정 안내, 신고와 반응, 문의 접수와 답변, 개인 메시지와 공유 문의, 발행한 업데이트 스토리가 연결되어 있다. 기존 문의 알림도 읽을 수 있게 유지한다. 아래 표와 그림은 이 범위를 나타내며, Markdown 블로그 글의 자동 발행 알림은 아직 구현되어 있지 않다. 블로그 연결에는 별도의 사건과 수신 정책을 구현해야 한다.
알림을 만들기 전에 먼저 누가 해당 내용을 받아야 하는지 정해야 한다. 예를 들어 오락실에 일반 문의가 들어오면 현재 공식 담당자에게 알려야 하지만, 조회 권한이 있는 플랫폼 직원 모두를 매번 부를 필요는 없다. 뮤즈캣 담당자를 부를 때는 명시적인 멘션을 사용하며, 답장을 보낸 자신에게 같은 메시지를 다시 알리지 않는다.
서버는 계정 상태와 역할, 오락실 소유 관계, 대화 접근 자격과 사건별 수신 규칙을 적용한다. 기기 등록과 수신 설정이 있으면 푸시 대기도 만들고, 그렇지 않아도 알림함에는 계정의 알림을 남길 수 있다. 푸시 설정을 바꿨다고 문의를 읽을 자격까지 바꾸지는 않는다.
이미 만들어진 알림을 읽을 때에도 원본 신고·문의·대화의 현재 접근 권한을 확인한다. 담당자가 교체됐거나 계정의 권한이 사라졌다면 예전에 수신자였다는 이유로 내용을 계속 보여 주지 않는다. 대기 중인 푸시에도 이후의 상황을 확인할 별도 절차가 필요하다.
외부 푸시 서비스가 느리더라도 신고 등록을 오래 기다리게 할 필요는 없다. 예를 들어 신고는 저장됐는데 전송만 실패한 경우를 생각하면, 원래 작업을 취소할지 알림만 다시 보낼지 구분할 방법이 필요하다. 이 때문에 외부로 보낼 일을 먼저 저장하는 outbox 방식을 사용한다.
계정 알림은 user_notification, 기기 등록은 user_notification_device, 기기별 전달 대기는 user_notification_delivery에 저장한다. 신고나 메시지의 처리에서는 원본과 알림, 해당 기기의 전달 대기까지 하나의 트랜잭션으로 확정한다. 실제 외부 전송은 확정 후 별도 작업자가 수행하므로 외부 지연이 원본의 저장을 붙잡지 않는다.
같은 사건을 다시 처리할 가능성도 고려한다. 계정과 event_key의 조합으로 알림 중복을 막고, 알림과 기기의 조합으로 전달 대기 중복을 막는다. 원본 신고·메시지·발행물에 대한 참조는 이후 조회 권한 검사와 목적지 이동에 사용한다.
많은 계정으로 배포하는 공지는 발행 요청 안에서 모두 처리하지 않는다. 발행 상태를 저장한 뒤 배포 작업이 수신자를 묶음으로 나누어 처리하고, 각 묶음의 진행 위치와 알림·기기 대기를 함께 확정한다. 중단 후 같은 묶음을 다시 읽더라도 이미 읽은 알림을 초기화하거나 같은 발행물을 새 사건으로 만들지 않도록 했다.
알림함은 브라우저 푸시를 사용하지 않아도 확인할 수 있다. 한 기기의 전송 실패가 계정의 읽음이나 다른 기기의 전달을 되돌리지 않도록 각각 관리한다. 화면은 주기 조회와 메시징 변경 신호로 목록을 갱신하고, 이전에 더 불러온 항목과 새 결과를 알림 ID로 합친다. 현재 접근 가능한 원본을 기준으로 목록과 미확인 수를 계산한다.
항목을 선택하면 읽음 저장의 성공을 확인한 뒤 대화를 열거나 원본 화면으로 이동한다. 목적지에서도 접근 권한을 확인한다. 읽음 저장에 실패한 경우에는 오류와 재시도를 제공하므로 이동만 하고 읽음이 저장되지 않는 상황을 구분할 수 있다.
푸시는 이용자가 명시적으로 켤 때 브라우저 권한을 요청하고 기기를 등록한다. 알림함을 여는 일 자체로 권한 요청을 띄우지는 않는다. 서버는 endpoint를 현재 계정에 연결해 계정을 바꾼 브라우저에 이전 계정의 구독 소유가 남지 않도록 한다.
기기에는 언어도 저장한다. 화면 언어가 바뀌면 이미 동의한 현재 계정의 등록만 갱신하며, 이 동작으로 새 기기를 등록하거나 푸시를 켜지 않는다. 공지는 발행된 언어의 기기로 보내고, 비공개 문의와 DM은 잠금 화면에 원문이 드러나지 않도록 일반적인 도착 안내를 사용한다.
전송을 기다리는 사이에도 신고가 해결되거나 담당자가 바뀔 수 있다. 이용자가 기기를 해제하거나 공지를 보관하는 경우도 있다. 실제 전송을 맡는 Backend Full은 원본 상태, 수신자의 접근 권한과 메시지 수신 규칙, 기기의 소유자와 설정을 다시 확인한다. 더 이상 유효하지 않으면 외부로 보내지 않고 종료한다.
일시적인 실패에는 시도 횟수와 다음 실행 시각을 남기며 지수적으로 지연을 늘려 최대 다섯 번 시도한다. 제공자가 404나 410으로 만료를 알리면 해당 계정의 기기 등록과 연결된 대기를 정리한다. 이 과정으로 불안정한 외부 연결을 원래 신고나 메시지의 저장과 분리해 복구할 수 있다.
제공자가 요청을 수락했어도 운영체제가 실제 알림을 표시했다고 확정할 수는 없다. 전송 직후 완료를 기록하기 전에 작업자가 중단되면 재전송이 가능하며, 같은 알림 ID로 만든 push tag는 반복 표시를 합치는 데 사용한다. 이 한계까지 고려한 공통 기반 위에서 문의와 공지가 동작한다. 앞으로 블로그를 연결할 때에도 발행 사건, 식별자와 수신 정책을 실제로 추가해야 한다.
관련 글과 자료: API 경계의 변화