React

웹 에디터

WYSIWYG : What You See Is What You Get(보는 대로 얻는다)

yarn add react-quill

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

Next.js를 사용하고 있다면
이런 에러가 뜨게 되는데

Next.js는 서버사이드 렌더링(프리랜더링)을 지원하지만, 서버에서 페이지를 미리 렌더링하는 단계에는 브라우저상이 아니기 때문에 window나 document가 존재하지 않는다

이 문제를 해결하기 위해서는
document가 선언된 시점이후에 React-Quill을 import 해야 함!

=> Next.js의 dynamic import 방식을 사용하기

// import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
import dynamic from "next/dynamic";
// 프리랜더링시 document 오류를 막기 위해 Next.js의 dynamic import 방식 사용하기
// 프리랜더링 시에 import 하지 않고, 브라우저가 호출될 때 import 해주기

const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
// react-quill을 dynamic import 할건데, 서버사이드에서는 import 하지 않겠다 ! 라고 하는것

html 태그가 사용되는 걸 확인 할 수 있음

react-hook-form 과 함께 사용하기

기존 react-quill 코드에 useForm을 import 해준 뒤 각각의 input에 register를 넣어주기

export default function WebEditorPage() {
  const router = useRouter();
  const { register, handleSubmit, setValue, trigger } = useForm<IFormValues>({
    mode: "onChange",
  });

  const handleChange = (value: string) => {
    console.log(value);

    // register로 등록하지 않고, 강제로 값을 넣어주는 기능!!
    setValue("contents", value === "<p><br></p>" ? "" : value);

    // onChange가 됐는지 안됐는지 react-hook-form에 알려주는 기능!!
    trigger("contents");
  };

  const onClickSubmit = (data: IFormValues) => {
		// form submit시 실행할 함수
  };

  return (
    <form onSubmit={handleSubmit(onClickSubmit)}>
      작성자: <input type="text" {...register("writer")} />
      <br />
      비밀번호: <input type="password" {...register("password")} />
      <br />
      제목: <input type="text" {...register("title")} />
      <br />
      내용: <ReactQuill onChange={handleChange} />
      <br />
      <button>등록하기</button>
    </form>
  );
}

크로스 사이트 스크립트(XSS)

dangerouslySetInnerHTML을 이용하면 웹에디터에 입력한 html 태그가 적용된 형태로 내용을 받아올 수 있음
하지만 이러한 것은 공격 받을 여지가 매우 큰 위험한 방식

그래서 공격 코드가 들어있으면 자동으로 차단해주는 라이브러리를 이용한다 => DOMPurify

dangerouslySetInnerHTML={{
            __html: Dompurify.sanitize(data?.fetchBoard.contents),
          }}

OWASP TOP 10

OWASP란 Open Web Application Security Project의 약자로 오픈소스 웹 애플리케이션 보안 프로젝트

주로, 웹 관련 정보노출이나 악성파일 및 스크립트, 보안 취약점을 연구하며 10대 취약점을 발표하며, 3-4년에 한 번씩 정기적으로 업데이트 됨

A01 : Broken Access Control (접근 권한 취약점)
A02 : Cryptographic Failures (암호화 오류)
A03: Injection (인젝션)
A04: Insecure Design (안전하지 않은 설계)
A05: Security Misconfiguration (보안설정오류)
A06: Vulnerable and Outdated Components (취약하고 오래된 요소)
A07: Identification and Authentication Failures (식별 및 인증 오류)
A08: Software and Data Integrity Failures(소프트웨어 및 데이터 무결성 오류)
A09: Security Logging and Monitoring Failures (보안 로깅 및 모니터링 실패)
A10: Server-Side Request Forgery (서버 측 요청 위조)

매년OWASP 상위권을 유지하는 것 중 하나가 Injection

SQL쿼리문을 작성할때 조건을 통해 데이터를 주고 받는데, 이 조건을 직접 조작하여 공격하는 기법으로 현재는 이것을 ORM을 사용해 막고 있다

Hydration issue

에디터 부분 코드를 다음과 같이 수정하면

return (
	<div>
    <div style={{color: "red"}}>작성자: {data?.fetchBoard.writer}</div>
    {process.browser && (
			<div style={{color: "green"}}>제목: {data?.fetchBoard.title}</div>
		)}
    <div style={{color: "blue"}}>내용: 반갑습니다!<div>
  </div>
)

Hydration Issue 때문에 제목 부분이 녹색이 아니라 파란색으로 나오는 것을 확인 할 수 있다

해결하려면?!

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

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

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

return (
	<div>
    <div style={{color: "red"}}>작성자: {data?.fetchBoard.writer}</div>
    {process.browser ? (
			<div style={{color: "green"}}>제목: {data?.fetchBoard.title}</div>
		) : (
			<div style={{color: "green"}} />
		)}
    <div style={{color: "blue"}}>내용: 반갑습니다!<div>
  </div>
)

이렇게 수정하면 오류 없이 랜더링 된다

++

@toast-ui/editor

profile
어제보다 오늘 발전하는 프론트엔드 개발자

0개의 댓글