โœ๐Ÿป [Code Camp_TIL] 32์ผ์ฐจ: ์„ฑ๋Šฅ์ตœ์ ํ™”(memoization, useCallback(), useMemo(), React memo, CRP, Reflow & Repaint, prefetch & preload)

code_Jยท2023๋…„ 5์›” 1์ผ
0

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
39/41
post-thumbnail

32์ผ์ฐจ๋ถ€ํ„ฐ 34์ผ์ฐจ๋Š” ์„ฑ๋Šฅ์ตœ์ ํ™”์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•  ์˜ˆ์ •์ด๋‹ค.

์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ, ํ™•์žฅ์„ฑ(์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค์–ด์„œ ๊ฐ€์ ธ๋‹ค ์“ฐ๋Š” ๊ฒƒ), ์„ฑ๋Šฅ, ์œ ์ง€๋ณด์ˆ˜์„ฑ๊นŒ์ง€ ๊ณ ๋ คํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋Šฅ๋ ฅ์ด ๊ฐœ๋ฐœ์ž๋กœ์„œ ํ•„์š”ํ•œ ๋Šฅ๋ ฅ์ด๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์ตœ์ ํ™”๋Š” ์ค‘์š”ํ•œ ํŒŒํŠธ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.


memoization

๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์ด ๋งŽ์•„์งˆ์ˆ˜๋ก ์„ฑ๋Šฅ์€ ์ €ํ•˜๋œ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฆฌ๋ Œ๋”๋ง์„ ์ค„์—ฌ์ค„ ํ•„์š”๊ฐ€ ์žˆ๋‹ค.


useCallback(), useMemo()

const containerPage = ()=>{
	console.log("์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค.")

	let countLet = 0
	const [countState,setCountState] = useState(0)

	const onClickCountLet = ()=>{
		console.log(countLet+1)
		countLet += 1
	}

	const onClickCountState = ()=>{
		console.log(countState+1)
		setCountState(countState+1)
	}
    
	return(
		<div> 
			<div>================<div> 
			<h1>์ด๊ฒƒ์€ ์ปจํ…Œ์ด๋„ˆ ์ž…๋‹ˆ๋‹ค.</h1>

			<div> ์นด์šดํŠธ(let): {countLet} </div>
			<button onClick={onClickCountLet}> ์นด์šดํŠธ(let) +1 ์˜ฌ๋ฆฌ๊ธฐ! </button>

			<div> ์นด์šดํŠธ(state): {countLet} </div>
			<button onClick={onClickCountState}> ์นด์šดํŠธ(state) +1 ์˜ฌ๋ฆฌ๊ธฐ! </button>
			<div>================<div>
		</div>
	)
}

export default containerPage

์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ์„œ let ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ฝ˜์†” ๊ฐ’์€ ์˜ฌ๋ผ๊ฐ€์ง€๋งŒ ๋ฆฌ๋ Œ๋”๋Š” ์ผ์–ด๋‚˜์ง€ ์•Š์•„ "์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค."๋ผ๋Š” ์ฝ˜์†”์ด ์ฐํžˆ์ง€ ์•Š๊ณ  ์žˆ์œผ๋ฉฐ, ํ™”๋ฉด์—๋Š” ๊ณ„์† 0์ด ๋‚˜ํƒ€๋‚œ๋‹ค. ํ•˜์ง€๋งŒ state ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ฆฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜๊ณ  ์ˆซ์ž๊ฐ€ ์˜ฌ๋ผ๊ฐ”๋˜ countLet์ด 0์œผ๋กœ ์ดˆ๊ธฐํ™”๋œ๋‹ค. ์ฆ‰, useState๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ๊ฐ’์ด ๋‹ค์‹œ ๊ทธ๋ ค์ง„๋‹ค.


๋‹ค์Œ์œผ๋กœ ์•„๋ž˜ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ ์œ„ ํŽ˜์ด์ง€์— import๋ฅผ ํ•ด๋ณด์ž.

// ์ž์‹ ํŒŒ์ผ
const MemoizationPresenterPage = ()=>{
	console.log("ํ”„๋ฆฌ์  ํ„ฐ๊ฐ€ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค.")

	return(
		<div>  
			<div>================<div>
			<h1>์ด๊ฒƒ์€ ํ”„๋ฆฌ์  ํ„ฐ ์ž…๋‹ˆ๋‹ค.</h1>
			<div>================<div>
		</div>
	)
}

export default MemoizationPresenterPage

// ๋ถ€๋ชจ ํŒŒ์ผ
return(
		// ์ƒ๋žต
			<div>================<div>

		<MemoizationPresenterPage/>
		</div>
	)
}

export default containerPage

์ด์ œ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•˜๋ฉด ์ฝ˜์†”์— โ€œ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค.โ€ ์™€ โ€œํ”„๋ฆฌ์  ํ„ฐ๊ฐ€ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค.โ€ ๊ฐ€ ์ฐํžŒ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๋ถ€๋ชจ์˜ state๋ฅผ ๋ฐ”๊พธ๋ฉด ์ž์‹ ํŒŒ์ผ์—์„œ๋„ ๋ฆฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๊ต‰์žฅํžˆ ๋น„ํšจ์œจ์ ์ด๋‹ค. ์ด ๋ถ€๋ถ„์„ ์ตœ์ ํ™” ํ•˜๊ธฐ ์œ„ํ•ด์„œ react developer tools๋ฅผ ์„ค์น˜ํ•ด์„œ ์‚ดํŽด๋ณด์ž.

react developer tools ์„ค์น˜ ํ›„ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์˜ profiler ํƒญ ํ™œ์šฉํ•˜๊ธฐ

profiler์—์„œ ๋นจ๊ฐ„์ƒ‰์œผ๋กœ ๋ฐ•์Šค ์นœ ์„ค์ •์€ ๋ Œ๋”๋ง๋  ๋Œ€์ƒ์ผ ๋•Œ ์˜์—ญ์„ ํ‘œ์‹œํ•ด์ฃผ๋Š” ์„ค์ •์ด๋‹ค. ์ฒดํฌ๋ฅผ ํ•œ ํ›„์— state ์นด์šดํŠธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ถ€๋ชจ์™€ ์ž์‹ ์˜์—ญ์ด ๋™์‹œ์— ๋ Œ๋”๋ง ๋Œ€์ƒ์ž„์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋ฐ˜๋ฉด let ์นด์šดํŠธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์•„๋ฌด์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰, ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋„ ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.


๋ถˆํ•„์š”ํ•œ ๊ฐ’๋“ค์ด ์ง€์†์ ์œผ๋กœ ๋‹ค์‹œ ๋งŒ๋“ค์–ด์ง€๋Š” ์•Š๋„๋ก ์œ ์ง€์‹œ์ผœ์ฃผ๋Š” hooks๊ฐ€ ์žˆ๋‹ค! ๋ฐ”๋กœ useMemo()์™€ useCallback()์ด๋‹ค. ๋‘ ๊ฐ€์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

import { useCallback, useMemo, useState } from "react";
// useCallback, useMemo๋Š” react์—์„œ importํ•ด์ค˜์•ผ ํ•จ
import MemoizationWithChildPage from "./02-child";

export default function MemoizationPage(): JSX.Element {
  console.log("์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");

  let countLet = 0;
  const [countState, setCountState] = useState(0);

  // 1. useMemo๋กœ ๋ณ€์ˆ˜ ๊ธฐ์–ต
  const aaa = useMemo(() => Math.random(), []);
  console.log(aaa);

  // 2. useCallback์œผ๋กœ ํ•จ์ˆ˜ ๊ธฐ์–ต
  const onClickCountLet = useCallback((): void => {
    console.log(countLet + 1);
    countLet += 1; 
  }, []);

  // 3. useCallback์œผ๋กœ ํ•จ์ˆ˜ ๊ธฐ์–ต => state ์‚ฌ์šฉ ์ฃผ์˜
  const onClickCountState = useCallback((): void => {
    // console.log(countState + 1);
    setCountState((prev) => prev + 1);
  }, []);

  return (
    <>
      <div>=========================</div>
      <h1>์ €๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ์ž…๋‹ˆ๋‹ค!!!</h1>
      <div>์นด์šดํŠธ(let): {countLet}</div>
      <button onClick={onClickCountLet}>์นด์šดํŠธ(let) +1 ์˜ฌ๋ฆฌ๊ธฐ</button>
      <div>์นด์šดํŠธ(state): {countState}</div>
      <button onClick={onClickCountState}>์นด์šดํŠธ(state) +1 ์˜ฌ๋ฆฌ๊ธฐ</button>
      <div>=========================</div>

      {/* memo์—์„œ๋Š” props๊ฐ€ ์˜์กด์„ฑ๋ฐฐ์—ด ์—ญํ• ์„ ํ•จ */}
      <MemoizationWithChildPage qqq={countState} />
    </>
  );
}

useCallback์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ๊ฐ์‹ธ๋ฉด ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š๊ณ , ์ด์ „์— ๋ถˆ๋Ÿฌ์™”๋˜ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œํ‚ค๊ฒŒ ๋œ๋‹ค. state๊ฐ€ ์žˆ๋Š” ํ•จ์ˆ˜๋„ useCallback์œผ๋กœ ๊ฐ์ŒŒ๋”๋‹ˆ ์นด์šดํŠธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด๋„ ์นด์šดํŠธ๊ฐ€ ๊ณ ์ •๋œ๋‹ค.

์—ฌ๊ธฐ์„œ ํ•จ์ˆ˜๋Š” ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š์ง€๋งŒ ๊ฐ’์€ ์˜ฌ๋ ค์ฃผ๊ณ  ์‹ถ์„ ๋•Œ, prev๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค!


์ฐธ๊ณ ! useCallback์„ ์“ฐ์ง€ ๋ง์•„์•ผ ํ•  ๋•Œ
์˜์กด์„ฑ ๋ฐฐ์—ด์˜ ์ธ์ž๊ฐ€ 1~2๊ฐœ๋ณด๋‹ค ๋งŽ์•„์งˆ ๋•Œ๋Š” ์ฐจ๋ผ๋ฆฌ ๋ฆฌ๋ Œ๋”๋ฅผ ํ•˜๋Š”๊ฒƒ์ด ์œ ์ง€ ๋ณด์ˆ˜์—๋Š” ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‹ค. ์„ฑ๋Šฅ์ด ์กฐ๊ธˆ์ด๋‚˜๋งˆ ์ข‹์•„์ง€๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํŽธ๋ฆฌํ•œ ํŽธ์ด ํ›จ์”ฌ ์ข‹๋‹ค. ๋”ฐ๋ผ์„œ ์˜์กด์„ฑ ๋ฐฐ์—ด์˜ ์ธ์ž๊ฐ€ 2๊ฐœ๋ฅผ ์ดˆ๊ณผํ• ๋•Œ๋Š” ๊ทธ๋ƒฅ ๋ฆฌ๋ Œ๋”๋ฅผ ํ•ด์ฃผ๋Š”๊ฒŒ ์ข‹๋‹ค!


memo

memo๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

import {memo} from "react"	// memo๋Š” react์—์„œ import

const MemoizationPresenterPage = ()=>{
	console.log("ํ”„๋ฆฌ์  ํ„ฐ๊ฐ€ ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค.")
  
	return(
		<div>  
			<div>================<div>
			<h1>์ด๊ฒƒ์€ ํ”„๋ฆฌ์  ํ„ฐ ์ž…๋‹ˆ๋‹ค.</h1>
			<div>================<div>
		</div>
	)
}

export default memo(MemoizationPresenterPage)

์ด์ œ state ์นด์šดํŠธ๋ฅผ ํด๋ฆญํ•˜๋ฉด ํ”„๋ฆฌ์  ํ„ฐ๋Š” ๋ Œ๋”๋ง์ด ๋˜์ง€ ์•Š์•„์„œ ์ฝ˜์†”์— ์ฐํžˆ์ง€ ์•Š๋Š”๋‹ค.


map์˜ key์™€ memoization

map๊ณผ memo๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋–จ๊นŒ? ์•„๋ž˜๋Š” ์ฐจ๋ก€๋Œ€๋กœ ๋ถ€๋ชจํŒŒ์ผ๊ณผ ์ž์‹ํŒŒ์ผ์ด ์žˆ๋‹ค. Word๋ผ๋Š” ์ž์‹ํŒŒ์ผ์— memo๋ฅผ ํ–ˆ๋‹ค.

// ๋ถ€๋ชจํŒŒ์ผ
import { useState } from "react"
import Word from "./02-child"

export default function MemoizationParentPage(){
	const [data,setData] = useState("์ฒ ์ˆ˜๋Š” ์˜ค๋Š˜ ์ ์‹ฌ์„ ๋ง›์žˆ๊ฒŒ ๋จน์—ˆ์Šต๋‹ˆ๋‹ค.")

	const onClickChange = ()=>{
		setData("์˜ํฌ๋Š” ์˜ค๋Š˜ ์ €๋…์„ ๋ง›์—†๊ฒŒ ๋จน์—ˆ์Šต๋‹ˆ๋‹ค.")
	}
    
	return(
		<>
			{data.split("").map((el)=>(
				<Word key={index} el={el}/>
			))}
			<button>์ฒด์ธ์ง€</button>
		</>
	)
}
// ์ž์‹ํŒŒ์ผ
import { memo } from "react"

export default function Word(props: any){
	console.log("์ž์‹์ด ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค!",props.el)
	return <span>{props.el}</span>
}

export default memo(Word)

๋ถ€๋ชจํŒŒ์ผ๋กœ ์ ‘์†ํ•ด์„œ ์ฒด์ธ์ง€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, data ๊ฐ’๊ณผ setData ๊ฐ’์—์„œ ๋‹ค๋ฅธ ๊ฐ’๋งŒ ์ฝ˜์†”์— ์ฐํžˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. "์˜ํฌ๋Š”", "์ €๋…์„", "๋ง›์—†๊ฒŒ"๋งŒ ์ฐํžŒ๋‹ค.


๋งŒ์•ฝ, key๊ฐ’์„ uuid๋กœ ์„ค์ •ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

import { useState } from "react"
import Word from "./02-child"
import {v4 as uuidv4} from "uuid"

export default function MemoizationParentPage(){
	const [data,setData] = useState("์ฒ ์ˆ˜๋Š” ์˜ค๋Š˜ ์ ์‹ฌ์„ ๋ง›์žˆ๊ฒŒ ๋จน์—ˆ์Šต๋‹ˆ๋‹ค.")

	const onClickChange = ()=>{
		setData("์˜ํฌ๋Š” ์˜ค๋Š˜ ์ €๋…์„ ๋ง›์—†๊ฒŒ ๋จน์—ˆ์Šต๋‹ˆ๋‹ค.")
	}
	return(
		<>
			{data.split("").map((el)=>(
				<Word key={uuidv4} el={el}/>
			))}
			<button>์ฒด์ธ์ง€</button>
		</>
	)
}

uuid๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด memo๋ฅผ ํ•ด๋„, key ์ž์ฒด๊ฐ€ ๋งค๋ฒˆ ๋ณ€๊ฒฝ๋˜์–ด props๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— setData ๋ฌธ์žฅ์˜ ๋ชจ๋“  ๋‹จ์–ด๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋œ๋‹ค. ๋”ฐ๋ผ์„œ uuid๋Š” ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ์ดˆ๋ž˜ํ•˜๋ฏ€๋กœ ํ•„์š”ํ•œ ์ƒํ™ฉ์—์„œ๋งŒ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.


CRP(Critical Rendering Path)


๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ Œ๋”๋ง์€ ์–ด๋–ค ์ˆœ์„œ๋กœ ์ผ์–ด๋‚ ๊นŒ?

๋จผ์ € ํ™”๋ฉด์„ ๊ทธ๋ ค์ฃผ๋Š”๋ฐ ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค(html, css, js)๋ฅผ ๋‹ค์šด๋กœ๋“œ ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  HTML๊ณผ CSS์—์„œ ํ™”๋ฉด์— ๋ Œ๋”ํ•ด์•ผ ํ•  ์š”์†Œ๋“ค์„ ๊ตฌ๋ถ„ํ•œ ํ›„ ๋ Œ๋”๋˜์–ด์•ผ ํ•  HTML,CSS ์š”์†Œ๋ฅผ ํ•ฉ์ณ ํ™”๋ฉด์— ๊ทธ๋ ค์ฃผ๊ฒŒ ๋œ๋‹ค.

์ด์–ด์„œ ํ™”๋ฉด์— ๊ทธ๋ ค์ค„๋•Œ ํ•ด๋‹น ์š”์†Œ๋“ค์ด ์–ด๋Š ์œ„์น˜์— ๋†“์ผ์ง€ ๋จผ์ € ๊ทธ๋ ค์ฃผ๋Š” Layout Reflow์™€ ํ•ด๋‹น ์š”์†Œ๋“ค์„ ์ƒ‰์น ํ•˜๋Š” Paint Repaint๊ณผ์ •์ด ๋ฐœ์ƒํ•œ๋‹ค.

์ฐธ๊ณ ! ๋ Œ๋”ํŠธ๋ฆฌ
๋ Œ๋”ํŠธ๋ฆฌ๋Š” ์ตœ์ข…์ ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €์— ํ‘œ๊ธฐ๋  ์š”์†Œ๋“ค์ด๋‹ค.
๋ Œ๋”๋ง ์‹œ ํ™”๋ฉด์— ๋ Œ๋”ํ•ด์•ผ ํ•˜๋Š” ์š”์†Œ๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ์ž‘์—…์„ ํ•œ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ์ด ๋•Œ HTML์˜ ์š”์†Œ๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๊ฒƒ์ด DOM(Document Object Model), CSS์š”์†Œ๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๊ฒƒ์ด CSSOM(CSS Object Model)์ด๋‹ค.
์ด๋ ‡๊ฒŒ DOM๊ณผ CSSOM์ด ํ•ฉ์ณ์ง„๊ฒƒ์ด ๋ฐ”๋กœ ๋ Œ๋”ํŠธ๋ฆฌ๋‹ค.
์ฆ‰ 2๋ฒˆ๊ณผ์ •๊ณผ 3๋ฒˆ๊ณผ์ •์ด ํ•ฉ์ณ์ง„ 4๋ฒˆ๊ณผ์ •์˜ ๊ฒฐ๊ณผ๋ฌผ์ด ๋ Œ๋”ํŠธ๋ฆฌ๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.


Reflow์™€ Repaint

reflow๋ž€ ๋ Œ๋”๋ง๋˜์–ด์•ผ ํ•  ์š”์†Œ๋“ค์˜ ํ™”๋ฉด ์ƒ ์œ„์น˜๋ฅผ ๊ทธ๋ ค์ฃผ๋Š” ๊ณผ์ •์ด๊ณ , repaint๋Š” ์œ„์น˜๋ฅผ ์žก๊ณ  ๋‚œ ์ดํ›„ ์ƒ‰์น ์„ ํ•ด์ฃผ๋Š” ๊ณผ์ •์ด๋‹ค. Reflow๊ฐ€ Repaint๋ณด๋‹ค ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค!


Layout Shift

๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก ์กฐํšŒ ํŽ˜์ด์ง€์— ์ ‘์†ํ–ˆ์„ ๋•Œ, ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์ด ์žˆ๊ณ , ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•˜๋Š” ๋ฒˆํ˜ธ๊ฐ€ ๋ชฉ๋ก ์•„๋ž˜์— ์œ„์น˜ํ•œ๋‹ค๊ณ  ํ•˜์ž.

์ฒ˜์Œ ํŽ˜์ด์ง€๋ฅผ ์ ‘์†ํ–ˆ์„ ๋•Œ ๋ชฉ๋ก์„ ์กฐํšŒํ•ด์˜ค๋ฉด์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„์–ด์žˆ๋‹ค๊ฐ€ ๋‚˜์ค‘์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜จ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„์–ด์žˆ์„ ๋•Œ์—๋Š” ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•˜๋Š” ๋ฒˆํ˜ธ๊ฐ€ ์ƒ๋‹จ์— ์žˆ๋‹ค๊ฐ€, ๋ฐ์ดํ„ฐ๊ฐ€ ๊ทธ ์œ„๋ฅผ ๋น„์ง‘๊ณ  ๋“ค์–ด์˜ค๋ฉด์„œ ํŽ˜์ด์ง€ ์ด๋™ ๋ฒˆํ˜ธ๊ฐ€ ์•„๋ž˜์ชฝ์œผ๋กœ ์ด๋™ํ•˜๊ณ  ์ตœ์ข… ํ™”๋ฉด์ด ๊ทธ๋ ค์ง„๋‹ค.

์ด ๋ถ€๋ถ„์ด UI์ƒ์œผ๋กœ ์˜ˆ์˜์ง€ ์•Š๋‹ค. ๋”ฐ๋ผ์„œ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์˜ ๋†’์ด๋ฅผ ๊ณ ์ •์‹œ์ผœ์ฃผ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋„๋ก ํ•ด๋ณด์ž.

// import ์ƒ๋žต
const FETCH_BOARDS = gql`
  query fetchBoards($page: Int) {
    fetchBoards(page: $page) {
      _id
      writer
      title
      contents
    }
  }
`;

export default function StaticRoutedPage() {
  const { data, refetch } = useQuery<
    Pick<IQuery, "fetchBoards">,
    IQueryFetchBoardsArgs
  >(FETCH_BOARDS);

  console.log(data?.fetchBoards);

  const onClickPage = (event: MouseEvent<HTMLSpanElement>) => {
    void refetch({ page: Number(event.currentTarget.id) });
  };

  return (
    <>
      {/* ์ž„์‹œ ๋ฐฐ์—ด 10๊ฐœ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ, ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์„ ๋•Œ๋„ ๋†’์ด 30px์„ ์œ ์ง€ํ•˜์—ฌ reflow ๋ฐฉ์ง€  */}
      {(data?.fetchBoards ?? new Array(10).fill(1)).map((el) => (
        <div key={el._id} style={{ height: "30px" }}>
          <span style={{ margin: "10px" }}>{el.writer}</span>
          <span style={{ margin: "10px" }}>{el.title}</span>
        </div>
      ))}
      {new Array(10).fill(1).map((_, index) => (
        <span key={index + 1} id={String(index + 1)} onClick={onClickPage}>
          {index + 1}
        </span>
      ))}
    </>
  );
}

prefetch & preload


prefetch

prefetch๋ž€ ๋‹ค์Œ ํŽ˜์ด์ง€์—์„œ ์“ฐ๋ ค๊ณ  ๋ฏธ๋ฆฌ ๋ฐ›๋Š” ๊ฒƒ์ด๋ฉฐ, ํ˜„์žฌ ํŽ˜์ด์ง€๋ฅผ ๋ชจ๋‘ ๋ฐ›์•„์˜จ ์ดํ›„ ์ œ์ผ ๋‚˜์ค‘์— ๋‹ค์šด๋กœ๋“œ ํ•ด์˜ค๊ฒŒ ๋œ๋‹ค. prefetch๋ฅผ ์ด์šฉํ•˜๋ฉด ํŽ˜์ด์ง€๊ฐ€ ์ด๋™ํ•ด๋„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <title>ํ”„๋ฆฌํŽ˜์น˜</title>

    <!-- ํ”„๋ฆฌํŽ˜์น˜: ๋‹ค์ŒํŽ˜์ด์ง€๋ฅผ ๋ฏธ๋ฆฌ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์œผ๋ฏ€๋กœ, ๋ฒ„ํŠผ ํด๋ฆญ์‹œ ํŽ˜์ด์ง€์ด๋™ ๋น ๋ฆ„ -->
    <link rel="prefetch" href="board.html" />
  </head>
  <body>
    <a href="board.html">๊ฒŒ์‹œํŒ์œผ๋กœ ์ด๋™ํ•˜๊ธฐ</a>
  </body>
</html>

preload

preload๋Š” ํ˜„์žฌ ํŽ˜์ด์ง€์—์„œ ์“ธ ์ด๋ฏธ์ง€๋“ค์„ ๋ชจ๋‘ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์•„๋†“๋Š” ๊ฒƒ์ด๋‹ค.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <title>ํ”„๋ฆฌ๋กœ๋“œ</title>

    <!-- ํ”„๋ฆฌ๋กœ๋“œ: ํ•œ ๋ฒˆ์— 6๊ฐœ์”ฉ ๋ฐ›์•„์˜ค๋ฏ€๋กœ, bodyํƒœ๊ทธ์˜ ์ด๋ฏธ์ง€๋Š” ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ๋‹ค์šด๋กœ๋“œ -->
    <!-- ๋ˆˆ์— ๋ณด์ด๋Š” ์ด๋ฏธ์ง€๋ฅผ ๋จผ์ € ๋‹ค์šด๋กœ๋“œ ๋ฐ›์•„์„œ ๋ณด์—ฌ์ฃผ๊ณ , ํด๋ฆญํ•˜๋ฉด ์‹คํ–‰๋˜๋Š” JS๋Š” ๋‚˜์ค‘์— ๋ฐ›์•„์˜ค๊ธฐ. ๋”ฐ๋ผ์„œ, DOMContentedLoaded ์ดํ›„, Load๊นŒ์ง€ ์™„๋ฃŒ๋˜๋Š” ์ตœ์ข… ๋กœ๋“œ ์‹œ๊ฐ„์ด ๋” ์งง์•„์ง -->
    <link rel="preload" as="image" href="./dog.jpeg" />

    <!-- ์ผ๋ฐ˜๋กœ๋“œ -->
    <link rel="stylesheet" href="./index.css" />
    <script src="index1.js"></script>
    <script src="index2.js"></script>
    <script src="index3.js"></script>
    <script src="index4.js"></script>
    <script src="index5.js"></script>
    <script src="index6.js"></script>
  </head>
  <body>
    <img src="./dog.jpeg" />
  </body>
</html>

์œ„์™€ ๊ฐ™์ด ์ ์–ด์ฃผ๋ฉด, ์ด๋ฏธ์ง€๋ฅผ index.html๋ฅผ ๋ฐ›์•„์˜ฌ๋•Œ css์™€ js๋ณด๋‹ค ๋จผ์ € ๋ฐ›์•„์˜ค๊ฒŒ ๋œ๋‹ค.



profile
Web FE ๊ฐœ๋ฐœ์ž ์ทจ์ค€์ƒ

0๊ฐœ์˜ ๋Œ“๊ธ€