/ 우리는 이렇게 합니다 / 로딩 담당자의 노트
개발·인터랙션 · 기다림을 맡은 사람

기다림도, 디자인입니다.

저는 사이트가 “느리다”는 말을 들으면 가장 먼저 초시계를 켜지 않습니다. 화면을 봅니다. 같은 0.8초라도 빈 화면을 보여주면 답답하고, 들어올 모양을 미리 보여주면 짧게 느껴지거든요. 그래서 이 글은 읽는 글이 아니라 기다려보는 글입니다. 아래 위젯, 진짜로 동작합니다.

한 줄 직답

체감 속도는 실제 속도와 별개로 설계됩니다. 빈 화면 대신 들어올 콘텐츠의 모양(스켈레톤)을 미리 보여주고, 자리를 먼저 잡아 화면이 덜컥거리지 않게 하면, 같은 대기 시간도 훨씬 짧게 느껴집니다. Findable은 이 ‘기다림의 0.8초’를 따로 설계합니다.

요약

  • 빈 화면보다 스켈레톤이 체감 속도를 줄인다 — 들어올 모양을 미리 보여주기 때문.
  • 스피너는 형태를 모를 때만, 리스트·카드처럼 형태가 정해진 화면엔 스켈레톤.
  • 300ms 안에 끝나는 로딩엔 굳이 스켈레톤을 넣지 않는다 — 깜빡임이 더 거슬린다.
  • 이미지·폰트가 자리를 밀어내는 레이아웃 시프트(CLS)는 자리를 미리 잡아 막는다.

몇 해 전, 회사 대시보드가 “너무 느리다”는 컴플레인을 받았습니다. 로그를 까보니 응답은 0.9초. 빠르진 않아도 끔찍한 숫자도 아니었죠. 진짜 문제는 그 0.9초 동안 사용자가 새하얀 빈 화면을 보고 있었다는 거였습니다. 그날 저는 서버를 한 줄도 안 고치고, 빈 화면에 회색 카드 모양만 깔았습니다. 다음 주 컴플레인은 사라졌습니다. 속도는 그대로였는데요. 그때부터 저는 ‘기다림’을 따로 맡는 사람이 됐습니다.

빈 화면과 스켈레톤, 정말 그렇게 다를까요?

네, 다릅니다. 빈 화면은 사용자에게 아무 정보도 주지 않습니다. ‘멈춘 건가? 내 인터넷이 문제인가?’ 하는 불안이 시간을 늘립니다. 스켈레톤은 ‘여기 카드가 하나, 그 안에 제목과 설명이 들어올 거예요’라고 미리 말해줍니다. 사람은 다음에 올 것을 예측할 수 있으면 덜 답답해합니다. 말로는 와닿지 않으니, 직접 눌러보세요.

Live · 스켈레톤 로딩

기다림도 디자인합니다

'다시 로딩'을 누르면 스켈레톤이 콘텐츠로 채워집니다. 빈 화면보다 훨씬 덜 답답하죠.

운정 신도시 분양

숲세권 프리미엄 단지 — 콘텐츠가 도착했습니다.

그럼 스피너는 언제 쓰나요?

스피너가 나쁜 게 아닙니다. 자리를 잘못 잡았을 뿐이죠. 스켈레톤은 ‘들어올 모양’을 알 때 씁니다 — 카드 리스트, 프로필, 표처럼요. 반대로 결과의 형태를 미리 알 수 없는 짧은 작업(파일 업로드, 결제 승인, ‘저장 중’)에는 스피너나 진행 막대가 맞습니다. 형태도 모르는데 가짜 스켈레톤을 그려놓으면 콘텐츠가 도착할 때 모양이 어긋나 더 덜컹거립니다.

‘낙관적 UI’는 어떻게 기다림을 없애나요?

가장 좋은 기다림은 안 기다리는 겁니다. 낙관적 UI는 서버 응답을 기다리지 않고 ‘성공할 거야’라고 가정해 화면을 먼저 바꿉니다. 좋아요를 누르면 숫자가 즉시 1 올라가고, 댓글을 달면 바로 목록에 뜹니다. 서버는 그 뒤에 조용히 따라옵니다. 단, 실패했을 때가 중요합니다. 저는 실패하면 화면을 원래대로 되돌리고 “전송 실패, 다시 시도”를 분명히 띄웁니다. 조용히 되돌리면 사용자는 자기 행동이 먹혔다고 믿은 채 떠나니까요.

화면이 로딩 중에 덜컥 움직이는 건 왜죠?

제가 가장 싫어하는 현상입니다. 글을 읽으려는데 위에서 이미지가 늦게 도착하며 본문을 아래로 밀어내고, 누르려던 버튼이 순간 이동해 엉뚱한 광고를 누르게 되죠. 이걸 레이아웃 시프트(CLS)라고 합니다. 원인은 거의 항상 하나입니다 — 콘텐츠가 도착하기 전까지 자리를 안 잡아둔 것.

해법도 단순합니다. 이미지에는 미리 width·height(또는 aspect-ratio)를 박아 자리를 비워두고, 폰트는 font-display로 깜빡임을 줄이고, 광고·임베드도 빈 박스를 먼저 깔아둡니다. 위 위젯의 회색 썸네일 자리가 콘텐츠가 도착해도 그대로인 이유가 이겁니다. 자리를 미리 잡으면 화면은 움직이지 않습니다.

그래서 항상 스켈레톤을 넣느냐고요? 아닙니다

이게 ‘나만 아는’ 부분일지도 모르겠습니다. 너무 빠른 로딩에 스켈레톤을 넣으면 오히려 해롭습니다. 100~200ms 만에 데이터가 오는데 스켈레톤을 띄우면, 회색 블록이 깜빡하고 사라지는 것만 보입니다. 그 깜빡임이 ‘뭔가 잘못됐나?’ 하는 인상을 줍니다. 그래서 저는 보통 짧은 지연(예: 응답이 그보다 늦어질 때만 스켈레톤을 띄우는 식)을 둬서, 빠르면 아무 일도 없었던 것처럼, 느릴 때만 스켈레톤이 등장하게 합니다. 안 보여주는 것도 설계입니다.

제가 실제로 쓰는 ‘지연 후 스켈레톤’ 패턴은 이렇게 짧습니다. 빠르면 아무것도 안 띄우고, 느릴 때만 스켈레톤이 등장하게 하는 코드입니다.

JS · 지연 후에만 스켈레톤 띄우기
// 200ms 안에 끝나면 스켈레톤을 아예 안 보여준다(깜빡임 방지)
let showSkeleton = false;
const t = setTimeout(() => {
  showSkeleton = true;
  card.classList.add("is-loading");   // 회색 블록 등장
}, 200);

fetchData().then((data) => {
  clearTimeout(t);                     // 빨랐으면 타이머 취소 → 깜빡임 없음
  card.classList.remove("is-loading");
  render(card, data);                  // 자리는 그대로, 내용만 채움(CLS 0)
});

핵심은 마지막 줄입니다. 스켈레톤과 실제 콘텐츠가 같은 자리를 쓰기 때문에 내용이 도착해도 화면이 움직이지 않습니다.

같은 0.8초 대기빈 화면 로딩스켈레톤 로딩
체감 속도길게 느껴짐 — ‘멈췄나?’짧게 느껴짐 — 다음을 예측
이탈불안에 뒤로가기 누르기 쉬움곧 뜰 걸 알아 기다림
신뢰고장 의심 / 내 탓 의심사이트가 살아있다는 신호

기다림 하나를 이렇게 보는 사람이, 사이트 전체를 만듭니다

로딩은 사용자가 사이트를 처음 만나는 0.8초입니다. 그 짧은 순간을 진지하게 다루는 사람이라면 성능도, 버튼도, 흐름도 같은 태도로 다룹니다. Findable에서는 이런 디테일이 기본값입니다. 당신의 사이트, 그 첫 0.8초를 어떻게 채워드릴까요?

다른 담당자와의 연결

로딩은 혼자 일하지 않습니다. 기다림은 ‘속도’와 한 몸이거든요.

  • 실제 속도를 줄이는 일성능 담당의 몫입니다 — 이미지 최적화·코드 분할로 대기 시간 자체를 줄입니다. 저는 그래도 남는 시간을 덜 답답하게 만들 뿐이죠.
  • 버튼을 누른 뒤의 상태(로딩·완료 피드백)는 버튼 담당과 맞물립니다 — 누른 직후의 0.2초가 ‘두 번 누르기’를 막습니다.
  • 이 모든 기다림이 흐름 안에서 어디에 놓이는지UX 담당이 봅니다 — 어느 단계에서 기다림이 가장 위험한지를 정합니다.
스켈레톤이랑 스피너, 뭐가 다른가요?
스켈레톤은 콘텐츠가 들어올 자리의 모양을 회색 블록으로 미리 보여줍니다. 사용자는 ‘곧 카드 3개가 뜨겠구나’를 알게 돼 덜 답답해합니다. 스피너는 모양 정보가 없어서 ‘얼마나 더 기다려야 하지?’라는 불안을 줍니다. 리스트·카드·프로필처럼 형태가 정해진 화면엔 스켈레톤, 형태를 알 수 없는 짧은 작업엔 스피너가 맞습니다.
로딩이 빠른데도 스켈레톤을 꼭 넣어야 하나요?
아닙니다. 300ms 안에 끝나는 로딩에 스켈레톤을 넣으면 깜빡이기만 해서 오히려 더 거슬립니다. 저는 보통 응답이 그보다 빠르면 아무것도 안 띄우고, 느려질 가능성이 있을 때만 약간의 지연 후에 스켈레톤을 띄웁니다. ‘안 보여주는 것’도 설계입니다.
낙관적 UI(optimistic UI)가 뭔가요?
서버 응답을 기다리지 않고 성공할 거라 가정해 화면을 먼저 바꾸는 방식입니다. 좋아요를 누르면 숫자가 즉시 올라가고, 서버가 실패하면 되돌립니다. 사용자는 기다림을 거의 못 느낍니다. 단, 실패했을 때 조용히 되돌리지 말고 분명히 알려야 신뢰가 깨지지 않습니다.
화면이 로딩 중에 덜컥 움직이는 건 왜 그런가요?
이미지·광고·폰트가 늦게 도착하면서 자리를 밀어내기 때문입니다. 이걸 레이아웃 시프트(CLS)라고 합니다. 글을 읽다가 버튼이 갑자기 내려가 엉뚱한 걸 누르게 되죠. 해법은 콘텐츠가 들어올 자리를 width·height·aspect-ratio로 미리 잡아두는 겁니다. 그러면 콘텐츠가 도착해도 화면이 움직이지 않습니다.
체감 속도와 실제 속도, 뭘 먼저 고쳐야 하나요?
둘 다 필요하지만 순서가 있습니다. 실제 속도를 줄이는 게 근본이고(이미지 최적화·코드 분할 등), 그래도 남는 대기 시간을 스켈레톤·낙관적 UI로 덜 답답하게 만드는 게 체감 속도입니다. 실제로 느린데 포장만 하면 한계가 있고, 빠른데 피드백이 없으면 빠른 줄도 모릅니다. 성능 담당과 로딩 담당이 한 몸으로 움직이는 이유입니다.

이런 디테일로 사이트를 만듭니다

기다림의 0.8초까지 이렇게 다루는 팀이 당신의 홈페이지를 만들면 어떨까요. 무료 진단으로 시작하세요.

무료 진단 받기

이 글의 스켈레톤 위젯은 이 페이지에서 실제로 동작하는 코드입니다(외부 라이브러리 0). 체감 속도·이탈에 대한 설명은 일반 원칙이며 특정 성과를 보장하지 않습니다. 날조된 사례·수치는 사용하지 않았습니다.