[study] react 복습 1

유재민·2023년 2월 26일
0

# react 복습 1

# State

컴포넌트 내부에서 가지고 있는 컴포넌트의 상태값, 유동적인 데이터를 다루기 위한 객체

# let으로 변수 선언해서 관리하면 될껄 왜 state로 관리할까?

state는 일반 변수와 다르게 값이 변하게 되면 리렌더링이 일어난다. 값이 변함에 따라 관련 컴포넌트들이 업데이트되어야 하기 때문에 리렌더링이 발생하는 state를 사용하는 것이다.

# 직접 값을 조작하지 않고 setState를 사용하는 이유는?

만약 컴포넌트의 state를 직접 변경하면 react는 컴포넌트를 다시 렌더링해야 하는지 알 수 없다. setState 메소드를 사용하면 react는 state의 변화를 감지해 리렌더링을 발생시켜 컴포넌트를 업데이트 시킬 수 있다.

# setState는 비동기? 동기?

setState는 비동기로 동작한다. 비동기로 동작하는 이유는 일정 시간동안 변화하는 상태를 모아 한번에 렌더링하기 위해서이다. 리액트의 배치 업데이트(batch update)는 16ms(밀리세컨드)당 한번만 일어난다. 그러므로 상태 업데이트 후 바로 상태 값을 참조하여 다른 작업을 할 때 문제가 발생할 수 있다. 이런 경우 이전 상태를 바로 참조할 수 있는 prevState를 활용하여 상태를 업데이트 한 후 사용하는 방식으로 해결 할 수 있다. prevState는 setState안에 화살표 함수를 통해 파라미터로 이 전 state를 전달하는 방식으로 사용할 수 있다. (ex : setState((prev) => prev + 1)

# setState가 비동기 동작을 취했을 때 얻을 수 있는 이점

setState가 여러 번 호출 될 경우 호출 될 때마다 리렌더링이 발생하면 성능 저하가 발생할 수 있다. 그러므로 react는 batch update를 16ms마다 진행하여 만약 16ms 안에 100개의 state 변화가 일어난다면 변화 된 상태 값을 취합하여 한번에 업데이트 시키는 것이다. 결론은 setState의 비동기 동작으로 인해 성능 저하를 막을 수 있다는 것이다.

# react의 불변성 (setState의 얕은 비교)

불변성은 변하지 않는 상태나 값을 말한다. 리액트에서의 불변성은 값을 직접적으로 변경을 하지 않고, 기존의 값을 수정하지 않으면서 새로운 값을 만들어내는 것을 의미한다. 리액트는 얕은 비교를 통해 state의 업데이트를 감지한다. 얕은 비교는 실제 내부 값까지 비교하는 것이 아닌 참조하는 값에 메모리 주소를 비교하는 것이다. 기존 값과 최신 값에 얕은 비교를 수행하여 최신 값이 새로운 메모리 주소를 참조할 때 업데이트가 발생하게 되는 것이다. 이 때 배열이나 객체의 상태를 업데이트 하는 경우 리액트에서는 새로운 메모리 주소를 생성하기 위해 setState에 배열이나 객체를 할당하고 배열이나 객체 내부에 스프레드 연산자를 활용한 기존 값의 복사본과 최신 값을 할당해준다. (setState([...state, newState]), setState({...state, [key]: value})) 이렇게 복사본을 활용한 방식과 새로운 메모리 주소를 생성하는 방식을 통해 불변성을 지킬 수 있고 불변성을 지켜줌으로서 예상치 못한 사이드 이펙트를 방지할 수 있고 얕은 비교를 통해 효과적인 상태 업데이트를 할 수 있다. 만약 불변성을 지키지 않고 기존 값을 직접 변경할 경우 참조하는 값에 메모리 주소가 동일하기 때문에 리액트는 상태 변화를 감지할 수 없어 컴포넌트의 최신화를 유지할 수 없게 된다.

# react 렌더링 성능 향상 방법

(1) 렌더링 성능 측정 : react developer 익스텐션이나 lighthouse 를 활용하여 렌더링 성능을 측정하고 perpomance API나 console.time 메소드를 활용하여 함수 성능을 측정하고 개선한다.

(2) useMemo, useCallback : useMemo, useCallback 등을 사용하여 값이나 함수를 메모이제이션 해 사용한다.

(3) 코드 스플리팅 : Lazy와 Suspense를 사용하여 코드 스플리팅하고 번들의 크기를 줄여 당장 필요한 컴포넌트만 우선적으로 렌더링하도록 한다.

(4) 이미지 사이즈 최적화 : 이미지를 압축율이 좋은 이미지 포맷으로 변경하거나 사용자에게 보여지는 크기를 고려하여 사이즈를 줄인다. 또한 고정 사이즈를 사용하여 리플로우를 최소화할 수 있다.

(5) 이미지 레이지 로딩 : IntersectionObserver API와 같이 뷰포트를 감지하는 API를 활용하여 해당 이미지 보여져야 할 때 서버에 요청을 보내 이미지를 가져온다.

(6) 애니메이션 최적화 : 애니메이션은 리플로우와 리페인트를 발생시키는 원인이다. witdh와 같은 크기를 직접적으로 변경하는 애니메이션의 경우 cpu에서 처리하게 되는데 transform의 scale을 활용하여 크기를 변경시키는 경우 gpu에서 처리하게 되므로 성능 향상에 도움이 된다.

(7) 웹폰트 경량화 : 사용할 글자만 남겨놓은 서브셋 파일을 활용하여 웹폰트 파일의 크기를 줄인다.

(8) state와 props변경 최소화 : state와 props의 변경을 최소화하여 리렌더링을 최소화한다.

(9) 프롭시 드릴링(props drilling) 피하기 : 부모 컴포넌트에서 자식 컴포넌트로 전달하는 프롭스의 깊이가 깊어질수록 성능에 좋지 않다. 부모의 state가 변경되면 자식 컴포넌트 모두 리렌더링되기 때문이다. 개인적으로 2번 이상 넘겨주지 않도록 기준을 정해서 작업하고 있다. 넘어가는 경우 전역 상태 관리 라이브러리를 사용하여 해결할 수 있다.

(10) state의 분할 : 한 컴포넌트에 state를 몰아서 사용하는 경우 state 변화가 빈번하게 발생할 수 있기 때문에 state를 사용하는 컴포넌트 내부에 작성할 수 있도록 한다. 또한 객체 형태의 state의 경우 객체 프로퍼티 중 하나만 사용하는 컴포넌트가 있거나 하는 경우 분리해서 사용하는 것이 좋다.

react의 리렌더링 조건
(1) 부모 컴포넌트가 렌더링 될 때
(2) 자신의 state가 변경 될 때, 단 setState를 사용하여 변경해야한다. state를 직접 변경할 경우 state의 변경을 감지하지 못하기때문에 render 함수가 호출 되지 않는다.
(3) 자신이 전달받은 props가 변경될 때

profile
프론트엔드 개발자

0개의 댓글