-
[React] Section 13Frontend/React 2023. 11. 9. 20:10
리액트 DOM
웹에 대한 인터페이스
브라우저의 일부인 실제 DOM에 대한 작업을 하므로 사용자가 보고있는 화면에 무언가를 표시하는 역할은 리액트 DOM의 몫이다.
리액트는 컴포넌트를 관리하고 상태 객체를 관리하고 ... 하는 라이브러리일 뿐이다.
리액트는 컴포넌트만 신경쓴다. (props, context, state...)
최종적으로 리액트가 하는 역할이란 가상 DOM이라는 개념을 사용하는 것이라고 할 수 있다.
이 가상 DOM은 컴포넌트 트리의 현재 모양과 최종 모양을 정한다.
state, props, context, component에 변경이 발생하면 컴포넌트 함수가 재실행 되어 리액트가 이를 재평가하게 된다.
하지만 이 재평가가 DOM을 다시 렌더링하는 것은 아니다.
컴포넌트 -> state, props, context가 변경될 때 재평가된다. 이러면 리액트는 컴포넌트 함수를 다시 실행한다.
이에 반해
Real DOM -> 리액트가 구성한 컴포넌트의 이전 상태와 트리, 그리고 현재 상태간의 차이점을 기반으로 변경이 필요할 때만 업데이트 된다. Real DOM은 필요한 경우에만 변경된다.
Real DOM을 사용하게 된다면, 즉 브라우저에 직접 렌더링하는 것은 성능 측면에서 자원이 많이 필요하다. 페이지가 느려질 수 있다.
이렇게 리액트는 가상 DOM과의 비교를 통해 최종 스냅샷과 현재의 스냅샷을 실제 DOM에 전달하는 구조를 갖는다.
리액트 DOM은 전체 DOM을 재렌더링 하지 않는다.
props의 변경은 실제 DOM의 변경으로 이어질 수는 있지만 함수에서 재평가를 할 때는 부모 컴포넌트가 재평가 되는 것으로 충분하다.
실제 DOM을 통한 업데이트는 가상 스냅샷 간의 차이점만 반영되었다.
컴포넌트 재평가와 컴포넌트 함수의 재실행이 일어나도 실제 DOM이 다시 렌더링되거나 변경되는 것은 아니다!!!
컴포넌트가 재실행되면 모든 자식 컴포넌트들 역시 재실행, 재평가 된다.
React. memo
함수형 컴포넌트에만 가능
React.memo는 인자로 들어간 컴포넌트에 어떤 props가 입력되는지 확인하고 입력되는 모든 props의 신규 값을 확인한 뒤 이를 기존의 props 값과 비교하도록 리액트에게 전달한다.
부모 컴포넌트가 변경되었지만 그 컴포넌트의 props 값이 바뀌지 않았다면 컴포넌트 실행은 건너뀐다.
-> 불필요한 렌더링을 피하기 위해 최적화가 이루어진다.
-> 개별적인 성능 비용 (props 비교 등등...)이 필요하기 때문에 모든 컴포넌트에 적용 x
-> 컴포넌트 트리가 매우 크다면 React.memo는 매우 유용하게 쓰일 수 있다.
-> Button 컴포넌트를 React.memo로 래핑해도 재렌더링 되는 이유?
자바스크립트에서는, App 함수가 실행될 때마다 만들어지는 함수는 모두 새로운 함수이다...
원시값이라면 새로 생성되어도 === 가 true 가 맞지만 function은 false 이기 때문에 props 값 비교에서 재렌더링된다. (중요!!!)
useCallback
컴포넌트 실행 전반에 걸쳐 함수를 저장할 수 있게 하는 훅
매번 실행할 때마다 이 함수를 재생성할 필요가 없다는 걸 알릴 수 있다.
동일한 함수 객체가 메모리의 동일한 위치에 저장되므로 이를 통해 비교 작업을 할 수 있다.
useCallback은 우리가 저장한 함수를 리액트의 내부 저장 공간에 저장해서 함수 객체가 실행될 때마다 이를 재사용할 수 있게 된다.
const toggleParagraphHandler = useCallback(() => { setShowParagraph((prevShowParagraph) => !prevShowParagraph); }, []);-> 이 콜백 함수는 절대 변경되지 않을 것이라고 리액트에게 알려주는 배열이다.
따라서 App 컴포넌트가 다시 렌더링 되어도 항상 같은 함수 객체가 사용되게끔 한다.
클로저
useCallback에서 함수가 정의될 때 자바스크립트는 이 안에서 사용되는 함수 외부에서 사용하는 모든 변수를 잠그게 된다.
따라서 변수의 값은 변수가 저장된 시점의 값을 사용하게 된다.
useCallback을 사용하면서 어떤 환경에서든 함수 재생성을 하지 않게 막았기에 변수의 값은 최신이 아니고 처음 실행된 시점의 값을 가지고 있게 된다.
-> 함수 재생성을 필요로 할 때가 있을 수가 있기 때문에 문제가 된다.
-> 의존성 배열에 변수를 추가해야 한다.
State
리액트는 useState와 여기에 전달된 기본값에 대해서는 한 번만 고려되도록 처리한다.
컴포넌트가 DOM에서 완전히 삭제되거나 하지 않는 이상 상태의 초기화는 이루어지지 않는다.
상태 갱신 예약
리액트는 상태 갱신을 인지하고 있지만 이를 즉시 처리하지는 않는다.
리액트가 하는 일은 상태 변화가 발생하게 되면 이 상태 변화의 순서를 명확히 해서, 같은 타입임을 보증한다.
-> 함수 형태를 사용해야 리액트가 미완료된 상태 변경 작업에 대하여 최신의 상태를 사용하고 컴포넌트가 재렌더링 되었을 그 시점의 상태를 사용하지 않게 된다.
-> 컴포넌트가 재렌더링 되었을 때의 시점과 상태 변경이 예약되는 시점의 차이를 아는 것은 매우 중요!!!
두 가지 방법
1. prevState : 이전 스냅샷에 의존하는 상태를 업데이트
2. useEffect : 다른 상태에 의존하는 특정 상태를 갱신
상태 배치 작업
두 개의 상태 갱신이 같은 코드 조각에 존재한다면 즉 같은 함수가 서로 다른 프로미스에 있지 않고 같은 곳에 존재한다면 이 둘 사이에 시간 지연과 같은 현상은 일어나지 않는다.
-> 리액트는 이들에 대한 상태 갱신을 하나의 동기화 프로세스에서 같이 실행한다.
useMemo
const { items } = props; const sortedList = useMemo(() => { return prop.items.sort((a,b) => a-b); }, [items]);-> 재렌더링이 발생할 때마다 items가 변하기 때문에 (참조값이기 때문에) items도 useMemo로 래핑해주어야 한다.
... ... <DemoList title = {listTitle} items = { useMemo(() => [5,3,1,10,9],[])} /> ...-> 불필요한 재정렬을 막을 수 있다.
-> useMemo를 사용해 데이터를 저장하면 이는 메모리를 사용하는 것이고 함수 저장 또한 일정 성능을 사용하는 것이다.
'Frontend > React' 카테고리의 다른 글
[React] Section 15, 16 (0) 2023.11.13 [React] Section 14 (0) 2023.11.12 [React] Section 12 (0) 2023.11.07 [React] Section 11_3 (0) 2023.11.04 [React] Section 11_2 (0) 2023.11.04