🌙 이 블로그는 다크모드에서 코드 블록의 가독성이 더욱 향상됩니다. 화면 우측 하단의 달 모양 아이콘을 클릭하여 다크모드로 전환하시면 보다 쾌적한 읽기 환경을 제공받으실 수 있습니다.
- 현재 상황
- 모든 페이지 화면 및 기능 구현 -> mock 데이터로 구현
- 다음 과제
- api 연동 -> React-query 사용해 볼 예정
- 테스트
- 배포
마이페이지 성능 측정하기
초기 측정 결과 : 48점
- 초기 점수 : 48점
- 측정 도구 : LighHouse
- 성능을 많이 신경 못써서 점수가 낮게 나올거라고 생각하긴 했지만 48점은 충격적이네요. 빨간색이라 더 충격적..
- 간단하게 최적화 할 수 있는 방법으로 점수를 조금 올려보겠습니다
React.Memo, useCallback
- React.Memo :
- React에서 제공하는 고차 컴포넌트로 성능 최적화를 위해 사용된다. 부모 컴포넌트가 리렌더링 될 때 자동으로 자식 컴포넌트들도 리렌더링 된다. 이때 불필요한 리렌더링이 발생하여 성능에 부정적인 영향을 미칠 수 있다. React.Memo를 사용하면 부모로부터 받은 props가 변경되지 않은 한 컴포넌트가 다시 렌더링되지 않는다.
- 이 페이지에서 리렌더링이 많이 발생할 것 같은 RecipeCard 컴포넌트에 React.Memo를 감싸주겠습니다
- useCallback()
- useCallback 함수는 함수형 컴포넌트 내에서 성능 최적화를 위해 사용된다. 이 함수는 메모이제이션 된 콜백함수를 생성하여 불필요한 함수 생성을 방지하고, 렌더링 시에 함수가 새로 생성되는 것을 막는다. 일반적으로 React 컴포넌트에서 함수를 선언하면, 해당 함수는 매 렌더링 시마다 새로 생성된다. useCallback 함수를 사용하면 함수를 메모이제이션하여, 해당 함수의 의존성이 변경될 때만 새로운 함수를 생성한다.
- 이 페이지에서 리렌더링이 많이 발생하여 재생성되는 함수에 useCallback을 감싸주겠습니다.
2차 측정 결과
- 4점 올랐다 ....
- FCP는 줄은거 보니 렌더링 속도는 확실히 개선된 거 같네요 (1.1s →0.5s)
- 어떤 부분을 개선해야되는지 LightHouse 성능 지표를 분석해 봅시다!
LightHouse 성능 지표 분석
First Contentful Paint(FCP)
- FCP는 페이지가 로드될 때 브라우저가 DOM 콘텐츠의 첫 번째 부분을 렌더링 하는 데 걸리는 시간에 관한 지표이다. 위 결과에서 페이지에 진입하여 첫 콘텐츠가 뜨기까지 0.5초가 걸렸음을 알 수 있다. FCP는 총점을 계산할 때 10%의 가중치를 갖는다
- 초록색이 뜬 거 보니 비교적 양호한 편인 거 같다
Largest Contentful Paint(LCP)
- LCP는 페이지가 로드될 때 화면 내에 있는 가장 큰 이미지나 텍스트 요소가 렌더링 되기까지 걸리는 시간을 나타내는 지표이다. 위 결과에서 페이지에 진입하여 가장 큰 콘텐츠가 뜨기까지 3.9초가 걸렸음을 알 수 있다. LCP는 총점을 계산할 때, 25%의 가중치를 갖는다.
- 아무래도 레시피 카드에 있는 이미지의 렌더링이 오래 걸리는 거 같다.
Total Blocking Time(TBT)
- TBT는 페이지가 클릭, 키보드 입력 등의 사용자 입력에 응답하지 않도록 차단된 시간을 총합한 지표이다. 측정은 FCP와 TTI(Time to Interactive) 사이의 시간 동안 일어나며 메인 스레드를 독점하여 다른 동작을 방해하는 작업에 걸린 시간을 총합합니다. TBT는 총점을 계산할 때 30% 가중치를 갖습니다.
Cumulative Layout Shift(CLS)
- CLS는 페이지 로드 과정에서 발생하는 예기치 못한 레이아웃 이동을 측정한 지표이다. 레이아웃 이동이란 화면상에서 요소의 위치나 크기가 순간적으로 변하는 것을 말한다. CLS는 총점을 계산할 때 15%의 가중치를 갖는다.
LCP 개선하기 - Properly size images
이미지 사이즈 줄이기
- Properly size images 항목은 이미지를 적절한 사이즈로 사용하도록 제안합니다. 검사 결과에서 제안하는 방법대로 이미지를 적절한 사이즈로 변경하면 용량을 이미지당 대략 200 KiB정도 줄일 수 있습니다.
- 대략 200 KiB면 이미지 로드에 소용되는 시간을 대략 1.65s 단축할 수 있다고 해요. 실제로 이미지의 크기가 어느 정도 되는지 직접 확인해 봅시다
- 실제 이미지 사이즈(Intrinsic size)가 1465x695 px인데 화면에 그려지는 이미지의 사이즈(Rendered size)는 150x188 px입니다. 어차피 큰 사이즈의 이미지를 사용해도 저 크기만큼 표시하지 않을 거니, 처음부터 150x188 px에 맞는 이미지를 사용하는 게 좋을 것 같습니다. 그러면 어느 정도 사이즈로 만들어서 사용해야 할까요?
- 요즘 디스플레이는 같은 공간에 그려낼 수 있는 픽셀이 많아서 너비 기준으로 두 배정도 큰 이미지를 사용하는 것이 적절하다고 합니다. 즉 실제 이미지의 크기를 300x378 px로 줄이면 최적화할 수 있습니다. 그런데 어떻게 이미지 사이즈를 줄일 수 있을까요
- 일단 이미지가 어디서 오는지부터 파악합시다
- 자체적으로 가지고 있는 정적 이미지라면 사진 편집 툴을 이용하여 직접 이미지 사이즈를 조절하면 되는데, 이렇게 API를 통해 받아오는 경우에는 어떻게 이미지 사이즈를 조절해야 할까요
🎈이미지 CDN
- CDN(Content Delivery Network)
- 물리적 거리의 한계를 극복하기 위해 소비자(사용자)와 가까운 곳에 콘텐츠 서버를 두는 기술을 의미한다. 한국에 있는 사용자가 미국에 있는 서버에서 이미지를 다운로드하는 경우, 아무리 요즘 인터넷이 빨라졌다고 해도 서버와 사용자 사이에는 굉장히 큰 물리적 거리가 있기 때문에 다운로드에 시간이 오래 걸릴 것이다. 미국에 있는 서버를 미리 한국으로 복사해 두고, 사용자가 이미지를 다운로드하려고 할 때 미국 서버가 아닌 한국 서버에서 다운로드하도록 하는 것이다. 그러면 물리적 거리가 어느 정도 해소가 됐으니 다운로드에 걸리는 시간도 단축될 것이다. 이것이 일반적인 CDN의 개념이다.
- 이미지 CDN
- 이미지에 특화된 CDN이라고 볼 수 있다. 이미지 CDN에서는 이미지의 크기도 특정 형태로 가공하여 전해주는 기능도 있다. 이미지 CDN 서버의 주소에 쿼리스트링으로 가져올 이미지의 주소 또는 이름을 입력해 주고 필요에 따라 변경하고 하즞 형태를 명시해 주는 식이다.
- 이미지 서버
- 이미지 리소스에 특화된 서버로, 이미지 파일을 젖아하고 관리하며, 클라이언트에게 이미지를 제공한다. 이를 통해 이미지 서버는 전용된 리소스를 관리하고 웹 서버의 부하를 줄일 수 있다.
- 보통은 이미지 서버에서 이미지를 리사이징을 해서 클라이언트에게 보내준다고 한다
현재 외부 웹사이트에서 사용하는 이미지 url 가지고 img태그의 src속성을 통해서 이미지를 보여주고 있기 때문에 해당 서버에서 리사이징 기능을 지원하고 있는지 모른다. 즉, 서버에서 이미지 리사이징해서 불러오는 방법은 못한다는 거다. 그래도 포기하지 말자.. 서버에서 못하면 클라이언트에서 해보면 된다!
🎈 클라이언트 측에서 이미지 리사이징 하기
- browser-image-compression
- 브라우저에서 이미지를 압축하는 데 사용되는 JavaScript 라이브러리이다.
- 이미지 리사이징 라이브러리 중 사용자가 가장 많았다. (npm trends 비교)
- 타 라이브러리와 비교하여 가장 최근에 업데이트가 이루어졌다.
- 그러나 간헐적으로 사진이 회전, 축소된다는 이슈가 있다
- react-image-file-resizer
- react 애플리케이션에서 이미지 파일을 리사이징 하고 압축하는 데 사용되는 라이브러리이다.
- 위 라이브러리 다음으로 사용자가 많은 라이브러리이다
그러나 위 라이브러리들은 파일 객체의 정보를 가지고 조작하는 방식이다. 외부 웹사이트에서 사용하고 있는 이미지를 리사이징 하고 싶다.. 그러면 직접 외부 웹사이트의 이미지 리소스를 요청해서 리사이징 해보자!
🎈 CORS(Cross-Origin Resource Sharing) 에러
- CORS에러는 웹 애플리케이션에서 보안상의 이유로 다른 출처로부터 리소스에 대한 접근을 제한하는 보안 정책이다. 이를 통해 사용자의 개인정보와 데이터를 보호하고 웹 애플리케이션의 보안을 강화할 수 있다. 일반적으로 브라우저는 다음과 같은 상황에서 CORS 정책을 적용한다.
- 동일 출처 정책(Same-Origin Policy) : 기본적으로 브라우저는 동일한 출처(프로토콜, 호스트, 포트가 모두 같은 URL)에서만 리소스에 대한 접근을 허용한다. 즉 스크립트가 로드된 출처와 리소스를 요청하는 출처가 동일해한다.
- 크로스 출처 요청(Cross-Origin Request) : 브라우저는 보안 상의 이유로 다른 출처로의 리소스 요청을 차단한다.
- CORS 에러는 다음과 같은 상황에서 발생할 수 있다
- 웹 애플리케이션에서 다른 도메인으로의 AJAX 요청을 보내는 경우
- 서버에서 출처가 다른 리소스에 대한 요청을 허용하지 않는 경우
- 요청 헤더에 CORS 헤더가 없거나 잘못된 경우
어림없다. 바로 CORS 에러로 막혀버렸다. 그런데 여기서 궁금한 점이 생겼다. 그러면 <img> 태그의 src 속성은 어떻게 이미지를 응답받아와서 보여주는 걸까?
이미지 태그의 이미지 경로는 브라우저가 CORS 에러를 우회해서 가져오는 거라고 한다. 애초에 외부 사이트에서 이미지 리소스를 마음대로 리사이징 해서 사용하려고 했던 것부터가 잘못된 거 같다
결론 : 이미지 리사이징 못함.
LightHouse에서 측정한 결과로 이미지 리사이징만으로 렌더링 속도를 크게 줄일 수 있다고 나와서 이미지 리사이징을 못한다는 게 너무 아쉽다. 그래도 포기하지 말자 방법이 있지 않을까..
🎈 크기가 작은 이미지 불러오기
레시피 상세 페이지에서 데이터들을 스크랩 핑하면서 레시피의 썸네일로 상세 페이지에 있는 썸네일 이미지로 저장했었다. 스크랩핑은 페이지단위로 할 수 있기 때문에 상세 페이지에 있는 데이터로만 저장해서 쓰기 위해 썸네일 이미지 크기가 크지만 저장해서 사용하려고 했다. 그런데 레시피 목록 페이지에서 썸네일에서 사용하고 있는 이미지와 동일한데 사이즈만 작은 이미지를 사용하고 있었다. 이 이미지를 보고 바로 이거다..! 따로 저장하기 조금 번거롭지만 성능을 많이 최적화할 수 있기 때문에 바로 실행에 옮긴다.
3차 측정 결과
아직 멀었다.. LCP를 초록색까지는 아니어도 주황색까지 만들어보도록 하자...
'🤸♀️ 개발 회고 > Minicook' 카테고리의 다른 글
사이드 프로젝트 15편 | 검색창 Debounce (성능 개선, api요청 전) (0) | 2024.03.20 |
---|---|
사이드 프로젝트 14편 | LCP 성능 최적화 -2 (bundle size 줄이기, react-icons, cra-bundle-analyzer ) (0) | 2024.03.20 |
사이드 프로젝트 11편 | 헤더 (라우터, 웹 브라우저 캐싱, redux, localStorage) (0) | 2024.03.08 |
사이드 프로젝트 10편 | 로그인 & 회원가입 페이지 (서버 구축, Koa, MongoDB, api 명세서) (0) | 2024.03.08 |
사이드 프로젝트 9편 | 레시피 상세 페이지 (조건부 스타일링, props error 해결) (0) | 2024.03.08 |