React.js, Skeleton Component 구현
서론
React.js를 쓰다보면 비동기 네트워크 요청을 통해 데이터를 받아와 리렌더하는 과정에서
빈 화면이 나와 사용자에게 불편함을 줄 수 있습니다.
렌더링 되는 과정 가운데 뼈대가 되는 UI를 보여주면
어떠한 컨텐츠를 불러오는 중이구나 암시할 수 있어 사용자가 웹사이트를 이탈하는 것을 미연에 방지할 수 있습니다.
이러한 방법은 Youtube, Facebook 등 다양한 굴지의 기업에서 사용됨을 확인할 수 있습니다.
Skeleton을 사용하면 무조건 사용자에게 좋은 경험을 줄 수 있을까 생각해봅시다.
무조건 스켈레톤 화면을 보여주는게 사용자 경험에 도움이 될까요? | 카카오페이 기술 블로그
카카오페이에서 프론트엔드 개발을 하며 스켈레톤 UI와 사용자 경험 향상에 대해 고민한 내용을 공유합니다.
tech.kakaopay.com
요약하자면 요청시간이 짧은 것들은 오히려 스켈레톤 화면을 주는 것이 좋지 않을 수 있음으로
선택적으로 사용해야한다는 것입니다.
Skeleton Component를 만드는 법
import './PreBoxSkeleton.css';
import Shimmer from './Shimmer';
function PreBoxSkeleton() {
return (
<div className="pre-box-skeleton">
<div className="skeleton-img">
<Shimmer/>
</div>
<div className="skeleton-title">
<Shimmer/>
</div>
</div>
);
}
export default PreBoxSkeleton;
div container 하나가 img와 title을 감싸고 있는 형태의 간단한 JSX Component입니다.
(Shimmer 컴포넌트는 추후에 봅시다)
스켈레톤 코드의 핵심은 데이터를 로드해와 실제 보여줄 형태와 동일한 CSS Style을
사용자가 편안하게 볼 수 있도록 설정해두는 것입니다.
.pre-box-skeleton {
text-align: center;
background-color: darkgray;
border-radius: 5px;
padding: 10px;
width: 200px;
height: 112.5px;
margin: 5px;
}
.skeleton-img {
width: 200px;
height: 100px;
border-radius: 2px;
background-color: #ccc;
}
.skeleton-title {
font-size: 12px;
padding: 0px;
margin-bottom: 2px;
color: white;
background-color: #ccc;
}
다음과 같이 CSS를 내가 보여줄 이미지와 동일하게 설정하는 것이 포인트 입니다.
Skeleton Component와 실제 Component
실제 UI를 그리는 부분의 코드는 어떤식으로 작성해야 할까요?
일반적으로 React 훅 중 하나인 useState를 통해
컴포넌트의 상태를 관리하여 리렌더하는 과정을 거칩니다.
따라서 API 요청을 할 때 data값의 상태에 따라 그리는 컴포넌트를 설정하면 됩니다.
다음 코드와 같이 삼항 연산자를 통해 간단하게 나타낼 수 있습니다.
import { useState, useEffect } from "react";
import axios from "axios";
import PreBox from './Box/preBox';
import PreBoxSkeleton from './ui/Skeleton/preBoxSkeleton';
function Main() {
const [data, setData] = useState([]);
// API data 요청
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('http://localhost:3000/api/data');
const timer = setTimeout(() => {
setData(response.data);
}, 3000);
} catch (error) {
console.error('데이터를 불러오는 중 오류가 발생했습니다:', error);
}
};
fetchData();
}, []);
return (
<main>
{data.length > 0 ? <PreBox/> : <PreBoxSkeleton/>}
</main>
);
}
PreBox에는 요청받은 data를 props 값으로 넘겨주면 되겠죠! 절대 귀찮아서 그런거 아닙니다
API 요청 전에는 data값이 빈배열로 초기화 되어 있기 때문에 PreBoxSkeleton을 보여주지만
data값이 하나 이상들어오게되면 PreBox를 리렌더하는 것을 확인할 수 있습니다.
Shimmer
Shimmer란 로딩 중 사용되는 시각적 효과입니다.
Skeleton Component를 그냥 보여주는 것이 아니라
애니메이션 혹은 다른 기술들을 한 스푼 첨가해
사용자가 시각적으로 지루한 경험을 하지 않도록 만드는 것입니다.
CSS를 통해 간단한 애니메이션을 통해 Shimmer Component를 만들어봅시다.
import './Shimmer.css';
const Shimmer = () => {
return (
<div className="shimmer-wrapper">
<div className="shimmer"></div>
</div>
);
};
export default Shimmer;
Shimmer의 JSX구성은 간단합니다.
Shimmer를 덮는 wrapper와 Shimmer 두 가지로 구성되어 있습니다.
.shimmer-wrapper {
position : relative;
top: 0;
left : 0;
width : 100%;
height : 100%;
animation: loading 2.5s infinite;
overflow: hidden;
}
.shimmer {
position: absolute;
width : 20%;
height: 100%;
background: linear-gradient(to right, transparent 0%, rgba(255,255,255,0.2) 50%, transparent 100%);
box-shadow: 0 0 30px 30px rgba(255,255,255,0.1);
}
@keyframes loading {
0% {
transform: translateX(0%);
}
30% {
transform: translateX(50%);
}
100% {
transform: translateX(100%);
}
}
CSS의 keyframe을 이용해 Skeleton div안에 Shimmer Component를 넣음으로
그림자가 들어간 막대바가 2.5초 간격으로 무한히 옮겨가는 간단한 Shimmer를 만들었습니다.
결론
1. 기존 UI의 형태를 그대로 베낀 Skeleton UI를 만든다
2. useState 훅을 이용하여 삼항연산자 혹은 조건문을 이용해 data 상태에 따라 그리는 컴포넌트를 달리 설정한다.
3. Shimmer를 통해 Skeleton Component를 동적으로 보여준다.
React Framework 특성상 렌더시간이 길어지다 보면
사용자에게 좋지 못한 경험을 줄 때가 많습니다.
Skeleton Component와 Shimmer를 사용하면 사용자의 인내심을 길러줄 수 있습니다
보다 오래 사용자들이 웹사이트에 머물게 할 수 있지 않나 싶습니다.
그렇다고 남용하면 독이 될 수 있으니 주의하자
Generic Shimmer Loading Skeletons in SwiftUI
Apps often require separate loading, loaded, empty and error views. With SwiftUI we can solve this problem generically and give any view…
joshhomann.medium.com