리액트 렌더링과 메모이제이션

오다현·2026년 4월 10일
post-thumbnail

리액트 렌더링 원리

리액트 렌더링과 브라우저 렌더링은 공통적으로 자체적인 렌더링 프로세스가 있지만 과정이 아예 다르다.

브라우저 렌더링이란 html, css기반으로 웹에 필요한 ui를 그리는 과정으로 사용자에게 실제 화면을 보여주는 과정이다.
DOM 트리를 만들고 → Layout → Paint 과정을 거친다.

리액트 렌더링이란 ui를 그리는 게 아니라 props와 state 기반으로 ui가 바뀔 가능성이 있을때마다 발생하며 어떤 ui를 그릴지 계산하는 과정이다.
JSX → React.createElement → Virtual DOM 생성 과정을 거친다.

리액트 렌더링은 언제 일어날까?

렌더링은 처음에 당연히 보여야하니까 결과물을 보여주기 위해 최초에 렌더링이 일어난다. 그 이후엔 state 변화에 따라 리렌더링이 발생한다.

리렌더링시 리액트는 이전 트리와 현재 트리를 비교한다.
하지만 동일한 요소

<container>
	<Component key="1"></Component>
    <Component key="2"></Component>
	<Component key="3"></Component>
	<Component key="4"></Component>
	<Component key="5"></Component>
	<Component key="6"></Component>
<container>

가 있다면 어떻게 구별해야할까?

그래서 배열엔 항상 key가 있다.
여섯개 똑같이생긴 컴포넌트들이 컨테이너로 감싸져있을때 리렌더링이 발생한다면 key를 사용해서 변경된 요소들만 정확하게 식별할 수 있다.

리렌더링이 발생하면 이전 Virtual DOM 트리와 현재 Virtual DOM 트리를 비교하는데 이 두 트리 사이에서 비교하는데 각각의 컴포넌트 식별자를 key로 사용하여 이번 컴포넌트와 새로운 컴포넌트 사이에 변화가 있는지 살펴본다.

함수형 컴포넌트는 props와 state를 기반으로 렌더링이 일어나며 클래스형 컴포넌트는 생명주기가 있어서 렌더링을 하기 위해 개발자가 직접 렌더함수를 생성해야한다.

리액트 렌더링 과정

두 단계로 나뉘며 두 단계가 끝난 뒤 실제 화면이 업데이트 된다.
컴포넌트 루트부터 아래방향으로 내려가며 검사한다. 렌더링 결과물은 jsx문법으로 구성되고 React.createElement로 반환된다.

렌더

컴포넌트 루트에서 아래 방향으로 내려가며 작업한다.
이 단계의 목적은 순수 계산을 하며 렌더링 결과물을 jsx문법으로 구성하여 생성하는 것이다. 그 후 return 결과로 Virtual DOM이 생성되며 리렌더링시 변경사항을 확인하기위해 이전 Virtual DOM과 비교한다.

여기서 key, type, props 등을 보고 변경사항이있는지 판단한다.

커밋

실제 DOM에 반영되며 사용자에게 보여주는 단계이다.
렌더단게에서 변경사항이 감지되지않으면 커밋단계는 생략될수있다.

동기 렌더링 vs 비동기 렌더링

동기식 렌더링의 경우 한번 시작하면 끝까지 수행하기때문에 ui가 멈추는 등 성능저하가 생기거나 시간이 오래걸릴 수 있고 다른 작업을 지연시킬 수도 있다.

비동기식 렌더링의 경우 작업을 나눠서 처리하기 때문에 상황에 따라 다른 ui가 먼저 보일 수 있다. 이때 순서가 어긋나면 사용자에게 혼란을 줄 수 있기 떄문에 중요한 렌더링이 무겁고 시간이 오래걸릴 경우 가벼운 것부터 먼저 보여주는 방식으로 사용된다.
비동기로 처리하면 렌더링 우선순위를 조정하거나 필요시 중단, 재시작, 취소할 수도 있다.

렌더 단계에서 비동기로 실행하고 커밋 단게에서 동기로 실행된다.

<div>
	<p></p> (state 안바뀜)
	<p></p> (state 바뀜)
</div>

예를들어 컴포넌트가 한 파일에 여러개있을때 두번째 컴포넌트 state만 바뀌었다면 리액트에서는 해당 두번째 컴포넌트의 작업이 렌더링 큐에 들어가고 리액트는 트리 최상단부터 렌더링을 검사한다.
1. 첫번째 컴포넌트는 변경이 없으면 넘어가고 두번째를 리렌더링한다.
2. 두번째 아래에 자식컴포넌트가 있을경우 그 컴포넌트도 같이 렌더링된다.

memo로 최적화

하지만 자식컴포넌트에 memo를 추가할경우 그 컴포넌트의 props가 변경되지않으면 렌더링을 수행하지않을 수 있다.

메모이제이션

그럼 렌더링을 줄이기위한 최적화 훅이 뭐가있을까?
useMemo, useCallback, memo가 있다.

memo는 컴포넌트를 메모이제이션하여 props가 바뀔때만 렌더링되도록 한다.
useMemo는 값을 메모이제이션하며 의존성 배열의 값이 바뀔때만 다시 계산된다.
useCallback은 함수를 메모이제이션하여 불필요하게 함수가 재생성되는 것을 막는다.

리액트 state가 바뀔때 렌더링하는것 vs react.메모 렌더링 차이

리액트는 state가 바뀌면 해당 컴포넌트가 렌더링되고 부모가 렌더링하면 자식도 같이 렌더링될 수 있다.
하지만 React.memo는 부모가 렌더링되어도 자식의 props가 변경되지 않으면 렌더링되지 않을 수 있다.

값을 메모이제이션 왜 해야할까?

값이 바뀌면 다시 도는데 왜 써야할까? 문제는 값이 바뀌었을 때가 아니라 값이 안 바뀌었을 때이다.
왜냐하면 렌더링된다는 이유만으로 이 값을 계산하는 함수가 렌더링될 때마다 값이 안 바뀌어도 다시 실행될 수 있어 시간이나 메모리 사용량이 많은 함수라면 렌더링하는데 시간이 오래걸릴 수 있고 사용자는 렉걸린다고 느낄 수 있다.

useCallback은 왜 불필요하게 함수가 재생성될까?

함수형 컴포넌트는 렌더링될때마다 컴포넌트 전체가 다시 실행되는데 그 안에 있는 함수도 컴포넌트 내부에 선언되어 있기 때문에 컴포넌트가 실행될때마다 새로 생성된다. 같은 코드여도 이전 렌더링에서 만들어진 함수와 지금 렌더링에서 만들어진 함수는 서로 다른 함수다. 그래서 자식 컴포넌트에 함수를 props로 넘기는 경우 함수가 바뀌었다고 판단되어 불필요한 렌더링이 발생할 수 있다.

예를 들어 LoginForm이 렌더링될때마다 55~200줄 사이의 코드가 다시 실행된다면 그 안에 있는 변수와 함수도 계속 다시 생성된다. 이때 시간이 오래걸리는 계산이나 자식 컴포넌트에 전달되는 함수가 있다면 그 부분만 메모이제이션하여 불필요한 재생성을 줄일 수 있다. 이렇게 하면 렌더링할 때 같은 값을 다시 만들거나 같은 함수를 다시 생성하는 비용을 줄여 더 빠르게 렌더링될 수 있다.

그냥 모든걸 다 메모이제이션 하면 되지않을까?

아니다. 메모이제이션을 무조건 사용하면 안된다. 값을 비교하여 렌더링 혹은 재계산이 필요한지 확인하거나 이전 결과물을 저장해두고 다시 꺼내와야하는 비용이 존재하기때문에 각각의 언제 어떻게 사용하는지 정확히 알고 필요할 때만 최적화하기위해 사용한다.

간단한 연산을 수행하는 함수라면 메모리에 저장해놓는 것보다 매번 다시 계산하는 것이 더 빠를 수 있어 개발 후 폰으로 해볼때나 안좋은 컴터 느리거나 버벅이면 그때 최적화한다.

이 비용이 리렌더링보다 적을까?

코드짜기도 바쁜데 매번 렌더링 시간을 계산하면서 함수 속도나 이런걸 다 계산을 진짜 해야할까?

공식문서를 보면 확실하게 알 수 없으니 useMemo를 사용하지 않고 코드를 먼저 작성하고 이후에 필요한 부분만 추가하여 성능 최적화하는것이 좋다고 한다. 그래서 예측하고 작성하기보단 실제 개발자도구나 렌더링 확인을 통해 필요한 곳을 찾는게 좋다.

리액트는 이전 렌더링 결과를 다음 렌더링과 구별하기 위해 기본적으로 값을 저장하고 비교한다. 따라서 memo를 사용하면 얕은 props 비교 비용이 추가되기 때문에 오히려 비용이 커질 수 있다.

그럼 언제 써야하는가?

해당 컴포넌트에 렌더링이 자주 일어나며 그 렌더링 사이에 비싼 연산이 있고 자식 컴포넌트도 많이 가지고있으면 효과가 좋다.


그동안 생각하지 못하고 개발하다보니 불피요한 렌더링이 많았을거라 생각이 든다.. 렌더링 시나리오를 고려하여 트리구조를 개선하고 렌더링 횟수 등을 조절하여 성능좋은 프로젝트를 만들어볼 것이다.


스터디 퀴즈

  1. 무거운 연산이 있는 컴포넌트에서 메모이제이션을 적용하면 실제로 어느 정도 성능 차이가 발생하는가? 사진도 캡쳐해서 넣기

  2. 컴포넌트 함수 안에서 왜 if문을 쓰지못할까?

  1. 리액트는 root부터 트리를 따라 내려가며 렌더링을 진행하는데 어떤 트리 구조일 때 탐색과 렌더링이 비효율적으로 동작할까?
    반대로 어떤 구조가 효율적이며 어떤 상황에서 성능 문제가 발생하는가?

2개의 댓글

comment-user-thumbnail
2026년 4월 22일

리액트 메모이제이션관련 공부중이었는데 잘 읽어보았습니다..!

1개의 답글