-
22.06.27 회고(리플로우, 리페인팅 / Suspense)카테고리 없음 2022. 6. 28. 00:53
Facts(사실/ 객관)
[업무]
- 6.24 배포사항 배포 후 수정사항 반영
- 구독 랜딩, 상세페이지 워딩 수정
- 구독 1단계 수량 선택 셀렉트 박스 수정 - GA 결제 금액 제대로 안찍히는 오류 수정
- 1:1 미팅
[일상]
1. 업무 시간 종료 후 현겸님, 쥴리님과 서브웨이 사들고와 먹고 10시까지 공부하다가 옴
2. 집와서 하루 유튜브 시청시간 소진겸 시청하며 닭가슴살 200g, 고구마 80g 섭취 휴식
3. 추가 1시간 30분 아까 찾은 것 정리
Feelings(느낌,주관)
Findings(배운점)
1. 리플로우, 리페인팅(reflow 와 repainting)
리렌더링은 브라우저 동작원리와 관련이 있다.
DOM요소, 레이아웃, 색상 변경
브라우저는 DOM트리와 CSSOM 생성 -> 렌더트리 생성 -> 레이아웃 재계산-> 화면에 다시 그리는 과정을 거치는데
여기서 레이아웃 재계산하고 화면에 그리는 것 리플로우 (css로 구분하자면 text-align, width, height)
레이아웃 재계산 하지 않고 다시 그리는 것이 리페인팅 (background-color, color)
리액트는 가상돔(Virtual Dom)을 생성하여 이전 DOM과 비교하여 변경된 부분만 리렌더링한다.
최적화 TIP) 가능하면 상수와 관련된 것들은 참조를 덜 하게 컴포넌트 외부로 빼놓는다.
참고자료
1. https://chifuyu.tistory.com/24
2. React-Suspense
2-1. React.Suspense란?
렌더링이 준비되지 않는 컴포넌트 로딩화면을 보여주고 준비가 완료됐을때 해당 컴포넌트 보여주게 하는 리액트 내장함수이다.
2-2. 왜 사용할까 ?
1) React.lazy와 같이 사용하려고
: React와 같은 SPA는 첫 로드시 보여지는 페이지와 상관없이 모든 스크립트를 다 가져와서 시간이 걸리는 단점이 있다.
이때 React.lazy를 사용해서 컴포넌트를 동적으로 임포트하여 초기 렌더링 시간을 단축할 수 있다(이 것도 한계가 있다함 결국 서비스가 커지만 SSR을 해야 한다고 한다)
const SomeComponent = React.lazy(() => import('./SomeComponent'));
React.lazy를 사용해서 해당 페이지 이동 시 그때 해당 컴포넌트를 불러오는데 그때 불러오는 과정에서 로딩이 생긴다.
이 로딩되는 시간동안 화면을 보여지게 하는 것이 Suspense이다.
2) Data-fechting
data를 fetching하는 세 가지 접근이 있다
1. Fetch-on-render
const App = () => { const [todos, setTodos] = useState(); useEffect(() => { fetchTodos().then(todos => setTodos(todos)) }, []) if (!todos) return <p>Loading todos...</p> return ( <> <Todos data={todos} /> <Tasks /> // 이 컴포넌트는 또 다른 리퀘스트를 할 예정 </> ) }
waterfall방식으로 App 컴포넌트 마운트 후 todo를 fetching한다. 이때 fetching하는데 5초가 걸리고 Task에서 또 다른 fetching을 해야한다면 해당 fetching은 5초가 지난 다음에 시작할 수 있다.
이는 여러개의 async들이 병렬적으로 작동하지 않게 되어 UI렌더링 시간을 증가시킨다.
2. Fetch-then-render
const fetchData = () => { return Promise.all([fetchTodos(), fetchTasks()]).then(([todos, tasks]) => ({todos, tasks})) } const promise = fetchData(); // tasks, todos를 가져옴 const App = () => { const [todos, setTodos] = useState(); const [tasks, setTasks] = useState(); useEffect(() => { promise().then(data => { setTodos(data.todos) setTasks(data.tasks) }) }, []) if (!todos) return <p>Loading todos...</p> return ( <> <Todos data={todos} /> <Tasks data={tasks} /> </> ) }
1번과 달리 마운트 전에 네트워크 콜을 하고 있으며 병렬적으로 fetching을 한다.
하지만 예를들어 fetchTodos가 9초 fetchTasks가 2초 걸린다고 했을 때 9초가 지나야만 화면이 렌더링이 되므로 fetchTasks는 7초를 손해보게 된다.
(둘의 promise를 따로 사용할 수도 있겠지만 서비스가 커질 수록 코드가 지저분 해져 관리하기 힘들어 진다고 함)
그래서 렌더링 전에 호출하면서 화면을 블로킹하지 않는 방법인 render as fetch 을 쓰게된다.
3. render as fetch
suspense는 내부에 쓰이는 컴포넌트의 data가 준비되지 않았다고 판단하면(promise가 resolve 되지 않음) fallback UI를 렌더링한다. 만약 resolved된 data라면 문제없이 렌더링한다.
여기서 포인트는 렌더링 전에 네트워크 콜을 하여 빠르게 렌더링할 수 있다는 점이다.
참고자료
1. https://www.daleseo.com/react-suspense/
- 6.24 배포사항 배포 후 수정사항 반영