자바스크립트 도구를 “더 빠른” 언어로 다시 작성하는 것에 대해 회의적인 이유
- 생태계가 커지면서 자바스크립트가 갖고 있을 수 밖에 없는 다양성에 대한
느림 - 아래 중립 코멘트 중
- 더 빠른 언어로 작성했을때 속도 개선을 얻을 수 있었다면
- 자바스크립트로 재작성했을때에도 속도 개선을 얻을 수 있었을 것이다
(번역) React Labs: 뷰 트랜지션과 액티비티, 그리고 더 많은 것들
- 무엇보다 액티비티에 대한 관심
- 개념적으로는 언마운트 되어있고 시각적으로는 사용되고 있지만
- 상태는 저장해두며 미리 렌더링하는 효과
- 렌더와 커밋 단계를 나눠서 렌더링만 미리 해두고 커밋은 마운트 되어야할때에 실행
- 복잡한 ViewPager에서 미리 렌더링 해두고 상태를 유지할 수 있다면
- 상태가 저장되기에 시점에 따른 상태 초기화는 필요하겠지만
- 뷰포트에 따라 언마운트 마운트 하는 비용을 조금 줄일 수 있을지 모르겠다
[번역] React.memo 완벽 해부: 언제 쓸모 있고 언제 쓸모없는가
- 스프레드 어떻게 처리하지 왜 리렌더링 되는거지
- 느낌적으로 리렌더링 되는데
- 느낌적으로 안될 것 같은데 🤔
- props가 변했다면 리렌더링 된다
- props가 변하지 않았다면?
- 스프레드로 들어가도 리렌더링 되지 않아야하는데
- Parent 자체가 memo 되지 않은 상태라서 그런가
- 안티패턴 이였던 스프레드 예시에는
- 메모이제이션 되어있는 Child에 불필요하게 변경된 props가 포함되었기 때문이다
- props 통째로 전달하면 문제가 되는 상황은 이 안티패턴의 예시인듯하다 🤔
(번역) 이벤트 루프를 차단하지 않기 위한 실용적인 가이드
-
오래 걸리는 작업(동기)을 나누어서 처리하도록 Promise + setImmediate 로 관리(비동기)
-
이벤트 루프가 하나의 커다란 막대기를 작게 나뉘어진 막대기 로 바뀌었다
-
Promise만 넣어도 쪼개지지 않았을까 싶은데?
동기화 코드를 Promise로 전환한다고 해서 코드가 비동기화되지는 않습니다. 코드가 비동기적이 되려면 이벤트 루프에서 호출되어야 합니다. setImmediate는 콜백을 받아 정확하게 이를 수행합니다.- 뜨끔
// findNthPrimeAsync // await promiseCallback 를 호출하고 안에서 setImmediate 를 호출하는 형태 console.log('Calculating Async Prime...'); let asyncCount = 0; const asyncInterval = setInterval(() => { console.log('Async Event loop executed'); asyncCount++; }, 1); findNthPrimeAsync(4) .then(n => console.log('Async Prime is', n)) .then(() => clearInterval(asyncInterval)) .then(() => console.log('Intervals on event loop:', asyncCount)); // 실행 결과 (Sync) Calculating Sync Prime... Sync Prime is 7 Intervals on event loop: 0 // 실행 결과 (Async) Calculating Async Prime... Async Event loop executed Async Prime is 7 Intervals on event loop: 1- 위 로직이 실행되면 setInterval 은 호출되었으나 아직 자신의 차례가 오지 않아 로그가 남지 않고
- findNthPrimeAsync 가 호출되고 내부에서 await promiseCallback 이 호출되며
- 그 안에서 Promise 에 의해 비동기 요청되어 다음 루프로 넘어갔을때
- setInterval 콜백이 호출되고 이어서 promiseCallback 내부에서 요청된 setImmediate 콜백이 바로 매크로태스크에 즉시 등록되어 실행
-
Promise에 의해 다음 루프에 실행되도록 넘어갔기 때문에 setInterval 이 실행되는 것처럼
- 루프가 분리되어 다른 대기중이던 큐가 실행되었다
테스트를 해야겠죠?
- 테스트 코드가 뭐예요? 이걸요? 제가요?
- 테스트 작성이 어려운 코드는 무언가 복잡하니 개선할 필요가 있으며
- 테스트 코드도 프로덕트의 일부이다
- 모델도 테스트 하는 것일까
- 필요하다
- 그렇다면 이 모델에 대한 테스트가 어려운 것은 어떤 문제가 있는걸까
- 모델이 너무 크고 복잡하다는 것?
- 모델을 작게 분리하면 합쳐진 모델에 대한 테스트를 한번에 처리할 필요 없이
- 작은 모델에 대한 테스트만으로 충분할텐데 🤔
- 코드와 함께 살펴보는 프론트엔드 단위 테스트
- 테스트 코드에 대한 설명
- 구현 내용에 대한 것이 아닌 이 함수의 역할이나 기획 의도를 나타내도록
- 우선 작성해봐야하고
- 테스트 코드도 코드이니 유지보수, 개선되어야한다
- 테스트 코드에 대한 설명
- 단순히 커버리지가 의미 있는 테스트 인지 어떻게 검증할 수 있을까
- 어떤 의도를 테스트 하여 검증할 것인지 정리하고
- 검증하기 위해서는 어떤 환경이 필요한지
- 주어진 환경에서 예상되지 않은 방향으로 갈 수 있지는 않은지
콜스택 리액네 최적화 2025 문서
- Master JavaScript Performance in React Native (Part 1)
- Master JavaScript Performance in React Native (Part 2)
- 근데…
- 이거 2023 2024 2025 같은 내용 아닌가욥… 🙈
- Time to Interactive (TTI)
- 앱이 사용자 상호작용 하기 까지 걸리는 시간으로
- 단순 초기 로딩 속도만을 의미하는 것이 아닌
- 스크린 전환에도 걸릴 수 있는 시간
- Frames Per Second (FPS)
- 런타임 성능에 가장 큰 지표
- 스크롤, 스와이프, 버튼 처럼 사용자 상호작용이 일어났을때 얼마나 원활한지
- 보통 60 이상을 목표로 하는데
- FPS가 낮아지면 애니메이션이 끊기고 반응 속도가 느려져 사용자 경험이 악화된다
- 스레드
- RN 관련 JSI, Yoga, Hermes 가 UI 스레드 (메인)에서 초기화
- 그리고 JS 번들을 실행하고 JSI를 통해 메인 스레드와 통신하는 JS 스레드 초기화
- 터보 모듈을 초기화하는 네이티브 모듈 스레드
- Guide: How to Profile JS and React Code
- JS 스레드에서 실행되는 RN, JS 성능 관련 내용을 먼저 파악한다
- 대부분의 성능 문제는 이 곳에서 발생한다
- 리액트 컴포넌트가 리렌더 되는 상황
- 부모 컴포넌트가 리렌더 되었을때
- 상태가 변경되었을떄
- props가 변경되었을때
- context가 변경되었을때
- 강제 업데이트 되었을때
DevToolsProfiler> frame 그래프 를 통해 어디가 리렌더 되고 있는지 파악JavaScript Profiler> 실행된 JS 로직 중 실행 시간이 오래걸리는 것을 파악
- JS 스레드에서 실행되는 RN, JS 성능 관련 내용을 먼저 파악한다
- Guide: How to Measure JS FPS
- 초당 60 프레임을 그릴 수 있도록
- 60 fps 이면 충분하다고 생각하지만 120 fps 을 경험했던 사용자에게 60 fps 는 버벅임으로 느껴진다
- 어떡하죠 🥊
Perf Monitor로 현재 fps와 메모리 사용량을 확인할 수 있고- UI 스레드와 JS 스레드 각각의 지표를 비교하여
- 특정한 인터랙션에서의 fps 저하가 네이티브 인지 JS 인지 알 수 있다
- 성능 지표를 확인할때엔 dev 모드를 꺼야한다는 사실
- 추가로 flashlight를 이용해 앱 사용하는 동안 전체 평균 fps나 ram, cpu 확인
- Guide: How to Hunt JS Memory Leaks
- 메모리에 할당되었지만 GC 가 정리할 수 없어 발생하는 메모리 누수
- 분명 필요없어졌는데 정리되지 않아 계속 사용되고 있는 메모리
- 이벤트 리스너 등록되었으나 클린업 하지 않았을때
- 타이머 등록되었으나 클린업 하지 않았을때
- 클로저로 살아남은 커다란 데이터 🙈
DevTools > MemoryAllocation instrumentation on timeline를 이용해 누적 사용된 메모리에 여전히 살아있는 것을 누수로 판단Shallow size현재 객체가 보유한 메모리Retained size만약 객체가 삭제되어 해제될 메모리
- How FlatList is faster than a ScrollView
- 지나가다가 흥미를 느낀 타이틀에 잠시 멈추어
It contains a lot of heuristics and advanced JavaScript calculations to reduce the number of extraneous renderings that happen while you're displaying the data on screen and to make the scrolling experience always run at 60 FPS.FlatList internally uses a VirtualizedList, which implements "windowin"—a techn'que that only renders and mounts items currently visible in the viewport plus a small buffer. As you scroll, it dynamically unmounts items that move out of view and mounts new ones coming into view, maintaining a finite render window of active items. This significantly reduces memory usage and ensures smooth scrolling performance in most scenarios.- 역시 피땀눈물
- FlatList의 성능은 화면 밖에 있는 것을 그리지 않는 것으로부터 얻어내고 있으나
- 다음 스크롤이 얼마나 가능한지 미리 레이아웃을 계산해야만 하기에
- 같은 사이즈의 아이템을 그리는 경우에는 getItemLayout으로 금방 계산이 완료되지만
- 그렇지 않은 경우엔 기다려야하고 여기서 성능 문제가 발생할 수 있다
- ScrollView의 성능 대안으로 FlatList가 나왔으나
- 레이아웃을 모두 계산해야만 하기에 스크롤 했을때 아직 미처 계산 하지 못했거나 아직 그리지 못한 영역에 대하여 아무것도 보이지 않는 문제가 존재하고
- 아이템들을 메모리에 보관하여 재사용하기 때문에 메모리 문제로 이어진다
- JS 스레드에서의 연산 (레이아웃 계산) 이 많아 성능 문제로 이어진다며
- Shopify 의 FlashList를 대안으로 소개한다
FlashList에서 사용하는RecyclerListView는 메모리에 올라가 있는 뷰를 그대로 재사용하는 방식으로🥺- 같은 뷰를 계속 그리는 리스트라면 한번 메모리에 올라간 뷰를 계속 재사용하는 것이니 속도에 장점이 있다
- 메모리에 올라간 뷰를 재사용하는 컨셉을 ViewPager에 확장하여 사용해보고 싶었으나
- 얕은 지식으로 실패했다
estimateItemSize라는 속성이 FlashList의 재사용을 이끌어내기 위한 중요한 부분 같은데- 아이템 각각이 다른 사이즈를 가진다면 아무것도 재사용 할 수 없기에 장점이 사라지는건 아닐까
- flashlight 지표를 보면 FPS 자체가 엄청 차이나는 것은 아니나 다른 지표 자체가 높게 나오고 있어 비교해볼만하다
- 이름부터 레전드인
LegendList는 비교 영상을 보면 레전드인데?? 😯It is a drop-in replacement for FlatList and FlashList with better performance, especially when handling dynamically sized items.- (추가) 비교해보니 단순하게 사이즈가 동일한 경우에는 FlatList를 사용하는 것과 큰 차이가 느껴지지 않는다 😭
- Atomic state management
- 컨텍스트나 상태 드릴링 보다 아토믹 처리
- Presenting stale value while waiting for an update
- 상태 업데이트를 기다리지 않고 상호작용은 계속 이어질 수 있도록
- 단일 상태엔
useDeferredValue를 사용하여 UI 우선순위를 높게 두고 deferredValue 상태가 반영되기를 기다리는 것으로- 값 자체를 지연시킴
- 여러 상태가 포함된 경우에는
useTransition으로 UI 우선순위를 높게 둔 채 변경 요청된 상태가 완료될때에 UI 반영되도록- 상태 업데이트 자체를 지연시킴
- 둘 다 렌더링 성능에 우선순위를 둔 상황