<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>DOM on monkshark.dev</title><link>https://monkshark.github.io/tags/dom/</link><description>Recent content in DOM on monkshark.dev</description><generator>Hugo -- gohugo.io</generator><language>ko</language><lastBuildDate>Tue, 23 Jun 2026 11:00:00 +0900</lastBuildDate><atom:link href="https://monkshark.github.io/tags/dom/index.xml" rel="self" type="application/rss+xml"/><item><title>#2 - 남의 DOM 위에 세 들어 산다</title><link>https://monkshark.github.io/p/pr-lens-resilience/</link><pubDate>Tue, 23 Jun 2026 11:00:00 +0900</pubDate><guid>https://monkshark.github.io/p/pr-lens-resilience/</guid><description>&lt;p&gt;패널을 띄우는 건 쉬웠다. 어려운 건, 그 패널이 남의 페이지 위에서 — GitHub 가 마크업을 바꿔도, diff 를 천천히 그려도 — 깨지지 않고 버티게 하는 것이었다.&lt;/p&gt;
&lt;h2 id="셀렉터는-한-곳에-모으고-사다리로-쌓는다"&gt;&lt;a href="#%ec%85%80%eb%a0%89%ed%84%b0%eb%8a%94-%ed%95%9c-%ea%b3%b3%ec%97%90-%eb%aa%a8%ec%9c%bc%ea%b3%a0-%ec%82%ac%eb%8b%a4%eb%a6%ac%eb%a1%9c-%ec%8c%93%eb%8a%94%eb%8b%a4" class="header-anchor"&gt;&lt;/a&gt;셀렉터는 한 곳에 모으고, 사다리로 쌓는다
&lt;/h2&gt;&lt;p&gt;확장은 GitHub DOM 에 기댄다. 그런데 GitHub 는 자기 마크업을 수시로 바꾼다. 클래스 이름이 갈리고, &lt;code&gt;data-testid&lt;/code&gt; 가 생겼다 없어지고, 같은 시기에 옛 마크업과 새 마크업이 섞여 나오기도 한다. 셀렉터를 코드 곳곳에 흩뿌리면 그 변화 한 번에 전부 무너진다.&lt;/p&gt;
&lt;p&gt;그래서 DOM 을 읽는 지점을 셀렉터 모듈 하나로 모았다. 파일 컨테이너는 후보를 여러 개 묶어, 신·구 마크업을 한꺼번에 받는다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fileContainer&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;div.file.js-file&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;div.file[data-tagsearch-path]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;[data-testid=&amp;#34;file-diff&amp;#34;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;[class*=&amp;#34;diffTargetable&amp;#34;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;파일 경로를 뽑을 때도 한 방법에 걸지 않았다. &lt;code&gt;data-tagsearch-path&lt;/code&gt; 속성 → 앵커 텍스트 → &lt;code&gt;Diff for:&lt;/code&gt; 표 라벨 → 내부 속성 → 파일 헤더 링크 title 순으로 내려가며, 하나가 실패하면 다음을 시도한다. 수치(+/-)도 같은 식으로 &lt;code&gt;aria-label&lt;/code&gt; → 색상 클래스 → 옛 diffstat 까지 폴백을 깔았다.&lt;/p&gt;
&lt;h2 id="못-찾으면-조용히-비운다"&gt;&lt;a href="#%eb%aa%bb-%ec%b0%be%ec%9c%bc%eb%a9%b4-%ec%a1%b0%ec%9a%a9%ed%9e%88-%eb%b9%84%ec%9a%b4%eb%8b%a4" class="header-anchor"&gt;&lt;/a&gt;못 찾으면, 조용히 비운다
&lt;/h2&gt;&lt;p&gt;폴백을 다 내려가도 아무것도 못 찾는 날은 온다. 그럴 때 절대 하지 말아야 할 건, 에러를 던져 GitHub 페이지 자체를 망가뜨리는 것이다. 그래서 파일을 하나도 못 찾으면 패널은 깨지는 대신 &amp;ldquo;바뀐 파일 없음&amp;rdquo; 안내를 띄운다. 내가 못 읽는 게 사용자 페이지를 부수는 일이 되면 안 된다.&lt;/p&gt;
&lt;h2 id="내가-일으킨-변화에-내가-반응하지-않기"&gt;&lt;a href="#%eb%82%b4%ea%b0%80-%ec%9d%bc%ec%9c%bc%ed%82%a8-%eb%b3%80%ed%99%94%ec%97%90-%eb%82%b4%ea%b0%80-%eb%b0%98%ec%9d%91%ed%95%98%ec%a7%80-%ec%95%8a%ea%b8%b0" class="header-anchor"&gt;&lt;/a&gt;내가 일으킨 변화에 내가 반응하지 않기
&lt;/h2&gt;&lt;p&gt;GitHub 는 diff 를 한 번에 안 그린다. 스크롤하면 파일이 점점 붙는다. 그래서 DOM 변화를 &lt;code&gt;MutationObserver&lt;/code&gt; 로 지켜보다 다시 스캔해야 한다. 그런데 함정이 있다. 패널과 체크박스를 주입하는 것도 DOM 변화라, 내 주입이 옵저버를 깨우고, 그게 또 재스캔과 재주입을 부르는 무한 루프가 된다.&lt;/p&gt;
&lt;p&gt;두 겹으로 막았다. 변화가 전부 내 패널 안에서 난 거면 무시하고,&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isOwnMutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;every&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;closest&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#gh-prh-panel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;closest&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.gh-prh-seen&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;재스캔하는 동안에는 옵저버를 잠시 끊었다가, 끝나고 다시 잇는다. 내가 만든 변화가 나를 다시 깨우지 못하게.&lt;/p&gt;
&lt;p&gt;또 매번 목록을 통째로 다시 그리면 깜빡이고 느리다. 그래서 파일 경로와 수치로 시그니처를 만들어, 지난번과 같으면 &amp;ldquo;봤음&amp;rdquo; 표시만 토글하고 DOM 은 건드리지 않는다.&lt;/p&gt;
&lt;h2 id="돌아보면"&gt;&lt;a href="#%eb%8f%8c%ec%95%84%eb%b3%b4%eb%a9%b4" class="header-anchor"&gt;&lt;/a&gt;돌아보면
&lt;/h2&gt;&lt;p&gt;2부는 기능이 아니라 규율에 대한 얘기였다. 남의 DOM 위에 세 들어 사는 코드는, 집주인이 벽을 옮겨도 버티고(폴백 사다리), 못 버틸 땐 조용히 비키고(graceful degrade), 자기가 일으킨 먼지에 자기가 기침하지 않아야(자기 변경 무시) 한다. 화면에 안 보이는 이 세 가지가 패널을 오래 살아남게 한 진짜 이유였다.&lt;/p&gt;</description></item></channel></rss>