일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- link
- Critical Rendering Path
- 아키텍처
- 목킹
- styled-component
- Cache
- async
- Concurrent Mode
- react-native
- Babel
- JavaScript
- 리액트쿼리
- thread
- front-end mocking
- 동기
- next.js
- 컴포넌트
- vanilla-extract
- 리액트
- CSS-in-JS
- SWC
- 캐쉬
- 리액트네이티브
- amplify
- MSW
- mockoon
- react-query
- 기초
- react server component
- 쓰레드
- Basic
- 기본
- 개발자
- 자바스크립트
- 최적화
- 비동기
- React
- sprinkles
- next hydration
- mock service worker
- Today
- Total
Don’t worry about failures
concurrent mode에 대해 본문
리액트 18버전부터 등장한 개념으로 리액트에서 concurrent(동시성)에 대해 몇년동안 연구하고 분석한 결과 나오게 되었다.
Concurrent React
자바스크립트의 특성상 싱글쓰레드로 하나의 작업씩 처리가 된다. 렌더링 작업이 일어나고 있을 때 다른 작업을 진행못하고 blocking되는 현상이 존재한다. 하지만 우리는 평소에 과한 렌더링 작업이 없기에 느끼지 못할 수 있다. 간단한 테스트를 해보자.
input을 통해 들어온 값에 따라 1000개의 element가 새롭게 들어온다고 해보자
간단하게 아래와 같이 작성후
const [array, setArray] = useState([]);
const inputHandler = useCallback((e) => {
const inputValue = e.target.value;
const newArray = new Array(inputValue.length * 1000);
setArray([...newArray]);
}, []);
return (
<div className="App">
<input onChange={inputHandler}/>
<div style={{display: "flex", flexWrap: "wrap"}}>
{
array.map((value, index, array) => {
return (<div style={{marginTop: 20}} key={index}>
**
</div>);
})
}
</div>
</div>
);
인풋에 입력을 막해보면 결과는 input박스의 타이핑 속도가 매우 느려진다.
이는 *을 그리는 렌더링을 작업하는데 input박스에 입력을 계속하니까 input이 blocking되어 입력이 끊기게 되는 것이다.
이와 같은 이슈를 잡고 concurrent mode를 통해 유저의 경험을 더 증진시키고자 한다.
concurrent mode 이전에는
위와 같이 빠른 Input에 의해 버벅거리는 현상을 잡고자 디바운스, 쓰로틀을 통해 잡아갔다.
디바운스, 쓰로틀 또한 빠른 Input을 정해진 시간 동안의 부담을 덜어주며 이벤트 처리를 도와준다.
간단하게 디바운스와 쓰로틀에 대해 봐보자.
lodash를 활용한 코드는 다음과 같다.
function App() {
const [text, setText] = useState("");
// throttle
const inputHandler = useCallback(throttle((e) => {
setText(e.target.value);
}, 1000),[]);
// debounce
const inputHandler = useCallback(debounce((e) => {
setText(e.target.value);
}, 1000),[]);
return (
<div className="App">
<input onChange={inputHandler}/>
<div style={{display: "flex"}}>
{text}
</div>
</div>
);
}
throttle의 경우 위의 예제 처럼 1초 wait 시간을 줬을 때 1초마다 콜백 이벤트가 일어난다.
반면, debounce의 경우 마지막 입력을 시점으로 하여 1초가 지난 후 콜백 이벤트가 일어난다.
이 둘의 한계를 봐보면,
throttle의 경우 wait time에 따라 성능에 영향을 준다. 짧을수록 좋은 더 부드러운 효과를 주겠지만, 위의 * 그려주는 예시와 같은 문제에 더 가까워진다. wait time을 높이게 되면 그만큼 반응이 느려지게 되는 것이다.
debounce의 경우 입력 후 wait time만큼의 딜레이가 발생이 된다. 즉 시간에 따라 결정이 되지만 가장 합리적인 시간을 정하기가 어렵다.
위와 같은 한계를 극복할 수 있는 것이 concurrent mode이다.
concurrent mode는
리액트 18버전의 가장 핵심인 개념으로 리액트팀은 priority queues, multiple buffering 등의 내부적 기술을 통해서 UI 업데이트의 동시성을 구현하였고 유저 경험을 증진시키고자했다. 또한 리액트팀은 이러한 기술을 숨기고, 개발자들은 오직 유저 경험을 증진시키는데에 힘쓰도록 설계를 진행하였다.
즉, 리액트는 새로운 화면을 main 쓰레드를 blocking하는거 없이 background에서 준비할 수 있다.
하지만 항상 이와 같이 동시성 render를 가지는 것은 아니다. 리액트는 렌더링을 중간에 멈췃다가 다시 시작할수도 있고, 진행중인 것을 버릴수도 있다.
동시성 메커니즘은 어떻게 구현된걸까?
1. 우선순위
렌더링 작업에 우선 순위를 할당하여, 중요한 데이터의 렌더링을 우선 처리할 수 있다.
리액트 17버전 이전에서는 작업의 만료 시간을 기준으로 우선순위를 부여하는 메커니즘으로 구현됨.
17버전 이후부터는 Lane모델을 통해 우선순위를 처리한다.
Lane 모델에 대해
이 모델은 React의 내부 작업 스케줄링 및 렌더링 우선 순위를 관리하는 방법을 설명한다.
레인
Lane은 각 작업의 우선 순위를 나타내는 개념이다. 높은 우선 순위의 작업은 더 높은 레인에 할당된다.
높은 우선 순위의 레인은 사용자 상호작용과 관련된 작업을 처리,
낮은 우선순위의 레인은 데이터 렌더링 관련 작업을 처리.
Lane 모델은 우선순위를 두 가지 중요한 컨셉으로 분리한다.
- Task Prioritization, A 작업이 B 작업보다 급한가?
- Task Batching A 작업이 이 그룹 테스크에 속한가?
우선순위에 따라 우선 실행권을 부여하고, 작업 배치 개념을 착안하여 최종 작업 순서를 정하고 진행한다.
예를 들어 우선 순위가 CPU, I/O, CPU 순으로 작업이 예약 되어 있다고하면, I/O 작업을 다른 그룹으로 분리하여 일괄 처리함으로서 CPU 작업의 병목을 방지할 수 있다. 더 효율적으로 진행.
CPU 작업이 I/O작업보다 우선순위가 낮아 지속적인 양보가 발생하면, CPU 작업처리에 진전이 없을 여지를 방지하기 위해 I/O 작업들을 묶어 진행할 수 있도록 리액트 18버전에서 제공하는 Automatic batching의 기저에 있는 동작방식으로 처리가 된다.
레인의 종류
- SyncLane : 동기적 작업 처리 레인. 이산적 사용자 상호작용에 대한 업데이트. 우선순위 1
예) 사용자 버튼 클릭, 입력 - 리액트는 이러한 상호작용에 대한 이번트 처리 및 UI 업데이트를 SyncLane에서 처리.
- InputContinuousLane : 연속적인 사용자 상호 작용에 대한 업데이트. 우선순위 2
예) 사용자가 텍스트 입력란에 글자를 입력하는 동안 React는 이러한 입력에 대한 업데이트를 InputContinuousLane에서 처리.
- TransitionLane : UI전환과 관련된 작업처리. 우선순위 두번째 우선순위 3
예) 화면 전환, 애니메이션 효과, 모달 다이얼로그의 열고 닫기 등의 작업.
suspense, useTransition, useDefferredValue에 의해 생성된 업데이트
- DefaultLane : 대부분 일반적인 렌더링 작업을 처리. 우선순위 가장 낮음
예) 컴포넌트 초기렌더링, 상태변경에 따른 렌더링.
2. 작업분할
작은 단위로 분할하여 여러 작업을 동시에 작업을 수행한다. 쓰레드의 컨텍스트 스위칭 같이 작업.
이를 통해 메인 쓰레드가 blocking되지 않고 렌더링 진행.
위와 같은 기술을 통해서 리액트는 동시성을 처리하여 UI 업데이트에 있어 사용자 경험을 증진시키고자 노력하고 발전하고 있다.
참고:
https://ko.legacy.reactjs.org/blog/2022/03/29/react-v18.html
https://tecoble.techcourse.co.kr/post/2021-07-24-concurrent-mode/
사용자 경험 개선 2편 - react concurrent mode
❗ react suspense에 대한 이해가 필요한 글입니다. 사용자 경험 개선 1편을 참고해주세요. 👀 concurrent mode…
tecoble.techcourse.co.kr
'React' 카테고리의 다른 글
React StrictMode에 대해 (0) | 2024.03.21 |
---|---|
useTransition에 대해 (0) | 2024.03.16 |
React Big list state 관리 이슈 (0) | 2022.11.25 |
useCallback과 useMemo에 사용에 대해 (0) | 2022.05.08 |
react query에 대해 (0) | 2022.02.27 |