본문 바로가기
React TIL

[React] Day_34 데일리 정리

by 림졍 2024. 11. 6.

 

라이브두통... 아니 무슨 만성투동... 네이놈

 

라이브 코딩을 라이브두통이라고 적고

만성두통을 만성코딩으로 적는...

상태 이상...이 걸려버린 림졍.

결국 조퇴를 하고 쉬었다가.. 왔는데요...

노올랍게도 출석은 제대로 찍었는데... 강의실을 나가지 않아서

다들 출석한줄 알고 전화오고 어디냐고... 웅성웅성...

(저는 죽어있었는데요... 이게무슨일이지... ㅇ_ㅇ)

여튼 그렇게 죽다 살아나서 적어보는 TIL

가보자ㄱh.

 

 

 

React (리액트) 강의 정리 - 숙련 ver. (이이어서)

내일까지 적겠네요. 에휴

 

...언제끝나..?

 

 

 

memoization

 

리-렌더링의 발생 조건

  1. 컴포넌트에서 state가 바뀌었을 때
  2. 컴포넌트가 내려받은 props가 변경되었을 때
  3. 부모 컴포넌트가 리-렌더링 된 경우엔 모든 자식 컴포넌트.

 

최적화

 

리액트에서 리렌더링이 자주 발생한다 = 비효율적인 코드

 발생하는 cost를 최대한 줄여야 그 성능이 향상됨. → 최적화(Optimization) 필요

 

- 최적화하는 대표적인 방법

  1. memo(React.memo) : 컴포넌트를 캐싱
  2. useCallback : 함수를 캐싱 (react hook)
  3. useMemo : 값을 캐싱 (react hook)

 

 

memo(React.memo)

 

memo 정리

리-렌더링의 발생 조건 중, 3번째 조건 (이미지 첨부로 설명)

 

리-렌더링의 발생 조건 중, 3번째의 경우 (이미지 참고)

' 부모 컴포넌트 리렌더링 시, 자식컴포넌트는 모두 리렌더링 된다.'

여기서 만약, 자식은 바뀐게 없는데 렌더링이 된다?

비효율적인 코드가 되므로 React.memo로 문제해결

 

 

- 코드를 통해 알아보기

 

[ 디렉토리 구성 ]

[ 결과물 ]

[ + or - 클릭 시, 나오는 결과물 ]

 

 

모든 하위 컴포넌트가 리렌더링 되는중. (실제로 변한 것은 부모컴포넌트, App.jsx 뿐)

 

 

불필요한 하위 컴포넌트들의 리렌더링을 방지하고 싶다면? → React.memo를 사용!!

컴포넌트를 메모리에 저장, 필요할 때마다 갖다 쓰게됨!

부모 컴포넌트의 state변경으로 props 변경이 일어나지 않는 한, 컴포넌트는 리렌더링 X  ( = Components memoization )

import { memo } from "react";
export default React.memo(Box1);
export default React.memo(Box2);
export default React.memo(Box3);

 

 

 

 

useCallback

인자로 들어오는 함수 자체를 기억(메모이제이션)함.

 

- 예시 (Box1, count를 초기화 해 주는 코드라고 가정)

 

[App.jsx]

...

	// count를 초기화해주는 함수
  const initCount = () => {
    setCount(0);
  };

  return (
    <>
      <h3>카운트 예제입니다!</h3>
      <p>현재 카운트 : {count}</p>
      <button onClick={onPlusButtonClickHandler}>+</button>
      <button onClick={onMinusButtonClickHandler}>-</button>
      <div style={boxesStyle}>
        <Box1 initCount={initCount} />
        <Box2 />
        <Box3 />
      </div>
    </>
  );
}

...

 

 

[Box1.jsx]

...

function Box1({ initCount }) {
  console.log("Box1이 렌더링되었습니다.");

  const onInitButtonClickHandler = () => {
    initCount();
  };

  return (
    <div style={boxStyle}>
      <button onClick={onInitButtonClickHandler}>초기화</button>
    </div>
  );
}

...

 

[결과물]

 

 

[in console]

 

 

 

+ 버튼 or - 버튼 누를 때, 초기화 버튼 누를 때 모두 App 컴포넌트 & Box1 컴포넌트 리렌더링

 

 

어라라? React.memo를 통해서 Box1.jsx는 memoizatio 했는데도 리렌더링이 되네?

initCount 함수를 App.jsx에 정의해주었기 때문에 App.jsx가 리렌더링 그 내부 함수도 리렌더링이 됨.

함수 리렌더링 --> 상태가 변한거니까 Box1또한 리렌더링됨. 

 

+) 함수도 객체의 한 종류다!

모양은 같아도 다시 만들어지면 참조하던 주소값 달라짐 → ∴하위 컴포넌트인 Box1.jsx는 props가 변경되었다고 인식

// 내부 함수, 카운트를 0으로 설정해주는 초기화 기능만 필요 -> 리렌더링될 필요 X.
const onInitButtonClickHandler = () => {
  initCount();
};

 

∴ 함수를 메모리 공간에 따로 저장(캐싱) 후, 특정 조건이 아닌 경우엔 변경되지 않도록 만들어줌 (useCallback 사용)

// 변경 전
const initCount = () => {
  setCount(0);
};

// 변경 후
const initCount = useCallback(() => {
  setCount(0);
}, []);

 

 

- 조금 더 나아가기

 

[App.jsx]

...

// console.log를 이용해서 초기화하기 전 count 출력
// count를 초기화해주는 함수
const initCount = useCallback(() => {
  console.log(`[COUNT 변경] ${count}에서 0으로 변경되었습니다.`);
  setCount(0);
}, []);

...

 

[결과물]

현재 카운트 = 7

 

[in console]

7이 아닌 0으로 찍힌다..?

 

엥 이게 무슨일이고..?

useCallback, count = 0일 때의 시점을 기준으로 메모리에 함수 저장.

∴ count가 변경되기 전 그 초기값만을 기억하고 이 init함수는 시간이 멈춰벌임. (나도 멈춰주라 젭알.)

  • 함수를 굳이 리렌더링 할 필요없는데 리렌더링이 발생하는 것을 방지하고 싶다 → useCallback 사용
  • 그 안에서 변경되는 값을 추적 → dependency array 사용
...
// count 변경될 떄 변경된 count 값 기억가능!
// count를 초기화해주는 함수
const initCount = useCallback(() => {
  console.log(`[COUNT 변경] ${count}에서 0으로 변경되었습니다.`);
  setCount(0);
}, [count]);

...

출력 굳 ^-^)b

 

 

 

useMemo

복잡한 계산 결과값을 memoization할 때 많이 사용

동일한 값을 반환하는 함수를 계속 호출 = 필요없는 렌더링

cf. 캐싱 : 동일한 값 ->  특별한 (메모리)에 저장 후, 필요할 때 이미 저장한 값 꺼내서 사용하는 것 

해당 함수는 처음 (마운트과정) 렌더링 될 때에만 작업을 실행하고 그 결과값을 기억했다가 사용함(훨씬 효율적!!!)

 

- 사용방법

  • dependency Array의 값이 변경 될 때만 반환할_함수() 가 호출
  • 그 외 경우, 그냥 캐싱되었던 값 그대로 가져와서 씀

 

// as-is
const value = 반환할_함수();

// to-be
const value = useMemo(()=> {
	return 반환할_함수()
}, [dependencyArray]);

 

 

- 적용해보기

  • 컴포넌트 내부에 시간이 오래걸리는 무거운 작업이 있다고 가정, 리렌더링 때마다 해당 함수 = 결과값 출력에 많은 로딩 시간이 필요
  • 이런 경우, 어차피 결과값은 100으로 고정, 결과값만 저장해서 반환해주기 (캐싱)
import React, { useState, useMemo } from "react";

function HeavyButton() {
  const [count, setCount] = useState(0);

  const heavyWork = () => {
    for (let i = 0; i < 1000000000; i++) {}
    return 100;
  };

	// CASE 1 : useMemo를 사용하지 않았을 때
  const value = heavyWork();

	// CASE 2 : useMemo를 사용했을 때
  // const value = useMemo(() => heavyWork(), []);

  return (
    <>
      <p>나는 {value}을 가져오는 엄청 무거운 작업을 하는 컴포넌트야!</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        누르면 아래 count가 올라가요!
      </button>
      <br />
      {count}
    </>
  );
}

export default HeavyButton;

 

 

 

 

 

- 적용해보기 2

useEffect를 이용해서 me의 정보가 바뀌었을 때만 발동되게끔 dependency array를 넣어놓음,

 

import React, { useEffect, useState } from "react";

function ObjectComponent() {
  const [isAlive, setIsAlive] = useState(true);
  const [uselessCount, setUselessCount] = useState(0);

  const me = {
    name: "Ted Chang",
    age: 21,
    isAlive: isAlive ? "생존" : "사망",
  };

  useEffect(() => {
    console.log("생존여부가 바뀔 때만 호출해주세요!");
  }, [me]);

  return (
    <>
      <div>
        내 이름은 {me.name}이구, 나이는 {me.age}야!
      </div>
      <br />
      <div>
        <button
          onClick={() => {
            setIsAlive(!isAlive);
          }}
        >
          누르면 살았다가 죽었다가 해요
        </button>
        <br />
        생존여부 : {me.isAlive}
      </div>
      <hr />
      필요없는 숫자 영역이에요!
      <br />
      {uselessCount}
      <br />
      <button
        onClick={() => {
          setUselessCount(uselessCount + 1);
        }}
      >
        누르면 숫자가 올라가요
      </button>
    </>
  );
}

export default ObjectComponent;

 

[in console]

어...? 얼라리요?

그런데 엉뚱하게도 count를 증가하는 btn 입력시, 계속 log가 찍히는 것을 볼 수가 있음.....

(숫자가 올라가는 버튼을 눌러도 console창에 생존여부 메세지 출력.)

 

 

우리 생존로그는 왜그럴까? (안나면 입문강의를 참고하즈아..) -> 이건, 불변성과 관련이 깊음.

 

If 버튼이 선택되어 uselessCount state가 바뀌게 되면

 

→ 리렌더링

→ 컴포넌트 함수가 새로 호출

→ me 객체도 다시 할당 (이 때, 다른 메모리 주소값 또한 할당)

→ useEffect의 dependency array에 의해 me 객체가바뀌었는지 체킹 (이때 달라짐)

→ 리액트 입장에서는 me가 바뀌었구나 인식하고 useEffect 내부 로직이 호출됩니다.

 

 

 

// 이건 일치해요
const a = 1;
const b = 1;

console.log(a === b); // true

// 하지만 이건 달라요
const me = {
  name: "ted chang",
  age: 21,
};

const you = {
  name: "ted chang",
  age: 21,
};

console.log(me === you); // false

 

  // 값을 캐싱!!
  // 참조형 데이터의 메모리 주소 픽스하기
  // uselessCount가 증가되도 영향 X!
  const me = useMemo(() => {
    return { name: "Ted Chang", age: 21, isAlive: isAlive ? "생존" : "사망" };
  }, [isAlive])

  useEffect(() => {
    console.log("생존여부가 바뀔 때만 호출해주세요!");
  }, [me]);

 

[결론]

useMemo는 최적화가 필요한 경우에만 사용haza. 너무 많이 사용하면 성능이 떨어질 수 있음.(캐싱으로 인한 메모리 사용량 증가해 )

(정말로 계산 비용이 많이 드는 작업 or 렌더링에 영향을 줄 때에만 사용하도록.)

 

 

 

 

마무리 - 그래도 내일부터는 힘낼거에요. 새로운 친구랑 함께(?).

 

내일부터 새로운 친구랑 열심히 할거임. 암튼 그럼.

 

여러분 왠 뜬금없는 새로운 친구타령이냐구요?

네. 드디어 림졍도 키보드를 하나 질렀습니다.. 읍읍 (무려 7년만에요.)

현재 키보드도 말이죠 우선.. 타건감도 좋고 무선도 빠르게 적용되고.. 아주 쌈뽕하니 좋거든요?

하지만 치명적인 문제가 있습니다. 바로 백틱이 아예안됨

(출력값을 보여드림 §§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§)

 

??? : 에이 설마 진짜 백틱 하나가지고 바꾸는거..야? 라고 물으신다면

네. 진짜에요.

놀랍게도 남들 행복하게 그냥 누르면 되는 백틱있져

저는 복붙해서 붙여넣기하고있었답니다 ^-^)v

??? :  노트북으로 사용하고 있는데 그 키로도 불편한가요?

네. 제 키보드는 스탠딩상태로 유지되고 있거든요...

그래서 결국... 어제 이것저것찾아보다가 질렀구요?

저도 드디어 백틱과의 사투에서 벗어나네요 흑흑흑

여튼 내일은 후기랑 이것저것 적어서 돌아오겠스빈다

굳바3

 

 

 

KPT 회고

  • Keep :  그래도 TIL은 매일매일 쓰려는 나, 칭차내.
  • Problem : 텅---장.
  • Try : ... 당분간 손가락만 빨고 살아야지..
 

 

 

 
728x90
반응형

'React TIL' 카테고리의 다른 글

[React] Day_36 데일리 정리  (4) 2024.11.08
[React] Day_35 데일리 정리  (0) 2024.11.07
[React] Day_33 데일리 정리  (2) 2024.11.05
[React] Day_32 데일리 정리  (5) 2024.11.04
[React] Day_31 개인 프로젝트 후기  (3) 2024.11.01