DOM 객체 생명 주기 이해하기

여러분 안녕하세요. DOM 객체 생명주기에 관한 문제로 생각되는 문제를 해결하려고 하고 있습니다. 이 스레드 A tour of how the Widget (Virtual DOM) code in Discourse works 를 읽어봤지만 제 질문에 대한 답을 얻거나, 문제를 해결하기 위한 방향을 제시받지는 못했습니다. 어떻게든 이해하려고 노력하고 있지만 쉽지 않습니다.

저는 외부 서비스(정확히 말하면 Rumbletalk 채팅 엔진)를 래핑하는 매우 기본적인 플러그인을 작성했습니다. 플러그인은 제가 원하는 대로 작동하며, 채팅도 잘 동작합니다. 단, 채팅에서 사이트의 다른 부분으로 이동했다가 다시 채팅으로 돌아올 때만 예외가 발생합니다. 포함되는 JavaScript에서 중복된 채팅을 생성하려고 한다는 오류가 발생합니다.

해당 플러그인은 다음과 같은 HTML을 생성하는 단순한 hbs/위젯입니다.

<div style="height: 500px;">
  <div id="$MY_ID"></div>
  <script src="https://rumbletalk.com/client/?$MY_OTHER_ID"></script>
</div>

그리고 로그인용 JavaScript도 포함되어 있습니다.

어딘가 DOM에 렌더링된 HTML이 여전히 남아 있고, 이 script 태그가 다시 로드되고 실행될 때 이를 감지한다고 추정하고 있습니다. 개발자 콘솔에서 디버깅을 해보면, 로그인 코드에서 발생하는 것이 아니라 script 태그의 JavaScript에서 오류가 throw되는 것을 확인할 수 있습니다.

Rumbletalk 직원들에게 문의했지만 아직 답변을 받지 못했습니다. 다음으로 무엇을 살펴봐야 할지 조언을 주실 분이 있을까요? 제가 할 수 있는 유일한 방법은 window.onerror 핸들러를 추가한 후 location.reload()를 호출하는 것입니다. 물론 이는 좋은 해결책이 아닙니다. 페이지가 포커스를 잃을 때 채팅 엔진의 DOM 객체를 'flush’하는 방법이 있을까요?

도움 주시면 감사하겠습니다.

-=Bob

Bob, 안녕하세요 :wave:

당신의 문제는 HTML 수준을 조금 벗어나는 부분이 있습니다.

<script src="https://rumbletalk.com/client/?$MY_OTHER_ID"></script>

이 스크립트는 IIFE를 실행하고, 인스턴스를 초기화한 후 전역 스코프에 해당 인스턴스를 저장합니다. Discourse는 단일 페이지 애플리케이션(SPA)이므로 전역 스코프는 세션 전체에서 동일합니다. 스크립트가 처음 로드된 후 다음 코드로 이를 확인할 수 있습니다.

console.log(RumbleTalkChat)

따라서 스크립트가 경고하는 대상은 RumbleTalkChat["$MY_OTHER_ID"]입니다.

if (e.RumbleTalkChat[a.hash]) {
  throw new Error("Trying to create a duplicate chat with code: " + a.hash);
}

다음과 같은 코드를 사용하여 스크립트가 다시 로드되기 전에 해당 인스턴스를 삭제해 볼 수 있습니다.

delete RumbleTalkChat["$MY_OTHER_ID"]

참고로 이 문자열에는 ? 문자가 포함되어 있지 않습니다.

그럼에도 불구하고, 근본적으로는 스크립트가 왜 여러 번 로드되는지 파악하는 것이 더 나은 해결책일 것입니다. 스크립트에는 호출할 수 있는 openChat() 메서드가 있는 것을 볼 수 있습니다. 따라서 스크립트를 여러 번 로드할 필요가 없을 수도 있습니다.

불행히도, 임베드가 어디에 배치되는지 및 마크업이 어떻게 추가되는지에 대한 더 자세한 정보를 제공해 주셔야 원인을 정확히 파악할 수 있습니다.

조 씨, 안녕하세요.
IIFE에 대한 설명 감사드립니다. 뭔가 그런 방식일 거라고 짐작은 하고 있었지만, 제목에서처럼 DOM 스코프의 객체일 줄 알았거든요. 모듈 안에서 아래 검사도 봤습니다.

if (e.RumbleTalkChat[a.hash]) {
  throw new Error("Trying to create a duplicate chat with code: " + a.hash);
}

해당 모듈은 위젯이 렌더링될 때 페이지의 나머지 부분과 함께 로드됩니다. 이걸 초기화(initializer)로 옮기고, 그 다음에는 폼만 렌더링하는 방식으로 변경할 수 있는지 확인해 보겠습니다.

다시 한번 감사합니다. 앞으로 더 나아지길 바라면서!