Open minsour opened 4 years ago
글이 가독성이 너무 좋아서 술술 읽혔습니다 !! FE 성능에 대하여 공부해본 적이 없었는데, 덕분에 공부할 부분이 하나 추가되었습니다 ㅎㅎ 참고 자료로 주신 링크도 보니 너무 좋은 자료들이네요. 기회가 됐을 때 저도 참고해서 성능 분석을 해보고 싶습니다. 감사합니다 😊
오 성능 분석 신기하네요 수고하셨습니다. 👍👍👍
글을 읽으면서 궁금한게 생겼습니다.
이미지는 나중에 받아온다
초기 로딩 시 불필요한
둘 중 무엇인가요????? 저는 둘 다 인것 같은데 섞여있는것 같아서 음 조금 헷갈립니다.
@jominjimail
우선 초기 로딩 시 불필요한 이미지는 나중에 받아온다는 말은 2번입니다!
GET /products 로 30개의 상품 정보를 받아왔다고 했을 때, 그 30개의 상품 정보에 각각 image url 이 있을텐데요. 그 image url 은 보통 Storage 의 역할을 하는 서버에서 이미지를 저장하고 있는 경로에 해당하는 url 입니다. (AWS를 예를 들면 흔히 사용하는 S3가 되겠네요)
그럼 클라이언트에서 애플리케이션 서버로 상품 정보 30개를 요청해서 받아온 후, 상품 30개의 이미지를 띄우기 위해 이미지를 저장하고 있는 서버에 30번의 요청을 더 하게 되는 것이죠.
그래서 그 30번의 요청 중 화면에 보이지 않는 이미지에 대한 요청들은 뒤로 미뤄서 클라이언트의 (미세한)초기 로딩 속도와 스토리지 서버의 오버헤드를 줄일 수 있지 않을까? 하는 게 Lazy Loading에 대한 제 생각이구요!
그리고 말씀해주신 1번은 제가 적용은 안해놨는데, 적용을 한다면 유저한테 좀 더 좋은 UX를 제공할 수 있는 방법이라서 같이 구현해줘도 좋을 방법같습니다 ㅎㅎ
아 제가 무엇을 헷갈리는지 알았어요. 제가 구현했을 때는 화면에 보일 10개의 이미지를 요청하고 스크롤 내리면 추가로 10개의 이미지를 fetch 한다. --> 인데,
이제는 30개를 한 번에 fetch하고 화면에 보일 10개만 급하고 나머지 20개는 급하지 않으니깐 급하지 않은 건 뒤로 미룬다!! 이 말씀이군요.
생각해보니 fetch를 작은 단위로 하는 건 개선되어야 할 문제이구 민수님이 그 해결책을 주신 것 같네요. 감사합니다!👏
LAI (에라이?)
LAI (Loading And Interaction)는 FE 성능 분석을 할 때 주요 관심사입니다.
서비스에서 많이 사용되거나 사용자에게 가치 있는 화면이라면 LAI를 개선시킬 필요가 있습니다.
이번 이슈에서는 React로 구현한 리스트 컴포넌트에 Lazy Loading을 적용하면서 LAI(Loading And Interaction) 를 개선한 내용을 정리해 보려고 합니다.
1. 초기 로딩 속도(Loading) 개선
초기 로딩 속도를 개선하기 위해 Lazy Loading이라고 흔히 부르는 방식을 적용하였습니다.
Lazy Loading은 뷰 포트 바깥에 있는 이미지(초기 로딩 시 불필요한 자원)은 뒤로(Lazy) 미루도록 하는 방식입니다.
Lazy Loading은 Intersection Observer API 를 사용하여 구현하였습니다.
useLazyLoadingIO
IntersectionObserverList
로딩 속도 측정 및 분석을 위해 Waterfall 차트를 활용하였습니다.
Lazy Loading 적용 전
Lazy Loading 적용 후
Lazy Loading으로 Waterfall 차트의 높이(Request 수)를 줄여서 초기 로딩 시 불필요한 이미지는 나중에 받아오도록 함으로써, 브라우저의 초기 로딩 속도를 높이고 이미지를 응답해주는 서버의 오버헤드도 줄였습니다.
2. 인터렉션 속도(Interaction) 개선
인터렉션 속도를 개선하기 위해서는 기본적으로 Main Tread 에서 DOM 조작을 조심해야 합니다.
JavaScript가 DOM 을 건들면 Main Tread 에 의해 Rendering Pipeline이 동작하기 때문입니다.
Browser Rendering Process
Rendering Pipeline
Reflow, Repaint
생성된 DOM 노드의 Layout 수치(너비, 높이, 위치 등) 변경 시 영향 받은 모든 노드의(자신, 자식, 부모, 조상(결국 모든 노드) ) 수치를 다시 계산하여(Recalculate), 렌더 트리를 재생성하는 과정을 Reflow라고 하며, Reflow 과정이 끝난 후 재 생성된 렌더 트리를 다시 그리게 되는데 이 과정을 Repaint 라 합니다.
Reflow 가 트리거되는 경우
Intersection Observer API
MDN에서 위와 같이 설명하고 있습니다. 저는 이에 대해서 element.getBoundingClientRect가 scroll 이벤트에서 사용될 경우 Main Thread 가 과부하 될 수 있다는 의미로 이해했습니다.
그래서 저는 Intersection Observer API 를 사용하여 Lazy Loading을 구현한 것입니다.
성능 분석을 위해 Intersection Observer API를 사용하여 Lazy Loading 을 구현한 것과 scroll 이벤트에서 getBoundingClientRect를 호출하여 구현한 것을 비교해 보겠습니다.
scroll 이벤트에서 getBoundingClientRect를 호출하여 구현
각각의 ProductItem에 image DOM의 위치를 파악하는 행위를 하는 onScroll 함수를 scroll event를 바인딩하는 방식입니다.
Intersection Observer API 로 구현
제가 구현한 방식입니다. 위에 useLazyLoadingIO와 IntersectionObserverList를 참고해주세요.
크롬 개발자 도구의 Performance 탭을 통해 확인해보면 getBoundingClientRect()호출하는 과정에서 Recalculate Style 이 발생하는 것을 확인할 수 있으며, IntersectionObserver API를 사용하여 구현했을 때는 스크롤 하는 과정에서 Recalculate Style이 발생하지 않는 것을 확인할 수 있습니다.
소감
사실 처음 작성해보는 성능 리포트였습니다! 다 작성하고 보니 유의미한 성능 비교가 이루어진건가? 하는 의문이 남기도 하네요..ㅎㅎ 그래도 뭐라도 측정해보면서 한 층 더 성장한 것 같아 뿌듯하네요 👍 부족해 보이는 부분이 있으면 언제든지 피드백 주십쇼! 😀
Reference
http://sculove.github.io/blog/2019/04/11/fromTodyIamPA/ http://blog.hyeyoonjung.com/2019/01/09/intersectionobserver-tutorial/