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

이 기사에서는 페이지 간 전환을 단계별로 작성합니다. 우리는 또한 이 기술의 장단점과 그것을 한계까지 밀어붙이는 방법에 대해서도 이야기할 것입니다.
예
많은 모바일 앱은 보기 간의 전환을 잘 활용합니다. 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
를 호출합니다.
새 콘텐츠 구문 분석 및 추가
일반적으로 탐색되는 페이지에는 header
및 footer
와 같은 공통 요소가 있습니다. 모든 페이지에서 다음 DOM 구조를 사용한다고 가정합니다(실제로 Smashing Magazine 자체의 구조임):
var main = document.querySelector('main'); function changePage() { // Note, the URL has already been changed var url = window.location.href; loadPage(url).then(function(responseText) { var wrapper = document.createElement('div'); wrapper.innerHTML = responseText; var oldContent = document.querySelector('.cc'); var newContent = wrapper.querySelector('.cc'); main.appendChild(newContent); animate(oldContent, newContent); }); }
생명 있는!
사용자가 링크를 클릭하면 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에 대한 추가 정보:
- 사용자 경험 디자인의 스마트 전환
- 다중 장치 세계로의 전환을 위한 설계
- 웹 기술로 네이티브 경험 제공