방구석에 놔둔 개발 노트

1년차 웹 / 앱 프론트엔드 엔지니어의 좌충우돌 얼렁뚱땅 앞뒤짱구 생존기

2024-06-30: useEffect와 useLayoutEffect

❓ 면접에서 많이 물어보던 useEffect와 useLayoutEffect

얼마 전에 진행했던 과제에서도 그렇고, 이따금씩 면접에서도 이 둘에 대한 질문이 들어올 때가 있었다.

얼마 전까지는 단순히 면접 때문에 이를 외우고 있었다가, 과제에서의 리뷰에서 React에서는 useLayoutEffect를 쓰는 것을 권장하지 않는다는 이야기를 알게 되었다.

아차, 싶다가도 이왕 이렇게 된 거 이제 암기가 아니라 이해하는 타이밍이 됐다고 생각했고, 그래서 이 둘에 대해서 정리하는 시간을 가져보기로 했다.

✅ 둘의 작동 기준은 렌더링이 아니다?

부트캠프 당시에는 useEffet와 useLayoutEffect의 차이는 렌더링 전후로 작동한다는 이야기가 있었다.

하지만 useLayoutEffect에 대한 공식 문서 내용에는 아래와 같은 이야기를 볼 수 있다.

useLayoutEffect는 브라우저가 화면을 다시 그리기 전에 실행되는 useEffect입니다.

‘브라우저가 화면을 다시 그린다’라는 표현은 렌더링보다는 페인팅에 가까운데, 정말 렌더링 전후로 useEffect와 useLayoutEffect가 동작하는 걸까?

인터넷에서 찾아서 볼 수 있는 React Hook Flow Diagram을 보다면 그렇지 않다는 것을 알 수 있다.

  1. 위와 같이 Mount 이후 Update 과정을 보면 Render가 일어난다.
  2. Render 이후 React가 DOM 복사본, 가상 돔을 업데이트하고, 그 후에 LayoutEffect를 실행한다.
  3. 실행이 끝나면 브라우저는 화면을 페인팅한다.
  4. 페인팅이 끝나면 useEffect를 실행한다.

이처럼 useEffect와 useLayoutEffect의 차이는 Render가 아닌 paint 전후로 이 기능이 작동한다는 것을 알 수 있다.

💼 이쯤에서 돌이켜보는 useEffect의 역할

useEffect에 나온 영어 그대로를 보면 ‘효과를 적용한다’라는 의미이다.

여기서의 ‘Effect’는 바로 부수 효과, ‘Side Effect’를 가리키는데 공식 문서를 보면 이런 문장을 볼 수 있다.

Effect는 컴포넌트가 외부 시스템과 동기화를 유지할 수 있도록 합니다. 외부 시스템은 React에 의해 컨트롤되지 않는 모든 코드를 의미합니다.

즉, useEffect는 말그대로 API 연결을 위한 비동기 통신 등 React에서 컨트롤 할 수 없는 요소들을 관리하기 위해 사용되는 Hook이다.

Hook이기 때문에 당연히 클라이언트 환경에서만 동작하며, 컴포넌트의 최상위 레벨에서만 써야한다.

useEffect에는 크게 세 가지의 영역이 있다.

useEffect(() => {
    실행 코드 영역
    return () => {
        정리 코드 영역
    }
}, [의존성 배열])
  1. 실행 코드 영역은 외부 코드와 컴포넌트를 연결해주는 역할을 가진다. API 통신 등과 같은 사항들이 여기에서 주로 이루어진다.
  2. 정리 코드 영역은 연결이 끊어진 후에, 실행해야 할 코드들을 가리킨다. cleanup 함수라고도 부르기도 한다.
  3. 마지막으로, 의존성 배열은 이 useEffect가 다시 동작해야 할 때의 트리거가 되어주는 친구들의 집합이다. 배열 내 특정 값에 변화가 발생했을 경우, useEffect 안의 내용이 실행된다.

즉, useEffect와 useLayoutEffect 이 둘은 한 마디로 외부 코드와 컴포넌트와의 연결을 관리하는, 사이드 이펙트를 관리하는 Hook이라는 것이다.

😮 근데 useLayoutEffect는 안 쓰는 게 좋다고?

그런데, React 공식 문서에서는 useLayoutEffect를 쓰면 성능이 저하될 수 있다며 권장하지 않는다.

useLayoutEffect 내부의 코드와 이로 인한 모든 state 업데이트는 브라우저가 화면을 다시 그리는 것을 막습니다. 과도하게 사용하면 앱이 느려집니다. 가능하면 useEffect를 사용하세요.

두 번에 걸쳐서 렌더링하고 브라우저를 막는 것은 성능을 저하합니다. 가능하면 피하세요.

아까 전 Hook Flow 그림을 생각해보자.

useLayoutEffect는 화면을 그리기 전에 실행되기 때문에 그 상태에서 다시 렌더링이 일으킬 수 있다.

그러다보니 상대적으로 화면을 그려내는 시간이 더 오래 걸리게 되므로 이는 성능 저하의 원인이 될 수 있다.

아까도 말햇지만 React에서는 특정 요소의 위치를 바꿔줘야 하는 등 페인팅 전에 반영해야 할 사항이 아니라면 useLayoutEffect를 사용하지 않을 것을 권장하고 있다.

그러니 혹여 이 글을 읽고 있는 본인이 프로젝트 등에서 해당 Hook을 잘못 사용하고 있진 않은지 한번쯤 돌이켜보는 글이 될 수 있기를 바란다.

🔖 참고 문헌

useEffect – React

useLayoutEffect – React

반응형 effects의 생명주기 – React