[Vue.js] 스크롤 내리면 나오는 '맨 위로' 버튼 만들기

SHORTCUT

    반응형

    인트로

    렌탈스튜디오 예약시 간단하게 웰컴가이드 를 제공하고 있다. 기존에는 MVP(minimum value production) 를 위해 구글 독스를 이용해 PPT 형식으로 만들었다. 하지만

    • 가독성이 너무 좋지 않았고
    • 브라우저별 조작(제대로 드래그가 안되는 등)이 원활하지 못했다.

    홈페이지 개발의 갈증을 해소하고 싶은 만큼, 내가 직접 웰컴 가이드를 수정하고 싶다고 생각했다. 기존처럼 좌우로 스와이프 하는 형태의 웰컴가이드를 제작할까도 고민했지만, 많은 글을 전달해야 하는 정보 전달 특성 상 우리에게 익숙한 세로 스크롤 형태를 채택하게 되었다.

    하지만

    세로로 길게 되어있는 줄글의 형식 상 원하는 글로 바로 가는 것이 어려웠고, A부터 Z까지 다 훑어봐야 한다는 불편함이 있었다. 이를 해소하기 위해선

    1. 바로가기를 header에 넣어두고, 헤더를 fixed로 상단 고정 하자
    2. TOC(Table of Content)를 제공하여 전체 목차를 제공하자

    두가지 방안을 생각하게 되었고, 빠른 개발을 위해 2번을 채택하게 되었다.

    1번의 경우 기존에 구성한 CSS를 다시 편성해야 했다. 현재 MVP식의 개발을 위해 385px로 width를 고정한 상태여서 다시 div를 구성하고

    ..어쩌구 저쩌구.. 주절주절 ..


    암튼 그래서 현재 구성에 기능을 더하고자 2번을 채택했다.

    하지만2

    2번 방식을 선택하게 되면, 맨 위로 올라가는 버튼을 위치시켜야 한다.
    그리고 요즈음의 사용자 경험에 맞도록 하기 위해, 이 버튼은 스크롤을 내렸을 때만 등장하도록 개발해야 했다.

    Why UX?

    • 이미 많은 글이 존재하고 있기 때문에 시선의 분산이 발생
    • 385px의 제한된 크기에서 맨 위로 버튼의 심미성을 증가시키기엔 많은 노력이 필요
      나는 최소한의 노력으로 개발해야 하는 1인 개발자다..
    • 위 두 가지 이유로 인해, 유저는 맨 위로 버튼의 존재조차 모를 것

    하지만 스크롤을 내렸을 때 등장한다면,

    • 새로운 버튼의 등장(with Animation)으로 인해, 버튼에 대한 시각적 인지
    • 디자인적 요소에 대한 고민을 덜 수 있음(이미 시각적 인지가 가능하므로)

    적용기

    Vue에서 적용하는 방법은

    1. document.addEventListener('scroll', method); 를 통해, 스크롤 이벤트를 걸기!
      • 🤔 최신 프레임워크에서 구식 기능을 쓰는 식..
    2. v-containerv-scroll 을 활용하여, v-container에 스크롤 액션을 리스닝하기!
      • 🥰 Vue 프레임워크에 맞는 방식!

    이 중에 나는 1번을 선택했다.. 새로운 기능을 써보기도 싶었지만, 바닐라 JS에 좀 더 익숙해지고 싶었고, 이전에 사용해봤던 방식이라 빠르게 적용이 가능했다 (잊지말자! MVP!)

    특히,

    내년엔 꼭!

    리액트를 연습해볼 생각이기 때문에, Vue의 편한 기능에 물들어지면 안되었다..
    (라며 뗀석기 사용하기..)

    Vue3에서 스크롤 이벤트 걸기

    기본적인 스텝은 다음과 같다.

    1. 가상 DOM이 마운트 될 때, 이벤트 리스너 등록하기 + 이벤트 연결
      • mounted() {
          this.target = document.querySelector('#target');
          this.target.addEventListener('scroll', this.handleScroll);
        },
      • DOM 생성 후에 이벤트를 달아야 하기 때문에, created() 가 아닌 mounted() 를 해야한다.
        *lifeCycle에서 훑으며 읽은 내용이라 제대로 공부한 뒤에 수정하겠습니다
      • 필자의 경우 전체 홈페이지가 아닌, 일부 div에서만 발생하므로 해당 DOM에 이벤트를 등록했다.
        또한 이벤트 리스너 삭제를 하기 위해, 해당 객체를 data로 따로 저장하였다.
      • 전체 홈페이지에 거는 경우엔, document.addEventListener('scroll', ... ); 하면 된다.
    2. 1번에서 연결된 이벤트에 원하는 액션 달기
      • handleScroll: function (e) {
          this.scrollTop = e.target.scrollTop;
          if (this.scrollTop > 400) {
            this.isScrollDown = true;
          } else {
            this.isScrollDown = false;
          }
        }
      • 이벤트 발생하는 곳의 scrollTop 값을 가져오고, 해당 값이 특정 값을 넘어가면 버튼이 표시되도록 하기 위해 위처럼 작성했다.
    3. 페이지 이동(마운트 해제 전) 되기 전에, 이벤트 리스너 삭제하기
      • beforeUnmount() {
          this.target.removeEventListener('scroll', this.handleScroll);
        },
      • 스크롤을 달았던 객체를 다시 불러와서 삭제 전에 이벤트 리스너를 삭제한다.
      • vue2에서는 beforeDestroy()를 사용했었는데, vue3에서는 라이프사이클에서 사라지게 되었다.
        따라서 beforeUnmount() 를 사용했다.
      • 이벤트를 삭제하지 않으면, 해당 이벤트가 계속 등록되어 있어 사이드 이펙트가 발생 할 수 있다.

    적용 된 전체 코드

    <script>
    export default {
       data() {
          return {
             isScrollDown: false,    // v-show를 통해 맨위로 버튼을 등장시키는 변수
             scrollTop: 0,                     // 스크롤 값 저장용
             target: null,           // 이벤트가 등록되는 변수
          };
       },
    
       // 1번 가상 돔 마운트 시, 이벤트 등록
       mounted() {
          this.target = document.querySelector('#target');
          this.target.addEventListener('scroll', this.handleScroll);
       },
    
       // 3번 마운트 해제 전, 이벤트 삭제하기
       beforeUnmount() {
          this.target.removeEventListener('scroll', this.handleScroll);
       },
       methods: {
         // 2번 이벤트 액션
          handleScroll: function (e) {
            this.scrollTop = e.target.scrollTop;
            if (this.scrollTop > 400) {
              this.isScrollDown = true;
            } else {
              this.isScrollDown = false;
            }
          }
       },
    };
    </script>

    💥 이슈 사항

    중복호출.. 몬지..알지?..

    하지만 이렇게 작성하게 되면, 스크롤이 발생할 때마다 메소드가 반복 호출되게 된다.
    지금은 스크롤이 많지 않아 큰 문제는 되지 않지만, 이런 사소한 문제도 핸들링해보는 경험이 필요하다..

    반복 호출을 줄이기 위해선, 이벤트를 제어하는

    • 디바운싱(Debouncing)
    • 쓰로틀링(Throttling)

    을 사용해야 한다.

    이중에 쓰로틀링(Throttling)함수 호출 이후 특정 시간 안에 중복 호출 시 무시되도록 하는 기능인데, 쓰로틀링이 스크롤 액션간 많이 활용된다. 필자 또한 이를 활용하여 문제를 해결하고자 한다!

     

    그 해결기는 다음 포스트에 작성하겠다!

     

    완-성!

     

    반응형

    댓글

    Designed by JB FACTORY