들어가며, 리액트의 생명 주기에 대해 아주 간단히 알아보도록 하자 !
Mount
: 컴포넌트가 최초 실행될 때Render
re-Render
: 렌더링 동작을 다시 수행하는것UnMount
: 컴포넌트가 사라질 때즉, 컴포넌트가 처음 나타날 때
를 마운트
, 그 이후 값이 변경되어 컴포넌트가 변경된 상태에서 호출될 때
를 렌더링
이라고 부른다.
: 컴포넌트가 생성 시, 발생하는 생명주기
✔︎ constructor
✔︎ getDerivedStateFromProps
✔︎ render
✔︎ componentDidMount
: 컴포넌트 업데이트 시점에 호출되는 메소드
✔︎ getDerivedStateFromProps
✔︎ shouldComponentUpdate
✔︎ componentDidUpdate
: 컴포넌트가 화면에서 사라지는 것과 관련된 메소드
✔︎ componentWillUnmount
side effect
: 컴포넌트가 렌더링 된 이후, 비동기로 처리되어야 하는 부수적인 효과componentDidMount
와 componentDidUpdate
, componentWillUnmount
가 합쳐진 것 !mount
됐을 때unmount
됐을 때update
됐을 때⇒ 즉, 클래스형 컴포넌트에서 사용할 수 있었던 생명주기 메소드(componentDidMount
, componentDidUpdate
, componentWillUnmount
)를 함수형 컴포넌트에서도 사용 가능해짐!
useEffect (function, deps)
function
: 수행하고자 하는 작업
deps
: 의존성, 이 값에 의존하여 function 실행
특정 값
이 들어가는 경우: 컴포넌트가 mount될 때, 지정한 값이 업데이트될 때 useEffect 실행빈 배열
이 들어가는 경우: 따로 의존성을 갖지 않게 되어, 컴포넌트 mount 시 한 번만 실행됨.useEffect(() => {
... // 실행할 내용
return () => {
... // clenup
}
}
cleanup 함수
: 컴포넌트 unmount 시, 실행되는 함수useEffect(() => {
// 실행 함수
const timer = setTimeout(() => {
}, 3000);
// clean-up 함수
return () => { lock = true };
}, []);
컴포넌트 렌더링 - 화면 업데이트 - useEffect 실행
순서로 실행됨.useLayoutEffect()
를 활용하자!컴포넌트 렌더링 - useLayoutEffect 실행 - 화면 업데이트
순서로 실행되기 때문에, 화면 업데이트 전 동기화가 가능해짐.useLayoutEffect (function, deps)
useLayoutEffect(() => {
... // 실행할 내용
return () => {
... // clean-up
}
}
function
과 함수 실행 시점에 영향을 미치는 의존성 배열인 deps
를 매개 변수로 가짐.clean-up
함수 활용 가능.🐝 useEffect
: 컴포넌트 마운트 실행 → 브라우저가 화면에 DOM 그리기(화면 업데이트) → effect 함수가 실행됨
🐿️ useLayoutEffect
: 컴포넌트 마운트실행 → effect 함수 실행 → 브라우저 화면에 DOM 그리기(화면 업데이트)
🐝 useEffect
: 컴포넌트들이 render, paint된 후 실행되기 때문에, 비동기적으로 실행이 이루어짐
paint
: 실제 스크린에 Layout을 표시하고 업데이트하는 과정🐿️ useLayoutEffect
: 컴포넌트들이 render된 후 실행되고, 후에 pain가 되기 때문에 동기적으로 실행이 이루어짐
**기본적인 작업 시에는 useEffect()를 사용**
하는 것이 좋다.이전에 계산한 값을 메모리에 저장
함으로써 동일한 계산의 반복 수행을 제거
하여 프로그램 실행 속도를 빠르게 하는 기술즉, 동일한 값을 반환하는 함수를 반복적으로 호출해야 한다면, 처음 값을 계산할 때 해당 값을 메모리에 저장하여 필요할 때마다 다시 계산하지 않고 메모리에서 꺼내서 사용
useMemo(calculateValue, dependencies)
calculateValue
: 캐시하려는 값을 계산하는 함수dependencies
: 의존성 배열특정 값
이 들어가는 경우: 콜백 함수 재 호출 → memoization된 값 업데이트 → 다시 memoization 실행빈 배열
이 들어가는 경우: 따로 의존성을 갖지 않게 되어, 컴포넌트 mount 시 한 번만 값 계산 → 이후 항상 memoization된 값 꺼내와서 사용구성 요소의 최상위 레벨에서 호출
해야 함!restrict mode
에서는 계산 함수를 2번 호출
함성능 저하
가 발생하게 됨꼭 필요한 경우에만 사용할 것
을 권장한다 !const MyComponent = React.memo((props) => {
return (/*컴포넌트 렌더링 코드*/)}
);
reference
여부 비교)공통점
차이점
React.memo
: 함수이기 때문에 클래스형 컴포넌트와 함수형 컴포넌트 모두 사용 가능.useMemo
: hook이기 때문에 오직 함수형 컴포넌트 내에서만 사용 가능.렌더링과 마운트에 대한 내용과 useEffect vs useLayoutEffect를 이 잘 정리해주셔서 이해가 잘 됐습니다!
cleanup을 사용하는 경우를 조금 더 자세히 알아봤는데 언마운트 이전/ update 직전 사용한다고 하고 작동 순서는
re-render → 이전 effect clean up 함수 → effect순서로 작동한다고 하네요
컴포넌트가 unmount되면 비동기 효과를 정리하는 것이 좋다고 하는데 비동기 side effect가 prop또는 state 값에 따라 달라지는 경우도 컴포넌트가 업데이트 될때 이를 clean up함수로 정리하는 것을 고려해보는 것이 좋다고 합니다.
https://hackernoon.com/cleanup-functions-in-reacts-useeffect-hook-explained
정말 세세하게 하나하나 다루어 주셔서 감사합니다 ! 특히 useMemo와 React.memo의 차이점, 그리고 useLayoutEffect에 대해 설명해주신 부분은 제가 잘 몰랐던 부분이라 새롭게 알아가네요옹
저는 useLayoutEffect에 대해 더 알아보았어요! 공식문서에서도 useLayoutEffect은 성능 저하를 일으킬 수 있으니 useEffect 사용을 권장한다고 하여,, 그렇다면 이걸 도대체 정확히 어느 경우에 쓰는거지 ? 라는 의문이 생기더라구요,,
찾아본 결과 ! 보통의 컴포넌트의 경우에는 render시에 정확한 위치와 크기를 몰라도 된다고 해요 그저 JSX를 리턴하고 브라우저에서 위치랑 크기를 계산한 후 repaint되는 과정을 거치게 돼요!
하지만,,레이아웃 요소들 중에서 정확한 위치값을 사용하여 렌더링 하는 경우에 useLayoutEffect를 사용할 수 있다고 하네요 !
공식문서에서 예로 드는 경우는, 호버시 호버 한 element 바로 옆에 나타나는 tooltip이 있다고 가정해봅시다. 이때 레이아웃 상에서 공간이 적당히 있다면 element위에 tooltip이 나타나겠지만, 공간이 적당하지 않다면 tooltip이 element의 밑에 나타나게 되겠죠? 때문에 처음부터 tooltip을 정확한 위치에 위치시키기 위해서는 tooltip의 높이값을 알고 있어야 하고 그에 따른 위치를 useLayoutEffect를 사용하여 지정하면 브라우저가 repaint하기 전에 위치를 정하게 되므로 불필요하게 두 번 tooltip을 위치시키게 되지 않는다고 합니당
(useEffect를 사용해서 위치시키면 공간이 부족한 경우 처음에는 tooltip이 hover한 element의 위에 나타났다가 flicker(깜박거림!)후에 위치가 아래로 조정되어 나타나는 현상이 나타난다고 해요 !)
공식문서의 부분을 번역해서 작성해둔 것이라 ! 더 정확한 예시를 코드와 함께 확인해보고 싶으시다면 아래 링크 들어가서 보시면 될 것 같습니당 😆
https://react.dev/reference/react/useLayoutEffect
글 너무 알차고 좋았어요!! useEffect 부터 useMemo까지 익숙하지만 익숙하지 않은 hook들을 살펴 볼 수 있었던 시간이었던 것 같습니다!
특히 useMemo와 React.memo를 나누어서 공통점, 차이점으로 설명해주신 게 너무 좋았습니다
관련해서 조금 더 찾아보았는데요,
일단 memoization
상황에서는 어떤 함수를 어떤 매개변수와 함께 불렀는 지 여부로 캐싱을 진행한다고 합니다. 따라서, React.memo로 한 번 감싸주면 App이 리랜더링되더라도 감싸준 컴포넌트는 리랜더링되지 않는다고 합니다.
React.memo => 한 컴포넌트를 같은 프로퍼티로 리랜더링하는 경우가 빈번할 때 사용
즉, property가 계속 변경되는 컴포넌트를 React.memo
로 묶는다 => 계속 비교하는 과정만 추가됨
또한, 참조 타입 props가 들어 있는데 React.memo로 묶는다면?
참조 타입은 리랜더링될 때마다 새로운 주소에 할당되므로 React.memo는 주소값만 살펴보기 때문에 쓸데 없는 리랜더링이 더욱 일어나게 된다고 합니다!!
useMemo => 컴포넌트가 아닌 복잡한 계산의 결과 값을 memoization해 최적화하는 hook 따라서 useMemo는 컴포넌트가 아닌 값이기에 값을 계산하는 과정이 최적화되는 것!
useMemo는 또한 랜더링 과정 중에 발동됩니다. 따라서, 만약 useMemo가 오래 걸리는 작업을 잡고 있다? => 렌더링 과정 중 시간 소요가 늘어나고, 화면 구성도 늘어난다고 합니다. useMemo는 렌더링 작업 중에 시행되기 때문에 비동기 작업의 결과값이 적절히 반영된다고 할 수 없다고 해요!!
최적화 최적화,,, 정말 많이 들어봤지만 사실 실제에서는 쓴 경우가 0이라,,, 실습 기대하고 있겠습니다!
와... 리액트 생명주기 함수와 useEffect, useMemo에 대해 정말 꼼꼼하게 정리해주셔서 너무 유익했습니다! 이렇게 정리해보니 함수와 hook이 참 많은데 각각을 완벽히 이해하기가 쉬운 일은 아니네요. 무척 헷갈립니다. ㅠㅠ
그 중에서도 저는 clean up 함수에 대해 가장 생소해서 좀 더 알아봤습니다. useEffect에서 return할 때 사용하는 함수라니... 저는 useEffect에서 무언가를 리턴해 본 적은 한번도 없는 것 같거든요..! clean up 함수는 useEffect hook을 사용할 때, 컴포넌트가 사라지는 시점(unmount)과 특정 값이 변경되기 직전(deps update 직전)에 실행하는 작업을 지정하는 함수라고 합니다. 이 clean up 과정을, 연이어 발생한 이벤트를 그룹으로 묶어서 처리하여 불필요한 재렌더나 로직의 재 수행을 막는 Debouncing
이라고도 하더라구요..! 참고자료
clean up은 치명적인 오류를 막기도 하는데요, 예를 들어 서버 통신에서의 promise가 resolve되기 전에 컴포넌트가 unmount되어버리면, useEffect에서 이미 unmount된 컴포넌트의 state를 업데이트하려고 하기 때문에 에러가 나게 됩니다. 이런 상황에서 unmount전에 반드시 실행되어야 할 일들을 return () => { //here }; 안에 제시함으로써 해결이 가능합니다. 정말 유용하네요...! 참고자료
좋은 글 잘 봤습니다 !!! 알고 있다고 생각했던 부분도 다시 한번 읽어보니 새로운 부분이 많네요 ㅎㅎ
저는 유튜브도 많이 참고하며 공부하는 편인데, 이 영상도 도움이 많이 되더라고!! 이해도 잘되고 ㅎㅎ
링크 같이 첨부해둡니다 🔥https://youtu.be/kyodvzc5GHU?si=9eH02h3qVczNmoKd
useLayoutEffect는 이번 아티클을 통해 처음 알게 되었어요! 상황에 맞게 useLayoutEffect도 잘 활용해 봐야겠다는 생각이 들었습니다.
추가적으로 저는 React를 공부할때 항상 공식문서를 봐요 ! 해당하는 내용의 공식문서도 함께 남겨둘테니 확인해보는 것도 도움이 될 수 있을 거 같습니다 😊
https://ko.legacy.reactjs.org/docs/hooks-effect.html
다시 한번 좋은 글 감사합니다 !