본문 바로가기
  • 조금 느려도, 꾸준히
WEB/React

[Scroll Magic] Failed to execute removeChild on Node 에러 해결

by chan 2021. 11. 1.
반응형

포트폴리오 사이트에 이력서 페이지를 추가하려고 react-router 를 이용하여 resume 페이지를 추가하였는데, 제목과 같은 에러를 만나 해결 방법을 정리해 두고자 한다.

 

# 문제 상황

resume 페이지를 추가하고, 상단 탭의 resume 버튼을 누르면 react-router-dom 의 useHistory 훅을 이용하여 resume 페이지에 해당하는 url을 푸시한다.

의도했던 동작은 url을 푸시하면서 resume 페이지가 보여야 하는데, resume 컴포넌트가 마운트 되지 않고 아래와 같은 에러를 내뱉었다.

새로고침을 하면 resume 페이지가 정상적으로 보이는 것을 확인했다.

 

# 원인 고찰

우선 새로고침 시 resume 페이지가 정상적으로 보이는 것을 확인했으므로, url을 푸시하여 resume 페이지로 이동하는 과정에서 그 전 페이지의 컴포넌트 내부 Node 객체들의 removeChild 함수 작동에 이상이 생겼음을 짐작할 수 있다.

직관적으로 스친 생각은 scroll magic 의 컴포넌트를 사용하면서 컴포넌트가 unmount 될 때 이슈가 있지 않았을까? 하는 것이었다.

그리고 확인해본 결과, resume 페이지로 가기 전 main 페이지에서 컴포넌트는 다음과 같은 구조로 작성되어 있었다.

import { Controller } from 'react-scrollmagic';

export function Main() {
    return <Controller>
        <Home />
        <About />
        <Works />
        <Contact />
    </Controller>;
}

 

Controller 부모 컴포넌트 내부에 Scene 컴포넌트로 이루어진 각각의 Home, About, Works, Contact 가 들어있는 구조인데, 아차 싶었다.

원래는 Main 페이지 하나만 가지고 페이지 이동 없이 스크롤만 하도록 구성해서 이슈를 알아차리지 못했는데, Controller 컴포넌트를 최상단 부모 컴포넌트로 사용한 것이 원인이었다.

react-scrollmagic 라이브러리의 Controller 컴포넌트의 코드는 다음과 같다.

class Controller extends React.Component<ControllerProps, ControllerState> {
  controller: any;

  state: ControllerState = {
    controller: null,
  }

  componentDidMount() {
    const { children, ...controllerProps } = this.props;
    this.setState({
      controller: new ScrollMagic.Controller(controllerProps)
    });
  }

  componentWillUnmount() {
    this.controller = null;
  }

  render() {
    const { children } = this.props;
    const { controller } = this.state;

    if (!controller) {
      return children;
    }

    return (
      <ControllerContext.Provider value={controller}>
        {children}
      </ControllerContext.Provider>
    );
  }
}

componentWillUnmount() 함수를 보면, this.controller 가 null 이 되면서 children 만을 반환하게 된다. 즉 history push 를 통해 resume 로 이동하는 과정에서 Virtual DOM 에는 <ControllerContext.Provider> 라는 root 컴포넌트가 여전히 존재하고, 실제 DOM에서는 제거해야 하는 해당 root 컴포넌트가 존재하지 않기 때문에 removeChild 실행 과정에서 에러가 나게 되었던 것이다.

 

# 해결 

간단하게 해당 Controller 컴포넌트를 <div> 컴포넌트로 한 번 감싸주면 해결된다. 이렇게 되면 Virtual DOM, real DOM 모두 div 로 이루어진 root 컴포넌트가 존재하기 때문에 삭제하는데 이슈가 생기지 않는다. React 는 root 컴포넌트만 신경쓰고, 내부 child 컴포넌트들의 변화는 신경쓰지 않는다.

import React from 'react';
import { Controller } from 'react-scrollmagic';

import { About } from './about';
import { Contact } from './contact';
import { Home } from './home';
import { Works } from './works';

export function Main() {
    return <div>
        <Controller>
            <Home />
            <About />
            <Works />
            <Contact />
        </Controller>
    </div>;
}

 

참고 https://stackoverflow.com/questions/54880669/react-domexception-failed-to-execute-removechild-on-node-the-node-to-be-re

 

반응형

'WEB > React' 카테고리의 다른 글

포트폴리오 사이트 작성기 (React)  (0) 2021.10.14

댓글