기존 CSR(Vue) 프로젝트를 SSR(Remix)로 전환하며 차트 라이브러리(ECharts) 때문에 이슈를 겪었다.
회사 모든 서비스의 차트 라이브러리는 ECharts로 통일되어 있어 기술스택 변경없이 그대로 들고 간 거였는데, 서버에서 렌더링되지 않는다는 치명적인 단점이 있었다.
그래서 차트를 그리기 위해 useEffect를 통해 마운트가 끝난 시점에 렌더링 시키는 방식으로 사용을 했다.
📌 문제 정의 : 스켈레톤 UI가 깜빡이는 것처럼 보인다.
차트만 클라이언트에서 렌더링되다 보니 혼자 뿅하고 나타나는 것처럼 보이길래, UX 개선을 위해 스켈레톤 UI를 도입했다.
하지만 렌더링이 빨리 끝나는 환경에서는 스켈레톤이 화면에 나타났다가 사라지는 게 깜빡이는 것처럼 보여 오히려 UX가 저하되는 느낌을 받았다.
카카오페이 기술블로그에서도 동일한 이슈를 다룬 적이 있다. 흔한 문제인 것 같다.
짧은 로딩 시간에서는 스켈레톤 UI를 보여주지 말고 바로 차트가 렌더링 되도록 수정해보고자 했다.
📌 시도1: 타이머 사용
상단에 참조한 카카오페이 기술블로그에서도 볼 수 있듯이 이 문제는 보통 타이머를 통해 해결하는 게 보편적이다.
일정 시간 동안 무조건 기다렸다가 그 이후에만 실제 컴포넌트를 그리는 것이다.
const DeferredComponent = ({ children }: PropsWithChildren<{}>) => {
const [isDeferred, setIsDeferred] = useState(false);
useEffect(() => {
// 200ms가 지난 후에만 children을 렌더링
const timeoutId = setTimeout(() => {
setIsDeferred(true);
}, 200);
return () => clearTimeout(timeoutId);
}, []);
if (!isDeferred) {
return null;
}
return <>{children}</>;
};
스켈레톤 UI 컴포넌트를 해당 DeferredComponent로 감싸서 사용하면 된다.
실제 진행 중인 프로젝트에 적용해봤을 때도 잘 동작하는 듯 했다.
그러나 어떤 환경에서든 동일한 동작을 했으면 좋겠다는 생각에 여러 환경에서 테스트를 해보다가 허점을 발견했다.
📌 시도1 실패 이유 : 하이드레이션 자체가 늦어버리면....
네트워크가 느린 상태에서는 시도1이 기대처럼 동작하지 않는다.
하이드레이션 자체가 늦어지기 때문이다.
SSR에서는 실행 순서가 다음과 같이 진행된다.
1. 서버가 HTML을 렌더링해서 내려줌
2. 브라우저가 HTML을 먼저 그림
3. JS가 로드되고 hydration이 진행됨
4. 하이드레이션이 끝난 후 useEffect가 실행됨
즉 JS타이머는 하이드레이션이 끝난 시점부터 카운트를 시작하게 되기 때문에 하이드레이션이 늦어지면 빈 화면을 보게 된다.
스켈레톤을 도입한 목적이 로딩 중임을 알려주기 위함이었는데,
로딩이 오래 걸릴 때 보여주지 못한다는 사실이 마음에 들지 않았다.
📌 시도2 : CSS 애니메이션 사용 => 해결 ✅
그래서 JS 실행 시점과 상관없이 적용 가능한 방법을 찾는 걸로 접근 방식을 바꿨다.
그렇다면 답은 CSS!
CSS는 브라우저가 HTML을 그리는 순간 바로 적용이 되기 때문에 JS 타이밍과는 상관이 없다.
.skeleton {
opacity: 0;
/* forwards 필수! */
animation: fade-in 0.3s ease-in-out forwards;
animation-delay: 200ms;
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
동작 방식은 간단하다.
1. 스켈레톤은 opacity: 0으로 시작해서, 처음 200ms 동안은 DOM엔 존재해도 화면으로는 보이지 않는다.
2. 로딩이 200ms 안에 끝나면 스켈레톤은 그대로 안 보이는 상태로 두고 실제 차트가 렌더링 된다.
3. 로딩이 200ms를 넘기면 그 시점부터 스켈레톤이 보이기 시작한다.
이렇게 하면 하이드레이션이 얼마나 늦어지든 상관없이 정해진 지연 시간 이후에 보이게 된다
JS로 짠 로직은 JS가 실행되기 전까지 아무 의미가 없다는 간단한 사실에 대해 다시 한 번 상기하게 했던 경험..~
'Tech > TypeScript' 카테고리의 다른 글
| [TS 기초] 타입스크립트로 블록체인 만들기 (0) | 2023.04.12 |
|---|---|
| [TS 기초] TS의 객체 지향적 특징 (0) | 2023.03.21 |
| [ TS 기초 ] 함수 - Call signiture / 오버로딩 / 제너릭 (0) | 2023.03.21 |
| [ TS 기초 ] 타입스크립트란 (0) | 2023.03.21 |
| [Typescript / TS] 영화 웹 서비스 만들기 (0) | 2023.02.07 |