페이지 전환을 통한 사용자 흐름 개선

게시 됨: 2022-03-10
빠른 요약 ↬ 사용자 경험이 중단될 때마다 이탈 가능성이 높아집니다. 한 페이지에서 다른 페이지로 변경하면 콘텐츠가 없는 흰색 플래시를 표시하거나 로드하는 데 너무 오래 걸리거나 그렇지 않으면 새 페이지가 열리기 전의 컨텍스트에서 사용자를 꺼내어 이러한 중단이 발생하는 경우가 많습니다.

페이지 간 전환은 사용자의 컨텍스트를 유지(또는 개선)하고, 주의를 유지하고, 시각적 연속성과 긍정적인 피드백을 제공하여 경험을 향상시킬 수 있습니다. 동시에 페이지 전환은 미학적으로 즐겁고 재미있을 수 있으며 잘 수행되면 브랜딩을 강화할 수 있습니다.

Page Transitions

이 기사에서는 페이지 간 전환을 단계별로 작성합니다. 우리는 또한 이 기술의 장단점과 그것을 한계까지 밀어붙이는 방법에 대해서도 이야기할 것입니다.

많은 모바일 앱은 보기 간의 전환을 잘 활용합니다. Google의 머티리얼 디자인 가이드라인을 따르는 아래 예에서는 애니메이션이 페이지 간의 계층적 및 공간적 관계를 전달하는 방법을 확인합니다.

웹사이트에 동일한 접근 방식을 사용하지 않는 이유는 무엇입니까? 페이지가 변경될 때마다 사용자가 순간이동하는 것처럼 느끼는 것이 괜찮은 이유는 무엇입니까?

점프 후 더! 아래에서 계속 읽기 ↓

웹 페이지 간 전환 방법

SPA 프레임워크

손을 더럽히기 전에 SPA(단일 페이지 응용 프로그램) 프레임워크에 대해 말해야 합니다. SPA 프레임워크(예: AngularJS, Backbone.js 또는 Ember)를 사용하는 경우 모든 라우팅이 이미 JavaScript에서 처리되기 때문에 페이지 간에 전환을 만드는 것이 훨씬 더 쉬울 것입니다. 선택한 프레임워크를 사용하여 페이지를 전환하는 방법을 보려면 관련 문서를 참조하십시오. 몇 가지 좋은 예제와 자습서가 있을 수 있기 때문입니다.

잘못된 방법

페이지 간 전환을 생성하려는 첫 번째 시도는 대략 다음과 같았습니다.

 document.addEventListener('DOMContentLoaded', function() { // Animate in }); document.addEventListener('beforeunload', function() { // Animate out });

개념은 간단합니다. 사용자가 페이지를 떠날 때 하나의 애니메이션을 사용하고 새 페이지가 로드될 때 다른 애니메이션을 사용합니다.

그러나 이 솔루션에는 몇 가지 제한 사항이 있음을 곧 알게 되었습니다.

  • 다음 페이지가 로드되는 데 얼마나 걸릴지 모르므로 애니메이션이 매끄럽지 않게 보일 수 있습니다.
  • 이전 페이지와 다음 페이지의 콘텐츠를 결합하는 전환을 만들 수 없습니다.

사실, 유동적이고 원활한 전환을 달성하는 유일한 방법은 페이지 변경 프로세스를 완전히 제어 하여 페이지를 전혀 변경하지 않는 것 입니다. 따라서 우리는 문제에 대한 접근 방식을 바꿔야 합니다.

옳은 길

올바른 방법으로 페이지 사이에 간단한 크로스페이드 전환을 만드는 것과 관련된 단계를 살펴보겠습니다. 여기에는 pushState AJAX(또는 PJAX) 탐색이라는 항목이 포함되며, 이는 본질적으로 당사 웹사이트를 일종의 단일 페이지 웹사이트로 전환합니다.

이 기술은 부드럽고 쾌적한 전환을 달성할 뿐만 아니라 이 기사의 뒷부분에서 자세히 다룰 다른 이점도 얻을 수 있습니다.

기본 링크 동작 방지

첫 번째 단계는 사용할 모든 링크에 대한 click 이벤트 리스너를 생성하여 브라우저가 기본 동작을 수행하지 못하게 하고 페이지 변경을 처리하는 방식을 사용자 지정하는 것입니다.

 // Note, we are purposely binding our listener on the document object // so that we can intercept any anchors added in future. document.addEventListener('click', function(e) { var el = e.target; // Go up in the nodelist until we find a node with .href (HTMLAnchorElement) while (el && !el.href) { el = el.parentNode; } if (el) { e.preventDefault(); return; } });

이벤트 리스너를 각각의 특정 노드에 추가하지 않고 부모 요소에 추가하는 이러한 방식을 이벤트 위임이라고 하며, 이는 HTML DOM API의 이벤트 버블링 특성으로 인해 가능합니다.

페이지 가져오기

이제 브라우저가 페이지를 변경하려고 할 때 중단되었으므로 Fetch API를 사용하여 해당 페이지를 수동으로 가져올 수 있습니다. URL이 주어졌을 때 페이지의 HTML 콘텐츠를 가져오는 다음 함수를 살펴보겠습니다.

 function loadPage(url) { return fetch(url, { method: 'GET' }).then(function(response) { return response.text(); }); }

Fetch API를 지원하지 않는 브라우저의 경우 폴리필을 추가하거나 좋은 구식 XMLHttpRequest 사용을 고려하십시오.

현재 URL 변경

HTML5에는 웹사이트가 페이지를 로드하지 않고도 브라우저의 기록에 액세스하고 수정할 수 있도록 해주는 pushState 라는 환상적인 API가 있습니다. 아래에서는 현재 URL을 다음 페이지의 URL로 수정하는 데 사용합니다. 이것은 이전에 선언된 앵커 클릭 이벤트 핸들러를 수정한 것입니다.

 if (el) { e.preventDefault(); history.pushState(null, null, el.href); changePage(); return; }

눈치채셨겠지만, 우리는 또한 changePage 라는 함수에 대한 호출을 추가했습니다. 이에 대해서는 곧 자세히 살펴보겠습니다. 브라우저의 활성 기록 항목이 변경될 때(사용자가 브라우저의 뒤로 버튼을 클릭할 때와 같이) 실행되는 popstate 이벤트에서도 동일한 함수가 호출됩니다.

 window.addEventListener('popstate', changePage);

이 모든 것을 통해 우리는 기본적으로 능동 및 수동 모드가 있는 매우 원시적인 라우팅 시스템을 구축하고 있습니다.

활성 모드는 사용자가 링크를 클릭하고 pushState 를 사용하여 URL을 변경할 때 사용되는 반면 수동 모드는 URL이 변경되고 popstate 이벤트에 의해 알림을 받을 때 사용됩니다. 두 경우 모두 새 URL을 읽고 관련 페이지를 로드하는 changePage 를 호출합니다.

새 콘텐츠 구문 분석 및 추가

일반적으로 탐색되는 페이지에는 headerfooter 와 같은 공통 요소가 있습니다. 모든 페이지에서 다음 DOM 구조를 사용한다고 가정합니다(실제로 Smashing Magazine 자체의 구조임):

생명 있는!

사용자가 링크를 클릭하면 changePage 함수 해당 페이지의 HTML을 가져온 다음 cc 컨테이너를 추출 하여 main 요소에 추가 합니다. 이 시점에서 페이지에 두 개의 cc 컨테이너가 있습니다. 첫 번째는 이전 페이지에 속하고 두 번째는 다음 페이지에 속합니다.

다음 함수인 animate 는 두 컨테이너를 겹치고, 이전 컨테이너를 페이드 아웃하고, 새 컨테이너를 페이드 인하고, 이전 컨테이너를 제거하여 크로스페이드를 처리합니다. 이 예에서는 Web Animations API를 사용하여 페이드 애니메이션을 만들고 있지만 물론 원하는 기술이나 라이브러리를 사용할 수 있습니다.

 function animate(oldContent, newContent) { oldContent.style.position = 'absolute'; var fadeOut = oldContent.animate({ opacity: [1, 0] }, 1000); var fadeIn = newContent.animate({ opacity: [0, 1] }, 1000); fadeIn.onfinish = function() { oldContent.parentNode.removeChild(oldContent); }; }

최종 코드는 GitHub에서 사용할 수 있습니다.

그리고 이것이 웹 페이지 전환의 기본입니다!

주의 사항 및 제한 사항

우리가 방금 만든 작은 예제는 완벽하지 않습니다. 사실, 우리는 아직 몇 가지 사항을 고려하지 않았습니다.

  • 올바른 링크에 영향을 미치는지 확인하십시오.
    링크의 동작을 변경하기 전에 변경되어야 하는지 확인하는 검사를 추가해야 합니다. 예를 들어 target="_blank" (새 탭에서 페이지가 열림)가 있는 모든 링크, 외부 도메인에 대한 모든 링크 및 Control/Command + click (페이지를 새 탭).
  • 기본 콘텐츠 컨테이너 외부의 요소를 업데이트합니다.
    현재 페이지가 변경되면 cc 컨테이너 외부의 모든 요소는 동일하게 유지됩니다. 그러나 문서 title , active 클래스가 있는 메뉴 요소 및 웹사이트에 따라 잠재적으로 많은 다른 요소를 포함하여 이러한 요소 중 일부를 변경해야 합니다(지금은 수동으로만 수행할 수 있음).
  • JavaScript의 수명 주기를 관리합니다.
    우리 페이지는 이제 브라우저가 페이지 자체를 변경하지 않는 SPA처럼 작동합니다. 따라서 특정 이벤트 바인딩 및 바인딩 해제, 플러그인 재평가, 폴리필 및 타사 코드 포함과 같은 JavaScript 수명 주기를 수동으로 처리해야 합니다.

브라우저 지원

우리가 구현하는 이 탐색 모드에 대한 유일한 요구 사항은 모든 최신 브라우저에서 사용할 수 있는 pushState API입니다. 이 기술 은 점진적인 향상으로 완전히 작동합니다 . 페이지는 여전히 일반적인 방식으로 제공되고 액세스할 수 있으며 JavaScript가 비활성화되어도 웹사이트는 계속 정상적으로 작동합니다.

SPA 프레임워크를 사용하는 경우 탐색을 빠르게 유지하기 위해 대신 PJAX 탐색을 사용하는 것이 좋습니다. 그렇게 하면 레거시 지원을 받고 더 SEO 친화적인 웹사이트를 만들 수 있습니다.

더 나아가

우리는 이 기술의 특정 측면을 최적화하여 이 기술의 한계를 계속해서 확장할 수 있습니다. 다음 몇 가지 트릭은 탐색 속도를 높여 사용자 경험을 크게 향상시킵니다.

캐시 사용

loadPage 함수를 약간 변경하여 이미 방문한 페이지가 다시 로드되지 않도록 하는 간단한 캐시를 추가할 수 있습니다.

 var cache = {}; function loadPage(url) { if (cache[url]) { return new Promise(function(resolve) { resolve(cache[url]); }); } return fetch(url, { method: 'GET' }).then(function(response) { cache[url] = response.text(); return cache[url]; }); }

짐작하셨겠지만 Cache API 또는 다른 클라이언트 측 영구 저장소 캐시(예: IndexedDB)와 함께 보다 영구적인 캐시를 사용할 수 있습니다.

현재 페이지에 애니메이션 효과 주기

크로스페이드 효과를 사용하려면 전환이 완료되기 전에 다음 페이지가 로드되고 준비되어야 합니다. 또 다른 효과로 사용자가 링크를 클릭하는 즉시 이전 페이지에 애니메이션을 적용하기 시작할 수 있습니다. 그러면 사용자에게 즉각적인 피드백이 제공되어 인지된 성능에 큰 도움이 됩니다.

Promise를 사용하면 이러한 상황을 매우 쉽게 처리할 수 있습니다. .all 메서드는 인수로 포함된 모든 약속이 해결되는 즉시 해결되는 새 약속을 만듭니다.

 // As soon as animateOut() and loadPage() are resolved… Promise.all[animateOut(), loadPage(url)] .then(function(values) { …

다음 페이지 미리 가져오기

PJAX 탐색만 사용하면 브라우저가 새 페이지의 스크립트나 스타일을 구문 분석하고 평가할 필요가 없기 때문에 페이지 변경은 일반적으로 기본 탐색보다 거의 두 배 빠릅니다 .

그러나 사용자가 링크 위로 마우스를 이동하거나 터치하기 시작할 때 다음 페이지를 미리 로드하기 시작하여 더 나아갈 수 있습니다.

보시다시피 일반적으로 사용자의 호버링 및 클릭에 200~300밀리초의 지연이 있습니다. 이것은 데드 타임 이며 일반적으로 다음 페이지를 로드하기에 충분합니다.

즉, 병목 현상이 쉽게 발생할 수 있으므로 현명하게 프리페치하십시오. 예를 들어 긴 링크 목록이 있고 사용자가 이를 스크롤하는 경우 링크가 마우스 아래로 전달되기 때문에 이 기술은 모든 페이지를 미리 가져옵니다.

프리페치 여부를 결정할 때 감지하고 고려할 수 있는 또 다른 요소는 사용자의 연결 속도입니다. (향후 Network Information API를 통해 가능하게 될 것입니다.)

부분 출력

loadPage 함수에서 전체 HTML 문서를 가져오고 있지만 실제로는 cc 컨테이너만 필요합니다. 서버 측 언어를 사용하는 경우 요청이 특정 사용자 지정 AJAX 호출에서 오는지 여부를 감지하고 필요한 경우 필요한 컨테이너만 출력할 수 있습니다. 헤더 API를 사용하여 가져오기 요청에서 사용자 지정 HTTP 헤더를 보낼 수 있습니다.

 function loadPage(url) { var myHeaders = new Headers(); myHeaders.append('x-pjax', 'yes'); return fetch(url, { method: 'GET', headers: myHeaders, }).then(function(response) { return response.text(); }); }

그런 다음 서버 측(이 경우 PHP 사용)에서 필요한 컨테이너만 출력하기 전에 사용자 정의 헤더가 존재하는지 여부를 감지할 수 있습니다.

 if (isset($_SERVER['HTTP_X_PJAX'])) { // Output just the container }

이렇게 하면 HTTP 메시지의 크기가 줄어들고 서버 측 부하도 줄어듭니다.

마무리

몇 가지 프로젝트에서 이 기술을 구현한 후 재사용 가능한 라이브러리가 매우 유용하다는 것을 깨달았습니다. 매번 구현하는 시간을 절약할 수 있어 전환 효과 자체에 집중할 수 있습니다.

따라서 이러한 모든 복잡성을 추상화하고 개발자가 사용할 수 있는 훌륭하고 깨끗하며 간단한 API를 제공하는 작은 라이브러리(4KB 축소 및 gZip'd)인 Barba.js가 탄생했습니다. 또한 보기를 설명하고 재사용 가능한 전환, 캐싱, 프리페치 및 이벤트와 함께 제공됩니다. 오픈 소스이며 GitHub에서 사용할 수 있습니다.

결론

이제 크로스페이드 효과를 만드는 방법과 PJAX 탐색을 사용하여 웹 사이트를 SPA로 효과적으로 변환하는 방법의 장단점을 살펴보았습니다. 전환 자체의 이점 외에도 간단한 캐싱 및 미리 가져오기 메커니즘을 구현하여 새 페이지 로드 속도를 높이는 방법도 살펴보았습니다.

이 전체 기사는 내 개인적인 경험과 내가 작업한 프로젝트에서 페이지 전환을 구현하면서 배운 것을 기반으로 합니다. 질문이 있는 경우 주저하지 말고 댓글을 남기거나 Twitter에서 저에게 연락하세요. 제 정보는 아래에 있습니다!

SmashingMag에 대한 추가 정보:

  • 사용자 경험 디자인의 스마트 전환
  • 다중 장치 세계로의 전환을 위한 설계
  • 웹 기술로 네이티브 경험 제공