ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 리액트에서 EventEmitter를 이용한 이벤트 호출 _ Node.js
    Back-end/Node.js 2023. 4. 13. 11:49
    반응형

    이번 주의 개인적으로 가장 큰 미션 중 하나는 필드가 연달아 3개가 있을 때, 두 번째 세 번째 필드가 첫 번째 필드의 상태를 보고 자동으로 바뀌는 것을 구현하는 것이었다.

     

    좀 더 자세히는 초기 상태를 Select Field로 두고 Select Field 내용을 api 통신을 통해 받아오는데 이 때 받아올 내용이 없다면 첫 번째 필드를 TextField로 바꾸고, 두 번째, 세 번째 필드에서도 초기에 Select Field 였다가 첫 번째 Field가 TextField로 바뀐 것을 감지 한 후 TextField로 바꾸는 작업이다.

     

    물론 각 필드를 하나씩 구현하면 쉽게 만들 수 있다.

    하지만 각 필드는 사실 하나의 컴포넌트를 렌더링 한 것이다.

    이를 위해서 Node.js에서 제공하는 eventEmitter를 사용해보았다.

     


    Event emitter in Node.js

    EventEmitter는 특정 이벤트에 리스너 함수를 달아서 이벤트가 발생했을 때, 이를 알아차리도록 만들어진 api이다.

    이는 인스턴스 자체 내에서 이벤트와 연결 된 모든 이벤트와 리스너를 추적하며, 사용방법은 크게 on, off, emit으로 구분할 수 있다.

     

    eventEmitter.on() Method

    이벤트를 구독(혹은 등록)하는 메소드이다. 

    이를 등록하여 이벤트가 트리거될 때 실행될 콜백 함수를 추가하는데 사용이 된다.

    그리고 이 이벤트를 트리거 시키는 함수가 eventEmitter.emit() method 이다.

    eventEmitter.on('hello', () => {
      console.log('HELLO')
    })

     

    eventEmitter.emit() Method

    이벤트를 발생시키는 메소드이다.

    emit과 비슷한 것으로 listeners() mehtod가 있는데, 이는 배열 형식으로 함수를 리턴해준다.

    eventEmitter.emit('hello')

     

    eventEmitter.off() Method

    우리는 흔히 event.addEventListener()를 사용할 때, 웹 어플리케이션의 메모리 누수를 방지하기 위해서 removeEventListener()를 사용한다.

    removeEventListener()를 사용하지 않고 DOM에서 element를 지우게 되면 여전히 listener는 메모리에 남아있게 되고 이는 시간이 지남에 따라 성능 이슈, 메모리 이슈를 불러일으킬 수 있기 때문이다.

    이와 똑같이 eventEmitter 또한 구독한 것을 remove해줘야하는데 그 메소드가 off이다.

    eventEmitter.off('hello', () => {
      console.log('HELLO')
    })

     

    이 외에도 once(), removeAllListeners() 등의 메소드가 있다.

     

    그런데 사실 위와 같이 함수를 eventEmitter 메소드 안에 쓰는 것은 좋지 않으며 함수를 따로 빼서 사용하는 것이 좋다.
    위의 예시처럼 사용할 때, 눈으로 봤을 때는 같은 함수 같지만 Node.js가 서로 다른 함수로 인식한다고 한다.
    그렇기에 함수는 따로 빼서 관리하면 효율적일 것이다.

     


    그렇다면 내가 사용한 실상황들을 한번 보자.

    우선 현재 상황은 waitFor라는 것을 임시로 만들어서 autoComplete을 할 때 위의 필드를 보고 disable 처리를 하고 있다.

    아래 이미지에서 "테이블"과 "뷰 아이디 또는 이름"이 disable 되어있는 이유가 그것이다.

    베이스에서 value가 선택 되어야 테이블이 able로 바뀌고, 또 테이블의 value가 선택되어야지 "뷰 아이디 또는 이름"이 able로 바뀌게 되는 형태이다.

    이는 require라는 이름으로 ['base', 'table'] 두 가지를 내려줘서인데, 이 때 "테이블"은 require의 'base'를 보고 있고, "뷰 아이디 또는 이름"은 require의 'table'을 보고 있다.

    베이스의 내용물은 api를 통해서 불러오는데 만약 내용이 없으면 400 에러를 던져주게 되고, 400이 나타났다면 베이스가 autoComplete에서 TextField로 바뀌면 된다.

    그리고 그 아래의 필드들도 베이스가 바뀐 것을 보고 차례로 TextField로 바뀌면 된다.

     

    이를 위해 몇 군데 손을 본게 있지만 주요 부분만 보자면,

      const [requireError, setRequireError] = useState(false);
    
      const handleRequireSuccess = () => {
        setRequireError(false);
      };
    
      const handleRequireFailure = () => {
        setRequireError(true);
      };
    
      useEffect(() => {
        if (require.length > 0) {
          require.forEach((name) => {
            eventEmitter.on(`${name}:success`, handleRequireSuccess);
            eventEmitter.on(`${name}:failure`, handleRequireFailure);
          });
    
          return () => {
            require.forEach((name) => {
              eventEmitter.off(`${name}:success`, handleRequireSuccess);
              eventEmitter.off(`${name}:failure`, handleRequireFailure);
            });
          };
        }
      }, [require])

     

      const [options, objOptions] = useMemo(() => {
        if (getSaasDataQuery.status === 'fulfilled') {
          const { data, isError, isFetching, isLoading } = getAPI;
    
          eventEmitter.emit(`${name}:success`);
    
          return [ ... ];
        } else if (isError) {
          eventEmitter.emit(`${name}:failure`);
        } else if (isLoading || isFetching) {
          eventEmitter.emit(`${name}:success`);
        }
        
        return [[], {}];
      }, [getSaasDataQuery, name]);
    필드명에 따라서 on을 통해 구독을 시켜주고, api 통신을 하고 받아온 정보를 토대로 emit을 실행해주면 된다.

    그리고 state로 관리하고 있는 requireError를 통해서 textField를 보여줄지, autoComplete을 보여줄지 render 부분을 정해주면 원하는대로 기능을 구현할 수 있었다.

     

    반응형

    'Back-end > Node.js' 카테고리의 다른 글

    typescript react + typescript express s setting  (0) 2023.01.28
Designed by Tistory.