ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스압/데이터주의] 웹 최적화 방식 모음 - 3.3 UX 트릭
    프로그래밍/Web 2021. 2. 14. 18:25

    3.3 UX 트릭

    객관적인 성능이 올라가는 것이 아니라 사용자가 빠르다고 느낄 수 있게 만드는 트릭들.

    UX는 글로만 설명하기 힘든 부분이 많기 때문에 가급적 GIF나 사진자료 통해 직관적으로 이해할 수 있도록 힘을 썼다.

     

    초반에 나오는 것들은 많이들 접해봤겠지만, 후반에 나오는 낙관적 UI(Optimistic UI)와 피츠의 법칙 활용은 일반 개발자에게 상당히 생소한 존재며 흥미로운 방법론들이 많다.

    UX에 관심이 있는 사람이 아닐지라도 인사이트를 얻어갈 수 있을 것이다.

     

    3.3.1 웹폰트 로드

    개요

    분류: Font

    앞선 폰트 압축과 서브셋에 이어 로드 최적화를 다루어본다. [웹 폰트 사용과 최적화의 최근 동향, 웹폰트 최적화, 웹폰트 사용하기 (웹폰트 101), A Comprehensive Guide to Font Loading Strategies, Faster Font Loading with Font Events, FOUT, FOIT, FOFT, Optimizing Web Font Rendering Performance, 웹폰트 최적화 기법에 관한 몇가지 이야기]

     

    - 렌더링 차단방식

    폰트 렌더링 방식은

    • FOIT(Flash Of Invisible Text): 텍스트가 보이지 않은 상태(invisible)에서 지정된 폰트로 바뀌며 번쩍임 (크롬, 파이어폭스)
    • FOUT(Flash Of Unstyled Text): Fallback 폰트(unstyled)에서 지정된 폰트로 바뀌며 번쩍임 (IE)

    으로 나뉘어져 있다.

     

    이중 컨텐츠를 먼저 보여는 FOUT 방식이 UX에 좋다.

    그러나 글꼴의 자간, 높이 등 서식 때문에 레이아웃이 변경될 수 있다.

     

    - FOUT 사용

    font-display 속성

    요즘 브라우저들은 font-display를 적용하여 FOUT처럼 사용할 수도 있다.

    • auto: block과 비슷
    • block: FOIT처럼 작동, 로딩되지 않을 때는 텍스트를 렌더링하지 않으며(최대 3초) 웹폰트가 로딩되면 적용
    • swap: FOUT처럼 작동, 우선 Fallback으로 렌더링하고 웹폰트가 로딩되면 적용
    • fallback: 100ms 동안 렌더링하지 않다가 Fallback으로 렌더링, 3초안에 로딩이 완료되면 완료된 폰트로 아니라면 Fallback 폰트 유지
    • optional: 100ms 동안 렌더링 하지 않다가 Fallback으로 렌더링, 브라우저가 네트워크 상태를 파악해 웹폰트로 전환여부를 결정

     

    @font-face {
      font-family: 'Awesome Font';
      font-display: swap; /* or auto, block, fallback, optional */
      src: local('Awesome Font'),
        url('/fonts/awesome-l.woff2') format('woff2');
    }

    @font-face의 font-display를 swap으로 설정하면 된다.

     

     

    자바스크립트 라이브러리

    FOUT 방식으로 로드하도록 도와주는 라이브러리가 역시 존재한다.

    이 중에서는 Font Face Observer가 가볍고 성능에서도 낫다고 알려져 있다.

    현재 메인테이너가 참여를 안하는 중인데..

     

    풀리퀘스트를 날린 사람이 만든 Font Face Observer ES를 사용하면 트리 쉐이킹을 사용할 수 있으니 요것을 쓰는게 좋을 듯 하다.

    Font Face Observer의 사용방법은 간단하다.

     

    Font Loading API

    최근에는 아예 Font Loding API가 나왔는데 Font Face Observer와 비슷하다. [Getting started with CSS Font Loading, CSS Font Loading API's FontFaceSet Sample]

     

    대략 다음같이 사용할 수 있다.

    var font = new FontFace("Awesome Font", "url(/fonts/awesome.woff2)", {
      style: 'normal', unicodeRange: 'U+000-5FF', weight: '400'
    });
    
    // don't wait for the render tree, initiate an immediate fetch!
    font.load().then(function() {
      document.fonts.add(font); // add font to document
      document.body.classList.add('fonts-loaded'); // enable font with CSS class
    });
    

     

    - 부작용 보완

    그러나 FOUT도 부작용이 존재한다고 하였다.

    대표적인 것이 서식에 의한 레이아웃 변경, 그리고 번쩍임이다.

     

    비슷하게 만들기

     Font style matcher를 사용해 비슷하게 보이도록 맞추어보자.

     

    그 후, 스타일을 지정해주면 끝.

     

    FOUT 최소화

    FOUT이 UX에 좋기는 하나 깜박이는 것은 좋지 않다.

    따라서 작은 시간내에 로드가 된다면 깜박이지 않게 만드는 것을 고려할만 하다.

     

    400ms 이내에 전송이 되면 깜빡임 없이 바로 보이게 할 수도 있다.

    .fonts-loaded body {
         font-family: 'Nanum Gothic', sans-serif, Lucida Sans Unicode, arial;
    }
    
    .blocking-time {
        opacity: 0;
    }
    
    .fonts-loaded.blocking-time {
        opacity: 1;
    }
    document.documentElement.classList.add('blocking-time');
    setTimeout(function() {
        document.documentElement.classList.remove('blocking-time');
    }, 400)
    var font = new FontFaceObserver('Nanum Gothic');
    font.load(null, 3000).then(function() {
      document.documentElement.classList.add('fonts-loaded');
    });

     

    FOFT(Flash of Faux Text)

    모든 웹폰트가 다 다운로드 받은 후, 로드 할 필요는 없다.

    만약 관련된 폰트가 먼저 다운로드 받아졌을 경우, 먼저 적용해보는 것도 좋은 방식

     

    예: 시스템 폰트 - 웹폰트 Regular - 웹폰트 Bold/Italic

    시스템 폰트(FOUT)

    먼저 다운로드된 폰트 적용(FOFT)

    모든 폰트 적용

     

    3.3.2 스켈레톤 스크린

    개요

    분류: Content, CSS

    스켈레톤 스크린(Skeleton Screen) 또는 스켈레톤 UI라 부르는 것은 로딩이 조금 더 빠르다고 느끼게 한다. [How to Speed Up Your UX with Skeleton Screens, Improve UX in React apps by showing skeleton UI, Everything you need to know about skeleton screens]

     

    컨텐츠가 로딩될 때까지 둘 중 어느 것처럼 보이는 것이 나을지 생각해보자.

     

    유튜브나 인스타그램 등에서 빈번히 쓰여서 알겠지만 빠르다는 느낌을 준다.

     

     

    해결방법

    CSS로 제작 할 수 있는 가이드.

     

    CSS로 만들 때 참고할만한 라이브러리, 생성기

     

    리엑트를 사용한다면 아주 좋은 라이브러리들이 존재한다.

     

    특히 React Content Loader는 Create React Content Loader에서 50가지가 넘는 예제와 다양한 옵션으로 스켈레톤 스크린 코드를 만들어낼 수 있다!!

     

    UI 참고용으로 다음 것도 한번 볼만 하다.

     

    3.3.3 이미지 자리 표시자

    개요

    분류: Content

    이미지 Lazy Load시 생기는 빈공간에 쓸 방법이 몇가지 있다.[How to use SVG as a Placeholder, and Other Image Loading Techniques(medium), How to create a kick-ass image preview with LQIP]

     

    • 빈공간: 자리를 채우지 않고, 그대로 나둠
    • 자리 표시자: 빈자리를 채울 때 사용할 만한 아이콘
    • 색 채우기: 이미지를 대표하는 색을 이용해 채우기
    • 가벼운 이미지: 가볍게 만들어진 이미지로 채우기

     

    빈공간은 UX에서 나쁘며, 매번 레이아웃 계산을 해야 하기 때문에 성능에 악영향을 끼친다.

    이하 다른 것들의 장단점과 몇가지 변형을 다루어보고자 한다.

     

    - 자리 표시자(Place Holder)

    자리표시자라는 이미지 요청을 실패했거나, 기본 아이콘으로도 사용할 수 있어 데이터가 더 필요하지 않으면서 UX를 향상시킬 수 있다.

     

    자리 표시자로 사용할 만한 것은

    Placeholder Image & Icons Image

    • 자리표시자 이미지
    • 해당 자리를 나타내는 기본 이미지

     

    자리표시자를 자동적으로 생성해주는 서비스도 있다.[21 of Best Placeholder Image Generator]

    이 서비스들 중 하나를 골라 사용하면, 자리표시자 이미지를 동적으로 생성해서 사용하는 것이 가능하다.

     

    예를 들어

    <img src="https://dummyimage.com/300x200/000/fff.png">

    는 다음 이미지다.

     

    - 색 채우기

    상징하는 색(Dominant Color)을 채운 후, 전환하는 예도 있다.[Dominant Colors for Lazy-Loading Images, How Medium does progressive image loading]

    전환이 부드럽고, 직관적이며 작기 때문에 트래픽 비용도 크지 않다.

    단색

    그라디언트

     

    색은

    • 단색
    • 그라디언트

    로 나뉠 수 있는데, 아무래도 그라디언트에서 용량이 살짝 커지는 편.

     

    단점이라면 이미지 프로세싱을 통해 색을 뽑아내야 한다는 것.

    인공지능 시간에 보던 k-means clustering을 사용하거나, 색상 양자화를 하면 이미지를 대표하는 색을 만들 수 있다.

     

    그 후 base 인코딩으로 내장하면 끝

    <img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
         data-src="https://s-media-cache-ak0.pinimg.com/474x/50/1b/74/501b74902935b063816ea8e14f460ca0.jpg"
         alt="Ghost In The Shell">

     

    CSS로 그라디언트를 만드는 것은 Gradify(깃허브)에서 확인 가능.

     

    - 가벼운 이미지

    역시 전환이 자연스럽기 때문에 사람들은 빠르다고 느낄 수 있다.

     

    실루엣

     

    손실된 저용량 이미지

     

    실루엣을 그리기 위해서는 contour나 node-potrace을 참고하면 된다. [Drawing images using edge detection and SVG animation]

     

    LQIP(Low Quality Images Placeholder)SQIP(SVG-based LQIP)를 사용해 블러, 모자이크, 폴리곤 형태의 이미지를 만들 수 있다.

    SQIP의 art는 마치 인상파와 야수파를 섞은 종이소재 작품 같다. ㅎㅎ

     

    웹팩의 경우, 다음을 사용해 편히 사용할 수 있다.

    +.

    CSSConfRender Conf처럼 들로네 삼각 분할 (Delaunay Triangulation)을 이용해 삼각 폴리곤처럼 만든 polyserver도 있다.

     

     

    - 비디오

    비디오의 경우 poster 속성을 사용해 재생 전까지 이미지를 보여줄 수 있다.

     

    3.3.4 로딩 인디케이터와 인터렉션

    개요

    분류: Content

    적절한 로딩 인디케이터와 인터렉션은 사용자에게 빠르다고 느끼게 만들 수 있다.[XD 필수 항목: 애니메이션 진행률 표시기의 모범 사례, Everything you need to know about Loading Animations, Improving the UX of Progress Indicators and Feedback Notifications, 모바일 앱에서의 대표적인 애니메이션 활용 방법]

    시스템 상태의 가시성은 유저 인터페이스 디자인에서 가장 중요한 원칙 중 하나입니다. 사용자는 사용 중인 시스템에 대해 알고 싶어하는데, 특히 시스템이 한창 가동 중일 때 언제든지 현재 상황을 파악하여 시스템 상태를 파악하길 원합니다. 대기 상태의 애니메이션 진행률 표시기는 작업이 진행 중이거나 로딩되고 있을 때 사용자에게 시스템 상태를 알리는 가장 일반적인 방법입니다.

    현재 웹에서 애니메이션이 빈번히 사용되는 것은 아니나 점점 APP화가 되어가는 추세이기 때문에(PWA, SPA등) 나중에는 인터렉션의 중요성이 부각될 것이라 생각한다.

     

    로딩 인디케이터

    로딩 인디케이터는 다음의 장점을 가진다.

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

     

    로딩 인디케이터에는 크게 프로그레스 바와 스피너가 있는데 다음과 같이 써야 한다. [Progress Bars vs. Spinners: When to Use Which]

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

    프로그레스 바와 스피너

     

    애니메이션과 인터렉션

    다음글에서 설명이 매우 잘 나와있어 따로 안해도 될 듯. [[번역]UX에서 적절한 애니메이션효과를 위한 가이드 (2편), 모션으로 사용성 만들기 : 모션 선언문의 UX (2편), ease-out, in; ease-in, out]

     

    해결방법

    로딩 인디케이터

    로딩 인디케이터는 오래 걸리는 작업에만(약 5초 이상) 사용해야한다. [Stop Using A Loading Spinner, There’s Something Better, Mobile Design Details: Avoid The Spinner]

    성능이 좋을 때 로딩 인디케이터를 사용하면, 잠깐 비쳤다 사라져 불안감을 느끼게 하고 기다려야 한다는 사실을 인식하여 느리다고 느낀다.

    시계가 똑딱 거리는 것을 보는 것과 마찬가지.

    • 성능을 향상 시키는 것이 가장 좋은 방법
    • 화면에 잠깐 나오는 것은 좋지 않음
    • 전환 애니메이션이나 스켈레톤 스크린을 사용하여 감추는 것을 고려

     

     

    로딩 인디케이터는 사용자에게 충분한 정보를 직접적/간접적으로 제공해야 한다.

    • 왜 로딩 인디케이터가 표시되고 있는지
    • 오래 걸린다면 얼마나 걸리는지
      • 이때, 인식되는 값을 대기 시간과 일치 시켜야 한다.
    • 어떤 작업을 하고 있는지

     

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

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

    정보를 보여주는 것은 게임에서 자주 쓰이는 방식. [pull to refresh airbnb, Case study #01 배달의 민족배민키친 로딩, Any one else stuck on creative loading screen?]

     

     

    네이티브나 간단한 로딩 인디케이터를 사용하는 것도 좋다.

    • 개발 시간 단축(개발자, 디자이너 모두)
    • 처리 리소스를 아낌
    • 사용자는 앱 성능 대신 인터넷 연결 또는 장치 속도에 대해 불평 할 가능성이 높음

    네이티브 로딩 스피너 느낌[Mobile Loading Spinners Sketch Resource, SVProgressHUD, Native iOS and Android loading spinner (progress bar indicator) overlay]

    개인적으로는 2번째 로딩 스피너를 좋아한다.

    ios, android, 웹에 다 적합한 느낌.

     

    마지막으로 로드 인디케이터 동작에 대한 이야기. [Faster Progress Bars: Manipulating Perceived Duration with Visual AugmentationsDoes Progress Bars’ Behavior Influence the User Experience in Human-Computer Interaction?, 진행바 속도가 빠르다고 느끼게 만드는 방법, '로딩바 - 프로그레스바'의 비밀]

    • 프로그래스바의 강조를 자주, 스피너라면 회전수를 늘림 
    • 세로무늬가 거꾸로 진행하는 것이 더 빠르다 느낌
    • 진행 마지막에 가속화(처음에 느리게, 마지막에 빠르게)
    마지막의 가운데 두개가 가장 빠르다고 느낌

     

    애니메이션과 인터렉션

    동작 속도

    • 100~350ms 정도
    • 리스트 아이템은 250ms 이하로
    • 이동 시간 때문에 화면이 작은 기기에서는 빠르고, 큰 기기에서는 느리게
    • 작은 요소는 빠르고, 큰 요소는 느리게

     

    동작 가속 [비대칭 애니메이션 타이밍, 사용자설정 easing]

    • 바운스 효과는 제거
    • 명확하고 날카롭게
    • 이징은 항상 사용(선형 동작 보다 가속을 붙혀서)
    • Ease-in(가속)은 사라질때, Ease-out(감속)은 나타날 때
    • Ease-in-out(표준곡선, 가속 후 감속) 시 비대칭으로

    Ease-in-out 효과를 사용시 애니메이션 곡선도구를 활용해보자.

    애니메이션 곡선도구

     

    연출

    • 같은 컨테이너의 요소는 동일한 방향으로
      • 표의 경우 대각선으로
    • 같은 컨테이너에서 가능한 적은 요소에만 적용
    • 같은 컨테이너의 요소는 Offset(상쇄, 원래 상태로), Separateness(분리) 되도록
    • 크기 변화가 균일하면 직선, 불균일하면 커브로 이동
      • 커브로 동작시, 스크롤 인터페이스 주축(보통 Virtical out)과 일치해야 함
    • 계층화 되었을 시 공간적인 관계를 가짐
      • 움직이는 물체끼리는 통과하지 않도록
      • 떠오르게 만들시에는 가능
    • 상호작용시 공간적/시간적 계층관계 만들기
    • 사용성이 변경 될 때는 내러티브(이야기, 맥락, 서술)에 맞는 연속적인 상태로
      • 문자와 숫자 값은 동적으로 변경
      • 보이거나 숨겨질 때 연속적으로
      • 현재화면에서 새로운 오브젝트가 생성시 Cloning
      • 새로운 오브젝트 생성시 차원 이용
      • 모호한 동작시 정적인 상태에서 일시적으로 움직임
      • 스크롤 시 2차원적인 공간에 계층구조
      • 오브젝트, 공간을 탐색시 연속적이고 공간적이게

     

     

    여기 이쁘고, 좋은 예들이 존재한다. [다음웹툰의 UX 개편 이야기, UX Design: How to Use Animations in Mobile Apps]

     

    홈 화면 로딩, 탭전환, 리스트 로딩, 페이지 전환

     

     

    3.3.5 낙관적 UI

    개요

    분류: Content

    낙관적 UI(Optimistic UI)는 작동하는 척 일종의 사기(?, 그냥 블러핑이라 해두자)를 치는 것 이다. [옵티미스틱 UI의 계산된 거짓말, Optimistic UIs in under 1000 words, Being optimistic in UI, The 3 White Lies Behind Instagram’s Lightning Speed, Secrets to Lightning Fast Mobile Design, 3 Tips To Make Your Application Feel Faster]

     

    먼저 작동완료 된 것처럼 표시하고, 나중에 업데이트를 하자는 뜻인데, 절차는 다음과 같다.

    기존 방식과 낙관적 UI

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

     

    바로 전에 나왔던 애니메이션 효과를 이용한 방법도 동작하고 있는 것처럼 시간(약 100~350ms)원리이니 일종의 Optimistic UI라 할 수 있겠다.

    더 전의 스켈레톤 스크린도 그렇고.

     

    해결방법

    예시 및 장점

    우선 얼마나 유용한지 예제를 보자.

    일반 vs Optismitic UI

     

    간단한 데모예제로는 이런 느낌 정도?

     

    특히 메세징에 유용하다.

    먼저 보낸척 표시하고 작은 로딩바가 표시되는 것을 확인가능하다.

    맥 iMessage와 인스타그램

     

    인스타그램의 좋아요 표시, Audible의 다운로드 받으며 듣기(스트리밍)도 비슷한 예제.

     

    마지막으로 트렐로 예인데, 웹에서 일반앱급 느낌을 내게 만들어준다.

    UI와 서버상태를 비동기로 운영한다고 봐도 무방.

     

    페이스북이나 인스타그램에서 잘 써먹고 있는 기법이라 그런지 잘 짜여진 코드 예제들도 많다.

    그리고

    이 정말 편해보인다.

     

    단점 보완

    단점은 업데이트중인가, 오류가 났는가를 파악하기 힘들다는 것이다.

    따라서 이것역시 UI상으로 표시를 해줘야 한다.

     

    업데이트시에는 로딩 인디케이터나 상태 아이콘을 표시해주고

    오류시에는 모달창으로 띄우거나 실패했다는 메세지를 띄울 수 있다. (UX상 후자가 좋음)

     

    사용자가 앱을 꺼서 파악을 못할 경우엔

    아이콘에 경고표시를 해주면 된다.

     

    3.3.6 피츠의 법칙과 UI/UX 개선

    개요

    분류: Content, JS

    우선 알아두어야 할 것은 피츠의 법칙(Fitts's Law)이다. [The Glossary of Human Computer Interaction, 피츠의 법칙]

    아주 건단하게 설명하자면, 목표지점으로 빠르게 이동하는데 시간이 거리와 목표 폭 사이의 비율에 관한 것이다.

    목표B에서 허용하는 너비 W와 거리 D

    당연하게 느껴질 수도 있겠지만

    • 너비 W는 크게
    • 거리 D는 작게

    만드는 것이 좋다.

     

    해결방법

    피츠의 법칙에 대한 이해를 돕기위한 퀴즈가 존재한다. [A Quiz Designed to Give You Fitts(번역)]

    이건 정말 원문을 보기 권한다.

     

    오래된 글이라 PC위주의 팁이지만, 잘 사용한다면 웹에서도 충분히 활용가능하다.

    적어도 내눈에는 그리 보인다.

     

    - 레이블과 툴바

    레이블은 버튼의 일부가 되어 너비가 커지므로 더 빠르게 접근 가능하다.

    레이블이 없으면 버튼이 모이게 되는데, 원치않는 버튼을 누르는 것을 막기위해 다가갈 때 속도를 늦추게된다.

    Windows32 Toolbars

     

    MacOS Toolbars

     

    - 줄배열과 가장자리 배치

    배열을 여러줄이 아니라 한줄이고, 가장자리와 맞닿아 있아면 빠른 접근 가능하다.

    한줄로 만들면 방금 언급했던 것처럼 원치않는 버튼을 누르는 것을 막기위해 속도를 늦추는 행위가 사라진다.

    여러줄은 한줄로[10 ESSENTIAL TOOLS & TIPS ALL PHOTOSHOP BEGINNERS SHOULD LEARN]

     

    가장자리에 배치하게되면 속도를 늦출이유가 없어 빠르게 누를 수 있다.

    가장자리와 맞닿아있게[Reattaching The Toolbar, Perspective Crop Tool Missing]

     

    - 가장 빨리 접근 가능한 위치

    오른손잡이 기준, 현재위치, 우측하단, 좌측상단, 우측상단, 좌측하단으로 접근이 빠르다.

     

    어찌보면 당연하겠지만, 현재자리에서 바로 누를 수 있는 것에 접근이 빠르다.

    현재자리에서 바로 부를수 있는 대표적인 메뉴가 바로 컨텍스트 메뉴.

    현재 위치의 컨텍스트 메뉴(오른쪽 클릭)

     

    사람의 팔 구조상 호를 그리며 움직이게 되는데, 때문에 아무렇게나 움직이다보면 네 귀퉁이에 닿게된다.

    이를 이용한 것이 바로 윈도우 시작버튼과 KDE의 화면경계 기능.

    윈도우 시작버튼과 KDE 화면경계 기능

    역시 구조상의 이유로 우측하단과 좌측상단, 우측상단과 좌측하단순으로 접근성이 높다.

     

     

    잘만들어졌다고 칭찬받는 삼성 One UI도 이 원칙하에 있다.

    패블릿이라 불릴정도로 크기가 커진 스마트폰은 상단에 손이 닿기가 어렵고, 좌우로 접히는 폴더블과 원형태의 스마트워치는 가운데를 터치하기가 어렵다.

    때문에 아래 사진들을 보면 하단과 가장자리 위주로 UI가 구성되어있음을 알 수 있다.

    삼성 One UI [One UI 2]

     

    - 작업표시줄의 자동숨김

    작업표시줄 자동숨김은 비효율적이다.

     

    계속 말해왔지만 화면 가장자리는 접근성이 매우 좋다.

    고정된 작업표시줄을 사용하면 수많은 아이콘에 접근할 수 있지만, 자동숨김처리된 작업표시줄을 사용하면 작업표시줄 하나를 접근하기위해 시간을 낭비해야 한다.

    또한, 화면 가장자리에 있는 요소를 선택하려할 때 작업표시줄이 나타나 원하는 작업을 못하는 경우가 생긴다.

    작업표시줄 헤멤 [How To Hide Windows 10 Taskbar Using A Hotkey]

     

    - 프로그램 드롭다운 메뉴위치

    프로그램창 내부보다 디스플레이 상단에 메뉴를 배치한 것이 더 빠른 접근 가능하다.

     

    이유는 역시 화면 가장자리라 접근이 빠르기 때문.

    항상 같은 자리에 있을 것이라는 것을 알며, 화면크기가 작을때도 접근하기 좋다.

    프로그램 창의 메뉴와 디스플레이 상단 메뉴

    - 드롭다운과 탈출각도

    메뉴를 누르려고 할 때 각도가 좁으면 실수로 다른 항목을 누르기 쉽다.[Making Menus Escapable]

    각도가 좁으면 객체의 너비가 줄어들기 때문.

    OS X: 65도, Ubuntu: 50도

    또한, 사용자는 텍스트를 대상으로 생각하고 움직일 것이기 때문에 탈출각도와 완전히 틀어진 우분투의 텍스트배치는 실수를 유발한다.

     

    으아.. 화가난다..[Ubuntu Volume Indicator Applet - Maverick (Terrible Design!)]

     

    이를 보완할 몇 가지 방법을 소개한다.

     

    크기 조절

    메뉴의 크기를 넓게 만들면 탈출각도가 넓어진다.

    하지만, 실제로 메뉴를 탈출하기는 어려울 수도 있다는 점은 고려대상.

     

    시간과 공간의 여유

    윈도우는 메뉴가 사라져버리는 것을 10~100ms처럼 여유시간을 두는 방식으로 해결한다. [Hysteresis]

     

    그러나 애플은 탈출각도란 개념을 이용해 직선으로 갈 수 있게 만든다.

    무려 80년대(정확히 1986년)에 구현했었다고. [Mac OS X DP2, Hierarchical Menus]

     

    그 후, NeXT와 OS X 초기 팀이 복제에 실패해 잊혀졌다가 더 나은 방식으로 왕귀했다.

    무조건 오른쪽의 부채꼴을 유지하는 것이 아니라, 메뉴의 크기를 고려하는 것이다.

     

    추후에 등장할 파트에서 이를 이용해 응답성을 극도로 높힌 흑마법을 선보일텐데 이론적 기반없이 처음 접했다면 감탄을 연발할 것이다.

     

    클릭할 때까지 활성화

    메뉴나 다른 공간을 클릭할 때까지 계속 활성화 시키는 방법도 있는데, 이 경우 갈수있는 모든 각도가 탈출각도가 된다.

    하지만 어딘가를 무조건 클릭해야 한다는 것은 커다란 단점.

     

    - 원형메뉴

    원형 팝업 메뉴는 선형 팝업메뉴보다 접근하기 쉬움

     

    원 주변 조각으로 움직이기 위해 조그만 움직여도 되며, 방향정보를 기억할 수 있기 때문이다.

    선형 메뉴 vs 원형 메뉴[New in Drawboard PDF: The Advanced Tools Drawer]

     

     

    - 선형 팝업 메뉴의 모든 옵션에 접근시간 균일화

    옵션의 크기 가속화

    포인터에서 멀수록 옵션의 크기를 강하게 주면 된다.

    예를 들어 포인터를 멀리 이동시킬수록 더욱 많은 양을 움직이는 식.

     

    모바일에서도 비슷한 방식이 관성 스크롤이란 방식으로 적용되어있다.

    쎄게(짧고 빠르게) 스크롤하면 약하게(길고 느리게) 스크롤하는 것보다 훨씬 멀리 움직이지 않은가? 

     

    관성 스크롤

    ios의 경우 overflow-scrolling을 쓰면 조절이 가능하다.

    .scroll-touch {
      -webkit-overflow-scrolling: touch; /* Lets it scroll lazy */
    }
    
    .scroll-auto {
      -webkit-overflow-scrolling: auto; /* Stops scrolling immediately */
    }

     

    ios에서 지원하는거라 변태 개발자들이 포팅이나 폴리필을 만들었을까봐 안드로이드나 기타 환경에서도 가능한지 여러가지를 찾아봤는데 아직 딱히 마음에 드는 것을 찾지 못했지만, 다음글은 참고할만 하다.[Recreating native iOS scroll and momentum, [번역]iOS10의 프리-패칭 API로 부드러운 스크롤 증진하기, [번역] 미래를 향한 스크롤 (Scroll to the future), Scrolling personality improvements in Microsoft Edge]

     

    빨아들이기

    객체에 일종의 중력장이 만들어지면 빨려들어가게 만들면 빨리 움직이더라도(부정확하더라도) 멀리있는 버튼에 접근하기 쉽다.

    마치 에임핵처럼(Aim hack?)

     

    모바일에서 비슷한 것을 찾으라면 패스트 스크롤(Fast Scroll)이나 사이드 인덱스바(Side Index Bar)라 불리는 물건.

    각 인덱스의 위치가 중력처럼 작용하여 빠르게 이동할 수 있다.

    Fast Scroll [Contacts XT - Address Book]

     

    가운데로 위치

    하위메뉴들의 중간부분이 계층에서 이동할 수 있게 만든다면, 비교적 균일하게 접근이 가능하다.

    중앙에 중요한 메뉴가 위치한다면 팝업이나 컨텍스트 메뉴에 좋은 방법.

    평행하게 vs 중앙으로

     

    3.3.7 기타 응답성 개선

    개요

    분류: Content

    이 파트에서는 기타 응답성(Respose time) 개선하는 방법들을 서술한다. [How to create the illusion of faster web pages (while also creating actual happier users)]

    앞서 나왔던 애니메이션이 일어날 것이라는 예정을 알려주는 CSS의 will-change 속성, 터치나 포인터 이벤트로 300ms 지연속도를 없애는 것은 대표적인 응답성 개선 방법이다.

    여기에서는 기타 응답성 개선 방안들을 다룬다.

     

     

    - 터치 응답성

    우선 중복된 부분으로는 이벤트와 API에서 다루었던 버튼에 터치 상태를 추가하여 300ms의 지연속도를 없애버리는 게 있다. [사이트에 터치 추가]

     

     

    - 스크롤링 응답성

    최근 브라우저들은 파이어폭스의 Async Pan/Zoom(APZ)처럼 Off-thread 스크롤링을 지원해  메인 쓰레드가 막히더라도 스크롤링을 지원한다. [Async Pan/Zoom (APZ) lands in Firefox Quantum, Scrolling on the web: A primer]

    apz

     

    원리를 대충 설명하자면, 입력을 받는 쓰레드를 따로 분리해 컴포지터 쓰레드로 전달이 되도록 하여 병목이 일어나더라도 비동기적으로 스크롤을 할 수 있는 것.

     

    이 기술의 파워를 알 수 있는 데모를 2가지 있다.

    Off-thread 스크롤링을 지원하는 브라우저라면

    첫번째 페이지의 "Junk me!"를 누르고 스크롤링을 하거나 두번째 페이지에서 그냥 스크롤링을 할 때 부드러움을 느낄 수 있다.

    그러나 Off-thread 스크롤링을 지원하지 않은 키보드 입력의 방향기를 사용하면 작동이 안되거나(1번째), 끊김이 심함(2번째)을 알수 있다.

     

    이 항목에서는 메인 쓰레드를 막지 않으면서 스크롤이 가능한 방법을 알아보자.

     

    CSS 활용

    계속 강조해오는 것이지만, 애니메이션이나 레이아웃들과 관련해서 CSS를 우선적으로 사용하는 것이 좋다.

    여기서는 모질라에서 공개한 문서에서 알려주는 예제를 기반으로 살펴도록 하자. [Scroll-linked effects, Asynchronous scrolling in Firefox]

     

    scroll-behavior를 smooth로 설정하면 항목끼리 부드럽게 움직일 수 있다.

    앞서 Scroll-Behavior의 Scroll up과 Scroll down 버튼을 눌러보면 알겠지만, 위아래로 부드럽게 움직인다.

     

    두번째는 posion을 어느 상한선에서 고정시키는 sticky.

    밑의 예제에서 스크롤시 Javascipt로 제작한 것은 깜박이고, 버벅이고 난리를 치나 CSS는 고고하게 자리를 지킨다.

     

    CSS Scroll Snap을 이용하면 CSS만으로 한화면씩 전환하는 테크닉을 만들 수 있다.  [Practical CSS Scroll Snapping, Well-Controlled Scrolling with CSS Scroll Snap]

     

    마지막으로 시차(parallax)를 두고 스크롤링을 하는 것인데 화려한 사이트에서 곧잘 볼 수 있다. [Practical CSS Parallax, CSS 로만 구현한 Parallax Scroll 원리 메모]

    구현이 살짝 복잡해지기 때문에 위 사이트들을 확인바란다.

    CSS mix-blend-mode & Awesome parallax scrolling, Landscape parallax using CSS

    링크의 코드들이나 리스트에 있는 링크의 Debug 버튼을 눌러보면 원리를 이해하는데 커다란 도움이 되리라 확신한다.

     

    이벤트 위임

    이벤트를 위임하여 처리할 때 event.preventDefault와 같은 패턴이 들어갈 수 있다. [event.preventDefault()를 활용한 이벤트 취소 방법, JavaScript에서 이벤트 전파를 중단하는 네가지 방법]

    그러나 body 영역 모두에서 이벤트가 취소가 되버리는 불상사가 일어날 수 있다.

    document.body.addEventListener('touchstart', event => {  
        if (event.target === area) {
            event.preventDefault();
        }
    });

     

    Scroll jank due to touch/wheel handlers demo

     

    이는 passive 속성을 true로 바꾸면 preventDefault를 호출하지 않아 성능개선이 가능하다.  [Improving scrolling performance with passive listeners, 스크롤 성능 개선을 위해 패시브 이벤트 리스너를 사용하는 사이트, { passive:true } 의 진정한 의미]

    이벤트 핸들러의 실행이 완료될 때까지 기다리지 않고 계속 스크롤을 할 수 있는 것이다.

    document.body.addEventListener('touchstart', event => {  
        if (event.target === area) {
            event.preventDefault()
        }
     }, {passive: true});

     

    응답성이 나아진 것을 확인 가능하다.

     

    만약 preventDefault를 쓰고 싶다면 event.cancelable로 취소가 가능한지 확인해보거나

    document.body.addEventListener('pointermove', event => {  
        if (event.cancelable) {
            event.preventDefault(); // block the native scroll
            /*
            *  do what you want the application to do here
            */
        }
    }, {passive: true});

     

    아예 touch-actionpointer-events와 같은 CSS로 이벤트 핸들러를 제한할 수 있다.

    #area {
      touch-action: pan-x;
    }

     

    아예 다른 방법도 있다.

     

    MDN의 Pointer Events를 읽다보면 스크롤 차단을 피할 수 있게 만들어졌다고 나온다!! [Using Pointer Events, Building a great touchpad experience for the web with Pointer Events]

    Pointer capture allows events for a particular pointer event to be re-targeted to a particular element instead of the normal hit test at a pointer's location. This can be used to ensure that an element continues to receive pointer events even if the pointer device's contact moves off the element (for example by scrolling).

     

    Comparison of Pointer and Touch Event Scroll Initiation GIF

     

    여러모로 포인터 이벤트는 정말 좋으니 잘 써먹도록 하자.

     

    requestAnimationFrame 활용

    입력 핸들러는 request AnimationFrame 콜백 직전에 실행되며, 이때 스타일을 변경하면 강제동기 레이아웃이 발생한다.

    만약 디바운스처리를 할 수 있다면 스크롤시 응답성이 향상되겠죠. [JS 지연처리: 디바운스와 스로틀, 스로틀(throttle)과 디바운스(debounce)]

    function onScroll (evt) {
    
      // Store the scroll value for laterz.
      lastScrollY = window.scrollY;
    
      // Prevent multiple rAF callbacks.
      if (scheduledAnimationFrame)
        return;
    
      scheduledAnimationFrame = true;
      requestAnimationFrame(readAndUpdatePage);
    }
    
    window.addEventListener('scroll', onScroll);

     

     

    - 메뉴 응답성 높이기

    앞서 흑마법이라며 칭송했던 것이 드디어 나왔다!! [아마존의 메가 드롭다운 메뉴 분석]

    더보고 말것도 없다. 일단 보자.

    정말 레이턴시가 없고 미친듯이 빠르지 않은가??

    처음봤을 때 솔직히 감동 받았다. ㅠㅠㅠ

     

    원리는 당연하게도?

    파란 삼각형 내부로 움직이면 유지, 밖이면 탈출하여 다른 메뉴를 보여주는 것.

     

    지금까지 본 UX 트릭중 가장 감동받은 것.

    몇년전 이 글 때문에 UX에 관심을 가지게 되었다.

     

    jQuery-menu-aim(Demo)이 원조고 이를 기반 혹은 비슷한 라이브러리들이 만들어졌다.

     

    빠른 것들을 쓰다가 이제 보편적인 메뉴(라고 쓰고 전투력 측정기라고 읽는 것)들과 비교해보자.

     

    CSS로만 멀티레벨 메뉴를 구현해도 빠르긴 하지만

    범위를 벗어나면 꺼져버리니 느린것만 못한 UX.

     

    유명한 이커머스 업체인 쿠팡에서도 이를 적용하고 있는듯 하다.

    쿠팡

     

    최근에는 KDE에도 적용되었다. [Implement a triangle menu filter for Kickoff's categories sidebar, Introduce TriangleMouseFilter]

     

    +.

    응답성에 관련된 글을 찾다보니 재귀적인 setTimeout을 이용한 기법이 나오는데 현대 브라우저 기준으론 비효율적인 아이디어라 생각한다. [응답없는 페이지가 되지 않게 하는 법 (feat. setTimeout), 스케쥴링: setTimeout and setInterval]

    let i = 1;
    setTimeout(function run() {
      func(i);
      setTimeout(run, 100);
    }, 100);

     

    오래 걸리는 작업은 워커 쓰레드를 이용해처리하고 메인쓰레드에서 꼭 유지시킬 필요가 있다면 requestIdleCallback을 사용하는게 낫다고 본다.

     

    그래도 +로 적었으니 팁을 적자면

    Timer Resolution이 윈도우(15ms), 리눅스(10ms)정도도되며,  브라우저에서 setTimeout(4ms)와 setInterval(10ms)의 각지연이 더 필요하다. [Windows Timer Resolution: Megawatts Wasted, High Resolution Timers, Timer resolution in browsers]

     

    따라서 약 25(15 + 10)ms보다 짧은 간격을 사용하는 것은 권장하지 않는다.

    그런데 25ms만에 끝날작업은 애초에 응답성 향상을 위해 재귀적 setTimeout을 이용한 기법을 사용할 이유가 없으며, 커다란 작업을 하기에는 타이머가 너무 빈번히 일어난다고 생각한다.

    250ms나 500ms 정도로 넉넉하게 간격을 두고 처리하고, 타이머를 여러개 설정해서 경쟁하지 않도록 하는 것이 좋을 듯.

     

    TAG

    댓글 0

Designed by black7375.