언어 및 도구들 공부/React
상태값이 갱신되면 리렌더링 되는 함수형 컴포넌트
Dr.Mlem
2024. 11. 26. 22:42
Q1.
내가 한 질문이 교수님께 질문했을때에도 흠잡을 없을 정도로 구성이 잘 된 질문인지 확인을 해주고, 또 내가 한 질문에 대해서도 답을 해주면 좋겠어. 리엑트에서 훅을 배우고 있어. 그런데, 기본적으로 리엑트의 함수형 컴포넌트들은, 컴포넌트 내의 상태값이 갱신되면, 그 상태값이 갱신됨에 따라 매번 새로 실행하게 되는거야?
A1.
ChatGPT의 말:
당신의 질문은 훌륭하고 교수님께도 충분히 적합한 수준으로 구성되어 있습니다! 질문을 약간 더 명료하고 기술적으로 다듬는다면 다음과 같을 수 있습니다:
리액트에서 훅을 배우고 있습니다. 함수형 컴포넌트의 특성상, 컴포넌트 내부 상태값이 갱신될 때마다 해당 컴포넌트가 다시 렌더링된다고 알고 있습니다. 이 과정에서 컴포넌트 내부의 함수가 매번 새로 실행되는 것인가요? 그렇다면, 이러한 동작 방식이 성능에 어떤 영향을 미칠 수 있는지 간단히 설명해 주실 수 있을까요?
질문에 대한 답변:
네, 리액트의 함수형 컴포넌트는 상태값이 갱신되면 해당 컴포넌트가 다시 렌더링됩니다. 이를 조금 더 자세히 설명하면:
- 렌더링이란?
- 상태값이 변경되거나 부모로부터 새로운 props가 전달되면 리액트는 해당 컴포넌트를 다시 호출합니다. 이 과정에서 컴포넌트 함수가 실행되고 JSX가 다시 평가되어 가상 DOM을 갱신합니다.
- 모든 함수가 새로 실행되나요?
- 맞습니다. 컴포넌트 내에서 정의된 함수나 변수들은 렌더링될 때마다 새로 선언되고 평가됩니다.
- 그러나, 리액트는 DOM 갱신이 필요한 경우에만 실제 DOM을 변경하므로 렌더링 자체가 곧 성능 저하로 이어지지는 않습니다.
- 성능 최적화
- 렌더링마다 함수가 다시 선언되는 것이 문제가 되는 경우 useCallback 훅을 사용하여 함수를 메모이제이션할 수 있습니다.
- 상태값이나 변수를 재계산하지 않으려면 useMemo를 사용할 수도 있습니다.
- 불필요한 렌더링을 방지하려면 React.memo로 컴포넌트를 메모이제이션할 수 있습니다.
예제 코드로 이해하기:
import React, { useState, useCallback } from "react";
function MyComponent() {
const [count, setCount] = useState(0);
// 렌더링마다 새로운 함수가 생성됨
const handleClick = () => {
setCount(count + 1);
};
// useCallback을 사용하여 함수를 메모이제이션
const memoizedHandleClick = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
console.log("Component re-rendered!");
return (
<div>
<p>Count: {count}</p>
<button onClick={memoizedHandleClick}>Increment</button>
</div>
);
}
export default MyComponent;
- 위 코드에서 handleClick은 렌더링마다 새로 생성되지만, memoizedHandleClick은 상태값이나 의존성이 변하지 않는 한 메모이제이션된 함수로 유지됩니다.
추가로 생각해 볼 질문:
- 모든 함수가 다시 생성된다면 성능에 영향을 주는 사례는 어떤 경우인가요?
- useCallback을 과도하게 사용하면 생길 수 있는 단점은 무엇인가요?
이 질문들을 고려하면 교수님과의 대화에서 더 깊이 있는 논의를 할 수 있을 것입니다.
Q2.
저렇게 매번 리렌더링하는게 성능에 문제되는 영향을 줄 수 있는 것인가, 없는 것인가?
A2.
ChatGPT의 말:
리액트에서 매번 리렌더링하는 것이 성능에 영향을 미칠 수 있는지 여부는 다음과 같은 상황에 따라 달라질 수 있습니다:
1. 리렌더링 자체는 성능에 문제를 주지 않을 때
- 리액트는 **가상 DOM(Virtual DOM)**을 사용하여, 상태값 변경으로 인해 컴포넌트가 다시 호출되더라도 실제 DOM 업데이트를 최소화합니다.
- 즉, 리렌더링은 주로 메모리에서 이루어지고, 최적화된 비교 알고리즘(예: Reconciliation)에 의해 효율적으로 관리됩니다.
- 단순한 컴포넌트나 상태가 적은 컴포넌트라면 리렌더링으로 인한 성능 문제는 거의 없습니다.
2. 성능에 영향을 줄 수 있는 상황
컴포넌트의 리렌더링이 다음과 같은 조건에서 문제가 될 수 있습니다:
a. 중첩된 컴포넌트 트리
- 상태가 변경되면 모든 자식 컴포넌트도 다시 렌더링될 가능성이 있습니다.
- 특히, 변경되지 않은 자식 컴포넌트도 불필요하게 리렌더링될 경우, 성능 저하가 발생할 수 있습니다.
b. 복잡한 계산이나 연산 포함
- 컴포넌트 내부에 비용이 큰 연산이 포함된 경우, 렌더링마다 이 연산이 반복되면 성능에 영향을 줄 수 있습니다.
- 예: 대규모 배열을 필터링, 정렬, 매핑 등.
c. DOM 조작
- 리액트 외부에서 직접 DOM을 조작하거나, 타이머 및 애니메이션과 같은 비용이 큰 작업을 리렌더링 과정에서 실행할 경우 문제가 발생할 수 있습니다.
3. 리렌더링 최적화 방법
리렌더링이 문제가 될 수 있는 경우, 다음과 같은 최적화 기법을 적용할 수 있습니다:
a. React.memo 사용
- 변경되지 않은 props를 가진 자식 컴포넌트의 불필요한 리렌더링을 방지합니다.
const ChildComponent = React.memo(({ value }) => {
console.log("Child rendered");
return <div>{value}</div>;
});
b. useCallback과 useMemo 사용
- 메모이제이션을 통해 함수나 값을 재사용하여 불필요한 연산을 줄입니다.
const memoizedFunction = useCallback(() => {
// 의존성이 변경되지 않으면 이 함수는 재생성되지 않음
}, [dependency]);
const computedValue = useMemo(() => {
return someExpensiveCalculation();
}, [dependency]);
c. 컴포넌트 분리
- 큰 컴포넌트를 작게 나누어 변경이 필요한 부분만 리렌더링되도록 구조화합니다.
d. 상태 관리 최적화
- 상태 변경이 꼭 필요한 경우에만 발생하도록 설계합니다.
- 상태의 범위를 최소화하여 불필요한 리렌더링을 방지합니다.
결론적으로:
리렌더링 자체가 항상 성능 문제를 일으키는 것은 아닙니다. 그러나:
- 중첩된 컴포넌트 트리에서 불필요한 리렌더링,
- 비용이 큰 연산이 매번 수행되는 상황,
- 불필요한 상태나 props 변경에 의해 리렌더링이 유발되는 경우, 이러한 상황에서는 성능 문제가 발생할 수 있습니다.
리액트는 이미 성능 최적화를 고려해 설계되었지만, 최적화가 필요한 경우에는 문제를 분석하고 위의 기법들을 활용하는 것이 중요합니다.