개발을 다하고 배포가 나간지 한참된 스펙에 갑자기 버그가 있다고 문의가 들어왔다.
영어로 i18n 번역이 되도록 해놓고, google translate로 한국어로 번역을 누르고 나면 다음과 같은 에러가 나면서 화면이 에러바운더리의 화면으로 바뀌는 것이다.
Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
처음에는 어떤 문제인지 전혀 감을 잡을 수 없었는데, 왜냐하면 구글의 번역 기능을 쓰기전에는 완벽하게 잘 동작하는 코드였기 때문이다. 구글 번역 기능이 문제를 일으킬거란 생각을 하지 못 하고, 코드를 찾아보다 30분쯤 지났을 때 google translate 관련 이슈가 있는지 찾아보기로 하였다.
찾아본 결과 나의 상황과 같은 이슈를 발견했고, 문제를 해결할 수 있었다.
// Case 1
<div>
{condition && 'Welcome'}
<span>Something</span>
</div>
// Doesn't throw
<div>
{condition && 'Welcome'}
</div>
// Case 2
<div>
{condition && <span>Something</span>}
Welcome
</div>
case 1, case 2처럼 글 2개를 하나의 div 안에서 조건부로 렌더링 할 때 나타난다고 한다.
google translate 내부적으로 아래와 비슷한 로직이 있고 해당 로직의 removeChild로 인해 에러가 발생한다.
const children = document.getElementById("parent").childNodes;
for (const myEl of children) {
if (myEl.nodeType === Node.TEXT_NODE) {
const fontEl = document.createElement("font");
fontEl.textContent = myEl.data;
myEl.parentElement.insertBefore(fontEl, myEl);
myEl.parentElement.removeChild(myEl);
}
}
case 1은 조건부로 사라질 때, textNode는 이미 parent의 child가 아닌데, react가 parent.removeChild(textNode) 를 call 해서 버그가 난다고 한다.
case 2는 텍스트 노드 앞에 노드가 조건부로 렌더링 된 후, 그런 다음 React가 parent.insertBefore(someNode, textNode)를 호출하여 노드를 삽입하려고 할 때, textNode가 더 이상 parent의 자식이 아니기 때문에 에러가 발생한다.
React의 재조정 과정 중에 textNode가 parent의 자식이 아니게 되는 타이밍 문제로 인해 에러가 발생할 수 있다고 한다.
첫번째가 textNode였는데, Node로 바뀌면서 문제가 발생 or 첫번째가 node였는데 textNode가 되면서 발생하는 문제인게 아닌가 한다.
우리의 문제는 i18n 사용하는 곳에서 조건문으로 <Trans i18nKey />
등을 사용하고 있는 곳이 문제였다.
그래서 이 부분을 <span><Trans i18nKey /></span>
으로 사용했더니 문제가 해결되었다.
이러한 문제를 방지하기 위해서라도, 기본적인 text라도 <span>
등을 이용하 딱딱 감싸놓으면 좋을 것 같다.