React에서 HTML 문자열을 렌더링해야 하는 경우가 있습니다. 이런 상황에서 dangerouslySetInnerHTML
을 사용하게 되는데, 이는 보안과 성능 측면에서 여러 문제를 일으킬 수 있습니다. HTML 아무 제약 없이 그대로 출력된다는 건 XSS 공격의 위험이 있고, 사용자 입력값이 직접 DOM에 주입될 수 있어 위험합니다.
또한 React의 Virtual DOM 을 우회하게 되어, 최적화되지 않고 불필요한 리렌더링을 통해 성능 저하의 원인이 될 수 있습니다
대안 : html-react-parse + DOMPurify
npm install html-react-parser dompurify @types/dompurify
dangerouslySetInnerHTML을 대신하기 위해 위 두 가지 라이브러리를 활용하려고 합니다.
- html-react-parser: HTML 문자열을 React 요소로 변환
- DOMPurify: HTML 콘텐츠 필터링 ( 보안성 증가 )
해당 라이브러리를 활용한다면 아래와 같은 형태로 HTML 스트링을 렌더링 할 수 있습니다
import DOMPurify from 'dompurify';
import parse from 'html-react-parser';
// 컴포넌트 내부
<>
{
parse(
DOMPurify.sanitize(
HTML String
),
)}
</>
해당 라이브러리를 간단히 활용한다면, 우려했던 XSS 공격을 방지하고 dangerouslySetInnerHTML을 사용하지 않고 HTML String을 렌더링 할 수 있습니다.
저렇게 활용기에, 저희 서비스는 HTML String이 렌더링 되는 경우가 종종 있어서 우선 custom hook을 통해 해당 기능을 재사용 가능하게끔 해보았습니다.
Custom Hook 구현
import DOMPurify from 'dompurify';
import parse from 'html-react-parser';
export function useHTMLParse(content: string) {
return parse(
DOMPurify.sanitize(content)
);
}
DOMPurify의 sanitize 활용하기 / useMemo를 통해 불필요한 리렌더링 막기
DOMPurify의 sanitize를 활용하면 보다 엄격하게 HTML String을 검사하고 필터링 하여 HTML tag와 속성에 대해 사용하는 것만 HTML로 렌더링 되게끔 할 수 있습니다. 엄격한 검사는 HTML String을 렌더링 하는 과정에서 보안을 더욱 향상해 줄 수 있습니다.
또한 useMemo를 통해 들어오는 HTML String이 변경될때만 HTML 파싱 작업이 실행되도록 하여, 렌더링 성능을 향상할 수 있습니다.
import DOMPurify from 'dompurify';
import parse from 'html-react-parser';
import { useMemo } from 'react';
export function useHTMLParse(content: string) {
return useMemo(
() =>
parse(
DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['strong', 'br', 'div'],
ALLOWED_ATTR: [
'target',
'align'
],
}
),
),
[content],
);
}
사용 예시
function ContentDisplay({ content }) {
const parsedContent = useHTMLParse(content);
return (
<div className="content-container">
{parsedContent}
</div>
);
}
결론
dangerouslySetInnerHTML 대신 HTML String을 렌더링 하는 custom hook 을 구현해 봤습니다. html-react-parser와 DOMPurify를 조합한다면 보안적으로도 훨씬 안정적인 방법으로 구현할 수 있고, 성능 또한 보다 향상할 수 있는 방법이라고 생각합니다.
'React' 카테고리의 다른 글
Cloudflare에서 성능저하가 심하게 발생한다! ( AWS EC2 + ACM + CloudFront + Route53 + WAF 로 전환하기 ) (0) | 2025.03.29 |
---|---|
[React] 비즈니스 / 도메인 로직을 통해 코드 응집도 높이기 (3) | 2024.12.03 |
[React] 뒤로가기 시 상태 유지하기 ( useNavigationType, popstate ) (1) | 2024.11.15 |
[React] 웹뷰로 연결된 Android Stuiod / IOS 에 데이터 주고받기 ( 브릿지 통신 ) (0) | 2024.11.11 |
[AWS] Sub Domain 연결하기 (5) | 2024.10.31 |