MVP 프로젝트 중간 발표를 끝내고 레이아웃 쉬프트라는 용어를 알게되었다. 그동안 장르별 필터를 적용한 메뉴 클릭 시 화면이 잠깐 위로 올라갔다 오면서 움찔하는 현상이 있었다. 나는 각각의 데이터를 불러오는 과정에서 새로운 장르를 클릭하면 리렌더링이 되어서 생기는 현상인 줄 알았다.
새로운 데이터를 불러오기 때문에 발생하는 문제는 맞긴 한데, 높이 설정이 되어있지 않은 상태에서 데이터를 불러오기 전에는 공백으로 있다가 데이터를 불러온 후 UI가 그려지기 때문에 발생하는 현상이었다. 이것이 바로 레이아웃 쉬프트이다.
Layout Shift는 웹 페이지의 화면 레이아웃이 변경되면서 예상치 못한 움직임이 발생하는 현상이다. 데이터가 로드되는 동안 데이터가 뿌려질 공간이 공백으로 있다가 데이터가 로드된 후 UI가 그려지면서 화면이 움직이게 된다. 이러한 움직임은 페이지와 상호 작용을 시도하는 동안 콘텐츠가 이동할 수 있으므로 사용자에게 특히 방해가 될 수 있다.
Layout Shift는 다양한 이유로 발생할 수 있지만 가장 일반적으로 다음과 관련되어 있다.
이미지 및 미디어 로딩
웹페이지의 이미지 또는 기타 미디어 요소가 HTML에 지정된 크기를 갖고 있지 않고 초기 렌더링 후에 로드되는 경우, 브라우저는 새로 로드된 내용을 수용하기 위해 콘텐츠를 리플로우해야 할 수 있다.
동적 콘텐츠 로딩
JavaScript를 통해 비동기적으로 또는 동적으로 로드되는 콘텐츠
이미지와 미디어 요소에 너비 및 높이 속성을 지정함으로써 브라우저는 초기 렌더링 중에 필요한 공간을 예약할 수 있어 레이아웃 시프트의 가능성을 줄일 수 있다.
콘텐츠가 동적으로 로드되는 경우, 개발자는 그에 대한 공간을 예약하기 위해 크기를 지정하거나 플레이스홀더 요소를 사용할 수 있다. 이렇게 하면 콘텐츠가 로드될 때 레이아웃이 드라마틱하게 변하지 않는다.
CSS에서 웹폰트를 사용하는 경우 font-display 속성을 사용하여 폰트가 로드되는 동안 어떻게 표시될지를 제어할 수 있다. 이는 텍스트 콘텐츠에서의 급격한 변경을 피하는 데 도움이 된다.
💡
중간 발표 튜터님 피드백
이러한 방법의 하나로 중간 발표 때 튜터님 피드백으로 스켈레톤 UI를 생각해 보라고 권유받았다. 피드백 후 바로 스켈레톤 UI가 무엇인지 찾아보았고, 우리 프로젝트의 UI와도 잘 어울릴 것이라고 판단하여 적용하기로 했다.
스켈레톤 UI는 웹 또는 모바일 애플리케이션의 성능을 향상시키고 사용자 UX를 향상기키기 위해 사용되는 방법이다. 실제 콘텐츠가 완전히 로드되기 전에 해당 자리에 기존 컴포넌트와 동일한 모양으로 임시 표시되는 단순화된 버전의 페이지 레이아웃을 만들어 주는 것이다.
로딩이 되는 듯한 애니메이션이 들어가서 이는 로딩 시간이 더 빨라진다는 착각을 불러일으키고 데이터를 잘 불러오고 있다는 시각적 피드백을 사용자에게 제공한다.
스켈레톤 UI는 사용자에게 백그라운드에서 어떤 일이 일어나고 있다는 느낌을 주어 인식되는 로딩 시간을 줄이고 대기하는 동안 사용자 경험을 향상시킨다.
로딩 시간이 그렇게 길지 않기 때문에 사용자는 콘텐츠가 로딩 중이더라도 거의 즉시 시각적 구조를 볼 수 있으므로 응답을 더 빠르게 인식한다.
콘텐츠가 로드될 때 레이아웃이 갑작스럽게 변경되는 것을 방지하고 사용자에게 보다 원활한 전환을 제공함으로써 시각적 연속성을 유지하는 데 도움이 된다.
-> 가장 문제였던 레이아웃 쉬프트 현상을 해결하기 위해 스켈레톤 UI를 고려하게 되었다. 바로 시각적 연속성을 통해 사용자 UX를 향상시키기 위함이다.
스켈레톤 UI의 목적은 데이터에 대한 지루한 대기시간을 줄이는 것이므로 너무 다르면 사용자는 스켈레톤 컴포넌트가 또 다른 독립적인 UI 컴포넌트라고 느낄 수 있기 때문이다.
@keyframes loading {
0% {
transform: translateX(0);
}
50%,
100% {
transform: translateX(460px);
}
}
.skeleton-item::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 30px;
height: 100%;
background: linear-gradient(to right, #f2f2f2, #ddd, #f2f2f2);
animation: loading 2s infinite linear;
}
위의 방법처럼 스켈레톤 애니메이션을 직접 만들지 않고 사용할 수 있는 라이브러리가 있다. Skeleton 라이브러리는 3가지가 있다.
react-content-loader
react-placeholder
React Loading Skeleton
나는 가장 많이 사용되는 react-content-loader를 이용하기로 했다.
설치
npm i react-content-loader --save
yarn add react-content-loaderFor React Native
npm i react-content-loader react-native-svg --save
yarn add react-content-loader react-native-svg
import React from 'react';
import ContentLoader from 'react-content-loader';
const GenreListSkeleton = () => {
return (
<ContentLoader
speed={2}
width={220}
height={300}
viewBox="0 0 220 300"
backgroundColor="#363636"
foregroundColor="#232323"
>
<rect x="0" y="0" rx="10" ry="10" width="220" height="200" />
<rect x="20" y="220" rx="10" ry="10" width="100" height="21" />
<rect x="20" y="280" rx="10" ry="10" width="340" height="17" />
</ContentLoader>
);
};
export default GenreListSkeleton;
단순히 ContentLoader로 감싸주기만 하면 스켈레톤 애니메이션이 자동으로 생성되어 편하다. 기존에 장르별 필터를 클릭할 때마다 움찔대던 현상도 사라지고 확실히 데이터가 로드되는 중이라는 것이 시각화로 보여지니 UI, UX적으로도 향상된 느낌이다.
📌
코드
💡코드
🔎코드
다른 웹페이지를 이용하면서 자주 봤었던 개념인데, 이게 스켈레톤 UI였구나...
사실 로드되는 이미지를 수동으로 넣어주는 것이라고는 생각을 못했던 것 같다.
그냥 자동으로 생성되는 줄 알았는데 로딩 스피너도 그렇고 직접 만들어서 넣어주는 것이라니...
스켈레톤 UI를 적용하니 확실히 UX적으로 개선된게 눈에 보여서 뿌듯했다. 기본적인 기능을 빨리 완성시키고 이런 디테일적인 부분들을 손보면 더 재밌을 것 같다.