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-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>
);
}
dangerouslySetInnerHTML을 이용하면 웹에디터에 입력한 html 태그가 적용된 형태로 내용을 받아올 수 있음
하지만 이러한 것은 공격 받을 여지가 매우 큰 위험한 방식
그래서 공격 코드가 들어있으면 자동으로 차단해주는 라이브러리를 이용한다 => DOMPurify
dangerouslySetInnerHTML={{
__html: Dompurify.sanitize(data?.fetchBoard.contents),
}}
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을 사용해 막고 있다
에디터 부분 코드를 다음과 같이 수정하면
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