Notice
Recent Posts
Recent Comments
Link
«   2025/10   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

죽이죽이

React : 렌더링 본문

React

React : 렌더링

죽이죽이 2024. 6. 17. 21:00

렌더링이란?

※ 렌더링이란?
- 컴포넌트의 상태값(props, state, context) 변함에 따라 변경된 UI를 그려내는 과정이다.


※ 렌더링이 이루어지는 순서
- Trigger : 렌더링 촉발

- Render : 컴포넌트 렌더링
- Commit : DOM에 커밋



※ 렌더링이 일어나는 조건
- 컴포넌트가 처음으로 렌더링(마운트)을 하는 경우
- 자신 또는 부모 컴포넌트의 상태값(state, props, context 등)이 변한 경우



※ 코드로 이해하는 렌더링 (전체 코드 구조)
- App.js : 진입점


- Parent.jsx : 부모 컴포넌트


- FirstChild.jsx : 자식 컴포넌트 - 1


- SecondChild.jsx
 : 자식 컴포넌트 - 2

 



※ 첫번째 테스트 : 상태값도 변하고 UI도 변하는 경우

- 최초 화면


- 최초 렌더링(마운트) 단계


- Set Flag 버튼 클릭을 통해 Parent 컴포넌트의 상태값 변경


- 변경 후 화면


- 결론 : useState로 선언한 상태값이 변경되었고, UI도 변경이 되어서 SecondChild 컴포넌트가 렌더링이 진행된것을 확인할 수 있다.



※ 두번째 테스트 : 상태값은 변했지만 UI에 변화가 없는 경우

- Parent.jsx 코드 수정
 : flag 값과 관계없이 항상 FirstChild만 호출


- 최초 화면


- 최초 렌더링(마운트) 단계


- Set Flag 버튼 클릭을 통해 Parent 컴포넌트의 상태값 변경


- 변경 후 화면 (최초 화면과 동일)


- 결론 : useState로 선언한 상태값만 변경이 되고 UI는 동일하지만, Parent, FirstChild 컴포넌트가 리렌더링이 진행된것을 확인할 수 있다.



※ 세번째 테스트 : 버튼을 클릭하는 사용자 인터렉션은 있었지만 상태값, UI 모두 변화가 없는 경우

- Parent.jsx 코드 수정 : 버튼을 클릭 하더라도 상태값 유지


- 최초 화면


- 최초 렌더링(마운트) 단계

 
- Set Flag 버튼 클릭을 통해 Parent 컴포넌트의 상태값을 동일하게 변경 (false -> false)


- 변경 후 화면 (최초 화면과 동일, 상태값도 동일)


- 결론 : 버튼을 클릭하였지만, useState로 선언한 상태값과 UI 변경이 없었기때문에 별도의 컴포넌트 리렌더링이 일어나지 않은 것을 확인할 수 있다.

 

 

 

React.memo

※ memo란?
- 컴포넌트의 성능 최적화를 위해 사용한다.
- 컴포넌트의 props가 변경되지 않은 경우 리렌더링을 건너뛸 수 있다.
- 이전 props와 현재 props를 얕은비교를 통해 비교한다.


※ 필요한 상황
- 동일한 props로 렌더링이 빈번하게 발생하는 컴포넌트일 경우 memo를 통해 성능상의 이점을 가져올 수 있다.


※ 불필요한 상황
- 위와 같은 상황을 제외하고는 성능상의 이점을 가져올 수 없기 때문에 굳이 필요하지 않다.
- props가 자주 변하는 경우 props 비교는 대부분 false를 가져올 것이기때문에 비교가 무의미하다.


※ 기본 사용법



※ 예시
- Parent.jsx
import React, { useState } from "react";
import FirstChild from "./FirstChild";
import SecondChild from "./SecondChild";

export default function Parent() {
  const [flag, setFlag] = useState([true, false, false, true]);
  console.log("Parent Component Render");

  const handleBtnClick = () => {
    console.log("<<< Button Clicked!!! >>>");
    // 버튼 클릭 시 Parent 컴포넌트 상태 변화
    setFlag((prev) => prev.map((e) => !e));
  };

  return (
    <div>
      <FirstChild flag="Same props without memo" />
      <SecondChild flag="Same props with memo" />
      <button onClick={handleBtnClick}>Set Flag</button>
    </div>
  );
}


- FirstChild.jsx

import React from "react";

export default function FirstChild({ flag }) {
  console.log("first child render");
  return <div>FirstChild</div>;
}

- SecondChild.jsx
import React from "react";

function SecondChild({ flag }) {
  console.log("second child render");
  return <div>SecondChild</div>;
}

// memo 사용
export default React.memo(SecondChild);

- 화면


- 버튼 클릭 결과


- 결론 : 
memo로 감싼 SecondChild 컴포넌트는 동일한 props가 들어오는 경우 리렌더링이 되지 않는것을 확인할 수 있었다.


※ 주의사항
- 부모 컴포넌트가 리렌더링 되면서 함수를 새로 생성하기 때문에 이전에 넘긴 함수의 메모리 주소와 현재의 함수의 메모리 주소가 달라지게 된다.
- 참조타입 같은경우 얕은 비교 시 참조하는 메모리 주소를 비교하게 되기 때문에 다른값으로 인식하게되어 memo를 사용하더라도 리렌더링이 일어나게 된다. (useState 안에서 선언된 참조타입은 해당 X)

 

 

 

useCallback

※ useCallback이란?
- 컴포넌트가 리렌더링될 때마다 함수를 새로 생성하는 것을 방지하고, 이전에 생성한 함수를 재사용하여 불필요한 렌더링을 줄여준다.
- 두번째로 넘겨주는 의존성 배열의 값이 변하지 않는 이상 항상 동일한 함수를 제공한다.



※ 필요한 상황
- 자식 컴포넌트에 함수를 props로 전달하는 상황에서 useCallback을 사용함으로써 자식 컴포넌트의 불필요한 리렌더링을 방지할 수 있다.
- 큰 비용이 드는 함수일 경우 의존성 배열을 통해 특정 상황에서만 리렌더링이 될 수 있게 해줄수 있다.


※ 불필요한 상황
- 함수가 매번 렌더링할때마다 변경되어야 하는 경우 불필요하다.


※ 기본 사용법



※ 예시 ( useCallback 미사용 시)
- Parent.jsx
import React, { useState } from "react";
import Child from "./Child";

export default function Parent() {
  console.log("Parent rendered");
  const [cnt, setCnt] = useState(0);

  const handleCnt = () => {
    console.log("더하기 버튼 클릭!!!");
    setCnt((prev) => prev + 1);
  };

  return (
    <div>
      <div>카운트 : {cnt}</div>
      <Child handleCnt={handleCnt} />
    </div>
  );
}


- Child.jsx

import React from "react";

const Child = React.memo(({ handleCnt }) => {
  console.log("Child rendered");
  return <button onClick={handleCnt}>+1 더하기</button>;
});

export default Child;

- 더하기 버튼 클릭 결과 : React.memo를 사용하였지만 자식 컴포넌트가 계속 리렌더링 되는 것을 확인


※ 예시 ( useCallback 사용 시)
- Parent.jsx 코드 변경
import React, { useCallback, useState } from "react";
import Child from "./Child";

export default function Parent() {
  console.log("Parent rendered");
  const [cnt, setCnt] = useState(0);

  // useCallback 사용
  const handleCnt = useCallback(() => {
    console.log("더하기 버튼 클릭!!!");
    setCnt((prev) => prev + 1);
  }, []);

  return (
    <div>
      <div>카운트 : {cnt}</div>
      <Child handleCnt={handleCnt} />
    </div>
  );
}

- 더하기 버튼 클릭 결과 : 자식컴포넌트 리렌더링 X


※ 주의사항
- useCallback이 함수를 메모이제이션하므로, 콜백 함수가 변경되지 않는 한 메모리에 계속해서 보존된다.
- 이는 메모리 사용량이 증가할 수 있음을 의미하기 때문에 적절하게 사용을 해주어야 한다.

 

 

 

useMemo

※ useMemo란?
- 계산 비용이 많이 드는 함수의 결과 값을 메모이제이션하여, 같은 입력이 들어왔을 때 이전에 계산된 값을 재사용한다.

- 두번째로 넘겨주는 의존성 배열의 값이 변하지 않는 이상 항상 동일한 값을 제공한다.


※ 필요한 상황
- 비용이 많이 드는 계산에 대한 결과값을 캐시하는데 유용하다.


※ 불필요한 상황
- 두번째 인자로 전달받는 의존성 배열의 값이 너무 빈번하게 변경이 될 경우 어차피 재연산이 필요하기때문에 불필요하다.


※ 기본 사용법


※ 예시
import React, { useState, useMemo } from 'react';

const ExpensiveCalculationComponent = ({ data }) => {
  const [isChecked, setIsChecked] = useState(false);

  // useMemo를 사용하여 연산 결과 메모이제이션
  const sum = useMemo(() => {
    console.log('Calculating sum...'); // 연산이 실행될 때마다 로그 출력
    return data.reduce((acc, value) => acc + value, 0);
  }, [data]); // data 배열이 변경될 때만 다시 계산

  return (
    <div>
      <label>
        <input
          type="checkbox"
          checked={isChecked}
          onChange={() => setIsChecked(!isChecked)}
        />
        Perform Expensive Calculation
      </label>
      
      {isChecked && <p>Sum: {sum}</p>}
    </div>
  );
};

const App = () => {
  const sampleData = [1, 2, 3, 4, 5];

  return <ExpensiveCalculationComponent data={sampleData} />;
};

export default App;

'React' 카테고리의 다른 글

React : 프로젝트 트러블 슈팅  (0) 2024.06.08
React : react-router-dom  (0) 2024.02.28
React : Redux 비동기 처리  (0) 2024.02.27
React : Redux ToolKit (RTK)  (0) 2024.02.27
React : Redux 사용하기  (0) 2024.02.27