# [트러블슈팅] 알림이 두 번 온다고 ?! 🔥

## 😼 댕댕어디가 서비스 설명

**댕댕어디가 서비스는** **반려동물과 함께 갈 수 있는 장소만 확인할 수는 없을까?🧐**  
댕댕어디가는 반려동물과 함께 외출할 수 있는 시설 정보를 공유함으로써 , 견주들이 보다 편리하고 쾌적하게 외출을 즐길 수 있도록 돕는 서비스이며, 이를 통해 반려동물과 함께하는 활동의 범위가 넓어지고,외출 시의 부담을 줄여주는 서비스를 제공한다✨

**🐾 주요기능**

* `땅따먹기`
    
* `장소 방문 예정 등록`
    
* `유저 맞춤 장소 추천`
    

✅ **알림이 오는 경우**

1. 내가 **땅의 주인**이 되었을 때
    
2. **방문 예정 등록**을 했는데, **다른 유저들도 같은 장소와 시간에 등록**했을 때
    

## 🛠️ 겪었던 문제

➡️ `백그라운드에서 알림이 두 번 오는 현상!`

포그라운드에서는 정상적으로 한 번만 도착했지만, 백그라운드에서는 알림이 중복으로 오는 문제가 발생했다… 엄청난 구글링과 AI 도움을 받고자 했으나…

도움이 많이 안 되었다… 🤨 그래서 차근차근 문제를 해결해 나가는 방식으로 ,, ~

## ✨ 해결 방법

➡️ `notification 대신 data로 전달!`

➡️ `백그라운드 상태일 때 푸시 알림을 표시하지 않도록 처리`

알림을 notification이 아닌 data 타입으로 주고받도록 변경하니 중복 문제가 해결되었다.

## 💡 왜 이런 현상이 문제가 발생했을까? 에 대한 생각을 했다

이유를 알기 위해서는 일단

✅ `notification`과 `data`의 차이를 알아야 한다.

**FCM에서 메시지를 보낼 때 두 가지 방식이 있는데**

`notification 메시지`

* FCM이 알아서 알림을 처리하고 표시함.
    
* 앱이 백그라운드에 있을 때 자동으로 시스템 트레이에 알림이 뜸.
    
* 개발자가 커스텀할 수 있는 게 거의 없음.
    

`data 메시지`

* 앱에서 직접 알림을 처리해야 함.
    
* 백그라운드에서도 알림이 자동으로 표시되지 않음.
    
* 개발자가 원하는 대로 알림 UI를 만들거나 데이터를 가공할 수 있음.
    

의 특징이 있으며

✅ **내가 이러한 문제를 겪었던 이유**

1. `notification`으로 보내면, FCM이 자동으로 알림을 띄움.
    
2. 그런데 앱에서도 같은 데이터를 받아서 push 알림을 추가로 띄우면, `중복 알림`이 발생!
    
3. 그래서 `data`로 변경하면, 앱이 직접 알림을 관리할 수 있어서 중복 문제를 해결할 수 있었다.
    

---

📍 `알림 로직`

`firebase-messaging-sw.js`

* `payload`로 처리하기
    

```javascript
export const setupOnMessageHandler = () => {
  onMessage(messaging, (payload) => {

      if (document.visibilityState === "hidden") {
        return; // 백그라운드 상태일 때 푸시 알림을 표시하지 않도록 !!! 꼭 해주기  
        }
  
    if (payload && payload.data) {
      const { title, body, image, icon} = payload.data;

      const notificationTitle = title || "알림";
      const notificationOptions = {
        body: body || "내용이 없습니다.",
        image: image || null,
        icon: icon || "/alarm-logo.png", //우리 플젝은 알림 로고도 제작했다 
      };

      const notification = new Notification(notificationTitle, notificationOptions);

      notification.onclick = function (event) {
        const url = payload.data.url; 
        event.preventDefault();

        if (url) {
          window.location.href = url; 
        } else {
          console.error("URL 데이터가 없습니다.");
        }
        notification.close();
      };
    } else {
      console.error("페이지가 백그라운드 상태입니다. 포그라운드 상태에서만 알림을 표시합니다.");
    }
  });
};
```

알림페이지에 들어가면

* ✔ **백엔드에서 사용자의 알림 동의 여부를 확인**
    
* ✔ **서비스 워커를 등록하여 푸시 알림 기능 활성화**
    
* ```javascript
        useEffect(() => {
            const fetchNotificationConsent = async () => {
              try {
                setIsLoading(true);
                const response = await axiosInstance.get(
                  "/api/v1/notifications/consent",
                  { withCredentials: true }
                );
        
                if (response.status === 200) {
                  setIsSubscribed(response.data.data.isNotificationConsent);
                } else {
                  console.error("알림 활성화 상태 확인 실패:", response);
                }
              } catch (error) {
                console.error("알림 활성화 상태 요청 중 오류:", error);
              } finally {
                setIsLoading(false);
              }
            };
        
            fetchNotificationConsent();
        
            if ("serviceWorker" in navigator) {
              navigator.serviceWorker
                .register("/firebase-messaging-sw.js")
                .catch((error) => {
                  console.error("Service Worker 등록 실패:", error);
                });
            } else {
              console.warn("이 브라우저는 Service Worker를 지원하지 않습니다.");
            }
          }, []);
    ```
    

## ✍🏻 트러블슈팅하며 배운 점

이번 문제를 해결하면서, 문제 처리 방식에 대한 깊은 이해가 필요하다는 것을 다시 한 번 느꼈다. 무조건적으로 AI에 의존하지 말자

**notification**과 **data** 메시지의 차이를 알게 된 후 FCM을 더 효과적으로 활용할 수 있다는 것을 깨달았다.

특히 백그라운드 상태에서의 알림 처리가 얼마나 중요한지 + 처리 방식에 따라 사용자 경험에 큰 영향을 미칠 수 있다는 점도 배웠다.✨
