Html-react-parser: TS ์˜ค๋ฅ˜: instanceof ์š”์†Œ๋Š” Next.js์—์„œ ํ•ญ์ƒ false๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2021๋…„ 01์›” 29์ผ  ยท  15์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: remarkablemark/html-react-parser

์žฌํ˜„ ๊ฐ€๋Šฅํ•œ ๋ฐ๋ชจ

https://codesandbox.io/s/quizzical-lake-16q4o?file=/pages/index.tsx

์‚ฌ์šฉ์ž ์ง€์ • ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๊ต์ฒดํ•  ๋•Œ ํ•ญ์ƒ false๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ any ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ๋ชจ๋“  ์œ ํ˜• ์ด์™ธ์˜ ๋‹ค๋ฅธ ์†”๋ฃจ์…˜์ด ์žˆ์Šต๋‹ˆ๊นŒ?

question

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ด ์ง€์นจ์„ ๋”ฐ๋ฅด๊ณ  ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. attribs ์—์„œ domNode attribs ์†์„ฑ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

typescript check domNode ๋Š” Element ์œ ํ˜•์ด ์•„๋‹ˆ๋ผ DOMNode ์ธ์Šคํ„ด์Šค์ด๋ฏ€๋กœ DOMNode ์— attribs ์—†๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์žฌ์‚ฐ.

๊ทธ๋ฆฌ๊ณ  ์ด ์กฐ๊ฑด์œผ๋กœ console.log(domNode instanceof Element) ํ•˜๋ฉด domNode์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ DOMNode ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•ญ์ƒ false๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. domNode ๋งค๊ฐœ๋ณ€์ˆ˜์— Element ์œ ํ˜•์„ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

import parse, { HTMLReactParserOptions } from "html-react-parser";
import { Element } from "domhandler/lib/node";

export function contentHandler(postContent: string) {
  const options: HTMLReactParserOptions = {
    replace: (domNode: Element) => {
      if (domNode.attribs) {
        if (domNode.attribs.id === 'shortcode') {
          return <div className="leadform">Shortcode</div>;
        }
      }
    },
  };

  return parse(postContent, options);
}

๋ชจ๋“  15 ๋Œ“๊ธ€

๋ฌธ์ œ๋ฅผ ์—ด์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค @purp1eeeeee

CodeSandbox ์˜ˆ์ œ์—์„œ๋Š” React ์š”์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฐ”๊พธ๊ธฐ๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฝ์–ด ๋ณด๊ธฐ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๋กœ๊ทธ๋ฅผ ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ์˜ ๋ฐ˜ํ™˜์œผ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

export default function IndexPage() {
   const html = "<h1>hoge</h1>";
   const options: HTMLReactParserOptions = {
     replace: (domNode) => {
-      console.log(domNode);
-      console.log(domNode instanceof Element);
+      if (domNode.name === "h1") {
+        return <h1>replaced</h1>;
+      }
     }
   };
   return <div>{parse(html, options)}</div>;
 }

์—…๋ฐ์ดํŠธ๋œ CodeSandbox๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ด ์ง€์นจ์„ ๋”ฐ๋ฅด๊ณ  ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. attribs ์—์„œ domNode attribs ์†์„ฑ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

typescript check domNode ๋Š” Element ์œ ํ˜•์ด ์•„๋‹ˆ๋ผ DOMNode ์ธ์Šคํ„ด์Šค์ด๋ฏ€๋กœ DOMNode ์— attribs ์—†๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์žฌ์‚ฐ.

๊ทธ๋ฆฌ๊ณ  ์ด ์กฐ๊ฑด์œผ๋กœ console.log(domNode instanceof Element) ํ•˜๋ฉด domNode์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ DOMNode ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•ญ์ƒ false๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. domNode ๋งค๊ฐœ๋ณ€์ˆ˜์— Element ์œ ํ˜•์„ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

import parse, { HTMLReactParserOptions } from "html-react-parser";
import { Element } from "domhandler/lib/node";

export function contentHandler(postContent: string) {
  const options: HTMLReactParserOptions = {
    replace: (domNode: Element) => {
      if (domNode.attribs) {
        if (domNode.attribs.id === 'shortcode') {
          return <div className="leadform">Shortcode</div>;
        }
      }
    },
  };

  return parse(postContent, options);
}

Next.js ํ”„๋กœ์ ํŠธ์— ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Typescript type-casting domNode ๋Š” html-react-parser๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•œ ํ›„ ์œ ์ผํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@kakaeriel
๋„ˆ๋ฌด ์ข‹์•„! ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค๐Ÿ‘

Typescript ํ”„๋กœ์ ํŠธ์— ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. @kakaeriel ์ฒ˜๋Ÿผ Typescript์˜ ์„น์…˜์„ ํฌํ•จํ•˜์—ฌ ๊ต์ฒดํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ด ์ง€์นจ์„ ๋”ฐ๋ฅด๊ณ 

@kakaeriel ์˜ ์†”๋ฃจ์…˜์ด ์ €์—๊ฒŒ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค. instanceof ์—ฐ์‚ฐ์ž๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋ฉฐ(์š”์†Œ๋Š” ์œ ํ˜•์ด ์•„๋‹˜) ์œ ํ˜• ์ •์˜๋ฅผ ์ฐพ๋Š” node_modules๋ฅผ ๋ช‡ ์‹œ๊ฐ„ ๋™์•ˆ ํƒ์ƒ‰ํ•œ ํ›„ ์ด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ฐพ๋Š” ๊ฒƒ์ด ์ƒ๋ช…์˜ ์€์ธ์ž…๋‹ˆ๋‹ค.

@kakaeriel ๋Œ€์ฒด ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. README.md ๋ฌธ์„œ๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด PR์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ๊ด€์‹ฌ์ด ์žˆ์œผ์‹ญ๋‹ˆ๊นŒ?

@remarkablemark ๋„ค ๋งž์Šต๋‹ˆ๋‹ค! ์ตœ๋Œ€ํ•œ ๋นจ๋ฆฌ PR์„ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์šฐ์™€. NextJS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

typescript check domNode ๋Š” Element ์œ ํ˜•์ด ์•„๋‹ˆ๋ผ DOMNode ์ธ์Šคํ„ด์Šค์ด๋ฏ€๋กœ DOMNode ์— attribs ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์žฌ์‚ฐ.

```

๊ทธ๋ ‡๋‹ค๋ฉด ์™œ ์ด๊ฒƒ์ด NextJS ์•ฑ์—์„œ๋งŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๊นŒ?
react-scripts typescript ์•ฑ์„ ์„ค์ •ํ–ˆ๋Š”๋ฐ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
https://codesandbox.io/s/html-parsing-and-replacing-elements-yjtv7?file=/src/index.tsx

@Naxos84๋Š” ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. Next.js์—๋Š” ๋‹ค๋ฅธ TypeScript ๊ตฌ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ @kakaeriel ์˜ ์†”๋ฃจ์…˜ ์„ ํ™•์ธํ•˜์„ธ์š”.

@kakaeriel ์ด PR์„ ์—ด์–ด README.md ์„ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ์„ธ์š”. ๋‹น์‹ ์ด ํ•  ์ˆ˜ ์—†๋‹ค๋ฉด, ๋‚˜๋Š” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”!

๋‚˜๋Š” ๊ทธ๊ฒƒ์ด NextJ์™€ ๊ด€๋ จ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
๋‚˜๋Š” ์กฐ๊ธˆ ๋” ์‹œ๋„ํ–ˆ๊ณ  ์–ด๋–ป๊ฒŒ ๋“  ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ๋‹ค.
@remarkablemark codesandbox๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  Stack Overflow์—์„œ ์–ธ๊ธ‰ํ•œ console.log(domElement.constructor) ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
grafik

์™œ ๊ฐ‘์ž๊ธฐ ์ž‘๋™ํ•˜๋Š”์ง€ ์ดํ•ด๊ฐ€๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!?

"๋‚ด"nextjs ํ”„๋กœ์ ํŠธ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์ง€๋งŒ.
์—…๋ฐ์ดํŠธ: ๊ทธ๋ฆฌ๊ณ  ์ด์ œ ๋‹ค์‹œ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค....

์‚ฌ์šฉ์ž ์ •์˜ typeguard๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

const isElement = (domNode: DOMNode): domNode is Element => {
    const isTag = domNode.type === "tag";
    const hasAttributes = (domNode as Element).attribs !== undefined;

    return isTag && hasAttributes;
};
const parserOptions: HTMLReactParserOptions = {
    replace: domNode => {
        if (isElement(domNode)) {
            return <p>It's an element.</p>;
        } else {
            return <p>It's something else</p>;
        }
    },
};

์•ˆ๋…•ํ•˜์„ธ์š” ์—ฌ๋Ÿฌ๋ถ„, ์ €๋Š” ์ด ์†”๋ฃจ์…˜์œผ๋กœ Next.js 10.x์—์„œ

@natterstefan ์†”๋ฃจ์…˜์„ ๊ณต์œ ํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

NextJS์—๋„ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์š”์†Œ๋Š” ํ•ญ์ƒ ์ •์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. domNode๋ฅผ any๋กœ ์ž…๋ ฅํ•˜๊ณ  ์˜ต์…˜ ์œ ํ˜•์„ HTMLReactParserOptions๋กœ ๋ณ€ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค. domhandler์˜ ์š”์†Œ๊ฐ€ SSR์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

const options = {
  replace: (domNode: any) => {
      // ...
  }
} as HTMLReactParserOptions;

์•„๋ž˜ ์ฝ”๋“œ๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

Screen Shot 2021-06-12 at 20 32 31

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰