diff --git a/frontend/src/contexts/TradeNotificationContext.tsx b/frontend/src/contexts/TradeNotificationContext.tsx index 034f655..8a82a67 100644 --- a/frontend/src/contexts/TradeNotificationContext.tsx +++ b/frontend/src/contexts/TradeNotificationContext.tsx @@ -417,17 +417,22 @@ export const TradeNotificationProvider: React.FC = ({ const purgeNotifications = useCallback((ids: string[]) => { if (ids.length === 0) return; const idSet = new Set(ids); + + // hiddenIds: 목록에서 숨김 처리 const hidden = loadHiddenIds(); ids.forEach(id => hidden.add(id)); saveHiddenIds(hidden); - setReadIds(prev => { - const next = new Set(prev); - ids.forEach(id => next.delete(id)); - saveReadIds(next); - readIdsRef.current = next; - return next; - }); + /** + * readIds 에서 삭제하지 않고 오히려 추가한다. + * 삭제된 ID 를 readIds 에서 제거하면, 백엔드가 동일 신호를 STOMP 로 재전송할 때 + * alreadyRead = false 가 되어 팝업이 다시 뜨는 버그가 발생한다. + */ + const nextRead = new Set([...readIdsRef.current, ...ids]); + readIdsRef.current = nextRead; + setReadIds(nextRead); + saveReadIds(nextRead, true); + setToastNotifications(prev => prev.filter(n => !idSet.has(n.id))); setAllNotifications(prev => prev.filter(n => !idSet.has(n.id))); if (detailSignal) { @@ -481,18 +486,38 @@ export const TradeNotificationProvider: React.FC = ({ ...new Set([ ...allNotifications.map(n => n.id), ...toastNotificationsRef.current.map(n => n.id), + ...allSeenIdsRef.current, ]), ]; - purgeNotifications(ids); - setToastNotifications([]); - saveReadIds(new Set()); + + /** + * readIds 를 new Set() 으로 초기화하지 않는다. + * 초기화하면 백엔드가 삭제된 신호를 STOMP 로 재전송할 때 + * readIds 에 없어서 팝업이 다시 뜨는 버그가 발생한다. + * 대신 삭제된 ID 를 readIds 에 추가하여 재전송 신호를 필터링한다. + */ + const nextRead = new Set([...readIdsRef.current, ...ids]); + readIdsRef.current = nextRead; + setReadIds(nextRead); + saveReadIds(nextRead, true); + + // hiddenIds 는 초기화 (전체 삭제 후 목록 완전히 비움) saveHiddenIds(new Set()); + + setToastNotifications([]); + setAllNotifications([]); + + if (detailSignal) { + setDetailSignal(null); + setDetailCentered(false); + } + try { await deleteAllTradeSignals(); } catch (e) { console.warn('[TradeNotification] 전체 삭제 실패:', e); } - }, [allNotifications, purgeNotifications]); + }, [allNotifications, detailSignal]); const openDetail = useCallback((item: TradeNotificationItem, options?: { centered?: boolean }) => { setDetailCentered(options?.centered ?? false);