일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- audio 객체
- CBR
- next-i18n
- matchmedia
- 다국어
- VBR
- 회고
- 부동소수점
- fabricJS
- 변수명
- ffmpeg
- 알고리즘
- railway #mysql #db배포
- 스택
- 해시
- 크로스브라우징
- react-i18n
- javascript #encodeuri #encodeuricomponent
- currenttime
- safari 버그
- fontfamily
- 프로그래머스
- 코드리뷰
- 폰트
- JS
- Safari
- 개발자회고
- TEXTBOX
- Today
- Total
Leo의 기술 블로그
[React] Rendering Phase 와 Commit Phase란 무엇일까? 본문
[React] Rendering Phase 와 Commit Phase란 무엇일까?
LeoEngineer 2023. 11. 29. 00:01시작하기에 앞서
한상훈님의 React 리렌더링에 관한 영상을 보다가 모르는 부분이 있어 공부도 하고 생각도 정리할 겸 글을 작성하려고 한다.
회사에서 리액트를 자신있게 사용하고 있지만 영상을 보던 중 react에 rendering phase와 commit phase의 단계가 있다는 사실을 알게 됐다.
사실 두 단계가 무엇인지 모르고 키워드조차 처음들어서 다시 한번 공부를 더욱 해야 함을 느끼는 시간이기도 했다.
바로 공부한 내용을 정리해보자!!

Render Phase와 Commit Phase란?
Render Phase란 함수 컴포넌트를 기준으로 얘기하면 함수 컴포넌트가 호출되어 JSX를 통해 Virtual DOM을 만들고 이전에 만들었던 Virtual DOM과 비교하는 diff 알고리즘을 통해 달라진 부분을 다시 그리라고 체크(인식)해서 Virtual DOM에 반영하는 단계이다.
Commit Phase는 앞에서 찾은 diff를 통해 생성한 Virtual DOM을 실제 DOM에 적용하고 라이프 사이클을 실행하는 단계다. 참고로 이 단계에서는 paint는 일어나지 않는다.
그럼 두 단계가 실제로 리렌더링을 위한 hook들과 무슨 관계가 있는 건데?
export default function App() {
const getCookie = useCallback(() => {
console.log('쿠키 맛있어');
}, []}
return <Children onChnageCookie={getCookie}/>
}
export default function Children({onChnageCookie}) {
return <div>쿠키좋아</div>
}
위에 작성된 두 개의 컴포넌트는 App 부모 컴포넌트에서 자식 컴포넌트에게 getCookie 함수를 props로 전달해주고 있다.
useCallback으로 해당 함수를 감싸고 있어 따로 특정 메모리에 해당 함수가 저장이 되어 부모 App 컴포넌트가 리렌더링 되어도 해당 함수는 새로 생성되지 않는다.
그렇기 때문에 자식 컴포넌트도 props가 변경되지 않아 리렌더링이 발생하지 않을 거 같지만 실제로는 리렌더링이 발생한다. 왜 그런 걸까?

이 과정을 이해하기 위해서는 리액트는 부모 컴포넌트가 commit phase가 수행이 됐다면 모든 자식 컴포넌트도 무조건 commit phase가 수행된다. 그렇기 때문에 새로운 JSX가 만들어지고 기존 JSX와 다르다고 인식을 해 새로운 컴포넌트를 생성해 리렌더링 과정이 발생하는 것이다.
이를 방지하기 위해서 리렌더링 시키고 싶지 않은 컴포넌트에 React.memo로 감싸주면 JSX도 메모리 어딘가에 저장을 해둬서 실제로 데이터가 바뀌지 않는 이상 메모리에서 기존 JSX에서 가져와 자식 컴포넌트가 리렌더링이 발생하지 않는 것이다.
그럼 언제 적절하게 useMemo, memo, useCallback을 사용하면 좋을까?
마지막으로 리렌더링을 방지하기 위해서 모든 컴포넌트와 함수 데이터에 해당 hook을 적용해 놓으면 좋을까?
답은 아니요다. 왜냐하면 해당 hook들은 결국 어느 공간에 영구적으로 데이터를 저장해 두기 때문에 메모리 부족으로 이어질 수 있다.
차라리 계속 새롭게 남은 메모리에 할당을 하는 방식이 성능면에서 더 좋을 수 있다.
그럼 이제 위에 존재하는 hook들을 언제 사용하면 좋을지 얘기해 보자.
memo는 그럼 언제 사용할까
리액트에서 자식이 배열인 경우 원소가 굉장히 많다면 자식 컴포넌트가 리렌더링 될 때 모든 자식 요소들의 컴포넌트도 모두 리렌더링이 발생한다.
이 경우 성능에 좋지 않을 수 있기 때문에 memo로 감싸서 실제로 부모로부터 전달받은 props가 실제로 변경되지 않는 이상 리렌더링이 발생하지 않도록 짤 수 있다.
그럼 useCallback과 useMemo는 언제 사용해?
memo의 경우 불필요한 리렌더링을 방지한다고 했다. 하지만 props가 변경될 경우 리렌더링을 막지 못한다.
정당하게 props가 변경되었다면 리렌더링이 되어야 하지만 함수나 변수를 전달받는 경우 부모 컴포넌트에서 해당 함수와 변수의 경우 값이 바뀌지 않았음에도 새로운 참조 메모리가 할당되어 새로운 함수와 변수가 선언된다.
이 때문에 자식 컴포넌트에서도 props로 전달받은 함수와 변수가 새롭게 바뀌었다고 인식해 불 필요한 리렌더링을 발생시킨다.
이를 방지하기 위해서 함수와 변수를 useCallback과 useMemo를 통해 특정 dependancy가 변경되었을 때만 새롭게 선언되어 라이프사이클을 제어할 수 있도록 작성해야 할 때 사용하면 좋을 거 같다.

감사합니다!! :)