React에서 스켈레톤 스크린 구현하기

게시 됨: 2022-03-10
빠른 요약 ↬ 이 튜토리얼에서는 스켈레톤 화면 UI가 무엇인지, 몇 가지 유형의 스켈레톤 화면 라이브러리와 장단점을 배웁니다. React Loading Skeleton을 사용하여 YouTube와 같은 스켈레톤 화면 UI를 빌드합니다. 그런 다음 선택한 스켈레톤 화면 React 패키지로 직접 실험할 수 있습니다.

스피너와 로더는 전통적으로 콘텐츠를 로드하는 데 시간이 걸릴 것이라고 사용자에게 알리는 방법이었습니다. 이 접근 방식은 훌륭하지만 현대 개발에서는 빠르게 쓸모 없게 되어 가고 있습니다. 스켈레톤 스크린은 대기 시간보다 진행 상황에 중점을 두어 로딩 시간 좌절을 줄이기 때문에 기존 로더를 완벽하게 대체하고 있습니다.

이 기사에서는 CSS React 또는 JavaScript 구문의 기본 사항을 다루지 않으므로 따라하기 위해 이러한 언어 중 하나에 대한 전문가가 될 필요는 없습니다.

로더와 스켈레톤 화면 UI의 차이점
로더와 스켈레톤 화면 UI의 차이점(큰 미리보기)

UI 및 UX 전문가는 사용자가 페이지에 콘텐츠가 로드되기를 기다리는 동안 사용자의 참여를 유지해야 한다고 가르칩니다.

콘텐츠가 로드되기 전에 스피너를 사용하여 사용자의 참여를 유도하는 아이디어는 훌륭합니다. 그러나 대부분의 사용자는 마치 시계처럼 움직이는 더미 애니메이션 스피너를 보고 지루해하기 때문에 결과가 이상적이지 않을 수 있습니다. Luke Wroblewski는 이에 대해 자세히 설명합니다.

스켈레톤 화면은 로딩 시간을 줄여 더 나은 사용자 경험을 제공합니다. 대기 시간 대신 진행 상황에 집중함으로써 정보가 화면에 점진적으로 표시될 것이라는 환상을 사용자에게 만듭니다. Bill Chung은 그의 연구에서 이것을 확인합니다.

스켈레톤 스크린이란 무엇입니까?

스켈레톤 화면은 실제 콘텐츠가 포함되지 않은 UI 버전입니다. 대신 로드되고 사용할 수 있게 될 때(즉, 네트워크 대기 시간이 허용되는 경우) 실제 콘텐츠와 유사한 모양으로 요소를 표시하여 페이지의 레이아웃을 모방합니다.

스켈레톤 스크린은 본질적으로 페이지의 와이어프레임이며 텍스트와 이미지를 위한 자리 표시자 상자가 있습니다.

점프 후 더! 아래에서 계속 읽기 ↓

스켈레톤 스크린의 독특한 점은 무엇입니까?

스켈레톤 UI는 페이지의 실제 UI와 유사하므로 사용자는 콘텐츠가 표시되기 전에도 웹 또는 모바일 앱이 얼마나 빨리 로드되는지 이해할 수 있습니다. 다음 프로젝트에서 스켈레톤 스크린 사용을 고려해야 하는 몇 가지 이유는 다음과 같습니다.

  • 스켈레톤 화면을 사용하면 페이지 레이아웃을 모방하는 것이 더 쉽습니다.
  • 콘텐츠가 점진적으로 로드됩니다(한 번에 모두 로드되지 않음).

스켈레톤 스크린은 다음이라고도 합니다.

  • 유령 요소,
  • 콘텐츠 자리 표시자,
  • 콘텐츠 로더.

Blockchain.com, YouTube, Facebook, Medium 및 기타 대형 기술 회사는 UX를 향상시키기 위해 콘텐츠가 로드되는 동안 골격 화면을 표시합니다.

블록체인닷컴

Blockchain.com 스켈레톤 화면 UI
Blockchain.com의 부분적으로 로드된 상태(그래프 분석에서 골격이 어떻게 사용되는지 확인)(큰 미리보기)

중간

중간 스켈레톤 화면 UI
미디엄의 스켈레톤 UI(큰 미리보기)

링크드인

LinkedIn 스켈레톤 화면 UI
2018년 LinkedIn의 홈 피드 로딩 상태(큰 미리보기)

스켈레톤 스크린의 종류

다양한 종류의 스켈레톤 스크린이 있습니다. 주요 기능은 텍스트 자리 표시자와 이미지(또는 색상) 자리 표시자입니다.

대부분의 개발자는 빌드하기 쉽고 개발자가 실제 콘텐츠의 내용에 대한 세부 정보를 요구하지 않기 때문에 페이지의 골격 UI로 텍스트 자리 표시자를 사용하는 것을 선호합니다. 대신 스켈레톤이 UI를 모방합니다.

색상 자리 표시자는 콘텐츠에 대한 세부 정보가 필요하기 때문에 만들기가 더 어렵습니다.

일부 인기 있는 패키지를 사용하면 웹 앱에서 스켈레톤 화면을 더 쉽게 구현할 수 있습니다. 두 가지 모두에 대해 자세히 살펴보겠습니다.

  • 반응 자리 표시자
  • React 로딩 스켈레톤

애플리케이션에 사용할 패키지를 고려하기 전에 각 패키지의 장단점을 살펴보겠습니다.

반응 자리 표시자

장점

  • 자리 표시자 구성 요소는 사용자 정의 스켈레톤 UI를 만드는 데 사용됩니다.
  • 펄스 애니메이션(예: 요소에 대한 모션 효과)이 지원됩니다.
  • 구성 요소 기반 API와 함께 제공됩니다.

단점

  • 뼈대 구성 요소는 별도로 유지 관리되므로 구성 요소의 스타일을 업데이트하려면 뼈대 구성 요소도 업데이트해야 할 수 있습니다.
  • 다양한 요구에 대한 여러 구성 요소가 있기 때문에 학습 곡선은 선형이 아닙니다.

다음은 react-placeholder 패키지를 사용하는 스켈레톤 구성 요소의 예입니다.

 import { TextBlock, RectShape } from 'react-placeholder/lib/placeholders'; import ReactPlaceholder from 'react-placeholder'; const GhostPlaceholder = () => ( <div className='my-placeholder'> <RectShape color='gray' style={{width: 25, height: 70}} /> <TextBlock rows={6} color='blue'/> </div> ); <ReactPlaceholder ready={ready} customPlaceholder={<GhostPlaceholder />}> <MyComponent /> </ReactPlaceholder>

react-placeholder/lib/placeholder 에서 TextBlockRectShape 가져오기 및 react react-placeholder placeholder 에서 GhostPlaceholder 가져오기, ReactPlaceholder 라는 기능 구성 요소를 만들었습니다. GhostPlaceholder 에는 div가 있고 div 내부에서 직사각형의 크기를 설명하고 색상 값을 전달하고 직사각형의 스타일을 정의하는 RectShape 구성 요소를 사용했습니다.

다음으로 TextBlock 구성 요소를 사용하여 행과 색상 값을 설정했습니다. TextBlock 구성 요소는 행 수와 텍스트 색상을 정의합니다.

MyComponentReactPlaceholder 구성 요소의 자식으로 전달합니다. 이 구성 요소는 ready 를 수신하고 GhostPlaceholder 구성 요소는 readycustomPlaceholder 소품에 대한 값으로 전달합니다.

MyComponent 는 스켈레톤 화면 UI가 표시될 때 로드됩니다.

자세한 내용은 설명서를 확인하십시오.

React 로딩 스켈레톤

장점

  • API 기반이며 모든 사용자 정의를 위한 소품이 있는 하나의 구성 요소가 있습니다.
  • 별도의 스켈레톤 구성 요소로 사용할 수 있으며 모든 구성 요소 내부에서도 직접 사용할 수 있으므로 유연합니다.
  • 테마 및 펄스 애니메이션을 지원합니다.

단점

  • 단순한 스켈레톤 UI에서는 구현하기 쉽지만 더 복잡한 스켈레톤에서는 복잡합니다.
  • 별도의 스켈레톤 구성 요소가 있으면 UI와 스타일이 변경될 때 유지 관리가 더 어려워집니다.

다음은 React Loading Skeleton의 예입니다.

 import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; const SkeletonComponent = () => ( <SkeletonTheme color="#202020" highlightColor="#444"> <section> <Skeleton height={50} width={50} /> </section> </SkeletonTheme> );

react-loading-skeleton 라이브러리에서 SkeletonSkeletonTheme 를 가져온 다음 속성으로 colorhightlightColor 를 사용하여 SkeletonTheme 구성 요소를 렌더링하는 기능 구성 요소를 만들었습니다.

SkeletonTheme 구성 요소는 테마 지정에 사용됩니다(예: 스켈레톤 UI에 색상 효과 추가).

마지막으로 섹션 내에서 높이 및 너비 속성과 적절한 값이 전달된 Skeleton 구성 요소를 정의합니다.

YouTube와 같은 스켈레톤 화면 UI 구축

스켈레톤 UI가 작동하는 방식을 보여주기 위해 React Loading Skeleton을 사용하여 YouTube와 같은 스켈레톤 화면을 만들어 보겠습니다.

반응 설정

React를 설정하는 가장 쉬운 방법은 Create React App을 사용하는 것인데, 이는 “공식적으로 지원되는 단일 페이지 React 애플리케이션을 만드는 방법입니다. 구성이 필요 없는 현대적인 빌드 설정을 제공합니다.”

이를 사용하여 구축할 애플리케이션을 부트스트랩합니다. 터미널에서 아래 명령을 실행합니다.

 npx create-react-app skeleton-screens && cd skeleton-screens

설치가 완료되면 npm start 를 실행하여 React 서버를 시작합니다.

React 앱 - Scaffold React 앱
React 시작 페이지(큰 미리보기)

스켈레톤 화면 없이 YouTube UI 만들기

먼저 YouTube 더미 데이터를 입력해 보겠습니다. 실제 끝점은 일반적으로 더미 데이터 대신 사용되지만 이 자습서에서는 더미 데이터를 사용합니다.

src/ 폴더에 파일을 만들고 이름을 data.js 로 지정하고 다음 코드를 추가합니다.

 const dummyData= [ { section: "Recommended", channel: "CNN", items: [ { id: "fDObf2AeAP4", image: "https://img.youtube.com/vi/fDObf2AeAP4/maxresdefault.jpg", title: "75 million Americans ordered to stay home", views: "1.9M views", published: "3 days agos" }, { id: "3AzIgAa0Cm8", image: "https://img.youtube.com/vi/3AzIgAa0Cm8/maxresdefault.jpg", title: "Gupta: The truth about using chloroquine to fight coronavirus pandemic", views: "128K views", published: "4 hours ago" }, { id: "92B37aXykYw", image: "https://img.youtube.com/vi/92B37aXykYw/maxresdefault.jpg", title: "Willie Jones STUNS Simon Cowell In Pitch Perfect Performance of 'Your Man'!", views: "2.47 million views", published: "1 month ago" }, { id: "J6rVaFzOEP8", image: "https://img.youtube.com/vi/J6rVaFzOEP8/maxresdefault.jpg", title: "Guide To Becoming A Self-Taught Software Developer", views: "104K views", published: "17 days ago" }, { id: "Wbk8ZrfU3EM", image: "https://img.youtube.com/vi/Wbk8ZrfU3EM/maxresdefault.jpg", title: "Tom Hanks and Rita Wilson test positive for coronavirus", views: "600k views", published: "1 week ago" }, { id: "ikHpFgKJax8", image: "https://img.youtube.com/vi/ikHpFgKJax8/maxresdefault.jpg", title: "Faces Of Africa- The Jerry Rawlings story", views: "2.3 million views", published: "2014" } ] }, { section: "Breaking News", channel: "CGTN America", items: [ { id: "tRLDPy1A8pI", image: "https://img.youtube.com/vi/tRLDPy1A8pI/maxresdefault.jpg", title: "Is Trump blaming China for COVID-19? You decide.", views: "876k views", published: "9 days ago" }, { id: "2ulH1R9hlG8", image: "https://img.youtube.com/vi/2ulH1R9hlG8/maxresdefault.jpg", title: "Journalist still goes to office during pandemic, see her daily routine", views: "873 views", published: "3 hours ago" }, { id: "TkfQ9MaIgU", image: "https://img.youtube.com/vi/_TkfQ9MaIgU/maxresdefault.jpg", title: "How are small businesses going to survive the economic downturn of the COVID-19 era?", views: "283 views", published: "4 day ago" } ] } ]; export default dummyData;

YouTube 형식을 복제하기 위해 ID, 이미지, 제목, 조회수 및 게시 날짜와 같은 속성이 있는 개체 배열이 있는 더미 데이터를 만들었습니다.

다음으로 YouTube UI를 만들어 보겠습니다. 세 가지 구성 요소가 있습니다.

Card 비디오의 썸네일, 제목, 조회수, 발행 날짜 및 채널에 대한 세부 정보를 보유합니다.
CardList 행의 모든 ​​카드를 반환합니다.
App dummyData 개체를 탑재하고 2초 동안 스켈레톤 UI를 로드한 다음 CardList 구성 요소를 반환합니다.

src 폴더 안에 폴더를 만들고 이름을 components 로 지정합니다. components 폴더 내에서 Card.js 파일을 만들고 여기에 다음 코드를 추가합니다.

 import React from "react"; const Card = ({ item, channel }) => { return ( <li className="card"> <a href={`https://www.youtube.com/watch?v=${item.id}`} target="_blank" rel="noopener noreferrer" className="card-link" > <img src={item.image} alt={item.title} className="card-image" /> <img src={item.image} alt={item.title} className="channel-image" /> <h4 className="card-title">{item.title}</h4> <p className="card-channel"> <i>{channel}</i> </p> <div className="card-metrics"> {item.views} • {item.published} </div> </a> </li> ); }; export default Card;

우리는 Card 구성 요소를 만들었습니다. 그 내부에서 react 에서 React 를 가져왔고 Card 구성 요소에서 사용할 수 있도록 itemchannel props를 분해했습니다. 하나의 비디오를 표시하는 각 Card 항목 구성 요소에는 썸네일, 조회수, 발행 날짜 및 제목이 표시됩니다.

카드 목록 구성 요소

components 폴더 내에서 CardList.js 파일을 만들고 여기에 다음 코드를 추가합니다.

 import React from "react"; import Card from "./Card"; const CardList = ({ list }) => { return ( <ul className="list"> {list.items.map((item, index) => { return <Card key={index} item={item} channel={list.channel} />; })} </ul> ); }; export default CardList;

이 구성 요소에서는 생성한 Card 구성 요소를 가져왔습니다. 카드는 list.items 를 통해 매핑하여 얻은 itemchannel 소품을 허용합니다. 그런 다음 이 구성 요소를 CardList 로 내보냅니다. App 구성 요소에서 사용할 것이기 때문입니다.

참고 : 이 구성 요소에 매핑된 항목 배열은 dummyData 의 개체 배열입니다.

앱 구성 요소

src/ 디렉토리의 app.js 파일 내에서 거기에 있는 코드를 삭제하고 여기에 다음을 추가하십시오.

 import React, { useState, useEffect } from "react"; import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { setLoading(true); const timer = setTimeout(() => { setVideos(dummyData); setLoading(false); }, 5000); return () => clearTimeout(timer); }, []); return ( <div className="App"> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> ); }; export default App;

이 구성 요소에서 우리는 우리가 만들고 App 구성 요소에 필요할 다른 파일 및 React 와 함께 useStateuseEffect 후크를 가져왔습니다.

우리 데이터는 더미 데이터이기 때문에 JavaScript setTimeout 메소드를 사용하여 2초 제한 시간 후에 콘텐츠를 로드하여 API 데이터처럼 목업해야 합니다.

다음으로 App 구성 요소에서 비디오 상태를 만들고 useState 를 사용하여 상태를 빈 배열로 설정합니다.

더미 데이터를 로드하기 위해 useEffect 후크를 사용합니다. 후크에서 setTimeout () 함수를 보유하는 변수 타이머를 만듭니다. 함수 내에서 비디오 상태를 dummyData 객체로 설정하고 2초 후에 데이터가 로드되는지 확인하고 마지막으로 마운트 해제하는 동안 타이머를 취소합니다.

마지막으로 비디오 상태를 매핑하고 list-section 을 포함하는 section 요소와 list props가 있는 CardList 구성 요소를 반환합니다.

CSS 추가

지금까지 실제 CSS가 없는 클래스를 많이 사용했습니다. src 폴더 내에서 App.css 의 모든 항목을 삭제하고 다음 코드로 바꿉니다.

 .App { max-width: 960px; margin: 0 auto; font-size: 16px; } .list { display: flex; justify-content: space-between; flex-wrap: wrap; list-style: none; padding: 0; } .section-title { margin-top: 30px; } .card { width: calc(33% - 10px); margin: 20px 0; } .card-link { color: inherit; text-decoration: none; } .card-image { width: 100%; } .channel-image { border-radius: 100%; padding: 0, 10px, 0, 0; width: 40px; height: 40px; } .card-title { margin-top: 10px; margin-bottom: 0; } .card-channel { margin-top: 5px; margin-bottom: 5px; font-size: 14px; } /* Tablets */ @media (max-width: 1000px) { .App { max-width: 600px; } .card { width: calc(50% - 22px); } } /* Mobiles \*/ @media (max-width: 640px) { .App { max-width: 100%; padding: 0 15px; } .card { width: 100%; } }

스켈레톤 화면 없이 YouTube UI가 어떻게 보이는지 봅시다. 페이지가 로드될 때 2초 동안 흰색 화면이 나타난 후 데이터가 즉시 로드되는 것을 볼 수 있습니다.

스켈레톤 화면이 없는 YouTube 같은 UI
스켈레톤 화면이 없는 YouTube 같은 UI(큰 미리보기)

React 로딩 스켈레톤 사용하기

콘텐츠의 글꼴 크기, 줄 높이 및 여백과 일치하도록 스켈레톤 화면을 세 심하게 만드는 다른 라이브러리와 달리 Skeleton 구성 요소는 로드 중인 콘텐츠 대신 구성 요소에서 직접 사용하도록 설계되었습니다.

다른 것보다 React Loading Skeleton을 선택한 몇 가지 이유를 살펴보겠습니다.

테마

React Loading Skeleton은 테마를 지원합니다. 따라서 SkeletonTheme 를 사용하여 모든 스켈레톤 구성 요소의 색상을 쉽게 변경하고 color props 에 값을 전달할 수 있습니다.

다음은 작동 방식을 보여주는 예입니다.

 import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; <SkeletonTheme color="grey" highlightColor="#444"> <p> <Skeleton height={250} width={300} count={1} /> </p> </SkeletonTheme> <SkeletonTheme color="#990" highlightColor="#550"> <p> <Skeleton height={250} width={300} count={1} /> </p> </SkeletonTheme> 
작동 중인 테마 효과
작동 중인 테마 효과(큰 미리보기)

지속

height , widthcolor props 외에도 duration prop을 지정할 수도 있습니다.

 <Skeleton duration={2} />

지속 시간은 기본적으로 1.2 입니다. 이것은 스켈레톤 애니메이션의 한 주기를 수행하는 데 걸리는 시간을 결정합니다.

자세한 내용은 설명서를 확인하십시오.

스켈레톤 화면 UI 구현

이제 react-loading-skeleton 설치하겠습니다. 터미널에서 다음 명령을 실행하여 패키지를 설치합니다.

 npm install react-loading-skeleton

스켈레톤 구성 요소

비디오 데이터에 대한 스켈레톤 구성 요소를 만들어 보겠습니다. components 폴더 안에 SkeletonCard.js 파일을 만들고 다음 코드를 추가합니다.

 import React from "react"; import Skeleton from "react-loading-skeleton"; const SkeletonCard = () => { return ( <section> <h2 className="section-title"> <Skeleton height={30} width={300} /> </h2> <ul className="list"> {Array(9) .fill() .map((item, index) => ( <li className="card" key={index}> <Skeleton height={180} /> <h4 className="card-title"> <Skeleton circle={true} height={50} width={50} />  <Skeleton height={36} width={`80%`} /> </h4> <p className="card-channel"> <Skeleton width={`60%`} /> </p> <div className="card-metrics"> <Skeleton width={`90%`} /> </div> </li> ))} </ul> </section> ); }; export default SkeletonCard;

정렬되지 않은 목록을 만들었습니다. 그 안에서 우리는 Array.fill() 메소드를 사용했습니다. 9개의 더미 데이터 항목이 있기 때문에 Array.fill() 메서드를 사용하여 items 개체의 길이를 반복하고 인덱스 값 없이 채웠으므로 배열이 비어 있습니다. 작동 방식을 알아보려면 Array.fill 설명서를 참조하세요.

다음으로, 우리는 빈 배열을 통해 매핑하여 뼈대 속성을 포함하는 목록을 반환하고 각 뼈대 속성의 값을 지정했습니다.

여기서 height 스켈레톤 사각형의 길이를 의미하고 width 는 너비를 의미하며 circle 은 스켈레톤 UI의 둥근 부분을 의미합니다.

React Loading Skeleton은 기본 펄스 애니메이션과 함께 제공되므로 편리합니다. 프로젝트에 맞게 Pulse 애니메이션을 만들 수 있지만 저에게 묻는다면 기본값을 사용하겠습니다.

마지막으로 완전한 소스 코드를 사용할 수 있습니다.

이제 완전한 기능을 갖춘 스켈레톤 화면 UI가 있습니다. 이 예에서는 콘텐츠를 표시하기 전에 5초 동안 골격을 보여줍니다.

지금까지의 결과를 살펴보겠습니다.

YouTube와 유사한 UI와 스켈레톤 화면 UI
YouTube와 같은 스켈레톤 UI(큰 미리보기)

결론

스켈레톤 화면은 완전히 비어 있는 화면을 마주하는 불편함을 피하고 사용자에게 콘텐츠가 로드되기 전에 어떤 모습일지 알 수 있도록 하여 사용자 경험을 크게 향상시킵니다.

지금까지 살펴본 패키지가 마음에 들지 않으면 페이지 레이아웃을 모방한 사각형과 원을 만들어 자신만의 스켈레톤 UI를 만들 수 있습니다.

아래 의견 섹션에서 피드백과 경험을 공유하십시오. 나는 당신이 무엇을 생각해내는지 보고 싶습니다!

이 기사에 대한 지원 리포지토리는 Github에서 사용할 수 있습니다.

참고문헌

  • "스켈레톤 스크린에 대해 알아야 할 모든 것", Bill Chung, UX Collective
  • "Skeleton Loading Pages With React", Anthony Panagi, Octopus Wealth
  • "React와 React Native를 사용한 스켈레톤 스크린", Chris Dolphin, Alligator.io
  • "React에서 스켈레톤 로딩 구현", Adrian Bece, DEV