Loading
로딩을 없애는 것을 최우선으로 두고, 피할 수 없는 로딩을 언제·어디에·무엇으로 보여줄지 다룹니다.

가장 좋은 답은 로딩을 없애는 것입니다. 속도를 개선하고 필요한 데이터를 미리 확보해서 사용자가 애초에 로딩 UI를 볼 일이 없도록 하는 방법을 먼저 고려합니다.
그래도 로딩을 완전히 피할 수 없는 상황은 남습니다. 이 문서를 읽으면 로딩 UI를 언제 보여주는 것이, 어디에 보여주는 것이, 무엇으로 보여주는 것이 가장 이상적인지 알 수 있습니다.
원칙
1. 가능한 경우 로딩 UI를 보여주지 않습니다
- 가장 좋은 로딩 경험에서는 사용자가 로딩이 있었다는 사실조차 알아차리지 못합니다.
- 로딩 UI를 어떻게 설계할지 고민하기 전에, 그 로딩을 사용자가 마주치지 않게 할 방법이 있는지 먼저 확인하세요.
2. 로딩 UI를 보여주지 않더라도 상호작용에 대한 피드백을 제공합니다
- 로딩 UI를 보여주지 않더라도 액션에 대한 피드백은 항상 존재해야 합니다.
- 아래에서 설명하는 대로 로딩 UI는 짧은 시간 안에는 생략해도 되지만, 사용자가 무언가를 눌렀을 때의 피드백은 언제나 즉각적이어야 합니다. Feedback을 참고합니다.
- 반응이 전혀 없는 UI를 보는 사용자는 제품이 "동작하지 않았다"고 인식하며, 자신이 수행한 같은 조작을 반복할 수 있습니다.
3. 맥락을 유지합니다
- 가능하면 현재 화면에 머문 채로 로드하고, 로드가 완료되면 화면을 전환합니다.
- 화면을 먼저 전환시킨 뒤에 빈 화면을 보여주거나 로딩만을 위한 화면을 띄우는 방식을 자제합니다.
4. 레이아웃은 안정적이어야 합니다
- 로딩 전후로 상호작용 가능한 요소의 위치가 바뀌면 사용자가 누르려던 것과 다른 것을 누르게 됩니다.
- 로딩 중에는 최종 콘텐츠가 차지할 공간을 미리 확보합니다. 로딩 UI를 콘텐츠로 교체할 때는 같은 자리에서 교체합니다.
언제 보여줄까
먼저, 로딩을 없앨 수 있는지 확인하세요
로딩 UI의 종류를 고르기 전 먼저 물어야 할 질문은 "이 로딩을 사용자가 마주치지 않게 할 수 있는가"입니다.
사용자가 다음 화면으로 이동하거나 오버레이를 열 가능성이 높다면, 다음에 보여줄 데이터를 유휴 시간에 미리 가져오(prefetch)세요. 미리 가져오기에 성공하면 이 문서의 나머지 대부분은 필요하지 않습니다.
데이터를 미리 가져올 땐 사용자 기기의 상황을 고려합니다. 모바일에서는 네트워크 비용 및 배터리 비용을 고려합니다.
어떤 정보를 미리 가져올지 잘 모르겠다면 아래 기준을 참고할 수 있습니다.
- 사용자가 진입할 가능성이 높은 경로에서 필요로 하는 정보
- 사용자가 진입할 가능성이 낮은 경로를 미리 가져온 뒤 사용되지 않는다면 낭비입니다.
- 금방 상하지(stale) 않는 정보
- 자주 stale해지는 정보라 어차피 다시 로드해야 한다면, 미리 가져올 필요가 없습니다.
- 재사용 가능성이 높은 데이터
- 여러 화면이 공유하거나 같은 상호작용에서 반복적으로 필요한 정보는 한 번 가져와 여러 번 쓸 수 있기에 미리 가져오는 비용에 비해 효율이 높습니다.
표시 시점: 200ms와 1초
200ms보다 빠를 것 같으면 로딩 관련 UI를 표시하지 마세요. 200ms보다 길어지는 순간부터 로딩 관련 UI를 표시하세요.
예상 시간을 미리 재서 판단하지 않습니다. 실제로 200ms가 지나는 시점을 기준으로 삼습니다. 그래서 빠르게 끝나는 요청에서는 로딩 UI가 아예 나타나지 않습니다.
이 200ms 동안에도 액션에 대한 피드백은 즉시 있어야 합니다. 눌림 상태, 요소의 상태 변화처럼 조작이 접수되었음을 알리는 반응은 지연 없이 나타납니다. 지연되는 것은 로딩 UI뿐입니다.
로딩 표시에는 임계값이 두 개 있습니다. 하나는 표시할지 말지를 결정하고 다른 하나는 어디에 표시할지를 정합니다. 적용 범위가 다르므로 구분해서 읽으세요.
| 임계값 | 무엇이 바뀌나 | 적용 범위 |
|---|---|---|
| 200ms | 로딩 UI를 표시하기 시작한다 | 모든 로딩 |
| 1초 | 로딩 UI를 트리거에서 목적지로 옮기고 목적지를 띄운다 | 트리거에서 기다리는 패턴에 한정 |
트리거에서 기다리는 패턴은 현재 화면의 트리거 요소에 로딩을 표시하고 준비가 끝나면 목적지를 띄우는 방식입니다. 새 표면을 먼저 띄우고 그 안에서 로딩을 보여주지 않습니다. 화면 전환과 오버레이 열기가 대표적입니다. 자세한 규칙은 트리거에서 기다렸다가 띄우기에서 다룹니다.
이 패턴에서 두 임계값은 하나의 타임라인 위에 놓입니다.
| 경과 시간 | 상태 |
|---|---|
| 0 ~ 200ms | 트리거에 눌림 피드백만. 로딩 UI 없음 |
| 200ms ~ 1초 | 트리거에 로딩 표시. 목적지는 아직 띄우지 않음 |
| 1초 경과 | 목적지를 띄우고 그 안에서 로딩 표시 |
| 확정 | 내용을 한 번에 표시 |
그 외의 로딩에는 200ms만 적용됩니다. 이미 화면에 있는 영역의 데이터를 갱신하는 경우처럼 띄울 목적지가 없다면 1초는 의미가 없습니다.
최소 노출 시간은 두지 않습니다
로딩 UI를 한 번 표시했더라도 최소 노출 시간을 강제하지 않습니다. 응답이 도착하면 곧바로 교체합니다. 최소 노출 시간을 두면 이미 도착한 콘텐츠를 일부러 늦게 보여주게 되고, 이는 로딩을 없애려는 이 문서의 방향과 반대입니다.
대신 전환으로 깜빡임을 막습니다.
- 로딩 UI는 나타날 때 서서히 나타나고, 콘텐츠로 교체될 때 crossfade합니다. 표시 지연 직후에 응답이 도착하면 로딩 UI는 완전히 드러나기 전에 사라지므로 무언가 스쳐 지나갔다는 느낌이 남지 않습니다.
- 로딩 UI와 콘텐츠는 같은 자리에서 같은 크기로 교체합니다. 크기가 달라지는 채로 겹치면 crossfade가 오히려 튀어 보입니다.
- 전환 시간은 짧게 유지합니다. 길어지면 그 자체가 최소 노출 시간이 되어 콘텐츠가 늦게 보입니다.
- Skeleton처럼 형태가 콘텐츠와 다른 로딩 UI는 두 화면을 동시에 겹치면 잔상이 보입니다. 이때는 로딩 UI를 먼저 걷어낸 뒤 콘텐츠를 이어서 올립니다.
- 동작 줄이기 설정에서도 이 전환은 유지합니다. 불투명도 전환은 이 설정이 줄이려는 움직임에 해당하지 않습니다.
로딩을 줄이는 순서
200ms를 넘길 것으로 보인다면 위에서부터 순서대로 확인하세요.
- 결과를 미리 반영할 수 있는가 → 낙관적 업데이트를 적용하고 로딩 상태를 두지 않습니다. (낙관적 업데이트 참고)
- 이미 확보한 데이터가 있는가 → 아는 것부터 즉시 그리고, 모르는 부분만 로딩 처리합니다. (이미 가진 정보 먼저 보여주기 참고)
- 기존 콘텐츠가 있는가 → 기존 데이터를 유지한 채 갱신하고 로딩 UI는 국소적으로만 표시합니다.
화면을 "로딩 중"과 "완료"의 이분법으로 보지 마세요. 데이터를 단위로 쪼개서 확보된 것부터 순차적으로 보여주는 방식이 기본값입니다.
여기서 걸러지지 않고 남은 로딩에만 로딩 UI가 필요합니다. 어떤 요소를 쓸지는 선택 기준에서 정합니다.
로딩 시간에 따른 선택

| 예상 로딩 시간 | 사용할 수 있는 로딩 요소 | 설명 |
|---|---|---|
| 200ms 이내 | 표시하지 않음 | 사용자가 로딩을 인식하기 어려운 시간입니다. 이때 로딩 요소를 표시하면 화면이 깜빡여 오히려 경험을 해칩니다. 단, 액션에 대한 피드백은 필요합니다. |
| 200ms ~ 4초 | Progress Circle(불확정), Skeleton | 사용자가 지연을 인식하기 시작하는 구간입니다. |
| 4 ~ 10초 | Skeleton, Progress Circle(불확정), Progress Bar | 사용자가 지루함을 느끼기 시작합니다. 무엇을 기다리는지 알려주기를 권장합니다. |
| 10초 이상 | Progress Circle(확정), Progress Bar | 시스템에 문제가 생긴 것이 아님을 알 수 있도록 진행 상황을 알리고, 가능하면 예상 소요 시간을 제공합니다. |
| 1분 이상 | Progress Circle(확정), Progress Bar | 진행 상황을 명확히 표시하고 백그라운드 처리와 취소 수단을 제공합니다. |
어디에 보여줄까
무엇을 보여줄지 못지않게 어디에 보여줄지가 중요합니다.
트리거에서 기다렸다가 띄우기
이 규칙은 사용자의 조작으로 아직 없던 표면이 나타날 때 적용합니다. 그 표면의 내용이 비동기로 정해지는 경우입니다. 화면 전환, 오버레이 열기, 드롭다운이나 메뉴 열기, 접힌 영역 펼치기가 모두 해당합니다.
이미 화면에 있는 영역의 내용이 바뀌는 경우(탭 전환, 필터 적용, 데이터 재요청)는 여기에 해당하지 않습니다. 그때는 기존 콘텐츠를 유지한 채 갱신합니다.
기본 규칙: 내용이 확정될 때까지 띄우지 않습니다
현재 화면에서 트리거 요소에 로딩 상태를 표시하고 준비가 끝나면 목적지를 띄웁니다. 새 표면을 먼저 띄워서 그 안에 빈 화면이나 스켈레톤을 채우지 마세요.
이렇게 하면 빈 화면이 노출되지 않습니다. 로딩 중에도 사용자는 기존 화면을 계속 탐색할 수 있습니다.
- 목록에서 상세로 이동한다면 누른 행에 로딩을 표시하고 준비되면 이동합니다.
- 액션시트를 연다면 트리거 버튼에 로딩을 표시하고 항목이 확정되면 엽니다.
대기하는 동안 같은 트리거를 다시 누른 입력은 무시합니다. 새로 요청하지도, 대기를 취소하지도 않습니다.
1초를 넘기면 띄우고 나서 로드합니다
대기가 1초를 넘기면 그만 기다리고 목적지를 띄워 그 안에서 로딩을 표시하세요. 타임라인은 표시 시점: 200ms와 1초를 참고하세요.
트리거에 로딩 UI를 오래 물고 있으면 사용자는 조작이 접수되지 않았다고 생각하고 같은 곳을 다시 누릅니다. 그래서 기다리는 방식은 대기가 짧을 때만 유효합니다.
이 전환에는 부수적인 장점이 있습니다. 기다리는 동안에는 사용자가 대기를 취소할 방법이 없습니다. 목적지를 띄우고 나면 그 표면을 닫는 동작이 곧 취소가 됩니다. 대기가 길어질수록 빠져나갈 수단이 생기는 셈입니다.
띄울 때 내용을 일부만 보여주지 않습니다
확정된 것 몇 개를 먼저 보여주고 나머지를 나중에 붙이면 뒤늦게 붙는 내용 때문에 이미 보이던 요소의 위치가 밀립니다. 잘못 누름은 사용자가 누를 대상을 이미 겨냥하고 있을 때 생깁니다. 겨냥할 대상이 없는 상태라면 크기가 변해도 문제가 되지 않습니다.
띄운 뒤에는 로딩 영역 하나만 고정된 크기로 보여주고 확정되면 한 번에 교체합니다. 이 영역은 내용의 개수나 위치를 암시하지 않습니다. 실제 내용의 형태를 흉내 낸 자리표시자는 사용하지 않습니다. 최종 구조를 모르는 상태에서 그리는 스켈레톤은 지킬 수 없는 약속이기 때문입니다.
기다릴 수 없는 경우
다음에 해당하면 기다리지 않고 아래 대체 방법으로 내려갑니다.
- 트리거에 상태를 표시할 표면이 없는 경우. 길게 누르기나 쓸어넘기기처럼 제스처로 여는 경우가 해당합니다.
- 트리거 없이 나타나는 경우. 알림 딥링크처럼 화면에 곧바로 진입할 수밖에 없는 표면은 화면 단위의 로딩으로 다룹니다.
- 대기하는 동안 트리거가 사라지는 경우. 다른 오버레이 위의 항목이나 일시적으로만 떠 있는 표면 위의 버튼이 해당합니다.
- 나타난 표면 안에서 하는 입력이 내용을 결정하는 경우. 검색이나 필터가 포함된 선택 오버레이는 띄우기 전에 내용을 확정할 수 없습니다.
- 내용이 계속 도착하거나 갱신되어 확정 시점이 없는 경우.
기술적으로 가능하더라도 다음에 해당하면 다른 방법을 선택하세요.
- 실패할 가능성이 뚜렷한 경우. 트리거는 실패 사유와 다시 시도할 수단을 담을 수 있는 자리가 아닙니다.
- 대기하는 동안 트리거가 화면 밖으로 밀려날 수 있는 경우. 스크롤되는 영역 안의 트리거가 해당합니다. 사용자가 부르지 않은 표면이 뜨는 것처럼 보이므로 트리거가 화면에서 벗어나면 띄우지 않고 대기를 종료합니다.
- 확정을 기다려야 하는 내용이 전체 중 일부인 경우. 이미 확정된 부분까지 함께 붙잡지 않습니다.
대체 방법
위에서부터 순서대로 검토하세요.
- 미리 가져오기. 트리거가 나타나는 시점에 내용을 확정해 로딩 자체를 없앱니다.
- 기다리기. 기본 규칙입니다.
- 최종 레이아웃을 알 수 있다면 즉시 띄우고 최종과 같은 개수·같은 크기로 내용을 비활성 상태로 먼저 보여준 뒤 확정되면 활성화합니다. 개수가 달라질 수 있다면 이 방법을 쓰지 않습니다.
- 최종 레이아웃을 알 수 없다면 즉시 띄우고 개수·위치를 암시하지 않는 하나의 로딩 영역을 고정된 크기로 보여준 뒤 교체합니다.
- 로딩이 길거나 실패와 재시도를 다뤄야 한다면 오버레이 대신 화면 단위의 표면을 사용합니다.
- 그래도 띄운 뒤에 내용이 늘어나야 한다면 표면의 크기를 먼저 확보하고 늘어난 내용을 내부 스크롤로 흡수합니다.
선택지 목록을 여는 경우
액션시트, 메뉴시트, 드롭다운에서는 위 규칙이 특히 중요합니다. 항목이 여러 개 나열되고 표면의 높이가 항목 수에 따라 달라지기 때문입니다. 이 절은 그런 경우만 다룹니다.
다음 오버레이는 이 절의 대상이 아닙니다. 항목이 하나뿐이거나, 높이가 고정되었거나, 이미 최대 높이에 닿아 내부 스크롤로 동작하는 경우입니다. 항목이 바뀌어도 보이는 항목의 위치가 흔들리지 않습니다.
높이가 변하면 잘못 누름이 생깁니다
열린 상태에서 항목이 추가되거나 사라지면 오버레이의 높이가 변합니다. 사용자가 누르려던 항목의 위치가 바뀌어 다른 항목을 누르게 됩니다. 항목 형태를 흉내 낸 스켈레톤을 써도 마찬가지입니다.
아래쪽에만 항목을 덧붙이는 것도 안전하지 않습니다. 오버레이 자체의 높이가 커지면 이미 보이던 항목의 위치가 모두 움직입니다. 위치를 지키려면 높이를 먼저 확보하고 늘어난 항목을 내부 스크롤로 흡수해야 합니다.
실시간으로 로드하는 것이 맞는 경우
검색, 필터링, 더 불러오기는 애초에 기다림의 대상이 아닙니다. 이때는 로딩 UI의 위치를 역할별로 나눕니다.
- 최초 로드: 오버레이 안 중앙
- 추가 로드: 목록 하단
- 필터링: 입력 필드 안
항목이 하나도 없을 수 있는 경우
항목이 하나도 남지 않을 수 있다면 트리거 설계의 문제로 다룹니다. 로딩 처리로는 풀리지 않습니다.
1. 트리거의 표시 여부를 결정하는 정보는 트리거를 그리는 데 쓰는 데이터와 같은 응답에 있어야 합니다.
예를 들어 댓글마다 관리 메뉴 버튼이 있다면 그 사용자가 그 댓글에서 무엇을 할 수 있는지는 댓글 목록 응답이 이미 알고 있어야 합니다. 댓글마다 따로 확인하면 요청이 항목 수만큼 늘어납니다. 판정이 끝날 때까지 버튼을 그릴지 말지도 알 수 없습니다.
2. 조건과 무관하게 항상 제공되는 항목이 오버레이에 하나도 없다면 그 트리거는 조건부 트리거입니다. 항목을 채우지 말고 트리거의 노출 조건을 정의하세요. 비어 있는 상태를 피하려고 의미 없는 항목을 넣지 않습니다. 닫기 외에 할 수 있는 일이 없는 오버레이는 항목이 하나도 없는 것과 같습니다.
3. 판정은 트리거가 화면에 나타나는 시점에 끝냅니다. 트리거를 누른 뒤로 미루지 마세요. 그 결과 항목이 없다면 트리거를 누를 수 있게 두지 않습니다.
- 사용자가 조건을 바꿔 항목을 얻을 수 있는 경우에는 트리거를 비활성으로 두고 그 이유를 알 수 있게 합니다.
- 어떤 방법으로도 항목을 얻을 수 없는 경우에는 트리거를 노출하지 않습니다.
- 판정이 늦게 도착한다면 처음부터 그리지 않고 자리만 비워둔 뒤 판정 후에 채웁니다. 없던 것이 새로 생기는 편은 안전합니다. 반대로 있던 것이 사라지면 잘못 누름의 원인이 됩니다. 이미 노출한 트리거를 나중에 감추지 마세요.
- 표시 여부 판정은 화면 표현에만 쓰입니다. 실제로 실행할 수 있는지는 실행하는 시점에 다시 확인해야 합니다.
4. 그럼에도 항목이 0개로 확정되었다면 오버레이를 열지 말고 이유를 알립니다. 스낵바처럼 현재 화면을 가리지 않는 수단을 사용합니다. 할 수 있는 일이 없는 오버레이를 열어 사용자에게 닫는 동작까지 요구하지 않습니다.
5. 항목이 없는 이유가 결과가 아니라 실패라면 다르게 다룹니다. 통신이나 인증 문제로 항목을 가져오지 못했다면 실패했다는 사실과 다시 시도할 수단을 함께 제공합니다. "항목이 없음"과 "항목을 가져오지 못함"은 사용자가 할 수 있는 행동이 다르므로 같은 화면으로 표현하지 않습니다.
하지 않는 것
- 기다린 뒤 항목이 0개라는 이유로 아무 반응 없이 대기만 종료하기
- 로딩 표시를 띄웠다가 항목 없이 사라지게 하기
- 항목 수에 따라 트리거의 동작을 바꾸기. 항목이 하나일 때 오버레이를 건너뛰고 바로 실행하면 같은 트리거가 상황에 따라 다르게 동작하게 됩니다.
이미 가진 정보 먼저 보여주기
이전 화면에서 이미 확보한 데이터는 전환 직후 바로 그리세요. 로딩 처리는 아직 확보하지 못한 데이터에만 적용합니다.
목록에서 상세로 이동하는 경우가 대표적입니다. 목록을 그리는 데 쓴 썸네일은 상세 화면에서도 곧바로 보여줄 수 있습니다. 본문처럼 목록에 없던 데이터만 로딩 상태로 두고 도착하면 제자리에서 교체합니다.
스켈레톤이 차지하는 면적이 줄고, 체감 속도가 빨라지며, 사용자가 누른 그 항목이 이어진다는 시각적 연속성이 생깁니다.
교체할 때의 수칙
- 저화질에서 고화질로 교체할 때는 같은 크기의 공간에서 교체해 레이아웃이 움직이지 않게 합니다.
- 교체는 crossfade로 처리합니다. 최소 노출 시간은 두지 않습니다의 전환 수칙을 따릅니다.
- 이어받은 값은 최신 값으로 교체하는 것이 기본입니다. 다만 그 값이 사용자의 다음 행동의 근거라면(가격, 재고, 마감 시각 등) 조용히 바꾸지 않습니다. 값이 달라졌다는 사실을 사용자가 알 수 있어야 합니다.
로딩 UI를 놓는 자리
- 로드되는 콘텐츠와 가장 가까운 곳에 놓습니다. 로딩 UI의 위치가 곧 로딩의 범위를 뜻합니다. 화면 중앙에 있으면 화면 전체를, 카드 안에 있으면 그 카드를 뜻합니다.
- 넓은 영역에서는 컨테이너 중앙에 놓되 화면 안에 보이도록 합니다.
- 인접한 로딩 UI가 여러 개라면 하나로 합칩니다. 화면 곳곳에 로딩 UI를 흩어놓지 않습니다.
- 같은 동작에는 항상 같은 자리, 같은 종류의 로딩 UI를 씁니다.
전면 로딩
화면 전체를 덮는 로딩은 사용을 최소화합니다. 다음 중 하나에 해당할 때만 사용하세요.
- 중단되면 안 되는 처리를 하는 동안 조작을 막아야 할 때 (결제, 제출 등)
- 3초 이상 걸리면서 결과가 큰 맥락 변화를 만들 때
그 외의 경우에는 로딩되는 영역에만 로딩 UI를 두고 나머지 영역의 조작은 계속 가능하게 둡니다.
상황별 적용
로딩 상황은 발생하는 사례에 따라 여러 가지로 나눌 수 있습니다. 당근에서는 사용자가 경험할 수 있는 로딩 상황을 다음과 같이 구분할 수 있습니다.
첫 진입 시

사용자가 처음 서비스에 접속할 때 발생하는 로딩 상황입니다.
구조를 예측할 수 있으면 Skeleton을, 없으면 Progress Circle(불확정)을 사용합니다.
페이지 전환 시

페이지 간 이동 시 새로운 콘텐츠를 로드하는 상황입니다.
가능하면 트리거에서 기다렸다가 띄웁니다. 이미 가진 데이터가 있으면 먼저 그리고 나머지만 Skeleton으로 처리합니다.
더 많은 정보를 로드 시

무한 스크롤 혹은 지연 로딩을 사용하는 경우로, 사용자가 페이지를 스크롤할 때 추가 콘텐츠가 로드되는 상황입니다.
목록 하단에 Progress Circle(불확정)을 표시합니다. 이미 보이는 항목의 위치는 바뀌지 않습니다.
데이터 재요청 시

새로운 데이터를 받고 싶은 상황으로, 가장 많이 경험하는 상황은 PTR(pull to refresh)입니다.
기존 데이터를 화면에 유지한 채 갱신하고 로딩 UI는 국소적으로만 표시합니다. 갱신 결과는 마지막 갱신 시점으로 알립니다.
입력 데이터를 저장 혹은 제출할 시

로그인이나 인증 과정과 같이 데이터를 저장하고 제출할 때 서버와의 통신으로 인해 로딩이 발생하는 상황입니다.
제출 버튼에 loading 상태를 표시합니다. 되돌릴 수 없는 처리라면 전면 로딩으로 조작을 막을지 검토합니다. 전면 로딩으로 화면을 오래 점유한다면 Progress Circle 대신 에셋으로 대기를 표현할 수 있습니다.
상태를 전환했을 시

새로운 상태에 필요한 데이터를 서버에서 가져오거나, 새로운 상태에 필요한 이미지·비디오 등의 리소스를 로드할 때의 상황입니다.
결과를 미리 반영할 수 있으면 낙관적 업데이트를 적용하고 로딩 상태를 두지 않습니다. 서버가 결정하는 값이 포함되는 경우처럼 미리 반영할 수 없다면, 전환될 영역의 구조를 예측할 수 있으면 Skeleton을, 없으면 Progress Circle(불확정)을 사용합니다.
무엇으로 보여줄까
선택 기준
- 최종 구조를 예측할 수 있는가 — 로딩이 끝났을 때 무엇이 어디에 놓일지 지금 알 수 있는가
- 소요 시간을 알 수 있는가 — 진행률을 계산할 근거가 있는가
| 소요 시간을 알 수 없음 | 소요 시간을 알 수 있음 | |
|---|---|---|
| 최종 구조를 예측할 수 있음 | Skeleton | Progress Bar |
| 최종 구조를 예측할 수 없음 | Progress Circle(불확정) | Progress Circle(확정) |
기계적으로 적용하지 말고 화면과 액션의 맥락에 따라 고르세요. 넓은 영역을 오래 점유하는 로딩에는 에셋을 대신 쓸 수 있습니다.
로딩 요소
당근에서는 데이터 로딩 상태를 효과적으로 전달하기 위해 세 가지 주요 요소를 활용하고 있습니다.
| Progress Circle | Progress Bar | Skeleton | |
|---|---|---|---|
| 형태 | 원형으로 된 로딩 인디케이터입니다. | 선형 막대 형태로 가로로 표시됩니다. | 페이지의 콘텐츠 구조를 추상적으로 묘사한 형태로, 배경을 회색 톤으로 표시합니다. |
| 특징 | 구체적인 콘텐츠 레이아웃을 미리 보여줄 필요가 없을 때 사용됩니다. | 시작과 끝이 명확한 설치, 업로드, 다운로드 및 기타 연속적인 프로세스를 표시할 때 적합합니다. | 길거나 복잡한 데이터가 로딩될 때 사용자가 곧 로딩될 콘텐츠의 레이아웃을 미리 이해할 수 있게 합니다. |
| 장점 | 스크린의 공간을 많이 차지하지 않습니다. 다양한 상황에 쉽게 적용할 수 있는 범용적인 방법입니다. | 사용자가 어느 정도의 시간이 남았는지 파악할 수 있게 해줍니다. | 사용자에게 콘텐츠가 실제로 어떻게 배치될지를 예상할 수 있게 하여 기다리는 동안의 불확실성을 줄입니다. |
Progress Circle
Progress Circle은 원을 이용해 로딩 상황을 표시하는 요소로, 로딩 유형에 따라 두 가지 방식으로 사용할 수 있습니다.
| Determinate Progress Circle | Indeterminate Progress Circle | |
|---|---|---|
| 목적 | 로딩 프로세스가 완료될 때까지 남은 시간을 시각적으로 알림 | 로딩 프로세스가 진행 중임을 알림 |
| 동작 | 원을 처음부터 끝까지 채움 | 끊임없이 회전 |

불확정 로딩 지표는 프로세스의 진행이나 지속 시간이 가변적이거나 알 수 없는 경우 사용됩니다. Progress Circle은 가장 다재다능하게 사용할 수 있는 불확정 로딩 표현 요소이기도 합니다.

확정 로딩 지표는 프로세스의 진행 또는 지속 시간을 알거나 추정할 수 있는 경우에 사용될 수 있습니다. 다운로드, 업로드, 파일 변환과 같은 프로세스와 같이 오래 걸릴 가능성이 있는 프로세스에 적합합니다.
Progress Bar

Progress Bar는 직선 트랙을 따라 애니메이션을 적용해 진행 및 로딩 상태를 표시하는 요소로, 당근에서는 주로 외부 링크로 진입 시 로딩이 필요한 경우 사용됩니다.
Skeleton

Skeleton은 콘텐츠가 로딩 중임을 시각적으로 전달하여 사용자가 화면의 구조를 추상적으로 이해하도록 돕는 요소입니다. 이를 통해 사용자는 실제 콘텐츠가 더 빠르게 로딩되고 있다고 인식할 수 있습니다.
Skeleton은 주로 목록이나 카드 뷰와 같은 반복되는 데이터 기반의 화면 또는 곧 나타날 콘텐츠의 구조를 미리 보여줄 수 있는 페이지에서 사용됩니다.
공통 수칙
- 불확정에서 확정으로 바꾸기는 권장합니다. 다만 원형과 막대 사이를 오가는 전환은 하지 않습니다. 모양과 크기가 달라 화면이 튀고 사용자가 혼란을 겪습니다.
- 같은 동작에는 앱 전체에서 항상 같은 로딩 UI를 사용합니다.
- Progress Circle와 Skeleton은 동작 줄이기 설정과 무관하게 평소와 동일하게 동작합니다. 멈춰 보이는 Progress Circle는 시스템이 멈춘 것으로 읽힙니다. 동작을 줄이는 쪽보다 해롭습니다.
Skeleton 사용 수칙
- 실제 레이아웃을 닮게 그리되 모든 요소를 다 그리지는 않습니다. 화면 앞부분의 몇 개만으로 구조는 충분히 전달됩니다.
- 남는 요소는 스켈레톤 대신 같은 크기의 빈 공간으로 자리만 예약해도 됩니다. 이미지 영역이 대표적입니다.
- 최초 로딩에만 사용합니다. 이미 데이터가 있는데 재조회하거나 필터를 바꿀 때는 스켈레톤을 쓰지 않고 기존 콘텐츠를 유지합니다.
- 한 컨테이너 안의 콘텐츠는 가능하면 한꺼번에 채웁니다. 순차적으로 채워야 한다면 위에서 아래 순서로 교체합니다.
- 최종 항목 수를 모르는 상태에서 항목 형태의 스켈레톤을 그리지 않습니다. 스켈레톤은 최종 레이아웃에 대한 약속입니다.
레이아웃이 명확하고 다양한 데이터가 로드되는 화면

레이아웃이 명확하고 이미지와 텍스트의 조합으로 이루어진 제품 상세 페이지의 경우 Skeleton을 사용하는 것이 적합합니다. 이러한 페이지는 콘텐츠 구조가 고정되어 있어 로딩 후에도 큰 변화가 없기 때문에, Skeleton을 사용하면 사용자가 페이지가 준비되고 있음을 시각적으로 쉽게 이해할 수 있습니다.
곧 나타날 콘텐츠의 구조를 미리 보여주고 싶은 경우

동네생활 상세 페이지의 댓글 영역에서 댓글은 대체로 반복적인 형태를 가지고 있기 때문에, Skeleton을 통해 사용자는 곧 로드될 댓글의 구조를 쉽게 예측할 수 있습니다. 이는 사용자가 어떤 콘텐츠가 표시될지를 쉽게 상상할 수 있게 합니다.
Progress Circle 사용 수칙
로딩의 범위가 한정적인 경우

Progress Circle는 공간이 제한되고 로딩 범위가 한정된 경우에 적합합니다.
예를 들어 채팅 화면에서 메시지를 보내는 동안 로딩이 발생하면, 로딩이 발생하는 대화창 옆에 Progress Circle을 표시하여 해당 메시지가 전송 중임을 나타낼 수 있습니다.
버튼 내에 표현되는 경우 Progress Circle를 참조해주세요.
에셋으로 표현하기
Progress Circle 대신 움직이는 에셋으로 대기를 표현할 수 있습니다.
에셋은 진행 상황을 전달하지 못합니다. 소요 시간과 최종 구조를 모두 알 수 없고, 로딩이 화면 전체에 준하는 넓은 영역을 점유할 때만 사용하세요. 좁은 영역이나 트리거 위에는 Progress Circle을 씁니다.
10초를 넘길 수 있는 로딩에는 에셋만 두지 않습니다. 진행 상황이나 예상 소요 시간을 함께 전달할 수단을 두세요.
낙관적 업데이트
결과를 로컬에서 예측할 수 있는 조작은 서버 응답을 기다리지 않고 즉시 화면에 반영합니다. 좋아요, 팔로우, 토글, 체크박스처럼 상태가 둘 중 하나로 바뀌는 조작이 해당합니다.
적용 조건 — 아래를 모두 만족할 때만 적용합니다.
- 실패 확률이 낮음
- 실패했을 때 이전 상태로 되돌릴 수 있음
- 결과를 로컬에서 정확히 예측할 수 있음
적용하지 않는 경우
- 되돌릴 수 없는 조작 (결제, 삭제, 전송 등)
- 실패했을 때 사용자가 이미 다음 행동으로 넘어갔을 수 있는 조작
- 서버가 결정하는 값이 결과에 포함되는 조작
실패했을 때
- 이전 상태로 되돌리되 되돌아갔다는 사실을 사용자가 알 수 있게 합니다. 조용히 되돌리기만 하면 사용자는 자신의 조작이 반영된 것으로 착각한 채 화면을 떠납니다.
- 스낵바 등으로 실패 사실을 알리고 필요하면 다시 시도할 수단을 제공합니다.
낙관적 업데이트를 적용하면 로딩 UI가 필요 없습니다. 폼 요소에 loading 상태를 두지 않는 이유가 여기에 있습니다.
로딩 과정의 단계

로딩 상태는 사용자와 시스템 간의 상호작용에서 필수적인 부분이며, 각 단계에서 적절한 피드백을 제공함으로써 사용자 경험을 향상시킬 수 있습니다. 로딩 상태는 다음과 같은 단계로 구성됩니다.
이와 같은 로딩 과정의 단계를 '동네 인증하기 시나리오'를 통해 각 단계를 설명하겠습니다.
시작 단계

사용자가 페이지 요청이나 데이터 제출과 같은 특정 작업을 시작하면 로딩이 시작됩니다. 이때, 사용자에게 로딩이 시작되었음을 알리는 초기 피드백을 제공합니다.
동네 인증 시 인증에 진입하는 순간 로딩이 진행 중임을 Progress Circle로 안내하며, 로딩이 200ms 이내에 완료되는 경우에는 표시하지 않습니다.
진행 단계

데이터 전송 및 서버 처리가 진행되는 단계입니다. 사용자에게 로딩이 진행 중임을 지속적으로 알리고, 진행 상황이 길어지는 경우 시각적으로 표현하여 로딩 상황을 예측할 수 있게 합니다.
동네 인증 시, 위치 정보를 가져오는 과정에서 첫 실행 후 5초가 지나면 안내 메시지를 통해 로딩이 진행 중임을 사용자에게 알리고 있습니다.
완료 단계

로딩에 성공했을 시 간단한 로딩의 경우 완료 메세지가 필요하지 않으며, 피드백이 필요한 상황의 경우 성공 메세지와 같은 성공 표시가 포함될 수 있습니다.
동네 인증 시 인증이 성공적으로 완료되면, 현재 위치를 기반으로 해당하는 동네 정보를 사용자에게 제공합니다.
실패 단계

로딩에 실패한 경우, 사용자에게 무엇이 잘못되었는지와 다음에 어떤 조치를 취할 수 있는지를 설명하는 오류 메시지가 표시되어야 합니다.
동네 인증 시 로딩이 시작된 지 10초 후에도 완료되지 않으면 실패 메시지를 표시하고, 사용자가 재시도할 수 있는 옵션을 제공합니다.
Related Documents
Last updated on