개발 slecs

관리자 알림 뱃지에 중요 알림 카운트와 재고변경 타입 추가

목차

관리자 화면 알림 뱃지에 두 가지를 추가했다. 하나는 중요 알림만 카운트하는 뱃지 쿼리, 다른 하나는 SUPPLY_CHANGE 알림 타입 라벨. 변경된 파일은 SQL 매퍼 하나, 뷰/스타일 하나. 숫자만 보면 간단한 작업이지만, 왜 이 시점에 이 방식으로 구현했는지 기록해두는 게 나중에 도움이 될 것 같아서 남겨둔다.

기존에 무엇이 빠져 있었나

기존 admin-notification 영역은 전체 미읽음 알림을 그냥 COUNT(*) 로 세서 뱃지에 올려줬다. 운영자 입장에서 뱃지 숫자는 "지금 당장 확인해야 할 것이 몇 개인가"에 대한 신호인데, 읽었든 안 읽었든, 중요하든 아니든 전부 합산해버리니까 실질적인 의미가 없었음. 뱃지가 30이어도 실제로 급한 건 2개인 상황 - 운영하다 보면 흔한 노이즈 문제다.

SUPPLY_CHANGE 타입도 마찬가지 맥락이다. 재고 변경 이벤트가 새로운 트리거 포인트로 생성되기 시작했는데, 프런트에서는 타입을 구분하지 않으니 ORDER, PAYMENT, SUPPLY_CHANGE 전부 똑같이 렌더링됐음. 알림 목록에서 내용을 열어보기 전까지는 어떤 알림인지 알 방법이 없는 구조.

두 가지 모두 단순 UI 패치로 끝낼 수 없었다. 뱃지 숫자가 달라지려면 쿼리 자체를 바꿔야 하고, 타입 라벨이 제대로 나오려면 타입 정의와 뷰 렌더링 로직을 맞춰야 한다. 그래서 SQL 매퍼부터 건드렸음.

구현 내용과 설계 결정

뱃지 카운트 쿼리

핵심은 is_important = 1 조건을 추가하는 것. 여기에 소프트 딜리트 구조에서 필수적으로 들어가야 하는 deleted_at IS NULL 조건도 함께 확인했다.

SELECT COALESCE(COUNT(*), 0) AS badge_count
FROM admin_notification
WHERE is_important = 1
  AND is_read      = 0
  AND deleted_at  IS NULL

COALESCE 로 감싼 건 알림이 아예 없는 상태 - 첫 배포 직후나 전체삭제 직후 - 에서 COUNT 결과가 NULL 로 넘어오는 케이스를 방어하기 위해서임. 매퍼에서 막아두면 프런트에서 null 체크를 이중으로 할 필요가 없어서 코드가 깔끔해진다.

deleted_at IS NULL 은 소프트 딜리트 구조에서 빠뜨리기 쉬운 조건인데, 이게 빠지면 삭제된 알림이 카운트에 잡혀서 뱃지 숫자가 절대 줄어들지 않는 버그가 생긴다. 실행 계획도 미리 뽑아서 풀 스캔 없이 인덱스를 타는지 확인했음.

알림 타입 라벨 정의

타입 라벨은 뷰에서 직접 문자열을 하드코딩하지 않고 매핑 객체로 분리했다. 나중에 타입이 추가될 때 뷰 로직을 건드리지 않아도 되는 구조.

타입 키 표시 라벨 설명
ORDER 주문 신규 주문 발생 이벤트
PAYMENT 결제 결제 완료 / 실패 이벤트
SUPPLY_CHANGE 재고변경 재고 수량 변동 이벤트

현재는 세 가지지만, 타입 컬럼이 확장에 열려 있는 구조라 라벨 매핑만 추가하면 변경 비용이 거의 없음.

폴링 vs 이벤트

실시간 갱신 방식도 결정이 필요했다. 선택지는 주기적 API 호출(폴링)과 웹소켓/SSE 기반 이벤트 푸시. 관리자 화면은 동시 접속자가 많지 않고, 알림 지연 허용 범위도 수 초 정도면 충분하다고 판단해서 기존 폴링 방식을 유지했음.

이벤트 방식은 구독 연결 관리, 재연결 처리, 서버 리소스 점유 같은 인프라 복잡도가 올라가는데, 이 화면에서 그 복잡도를 감수할 이유가 없었다. 뱃지 카운트 쿼리가 가볍고, 기존 폴링 주기로도 운영 흐름에 문제가 없으면 단순한 쪽이 맞다.

읽음/전체삭제 처리

읽음 처리와 전체삭제는 기존 API를 그대로 썼다. 중요한 건 새 필터링 로직 이후에도 이 처리들이 일관되게 동작하는지 확인하는 것. 전체삭제 후 뱃지가 0이 되는지, 일부 읽음 처리 후 숫자가 정확히 줄어드는지를 화면에서 직접 눌러가며 검증했음.

금융/결제 도메인에서 숫자 정합성

금융/결제 관련 화면에서 숫자가 맞지 않으면 신뢰가 무너진다. 이건 과장이 아니라 실제로 그렇다. 주문 건수, 결제 금액, 재고 수량 - 이 숫자들 중 하나라도 화면마다 다르게 나오면 운영자는 어느 화면을 믿어야 할지 모른다. 그 불신이 쌓이면 시스템 전체에 대한 신뢰 문제로 번진다.

그래서 이번 작업에서도 구현 후 검증을 수치 단위로 진행했음.

  • 변경 전 DB에서 직접 COUNT 뽑아 수치 메모
  • 변경 후 같은 조건으로 화면 뱃지 숫자와 DB 직접 쿼리 결과 비교
  • 읽음 처리/삭제 후 두 쪽 숫자가 같이 줄어드는지 확인
  • 관련 화면이 있는 경우 같은 데이터를 보는 다른 뷰와 cross-check

"대충 맞는 것 같다"는 판단으로 넘어가면 반드시 다시 돌아온다. 경험상 그런 케이스는 제일 바쁜 타이밍에 다시 터진다.


작업 자체는 하루 안에 끝났지만, 기능 하나가 버튼 하나 추가로 끝나지 않는다는 걸 이번에도 다시 체감했음. SQL 집계, 상태 관리, 예외 처리, 화면 렌더링, 권한 체크가 다 엮여 있어서 어느 하나를 빠뜨리면 특정 상황에서 숫자가 맞지 않거나 이상한 화면이 나온다. 변경 범위가 작을수록 "이 정도는 괜찮겠지"라는 판단이 들기 쉬운데, 그 판단이 나중에 제일 아프게 돌아온다는 걸 경험으로 알고 있다.

커밋은 SQL 매퍼 변경과 뷰 변경을 분리해서 두 개로 쪼갰음. 나중에 문제가 생겼을 때 어느 변경에서 깨졌는지 추적하기 쉬워지고, 리뷰할 때도 변경 의도가 명확하게 보인다. 커밋 메시지에는 "무엇을 바꿨다"보다 "왜 바꿨다"를 담으려 했음. 뱃지가 전체 알림을 카운트해서 운영자가 중요 알림을 노이즈 속에서 놓칠 수 있다는 맥락이 로그에 남아 있어야, 나중에 코드를 다시 볼 때 의도가 보인다.

댓글 0

첫 댓글 달아줘.