useEffect , useLayoutEffect

김동하·2023년 3월 10일
0

react

목록 보기
22/31

들어가며

리액트는 하면 할수록 어려운 거 같다.

상황에 따라 달리 해야할 것이 많고 (가령, 메모이제이션의 경우 무분별하게 useCallback, useMemo 사용은 메모리 잡아 먹으니까 비용이 큰 함수에만 적용해야 한다고 하는데 그 기준이 무엇이며, useCallback을 사용해도 리렌더링이 되는 경우가 있는데 render phase에만 실행되고 commit phase에 실행되지 않아 어쨌뜬 이점은 있다고 하지만 가시성이 없으니 그러니까 그렇게 알아둬. 느낌이라 뭔가 와닿지가 않는다)

useLayoutEffect도 돔 변경 후 브라우저 paint 이전에 작동하기 때문에 안 쓰는 것이 좋은데, 또 은근히 이점이 있는 것 같고... 아무튼 kentcdodds 선생님은 useLayoutEffect를 매우 좋아하는 것 같으니 그 이유를 들어보자.

리액트 렌더링

리액트 컴포넌트는 JSX를 실행(=렌더)하면서 React.createElement() 로 리액트 엘리먼트 트리를 만든다.

그리고 트리에 변화(UI 업데이트 등)이 생기면 기존의 엘리먼트 트리에서 변경된 부분을 계산하기 위해 재조정을 한다. 재조정은 재귀이기 때문에 부모가 리렌더링되면 자식도 리렌더링된다.

원래는 트리를 재귀로 돌면서 차이를 계산했는데, fiber라는 새로운 작업 단위(공부해야함...)가 18에 도입되고 연결리스트로 바꾸면서 재귀 호출의 문제, 그러니까 한 번 시작하면 중단할 수 없는 문제를 보완해서 동시성을 만들었다.

아무튼 렌더링이 되면 render phasecommit phase를 거치는데 러프하게 말해서 render phase는 가상 돔을 살펴 재조정하는 단계고 commit phase는 그 render phase를 바탕으로 실제 돔에 반영한다.

그리고 브라우저에 돔이 반영되면 useLayoutEffect()가 동기적으로 실행되고 (왜 동기적으로 실행되어야 할까...)

useLayoutEffect() 종료 후 브라우저 paint 그리고 마운트가 되면서 useEffect()가 비동기로 실행된다.

그래서 DOM을 변경해야 하는 코드가 있다면 useEffect() 대신 useLayoutEffect()에 작성하여, useEffect()가 작동하고 DOM을 변경하여 화면이 깜빡이는 것을 방지할 수 있다는데 useLayoutEffect()가 paint 이전에 동기로 실행되기 때문에 많이 시간이 필요한 업데이트를 한다면 사용자가 화면을 늦게 보니 좋지 않다고 한다. (그래서 하라는 거야 말라는 거야...)

예제

간이 메시지 앱이 있다고 가정하자. '메시지 추가하기' 버튼을 누르면 메시지가 채팅창에 추가되고 채팅창의 현재 스크롤이 가장 하단, 그러니까 새로 추가된 메시지가 보이는 곳으로 변경되어야 한다.

즉, 어떠한 이벤트 이후 상태가 변경되고 컴포넌트는 리렌더링하면서 UI를 업데이트한다. 돔이 브라우저에 나타나고 현재 채팅창의 height를 계산하여 top을 변경해야 하는 것이다.

그런데, 만약 채팅앱 상태가 업데이트 되었을 때 리렌더링 비용이 큰 다른 자식 컴포넌트도 리렌더링 되어야 한다고 가정해보자. 이때 useLayoutEffect와 useEffect의 차이는??????!

컴포넌트가 리렌더링 되면 DOM에 접근한 Ref를 사용하여 scroll 위치를 변경한다고 했을 때, useEffect는 paint 이후 실행되기 때문에 사용자는 메시지가 추가되어 height가 늘어난 UI를 보고 잠시 뒤 스크롤이 이동하게 된다.

하지만 useLayoutEffect를 사용하게 되면 paint 전에 scroll 이벤트도 발생하기 때문에 사용자는 업데이트 된 UI와 제 위치에 있는 scoll를 보게 된다. 그러니까, 무언가 화면에 미세한 변화가 안 생긴다는 것이다.

결론

이론상으로는 그렇긴 한데 계속 실험해봐도(sleep을 5초까지 해봤는데도 모르겠음) 그 미세한 차이를 잘 모르겠다. 이래서 어려운 거 같아... 그렇다고 하니까 그렇게 믿어야 하는 건가..

참고 -
https://kentcdodds.com/blog/useeffect-vs-uselayouteffect

profile
프론트엔드 개발

0개의 댓글