일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 쿠버네티스
- TypeScript
- react
- kubernetes
- 클라우드
- HTML
- 웹
- 자바스크립트
- 파이썬
- 컴퓨터공학
- 블록체인
- next.js
- AWS
- 알고리즘
- 이더리움
- k8s
- BFS
- 프론트엔드
- node.js
- 백엔드
- docker
- CSS
- 이슈
- 타입스크립트
- 리액트
- es6
- JavaScript
- 솔리디티
- 가상화
- 백준
- Today
- Total
즐겁게, 코드
TreeWalker 객체로 DOM을 순회하며 필터 적용하기 본문
개발을 진행하다 보면 문자열화된 DOM을 마크업에 렌더링해야 하는 경우가 종종 생기기 마련입니다.
💡 문자열 형태의 DOM을 purify하는 이유는 DOMPurify 라이브러리에 잘 소개되어 있습니다.
import parse from "html-react-parser";
const App = () => {
const purifiedString = parse("<div>hello world</div>");
return <main>{string}</main>;
};
export default App;
최근 진행하던 프로젝트에서 관련된 문제가 있었는데, 어떻게 문제를 해결했는지를 소개합니다.
📝 TL;DR : DOM을 순회하며 탐색하거나 필터해야 할 때는 TreeWalker 객체를 사용해 보세요.
먼저 흔한 모습은 아니지만, 모종의 이유로 백엔드에서 문자열 형태의 DOM을 내려주어 사용해야 하는 경우가 있다고 가정하겠습니다.
import parse from "html-react-parser";
const App = () => {
const MOCK_NEWS_DATA = [
"<div>[속보]: 이제 벌써 연말 다가와... 30대를 바라보고 있어</div>",
"<div>[경제]: 금리 인하 결정... 서민 부담 줄어드나</div>",
"<div>[IT]: 앗, 맥미니 타이어 20개보다 싸다!</div>",
];
return (
<main>
<ul>
{MOCK_NEWS.map((news) => (
<li key={news}>{parse(news)}</li>
))}
</ul>
</main>
);
};
export default App;
"한줄 뉴스" 라는 서비스를 구현하게 되어 DOM을 Purify한 뒤 화면에 그려준 모습입니다.
여기까지는 문제가 없는데, 만약 텍스트가 아닌 요소가 문자열에 포함되어 있다면 어떨까요?
import parse from "html-react-parser";
const App = () => {
const MOCK_NEWS = [
"<div>[속보]: 이제 벌써 연말 다가와... 30대를 바라보고 있어</div>",
"<div><img src='https://placehold.co/300x200'/>[IT]: 내수 위주의 한국 IT, 해법은 무엇인가...</div>",
"<div>[경제]: 금리 인하 결정... 서민 부담 줄어드나</div>",
"<div>[게임]: 3년만의 플레이스테이션 신작 발표 및 티저 공개 <img src='https://placehold.co/500x400' />...</div>",
"<div>[IT]: 앗, 맥미니 타이어 20개보다 싸다!</div>",
];
return (
<main>
<div>한줄 뉴스</div>
<ul>
{MOCK_NEWS.map((news) => (
<li key={news}>{parse(news)}</li>
))}
</ul>
</main>
);
};
export default App;
텍스트에 이미지 태그가 포함되면서, 당연하게도 레이아웃이 깨지는 모습입니다.
이런 상황은 어떻게 피할 수 있을까요?
TreeWalker 객체
DOM 트리를 이루는 기본 요소가 Node 라면, TreeWalker(MDN)는 DOM의 Node를 순회하거나 필터할 수 있는 기능을 제공하는 객체입니다.
예시
const treeWalkerRoot = document.createElement("div");
treeWalkerRoot.innerHTML = "<div>[게임]: 3년만의 플레이스테이션 신작 발표 및 티저 공개 <img src='https://placehold.co/500x400' />...</div>";
// const treeWalker = document.createTreeWalker(<순회를 시작할 노드>, <필터할 노드 유형>, <추가 필터 조건>);
// 텍스트만 필터하는 예시
const treeWalker = document.createTreeWalker(treeWalkerRoot, NodeFilter.SHOW_TEXT);
💡 각 NodeFilter 상수는 window 객체에 바인딩되어 있으며, 저마다 다른 값을 갖고 있습니다.
(Ex. NodeFilter.SHOW_ALL = 4294967295)
NodeFilter 상수의 종류는 이 문서(MDN)를 참고해 주세요.
이제 TreeWalker 객체로 DOM 문자열에서 텍스트만을 필터해 보겠습니다.
const getFirstTextNode = (articleText: string): string => {
const treeWalkerRoot = document.createElement("div");
treeWalkerRoot.innerHTML = articleText;
const treeWalker = document.createTreeWalker(
treeWalkerRoot,
NodeFilter.SHOW_TEXT, // 텍스트 노드만 선택
{
// acceptNode는 Array.filter처럼 필터할 노드를 선택할 조건을 다루는 함수입니다.
acceptNode: function (node: Node) {
// 공백이 아닌 텍스트 노드만을 허용
if (node.nodeValue && node.nodeValue.trim().length > 0) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_REJECT;
},
}
);
// 첫 번째 텍스트 노드를 반환
const textNode = treeWalker.nextNode();
return textNode ? textNode.nodeValue!.trim() : "";
};
const articleText = `<div>[게임]: 3년만의 플레이스테이션 신작 발표 및 티저 공개 <img src='https://placehold.co/500x400' />...</div>`
// [게임]: 3년만의 플레이스테이션 신작 발표 및 티저 공개
console.log(getFirstTextNode(articleText));
텍스트가 깔끔하게 파싱된 모습입니다.
import { getFirstTextNode } from "./getFirstTextNode";
const App = () => {
const MOCK_NEWS = [
"<div>[속보]: 이제 벌써 연말 다가와... 30대를 바라보고 있어</div>",
"<div><img src='https://placehold.co/300x200' />[IT]: 내수 위주의 한국 IT, 해법은 무엇인가...</div></div>",
"<div>[경제]: 금리 인하 결정... 서민 부담 줄어드나</div>",
"<div>[게임]: 3년만의 플레이스테이션 신작 발표 및 티저 공개 <img src='https://placehold.co/500x400' />...</div>",
"<div>[IT]: 앗, 맥미니 타이어 20개보다 싸다!</div>",
];
return (
<main>
<div>한줄 뉴스</div>
<ul>
{MOCK_NEWS.map((news) => (
<li key={news}>{getFirstTextNode(news)}</li>
))}
</ul>
</main>
);
};
export default App;
이제 DOM 문자열에 <img>
, <video>
등의 요소가 섞여 있더라도 텍스트 요소만을 깔끔하게 표시할 수 있게 된 모습입니다.
TreeWalker 객체가 없었다면 `<` 문자열을 포함하고 있는지 등을 복잡하게 검사해야 했을 텐데, DOM을 순회하며 특정 조건에 따라 필터해야 한다면 TreeWalker 객체를 사용할 수 있음을 기억해주시면 좋을 것 같습니다.
그럼, 오늘도 즐거운 코딩 하세요!
참고
https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker#instance_methods
https://developer.mozilla.org/ko/docs/Web/API/Document/createTreeWalker#whattoshow
'🎨 프론트엔드' 카테고리의 다른 글
스크롤 UI 구현하기 : Swiper Free Mode VS. overflow-x: scroll (0) | 2024.11.26 |
---|---|
XSS 공격의 위험성 체험하기 (1) | 2024.11.14 |
페이지 노출 여부를 판별하는 Page Visibility API 살펴보기 (0) | 2024.06.02 |
Server-Sent Event로 데이터 구독하기 (2) | 2024.04.27 |
electron-react-boilerplate에 테일윈드 끼얹기 (0) | 2023.09.30 |