꺼지지 않는 알림, 끝나지 않는 버그

3

발행일: 2025년 04월 26일

"으아악! 또야! 이번엔 유령 알림이라고!"

새벽 3시. 카페인과 에너지 드링크로 버티던 개발팀 사무실에 또다시 비명이 터져 나왔다. 이번 타겟은 페이스북의 심장 박동과도 같은 '알림(Notification)' 시스템이었다. 친구의 댓글, '좋아요' 표시, 그룹 게시물 업데이트… 사용자를 페이스북의 세계로 끊임없이 끌어들이는 핵심 동력이었다.

그런데 이 중요한 시스템이 최근 들어 심각한 말썽을 부리고 있었다. 그중에서도 개발자들을 가장 미치게 만드는 것은 바로 '유령 알림' 현상이었다.

분명 오른쪽 상단 지구본 아이콘에는 빨간색 숫자 '1'이 선명하게 떠 있다. 새로운 소식이 도착했다는 신호다. 하지만 아이콘을 클릭해 드롭다운 메뉴를 열어보면?

"텅 비었잖아! 아무것도 없다고!"

마치 사용자를 놀리기라도 하듯, 알림 목록은 텅 비어 있거나, 이미 확인한 과거의 알림들만 보일 뿐이었다. 숫자 '1'은 마치 지워지지 않는 낙인처럼 화면에 남아 사용자의 신경을 긁었다.

"대체 어디서 꼬인 거야? DB에는 분명 새 알림 플래그가 없는데, 왜 프론트엔드에서는 계속 '1'로 뜨는 건데?!"

팀 리더 마크가 버그 트래킹 시스템을 노려보며 울부짖었다. 알림 시스템의 로직은 생각보다 훨씬 복잡했다.

  1. 서버에서 새로운 알림 이벤트 발생.
  2. 데이터베이스에 알림 정보 저장 및 '읽지 않음' 상태 설정.
  3. 실시간 통신 채널(Comet, WebSocket 등)을 통해 클라이언트(사용자 브라우저)에게 신호 전송.
  4. 클라이언트 측 자바스크립트가 신호를 받아 알림 아이콘 옆 숫자 업데이트 ($('#noti-count').text('1');).
  5. 사용자가 아이콘 클릭 시, 서버에 알림 목록 요청.
  6. 서버는 '읽지 않음' 상태의 알림 목록 전송.
  7. 클라이언트는 받아온 목록을 드롭다운 메뉴에 렌더링.
  8. 사용자가 드롭다운 확인 시, 서버에 '읽음' 처리 요청 전송.
  9. 서버는 해당 알림 상태를 '읽음'으로 변경.
  10. 클라이언트는 알림 아이콘 숫자 제거 ($('#noti-count').text('');).

이 열 가지 단계 중 어느 한 곳이라도 삐끗하면, 유령 알림 같은 기괴한 현상이 발생했다. 비동기 처리 과정에서의 미묘한 타이밍 문제(Race Condition), 이벤트 리스너의 중복 등록이나 누락, 제이쿼리 콜백 함수 내부의 복잡한 상태 관리 로직 오류… 원인은 수십 가지 가능성으로 흩어져 있었다.

// 어딘가에 있을 법한 문제의 코드 조각
function updateNotificationCount() {
  // 비동기로 알림 개수를 가져온다.
  $.get('/api/notifications/count', function (data) {
    // 만약 다른 로직에서 동시에 카운트를 업데이트하려고 하면?
    // 또는 이 콜백이 예상보다 늦게 실행되면?
    if (data.count > 0) {
      // 어? 사용자가 방금 알림을 확인했는데,
      // 뒤늦게 도착한 이 코드가 다시 숫자를 표시해버린다!
      $('#noti-count').text(data.count);
    } else {
      // 여기서는 숫자를 지우는데, 다른 곳에서는 숫자를 표시하려 한다면?
      $('#noti-count').text('');
    }
  });
}

// 1초마다 알림 개수를 체크한다고 가정해보자.
setInterval(updateNotificationCount, 1000);

// 사용자가 알림 아이콘을 클릭했을 때도 카운트를 업데이트한다.
$('#noti-icon').click(function () {
  // 여기서도 updateNotificationCount()를 호출하면?
  // 또는 직접 DOM을 조작하면?
  // 상태의 일관성은 누가 보장하는가?
  setTimeout(function () {
    // 읽음 처리 후 약간의 딜레이를 준다고 가정
    $('#noti-count').text(''); // 직접 숫자를 지운다!
  }, 500);
});

"미치겠네 진짜… 상태 값이 대체 몇 군데서 관리되는 거야?"

한숨과 탄식이 사무실 공기를 무겁게 짓눌렀다. 단순해 보이는 알림 기능 하나를 안정적으로 유지하기 위해, 개발자들은 보이지 않는 수많은 '상태'와 씨름해야 했다. '새 알림이 있는가?', '총 몇 개인가?', '사용자가 드롭다운을 열었는가?', '읽음 처리가 완료되었는가?' 등등. 이 상태들이 코드 곳곳에 분산되어 제멋대로 변경되니, UI는 현재의 '진짜 상태'를 제대로 반영하지 못하고 혼란에 빠지는 것이다.

조던 워크는 이 아비규환을 조용히 지켜보고 있었다. 그의 시선은 버그 리포트나 특정 코드 라인이 아닌, 문제의 본질을 향하고 있었다.

'결국… 상태 관리의 문제다.'

그는 확신했다. 알림 시스템의 버그는 단지 빙산의 일각일 뿐이었다. 페이스북처럼 복잡하고 동적인 웹 애플리케이션에서, 현재의 방식(DOM 직접 조작, 분산된 상태 관리)으로는 더 이상 안정적인 UI를 보장할 수 없었다.

'UI는 결국 데이터(상태)를 시각적으로 표현한 것일 뿐이야. 그렇다면, 데이터가 변경되었을 때 UI가 알아서, '일관되게' 업데이트되도록 만들 수는 없을까? 개발자가 일일이 DOM을 찾아다니며 명령을 내리는 대신에?'

꺼지지 않는 알림 아이콘의 붉은 숫자. 그것은 단순한 버그가 아니었다. 웹 개발의 낡은 패러다임이 만들어낸 필연적인 오류, 그 고통스러운 상징이었다. 조던 워크의 마음속에서, 이 고통을 끝낼 새로운 방법에 대한 갈망은 더욱 뜨겁게 타오르기 시작했다.