문제점
백엔드와 통신까지 잘 연결했기에 더 이상의 난관은 없을 거라 생각했지만, 사진이 늦게 렌더링 되면서 사용자 경험이 저하되는 문제가 생겼다.
대부분 페이지가 마운트 될 때, 빈 화면이었다가 갑자기 배경이미지가 뜨는 현상이 일어났다. 예쁘게 다 완성해 놨는데, 사진 뜨는 속도가 느려서 완성도가 확 떨어진다는 게 너무 속상하고 스트레스였다.
물론 이렇게 한번 깜빡거리면 그 뒤로는 안 그랬지만, 그래도 너무 신경 쓰이는,,,
해결 시도 1)
이번 프로젝트에서는 이미지를 클라이언트 폴더 내부에 저장했기에, 용량이 너무 커서 그런가 싶기도 했다.
Squoosh
Simple Open your image, inspect the differences, then save instantly. Feeling adventurous? Adjust the settings for even smaller files.
squoosh.app
그래서 위의 사이트를 활용해서 이미지의 용량을 줄였다.
전체 이미지의 크기만 41.4MB였는데 6.9MB까지 줄였다. 하지만 변화는 없었다. 아무리 배경 이미지 크기를 KB 단위까지 줄여봐도 깜빡거리는 문제는 사라지지 않았다.
해결 시도 2)
lazy, suspense 이용
리액트 lazy는 컴포넌트를 비동기적으로 렌더링 해주고, suspense는 컴포넌트가 렌더링 되는 동안 로딩창을 띄워줄 수 있게 해주는 리액트 훅이다. 컴포넌트가 렌더링 될 때까지 로딩창을 띄우면 좀 낫지 않을까 해서 적용해 봤다.
하지만! 해결되지 않았다..
문제점은 컴포넌트가 마운트 된 ‘이후’ 사진이 느리게 뜨는 것이다. 하지만 lazy와 suspense는 컴포넌트가 마운트 될 때까지 지연시켜 주는 친구들이었기에 큰 의미가 없었다.
결국은 컴포넌트 마운트 이후에 사진이 나올 때까지 다른 화면을 띄워야 깜빡이지 않을 것... 다른 해결책을 찾아야 했다.
해결 ) useLayoutEffect(이미지 프리로딩)
useLayoutEffect는 브라우저가 화면에 DOM을 그리기 전에 effect를 수행하는 훅이다.
즉 리액트가 화면을 그리기 전에 먼저 수행되는 친구이다. 이 친구를 이용해서 이미지를 미리 받으면 될 것 같았다.
이미지 프리로딩을 위한 Hook을 생성했다.
import { useState, useLayoutEffect } from 'react';
const useImagePreload = (srcList) => {
const [loaded, setLoaded] = useState(false);
useLayoutEffect (() => {
let urls = srcList;
if (!Array.isArray(srcList)) {
urls = [srcList];
}
let loadedCount = 0;
const loadImage = (src) => {
const image = new Image();
image.src = src;
const handleLoad = () => {
loadedCount++;
if (loadedCount === urls.length) {
setLoaded(true);
}
};
image.addEventListener('load', handleLoad);
return () => {
image.removeEventListener('load', handleLoad);
};
};
urls.forEach((src) => {
loadImage(src);
});
}, [srcList]);
return loaded;
};
export default useImagePreload;
훅의 매개변수로 이미지 경로를 넣으면 useLayoutEffect가 img 객체의 src 속성에 매개변수(경로)를 할당시켜서 이미지를 미리 로드해 준다. 그리고 모든 이미지가 로드되면 loaded state를 true로 변경하고 반환한다.
이 훅을 통해 이미지 로드 여부를 확인할 수 있는 것!
1. useImagePreload 훅을 import 한다.
2. 프리로딩 할 사진(NotFoundImage)도 import 한다.
3. useImagePreload 훅에 NotFound(경로)를 전달하고 반환 값을 변수 loaded에 저장한다.
4. 삼항 연산자를 통해 이미지가 반환 중(false) 일 때는 로딩스피너를 띄우고, 이미지 반환이 끝났을 때(true) 기존의 코드를 띄운다.
console.log로 loaded를 출력해 보면, 초기에는 false가 나오다가, 이미지 로드 이후 다시 true를 반환하는 것을 확인할 수 있다. 이제 화면을 확인해 보면..!
마운트 된 이후에 배경이미지가 로드되기 전까지 로딩스피너가 나오는 것을 확인할 수 있다.
기존에 사용자는 이미지가 로드될 때까지 빈화면을 보고 있었다. 하지만 이제는 빈화면이 아니라 로딩 스피너를 보게 될 것이다. 사용자 경험측면에서는 훨씬 나을 것이라고 생각한다..
모바일에서는 PC화면에 비해 훨씬 느리게 보이기에... 정말 훨씬 훨씬 훨씬 훨씬 낫다.. 적어도 빈화면보다는 로딩화면이 낫기에..
그럼에도 불구하고
1. css의 background 속성으로 나타내는 이미지는 어떻게 적용해야 하는지..?
2. 배포 직전 사용자 경험을 급하게 올리기 위해 이 방법을 사용했지만.. 코드가 너무 더러워졌다. 이렇게 쓰는게 맞나?
3. 네이버 같은 사이트는 그 수많은 사진을 어떻게 불편함 없이 띄울 수 있는가....
4. 여전히 자잘한 깜빡거림이 있긴하다.. 왜 그런거야!
'프로젝트 > 학수고대 프로젝트' 카테고리의 다른 글
암호화 및 복호화로 경로 숨기기 (0) | 2024.04.07 |
---|---|
position을 지양하..지는 말고 생각 좀 하고 쓰자 (0) | 2024.04.03 |
TanStackQuery는 try-catch를 안써도 되나?! (2) | 2024.03.24 |
Zustand 사용기 (2) | 2024.03.17 |
함수형 컴포넌트 onClick 실행안되는 원인 (0) | 2024.03.11 |