프린세스 다이어리

브라우저 로딩 동안의 사용자 경험을 개선하는 방법들 본문

UX

브라우저 로딩 동안의 사용자 경험을 개선하는 방법들

개발공주 2021. 10. 7. 22:39
728x90

이 글을 임시저장을 하며 조금씩 쓰고 있는 차에, 마침 오늘 코드 리뷰 도중에 경력직 신규 입사자 분께서 질문을 하셨다. "혹시 네트워크 환경이 좋지 않을 때도 대비하여 작업하시는 건가요?" 화면을 공유하시곤, 크롬 네트워크 창에서 있는지도 몰랐던 작은 드롭다운을 열어 '빠른 3G' 탭을 클릭했다. 그리곤 우리가 열심히 작업한 웹뷰 페이지를 새로고침 했더니 안타까운 모습이 나타났다. 기본적인 마크업 화면에 데이터가 받아와지지 않은 휑한 모습으로 뻘쭘하게 몇 초를 기다린 후에야 온전한 페이지가 나왔다.

 


 

서비스를 구상하고 실제로 구현할 때 꼭 염두에 두어야 하는 UX 벌집 모형이 있다. 바로 2004년 피터 모빌이 제시한 "User Experience Honeycomb"이다. Useful, Usable, Desirable, Valuable, Findable, Credible, Accessble 이렇게 7개의 단어로 사용자 경험을 정의하고 평가할 수 있다. 이 7개의 항목이 충분히 만족된다면, 서비스에 대하여 사용자에게 경험적 가치가 증가한다는 논리다. 

 

 

그런데 여기서 알아야 할 사실은, 사용자 입장에서 3초 동안 웹사이트가 느리다는 판단이 드는 순간 벌집이고 뭐고 이탈률이 급격히 높아지기 때문에 위의 항목 중 대부분이 거의 무용지물이 된다는 것이다. 사람들은 1/10초 정도의 짧은 지연까지 감지할 수 있기 때문에, 1초만 지연되어도 집중력이 저하되고 신뢰감도 하락한다. 앞으로 새로운 페이지들을 몇 개나 방문할 것인데, 그때마다 몇 초씩 리소스를 기다려야 할 것이라는 예측은 금방 가능하다. 구글 리서치 자료에 따르면, 모바일 웹사이트의 로딩 시간이 3초 이상일 때 사용자의 32%, 5초 이상은 90%, 6초 이상은 106%, 마지막으로 10초가 넘으면 123%의 이탈률이 발생한다고 한다. 뭔가 답답하고 느리다는 생각이 든다는 인식이 들면 몇 초 지나지 않아 가차 없이 꺼버린다는 것이다.

 

구글에서 조사한 로딩시간 별 페이지 이탈률

 

사이트 속도는 서버의 반응 속도, 페이지나 그 구성 요소의 처리 속도, 브라우저에 의한 화면 렌더링 속도 등에 의해 크게 결정이 되기에, 절대적인 사이트 속도를 개선하는 것이 사용자 경험을 개선하고 이탈률을 막는 데 도움이 된다. 그런데 한편, 이렇게 웹사이트의 성능만을 최적화한다고 해서 사용자 경험이 갑자기 크게 좋아진다고 할 수는 없는 것 같기도 하다. 실제로 기술의 발전에 따라 인터넷 속도가 높아지고 사이트 최적화를 위해 수많은 프로그래머가 애쓰는 것에 비하면 최근 웹사이트의 로딩 속도는 10년 전에 비해 월등하게 빠르지는 않다는 조사 결과가 있다. 

 

 

위의 그래프는 모바일 페이지의 로딩 시간과, 미국의 평균 인터넷 속도를 함께 나타낸 것이다. 인터넷 속도가 꾸준히 증가하고 그 사이에 프로그래밍 기술력도 좋아졌을텐데, 모바일 페이지의 로딩 시간도 함께 증가하거나 일정 기간 동안 거의 그대로인 결과를 확인할 수 있다. 현실의 대다수의 사용자가 웹사이트를 접근했을 때 지루함을 느끼고 이탈하는 것을 막기 위해서는 사이트 성능뿐만 아니라, 시각적인 측면으로도 개선을 하여 로딩을 기다리는 시간 동안 긍정적인 인식을 주어야 한다는 것을 알 수 있다. 

 

브라우저의 로딩 시간 동안 사용자의 지루함을 덜기 위해서, 디자인적인 측면과 프로그래밍의 측면에서 개선할 방법은 수도 없이 많다. 우리 회사에서 적용하고 있는 것, 그리고 앞으로 적용하고 싶은 것들을 위주로 정리해보려 한다. 

 

1. UI/UX로 개선하기

 

1-1. 로딩 인디케이터

 

로딩 인디케이터에는 스피너와 프로그레스 바가 있다. 다음에 제공될 콘텐츠를 기다리는 동안, 사용자에게 아무런 정보가 없는 흰 화면은 사람을 지치게 만든다. 물리적인 시간과 거리는 정확하게 예측하고 계산할 수 있지만, 기다림에 대한 사람들의 인식은 물리학이 아닌 심리학의 영역이기 때문이다. 스피너와 프로그레스 바와 같은 로딩 상태를 알려주는 로딩 인디케이션은 시스템이 잘 작동되고 있는지, 에러가 발생했는지, 혹은 적어도 본인 주변의 네트워크 상황이 좋지 않은지에 대해 힌트를 얻고 해결할 수 있는 실마리를 준다. 따라서 기약 없이 빈 화면을 보고 있는 것보다는 체감상 빠르게 로딩이 되는 것처럼 느껴질 수 있다. 

 

로딩 인디케이터의 장점은 다음과 같다.

  • 시스템이 충돌하지 않고, 계속 처리시킴을 알려 사용자를 안심시킴.
  • 사용자가 얼마나 기다려야 하는지 예상 가능
  • 사용자가 볼 수 있도록 시각적인 것을 제공

성능이 좋은 환경이나 로딩이 거의 필요 없는 창에서는 로딩 인디케이터를 사용하지 않는 것이 권장된다. 잠깐 비쳤다가 사라지면 오히려 사용자들이 불안감을 느끼게 하고, 기다리지 않았음에도 기다려야 한다는 사실을 인식하여 느리다고 느낄 수 있다. 

 

로딩 인디케이터는 로딩 시간에 따라 다음과 같이 사용하는 것이 권장된다.

  • 1초 미만일 때: 아무것도 사용하지 않기
  • 1초 이상~3초 미만일 때: 스피너
  • 3초 이상일 때:  프로그레스 바
  • 멈추어 있으면 안됨

 

스피너 로딩

스피너 로딩은 무한루프 애니메이션으로 계속해서 반복된다. 대부분의 스피너는 1~2초로 짧은 로딩 시간에 사용이 되기 때문에 로딩 시간이 얼마나 걸리는지 정확한 정보를 담고 있지 않다. 만약 로딩이 3초 이상이 걸리는 곳이라면, 사용자가 계속 반복만 되는 모습을 보고 오류가 났다고 받아들일 수 있다. 이런 경우 스피너 로딩에 남은 퍼센트 또는 시간에 대한 정보를 담는다면 사용자가 진행상황을 보면서 기다릴 수 있게 된다.

 

프로그레스 바 로딩
프로그레스 바 로딩

 

프로그레스 바 로딩은 스피너 로딩과 다르게, 시각적으로 남은 시간의 정보를 보여주고 있다. 어느 정도 로딩이 된 상태인지 정보를 전달하기 때문에 하염없이 스피너만 빙빙 도는 빈 화면을 바라볼 때보다는 진행이 되고 있다는 안심을 줄 수 있다. 프로그레스 바는 시각적인 정보와 수치 정보 이 두 가지를 담을 수 있어 긴 로딩 시간에 사용하기 적합하다. 또한 사용자는 앱 성능보다는 사용자의 인터넷 연결 또는 장치 속도에 대해 불평할 가능성이 높다.


1-2. 애니메이션 사용하기

 

어떤 사용자도 로딩을 긍정적으로 인식하는 사람은 없을 것이다. 하지만 귀엽고 재밌는 애니메이션으로 로딩 화면을 꾸민다면 기다리는 시간을 덜 따분하게 느끼게 할 수 있다. 

 

재밌거나 창의적인 로딩 인디케이터는 사용자의 인식을 분산시킬 수 있다.

  • 눈에 띄는 색상 조합
  • 흥미롭거나 귀여운 아이디어
  • 브랜드 이미지 부여
  • 각종 팁이나 정보 보여주기

 

에어비앤비 브랜드 아이덴티티를 넣은 애니메이션 로딩
크롬 로고를 활용한 애니메이션 로딩

 

로딩 인디케이터와는 다소 거리가 있는 내용일 수 있지만 개인적으로 정말 좋아하는 로딩 애니메이션은 구글의 크롬 브라우저 디노 게임이다. 우리나라에서는 인터넷 연결이 불안정한 환경이 흔치는 않기 때문에 거의 디노 게임을 볼 일이 없지만, 데이터 연결이 불규칙한 인도, 브라질, 멕시코 또는 인도네시아와 같은 값비싼 모바일 데이터를 사용하는 시장에서는 매우 많은 횟수로 플레이된다고 한다. 대단한 컴퓨팅 성능이 필요하지도 않으면서, 인터넷 연결이 안 되고 있는 환경 속에서도 충분히 즐거움을 줄 수 있는 아이디어다.

한 달에 2억 7천만 회 재생되는 크롬의 디노게임

 

 

1-3. 스켈레톤 UI

 

스켈레톤 UI는 페이지의 빈 공간에 텍스트 및 이미지 리소스를 서버에서 받아와서 점진적으로 채워질 자리를 회색 또는 중간 톤의 채워진 모양으로 먼저 위치시켜 두는 것이다. 네트워크 대기 시간 동안 스켈레톤이 내용을 대체하여 보이게 되다가, 리소스가 도착하면 실제 사이트 콘텐츠로 대체된다. 

 

링크드인 홈 스켈레톤
유튜브 홈 스켈레톤

 

그런데 위의 사진과 같은 스켈레톤 화면을 스피너나 프로그레스 바, 애니메이션이 아닌 정적인 박스들로만 채운다면 현재 웹사이트 로딩이 진행 중인 건지 오류가 난 건지 알 수가 없다. 그래서 스켈레톤 화면은 거의 애니메이션 효과를 추가해서, 현재 브라우저가 멈추지 않았다는 인식을 사용자에게 준다. 

 

로딩중 스켈레톤 UI를 적용한 페이지

 

정적인 스켈레톤보다 동적인 스켈레톤을 보고 사용자들이 더 짧은 시간을 기다린다고 생각한다. 또 왼쪽에서 오른쪽으로의 웨이브 애니메이션을 더 짧게 인식을 한다. 물론 이 부분은 오른쪽에서 왼쪽으로 글을 읽는 몇몇 문화권에서도 비슷하게 생각할지는 모르겠다. 

 

1-4. 낙관적 UI(Optimistic UI)

 

낙관적 UI는 흔히 이벤트가 미리 발생된 것처럼 표시하고, 나중에 실제 업데이트된 상태를 표시한다는 아이디어다. 예를 들어 채팅 UI에서, 아직 서버의 응답을 받지 않은 상태지만 채팅이 이미 간 것처럼 대화를 일단 띄워 놓고 로딩을 하는 것이다. 이미 빠르게 작동한 척 일종의 거짓말이라고 할 수 있는데, 오류 시에는 모달 창으로 띄우거나 실패했다는 메시지를 띄우면 된다. 이전에 보았던 스켈레톤이나 애니메이션도 당장 동작을 하고 있는 것처럼 보여주는 원리이니 일종의 낙관적 UI라고 할 수 있다. 

 

낙관적 UI를 보여주는 방식은 다음과 같다. 

  • 이벤트 발생
  • 성공한 것처럼 상태(로컬) 업데이트
  • 요청 시작
  • 오류가 생겼는지 파악, 생겼다면 전 상태로 롤백[기준은 보통 2초]

낙관적 UI는 대화 앱에서 흔히 보인다.

 

단점은 해당 장면이 로딩 중인 건지, 오류가 난 건지 파악하기 힘들다는 것이다. 카카오톡에서도 먼저 클라이언트 측 UI에서 말풍선이 띄워지고 나서 보내지는 방식이기 때문에 가끔가다 인터넷 환경이 안 좋으면 이미 전송이 된 줄 알았는데 나중에 확인해 보니 전송되지 않은 상황을 종종 경험한다. 오류 시에는 모달 창으로 띄우거나 실패했다는 메시지를 띄우는 등의 UI 표시를 해 주어야 한다.

 

일단 UI에 업데이트하고 나중에 실패하면 문구나 안내팝업을 띄운다.

 

 


2. 프로그래밍으로 개선하기

 

구글에서는 사용자가 웹페이지 로딩에 대해 빠르고 느림을 느낄 수 있는 여러 순간을 정의하고, 성능을 측정하는 지표로 사용한다. 브라우저가 서버로부터 리소스를 받아 와 화면에 그려주는 작동 방식은 다양한 방식으로 설명할 수 있겠지만 여기에서는 시각적인 요소들이 어느 시점에 그려지는지가 중요하기 때문에 이 기준에 맞추어 정리해보았다. 

 

웹페이지가 그려지는 과정을 나타낸 그림이다.

 

2-1. TTFB(Time to First Byte)

 

사용자가 웹사이트를 호출하면, 웹 서버에서 수신한 첫 번째 바이트가 도착하는 시점이다. 즉, HTTP 요청에 걸리는 시간 + 서버의 요청 처리 시간 + 서버에서 클라이언트까지의 응답 시간이라고 보면 된다. 따라서 TTFB는 서버 성능과 직결된다.

TTFB가 왠지 익숙하다 했더니, 크롬 네트워크 창에서 자주 보이는 용어였다.

최적화 방법:

서버 애플리케이션 로직 최적화
DB 쿼리 최적화 또는 더 빠른 DB로 마이그레이션
더 많은 RAM 또는 CPU를 갖도록 서버 하드웨어 업그레이드
의도치 않은 리다이렉트 바로잡기
애플리케이션 캐시 활용
요청할 origin에 preconnect : <link rel="preconnect"> 
지나치게 큰 네트워크 payload를 피하기 
과도한 DOM size를 피하기
중요한 요청의 depth를 최소화 
요청 수를 줄이고 전송 크기를 작게 하기

 

프론트엔드 개발자로서 할 수 있는 일은 리다이렉트가 발생하고 있는 부분을 최대한 줄이고, 애플리케이션 캐시를 잘 활용하는 것이다. 웹페이지 주소뿐 아니라, 이미지와 스타일시트, 자바스크립트와 같이, 페이지를 구성하는 요소도 리다이렉트 될 수 있다. 301이나 302 상태 코드가 발생하는 요소들을 찾아, 리다이렉트가 의도치 않게 발생하고 있는 부분은 성능 향상을 위해 바로잡아야 한다.

 

한 리소스 파일 하나를 요청하는 데 긴 대기 시간(135.47ms)과 다운로드 시간(3.05ms)가 소요된다

 

또, 리다이렉트 작업을 마치고 HTTP 요청을 처리하기 위한 준비를 마쳤다면, 브라우저는 맨 먼저 서버로 요청을 보낸다. 응답이 오면 개별적인 리소스(이미지, css, js)가 사용자 PC에 있는지 캐시 데이터를 찾게 된다. 브라우저와 서버 사이의 통신을 최소한으로 하기 위해, 다시 방문한 사용자에게 좀 더 빠른 응답속도를 제공하려면 애플리케리션 캐시를 잘 활용하면 된다. 

 

 

2-2. FP(First Paint)

흰 화면에서 무언가가 처음으로 그려지기 시작하는 순간이다.

 

2-3. FCP(First Contentful Paint)

DOM 콘텐츠(텍스트, 이미지, 캔버스 렌더링 등)가 표시되기 시작하는 시점이다. 이 시점은 사용자가 이 웹 사이트가 실제로 동작한다고 인식하도록 해 주기 때문에 중요하다. 즉, 이 시점이 빠를수록 사용자 이탈률이 유의미하게 줄어든다는 것이다.

 

최적화 방법:

- 렌더링을 block 하는 리소스 제거
- CSS, js minify
- 사용하지 않는 CSS 제거

 

CSS 경량화 전 코드

body {}
h1 {
  color: blue;
}

 

CSS 경량화 후 코드

h1{color: blue;}

 

2-4. FMP(First Meaningful Paint)

사용자에게 의미 있는 콘텐츠(Hero elements)가 그려지는 시점이다. CSS, 자바스크립트 로드가 시작되고, 스타일이 적용되어 콘텐츠를 읽을 수 있지만 아직 이벤트리스너는 추가되지 않은 상태다. 사용자는 이 시점에서 페이지가 완전히 로드되었다고 인식한다. 참고로, FMP는 페이지 로드의 작은 차이에 지나치게 민감하기 때문에 일관성이 떨어져서 LCP(Largest Contentful Paint)를 측정하는 것을 권장하고 있다. 페이지에서 가장 용량이 큰 콘텐츠가 렌더링 되는 시점을 이야기한다. 로딩이 끝날 때까지 흰 화면 대신 의미 있는 이미지를 먼저 보여주면 사용자에게 긍정적인 인상을 줄 수 있다.

 

FCP 시점과 LCP 시점 차이

 

최적화 방법:

 

1. 느린 서버 응답 시간 해결

  • 서버 최적화
  • 가까운 CDN으로 라우팅
  • asset 캐싱
  • cache-first HTML 페이지 서빙
  • 서드파티 origin 리소스 preconnect

2. 렌더링을 block 하는 JS 및 CSS 해결

  • CSS block 시간 단축 - CSS Minify, 중요성 낮은 CSS는 defer, 중요 CSS는 inline으로 load
  • JavaScript block 시간 단축 - JS 파일 minify 및 압축

3. 느린 리소스(img, svg, video,...) 로드 시간 해결

  • 이미지 최적화 및 압축
  • 중요한 리소스 preload
  • 텍스트 파일 압축
  • 적응형 리소스 서빙 - 예: 네트워크가 느린 경우 video 대신 image 서빙
  • 서비스 워커를 통한 자산 캐싱

4. 클라이언트 사이드 렌더링의 경우

  • 중요한 자바스크립트 최소화 - js minify, 미사용 js defer, 미사용 polyfill 최소화
  • 서버사이드 렌더링 사용 - TTFB 증가, TTI 증가 고려해야 함
  • pre-rendering 사용 - TTI는 증가할 수 있으나, TTFB는 SSR보다 나음.

 

2-5. TTI(Time to Interactive)

자바스크립트의 초기 실행이 완료되어, 사용자와 페이지가 상호작용 가능하게 될 때까지의 시점이다. 대부분의 페이지 속도 테스트는 이 값을 기초로 사용한다. 콘텐츠를 볼 수 있지만 스크롤할 수 없거나 항목을 클릭해도 효과가 없으면 해당하지 않는다.

 

최적화 방법:

가비지 컬렉션으로 쓰레기 메모리 해제
DOMContentLoaded 활용
메인 스레드 작업 최소화 

 

DOMContentLoaded 이벤트와 onload 이벤트는 모두 "이제 모든 준비가 완료됐으니 마음대로 DOM을 조작해도 된다"라는 의미다. 그러나 두 이벤트는 발생 시점이 다르다.

 

1. domLoading
2. domInteractive
3. domContent Loaded
4. domComplete
5. onload 


onload 이벤트는 DOM에서 기본으로 제공하는 이벤트로 문서에 있는 모든 이미지, 스타일시트, 자바스크립트 등이 모두 다운될 때마다 발생한다. 이와 달리 DOMContentLoaded 이벤트는 기본적으로 DOM 생성에만 관련돼 있다. 이미지나 다른 요소를 다운로드하는 것과 관계없이 DOM이 로딩되고 난 직후에 발생합니다. 많은 양의 이벤트를 바인딩해야 하고 이미지나 스타일시트의 개수가 많은 페이지를 개발한다면 onload 이벤트보다는 DOMContentLoaded 이벤트를 이용하면 이전보다 체감 속도가 더 빨라진다.

 

필요 없는 메모리 해제를 담당하는 가비지 컬렉션 기능이 제대로 동작하지 않으면 브라우저가 응답 없음 상태가 되거나 실행 속도가 급격히 느려지게 된다. 웹 페이지에서 동시에 많은 변수가 생성되고 처리되는 동안 브라우저에서 허용한 임계치를 넘었을 때 가비지 컬렉션이 동작하게 되고, 가비지 컬렉션이 동작하면 스크립트 실행이 중단된다. 가비지 컬렉션이 완료되기 전까지는 스크립트가 동작하지 못해 페이지가 느려지고 사용자가 조작하기 어렵게 된다.

또 DOM(Document Object Model)의 생성과 삭제가 빈번한 페이지, 한 페이지에 다수의 AJAX 통신이 필요한 페이지, 이벤트 바인딩 수가 많은 페이지, 사용자 체류 시간이 긴 페이지를 개발할 때는 필요 없는 변수나 오브젝트 삭제, 이벤트 해제 등을 활용해 메모리를 관리해야 한다. 자바스크립트의 변수와 객체, DOM 객체, AJAX 통신 등을 사용할 때 성능을 높이려면 코드 스타일, 렌더링, UI 스레드와 타이머 사용, DOM 스크립팅, AJAX와 다이내믹 로딩 방법 등이 있다.

 

 

 

728x90
Comments