본문 바로가기

연재작/WEB - FE

웹은 당신의 눈을 속인다 (1) Web Browser는 이미 준비된 척을 한다.

언제부턴가 브라우저에서 페이지가 로딩되는 속도는 너무나 빨라졌다.

컴퓨터의 성능, 통신 기술의 향상으로 어쩌면 당연하다 할 수도 있겠다.

하지만 이게 정말 통신 기술, 하드웨어 발전만으로 빠르게 로딩되는 것일까?

 

그렇다면 그러한 발전에도 불구하고 느린 페이지가 여전히 존재하는 이유들은 무엇일까?

어쩔때는 느린 페이지에서 보여주는 자료의 양보다 빠른 페이지에서 보여주는게 더 많은 경우도 있다.

인터넷 회선 제공자들이 특정 서버에 더 빠른 데이터 속도를 보장해주는가? 아니다. 

모두 같은 양과 속도의 데이터를 서비스로 제공할 수 있다. 페이지에도 성능이 존재한다는 것이다.

 

Google은 2020년에 페이지의 성능이 알고리즘을 통한 검색 순위에 영향을 줄 것이라 공표한 바 있다.

그리고 지금 2024년의 developer 문서에는 대놓고 사용자 경험과 검색 순위에 대한 목차를 만들어 놓았다.

무엇이 문제인지 모르는 사용자는 불쾌한 경험의 화살표를 웹브라우저인 chrome으로 돌릴 수 있기에,

페이지의 성능은 굉장히 중요하다. 

 

그렇다면 어떻게 유저에게 좋은 성능을 제공할 수 있을까?

 

영화나 드라마에서 연인이나 좋아하는 사람을 자신의 집에 초대할 때 나오는 클리셰가 있다.

데리고 왔는데 집이 엉망이라 잠깐 밖에 세워두고 구석에 잡동사니를 쑤셔박아 일단 깔끔하게 보이게 해둔다.

그리고 상대방이 잠시 시간을 주거나 간다면 추가적인 정리를 하는 모습이다.

 

이것이 웹에서도 그대로 적용된다.

다만 좀 더 깊게 들어가보자. 시대가 발달하면서 주고 받는 데이터의 양도 정말 많아졌다.

개발자는 한정된 시간 내1. 외관상으로 깔끔하게 정리된 모습을 보여주고 2. 디테일한 부분을 추가해야 한다.

이를 위해서 개발자들은 수 많은 방식을 만들어냈다. 이번 글에서 소개할 Asynchronous Loading, Lazy Loading, Skeleton UI 등이 그것이다.

디테일한 부분은 다양하지만 이를 관통하는 원리는 다음과 같다고 생각한다.

중요해 보이면 먼저 로딩하기

 

로딩의 순서를 앞이나 뒤로 조절해서 사이트가 정상적으로 기능하는 척을 한다.

 

 

이를 설명하기 위해서 우선 Web Browser가 어떻게 페이지를 생성하는지에 대해 간략하게 알아볼 필요가 있다.

사용자는 서버로부터 html파일을 받아와서 parsing한 후, 한 줄씩 읽으면서 각각의 요소에 대해 DOM 트리를 그린다.

그리고 도중에 CSS 파일로 부터 각 요소별로 CSSOM 트리를 그리고, 이 두 개의 트리를 합쳐 Render 트리를 만든다.

이 Render 트리의 각 노드의 위치와 크기를 계산하고 (레이아웃)

노드를 픽셀로 변환해서 실제로 화면에 그린다. (페인팅)

여러 레이어로 나누어진 페이지의 경우 이를 최종 합성한다. (컴포지팅)

웹에서는 위와 같은 전반적인 과정을 Rendering이라고 부른다.

 

 

사용자가 페이지의 성능을 체감하는 부분은 다음과 같은 상황에서 발생한다.

 

그 요소중에 css나 js, 또는 img를 reference로 하는 요소들이 있다면

브라우저는 기본적으로 DOM 트리를 만드는 것을 중단한다.

그리고 해당하는 소스 파일을 서버로 부터 요청(이를 fetching이라고 함)해서 로드한 후 이 소스를 실행한다.

만약 이때 로드하는 css, js파일이 생각보다 크다면? 

그만큼 DOM 트리를 구성하는 시간이 느려질 것이고 렌더링이 지연될 것이다.

페이지가 늦게 완성되는 만큼 사용자는 이 웹 서비스는 느리구나 라고 인식을 하게 된다.

이를 방지하기 위해서 다음의 방식을 쓴다.

 

1. Defer와 Async를 활용한 JavaScript 로드 최적화

  • Defer: script defer src="a.js" 를 통한 방식이다. defer 속성은 JavaScript 파일을 HTML 문서 파싱 후에 실행되도록 설정한다. 이로 인해 HTML과 CSS가 먼저 로드되어 사용자에게 사이트가 빠르게 로드된 것처럼 보이게 한다. 중요한 스크립트가 페이지 로드 후 실행될 때 유용하다.
  • Async: script async src="a.js" 의 async 속성은 스크립트를 비동기적으로 로드하고, 다운로드가 완료되는 즉시 실행한다. 이를 통해 페이지 로딩이 지연되지 않도록 할 수 있지만, 실행 순서가 보장되지 않기 때문에 종속성이 있는 스크립트에는 신중하게 사용해야 한다.

위가 html 파싱 도중 3개의 JS파일 3개를 defer 속성을 통해 로드하는 과정이고,

아래는 3개의 JS파일을 Async 속성을 통해 로드하는 과정을 도식화 한 것이다.

두 개 모두 parsing을 하면서 fetching을 동시에 한다는 점에서 동일하지만

전자는 HTML의 파싱이 다 된 후에 JS파일들을 실행하고

후자는 fetching을 통한 로드가 완료되자마자 HTML parsing을 멈추고 바로 실행 하는 것을 보여주고 있다.

출처 : youtube 드림코딩 (https://youtu.be/tJieVCgGzhs?si=gF895wXKIsGl4stl)

 

2. Critical Rendering Path 최적화

  • Critical CSS: 초기 페이지 로드 시 가장 중요한 스타일(CSS)만 빠르게 적용하기 위해 Critical CSS를 <head>에 포함시킨다. 나머지 스타일은 페이지 로드 이후에 별도로 로드한다. 이를 통해 페이지가 빠르게 렌더링되며, 사용자에게 사이트가 준비된 것처럼 보이게 할 수 있다.

3. Preloading & Prefetching

  • Preload: link rel="preload" 를 사용하여 중요 리소스를 미리 로드할 수 있다. 예를 들어, 메인 배너 이미지나 중요한 웹 폰트를 미리 로드하여 사용자가 사이트에 도착했을 때 즉시 콘텐츠를 볼 수 있게 한다.
  • Prefetch: link rel="prefetch"를 사용하여 사용자가 곧 방문할 가능성이 있는 페이지의 리소스를 미리 로드한다. 사용자가 해당 페이지로 이동하면 거의 즉시 로드된다.

4. Font Display & FOUT (Flash of Unstyled Text)

  • Font Display: 웹 폰트를 로드하는 동안 텍스트가 비어 보이지 않도록, 기본 폰트로 먼저 텍스트를 표시하는 방식이다. 사용자는 텍스트가 즉시 보이기 때문에 사이트가 빠르게 로드된다고 느낄 수 있다. 이후 웹 폰트가 로드되면 자연스럽게 교체된다.
  • FOUT: 웹 폰트가 로드되기 전에 시스템 폰트로 텍스트를 잠시 보여주는 방식으로, 페이지가 텍스트 없이 빈 상태로 보이는 것을 방지할 수 있다.
  • 이 두 방식은 솔직히 불완전한 방식이며, 말하자면 빨리 보여주는 대신 미완성인 모습을 보여주는 형식이다. 아래의 그림과 같은 상황을 많이 봤을 것이다.

출처 : 위키피디아 (https://en.wikipedia.org/wiki/Flash_of_unstyled_content#/media/File:Wikipedia_FOUC.png)

 

아직 폰트가 로드되지 않았을 때, 브라우저의 기본 폰트를 사용해 컨텐츠를 보여주는 방식이다.

하지만 어떤 페이지에서는 이런 방식이 오히려 사용자에게 안좋은 경험을 줄 수 있다.

다만 이렇게라도 보여줘서 "준비된 척"을 하는 게 오히려 나을 때도 있다.

 

 

 

앞서서의 방식들은 빨리, 혹은 늦게라도 로드하는 방식이라면,

아래의 방식은 특정 조건까지는 아예 로딩을 하지 않는다.

5. Lazy Loading

  • 이미지나 비디오 같은 리소스는 사용자가 실제로 해당 콘텐츠에 접근할 때 로드되도록 설정할 수 있다. 이 방식은 페이지 초기 로딩 속도를 크게 개선하며, 특히 모바일 환경에서 성능 최적화에 유리하다. 브라우저의 기본 loading="lazy" 속성을 사용하거나 Intersection Observer API를 통해 구현할 수 있다.
  • 웹 서핑을 할 때, 스크롤을 내리다 보면 스크롤의 크기가 줄어들면서 아래에 계속해서 페이지가 보여지고 끝이 없이 보이는 무한 스크롤 현상을 경험해 본적이 있을 것이다. 이 무한 스크롤은 lazy loading을 통해 구현된다. 스크롤이 아래로 내려가야만 그때서야 컨텐츠를 로딩해서 실시간으로 추가하면서 보여주는 것이다.

 

마지막은 제목대로 "웹이 준비된 척"을 하는 기술이다.

6. Skeleton UI

  • Skeleton UI: 콘텐츠가 로드되기 전 화면에 뼈대 구조를 먼저 보여줌으로써, 페이지가 빠르게 준비된 것처럼 보이게 한다. 이는 사용자 경험을 개선하고, Cumulative Layout Shift (CLS) 문제를 줄이는 데 효과적이다. 사용자는 콘텐츠가 로드되기 전에 사이트가 거의 준비된 것으로 느끼게 된다.
  • 간혹 너무 빠르게 HTML을 로드하고 JS는 오히려 너무 늦게 로드한다면 문제가 생기는 경우가 있다. 

 

 

 

 

왼쪽의 움짤을 보면 cancel을 하려는데 갑자기 뭔가가 로드되면서 아래로 페이지가 밀린다. 그러면서 submit 버튼이 눌린다..

 

이런 현상은 빠르게 페이지를 표기하기 위해서 특정기능을 늦게 로드하다보니 생기는 현상으로, Cumulative Layout Shift (CLS) 라고 불린다. 이런 경우를 해결하기 위해 Skeleton UI를 사용한다.

 

참고로 CLS문제는 구글 SEO를 위해 사용자 경험을 측정하는 core web vital의 중요 지표 중 하나이다.

 

이미지 출처 : https://nicj.net/cumulative-layout-shift-in-practice/

 

 

 

 

오른쪽의 gif가 Skeleton UI의 방식이다.

페이지가 곧 보여주고자 하는 기본적인 뼈대를 간단하게나마

이미지로 노출하는 것이다. 어떻게 보면 이 또한 불완전한 형태로

보이기에 사용자에게 안좋아 보일 수는 있지만

오른쪽 처럼 애니메이션 기능을 추가한다면

사용자에게 현재 준비중이라는 이미지를 심어주기 때문에 상대적으로 효과가 있는 것으로 보여진다.

 

 

 

결론

이와 같은 방법들은 모두 웹 페이지가 더 빠르고 매끄럽게 로드된다고 사용자가 느끼도록 돕는 기술들이다. 웹은 실제로는 로딩 중일지라도, 이러한 기술들을 통해 사용자에게는 준비된 것처럼 보이게 함으로써, 웹의 "속임수"가 완성된다.

 

 

 

 

'연재작 > WEB - FE' 카테고리의 다른 글