카테고리 없음

[SocketIO] MongoDB Change Stream 중복 이벤트 문제 해결 과정

걍작 2025. 6. 24. 15:19

1. 문제 상황

  • MongoDB Change Stream을 사용해서 devices 컬렉션의 변경을 감지하고,
    관리자(admin)에게 실시간으로 socketlist 변경을 알리고 싶었음.
  • 하지만, DB에 변경이 일어날 때마다 Change Stream 이벤트 로그가 여러 번(2번 이상) 출력되어 혼란이 있었음.

 

2. 원인 분석

  • 서버 단독 실행으로 changeStream 등록이 이중으로 되었거나, 서버 실행이 중복되는 등의 문제가 아님을 확인.
  • 이벤트 등록 로그와 이벤트 실행 로그를 통해 saveSocketListOrder() 함수에서 newOrder 배열의 각 _id에 대해 updateOne을 반복 실행이 이루어진다는 것을 발견.
  • MongoDB Change Stream은 실제로 값이 바뀌는 document마다 이벤트를 발생시킴(6개의 리스트여도 2개만 변경되는 겨우 2번 실행 >> 이것 때문에 테스틑 2개만 나와서 예상을 못하고 방황함).
  • 즉, 여러 개의 document가 실제로 변경되면 그 수만큼 이벤트(로그)가 발생하는 것이 정상임.
     RUN() 함수{
     
      ... 중략 ...
      
        db = atlas_client.db(dbname); // db에 연결
        console.log(`Atlas MongoDB connected successfully!`);
        //changeStream을 사용하여 devices 컬렉션의 변경 사항을 감지하고, 관리자 네임스페이스에 이벤트를 전송
        console.log('Change Stream 생성!');
        const changeStream = db.collection('devices').watch();
        changeStream.on('change', () => {
          console.log('Change Stream 이벤트 발생!');
          adminNamespace.emit('socketlist-changed');
          console.log(`>> server(${process.env.PORT}) : devices collection changed, notifying admin namespace`);
        });
       
      ... 중략 ...
      
     }
     
     
>>CONSOLE LOG
Atlas MongoDB connected successfully!
MongoDB Adapter connected successfully!
process.env.PORT: undefined
HTTP Server mode is running on port 3000
Admin socket connected: lR4gWDCKDSycoGfeAAAB Account ID: your-account-id
admin socket event: save-socket-order
C:\Users\user\Dropbox\github\websocket-server /saveSocketListOrder()>> 
New order: [
  '684943ff060b6e0685cbc1ce',
  '683d48a69fbbc5f2c640f58b',
  '684a8aa7a14c86aaa07c9127',
  '683d50b97073568ada79662b',
  '683d4bef94154f1d636e80d6',
  '683d50df10e27ec77720480a'
]
Change Stream 이벤트 발생!
>> server(undefined) : devices collection changed, notifying admin namespace
Change Stream 이벤트 발생!
>> server(undefined) : devices collection changed, notifying admin namespace

 

3. 해결 흐름

  • Change Stream이 중복 생성된 것이 아니라,
    여러 document가 실제로 변경될 때마다 이벤트가 발생하는 것이 원인임을 확인.
  • 실제로 값이 바뀌는 document 수만큼 로그가 찍히는 것이 정상 동작임을 이해함.

4. 최종 해결 방법

  • debounce/throttle (지연/합치기) 기법을 사용해 짧은 시간 동안 여러 번 발생하는 이벤트를 모아서 한 번만 처리.( db 에서 실제 작동은 동일하게 변경 건 갯수만큼 동작 )
        //changeStream을 사용하여 devices 컬렉션의 변경 사항을 감지하고, 관리자 네임스페이스에 이벤트를 전송
        console.log('Change Stream 생성!');
        let changeStreamTimeout = null;
        const changeStream = db.collection('devices').watch();
        changeStream.on('change', () => {
          if (changeStreamTimeout) clearTimeout(changeStreamTimeout);
          changeStreamTimeout = setTimeout(() => {
            adminNamespace.emit('socketlist-changed');
            console.log(`>> server(${process.env.PORT}) : devices collection changed, notifying admin namespace`);
          }, 300); // 0.3초 동안 이벤트가 또 오면 마지막에 한 번만 실행
        });