Skip to content

스크롤 동작

클라이언트 사이드 라우팅을 사용할 때는 새 라우트로 이동하면 맨 위로 스크롤하거나, 실제 페이지 새로고침처럼 히스토리 항목의 스크롤 위치를 유지하고 싶을 수 있습니다. Vue Router는 이를 가능하게 할 뿐 아니라, 라우트 내비게이션 시 스크롤 동작을 완전히 사용자 정의할 수 있게 해줍니다.

참고: 이 기능은 브라우저가 history.pushState를 지원할 때만 동작합니다.

라우터 인스턴스를 만들 때 scrollBehavior 함수를 제공할 수 있습니다:

js
const router = createRouter({
  history: createWebHashHistory(),
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // 원하는 위치를 반환합니다
  }
})

scrollBehavior 함수는 내비게이션 가드처럼 tofrom 라우트 객체를 받습니다. 세 번째 인자인 savedPosition은 이것이 popstate 내비게이션(브라우저 뒤로/앞으로 버튼으로 트리거됨)일 때만 사용할 수 있습니다.

이 함수는 ScrollToOptions 위치 객체를 반환할 수 있습니다:

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    // 항상 맨 위로 스크롤합니다
    return { top: 0 }
  },
})

el을 통해 CSS 선택자나 DOM 요소를 전달할 수도 있습니다. 이 경우 topleft는 해당 요소에 대한 상대 오프셋으로 처리됩니다.

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    // 항상 #main 요소의 10px 위로 스크롤합니다
    return {
      // 다음과 같이 쓸 수도 있습니다
      // el: document.getElementById('main'),
      el: '#main',
      // 요소 위로 10px
      top: 10,
    }
  },
})

falsy 값이나 빈 객체를 반환하면 스크롤이 일어나지 않습니다.

savedPosition을 반환하면 뒤로/앞으로 버튼으로 이동할 때 네이티브와 비슷한 동작을 하게 됩니다:

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
  },
})

"앵커로 스크롤" 동작을 흉내 내고 싶다면:

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    if (to.hash) {
      return {
        el: to.hash,
      }
    }
  },
})

브라우저가 scroll behavior를 지원한다면 부드럽게 만들 수 있습니다:

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    if (to.hash) {
      return {
        el: to.hash,
        behavior: 'smooth',
      }
    }
  },
})

스크롤 지연하기

때로는 페이지에서 스크롤하기 전에 잠시 기다려야 합니다. 예를 들어 transition을 다룰 때는 transition이 끝난 뒤 스크롤하고 싶을 수 있습니다. 이를 위해 원하는 위치 descriptor를 반환하는 Promise를 반환할 수 있습니다. 다음은 500ms 기다린 뒤 스크롤하는 예제입니다:

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ left: 0, top: 0 })
      }, 500)
    })
  },
})

페이지 수준 transition 컴포넌트의 이벤트와 연결해 스크롤 동작이 페이지 전환과 잘 어우러지도록 만들 수 있습니다. 하지만 사용 사례의 다양성과 복잡성 때문에, 여기서는 특정 userland 구현을 가능하게 하는 이 기본 수단만 제공합니다.

고급 오프셋

페이지에 고정 네비게이션 바 같은 요소가 있다면, 대상 요소가 다른 콘텐츠 뒤에 가려지지 않도록 오프셋이 필요할 수 있습니다. 정적인 오프셋 값을 사용하는 방식은 항상 잘 동작하지는 않습니다. scroll-margin, scroll-padding을 사용하거나 ::before, ::after 가상 요소를 사용하는 CSS 기반 해결책을 시도할 수 있지만, 이런 접근은 예상치 못한 동작을 일으킬 수 있습니다.

이런 경우에는 오프셋을 수동으로 계산하는 편이 더 좋습니다. 간단한 방법은 CSS와 JavaScript의 getComputedStyle()을 결합하는 것입니다. 이렇게 하면 각 요소가 자신의 오프셋을 동적으로 정의할 수 있습니다. 예:

js
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    const mainElement = document.querySelector('#main')
    if (mainElement) {
      const marginTop = parseFloat(
        getComputedStyle(mainElement).scrollMarginTop
      )
      return {
        el: mainElement,
        top: marginTop,
      }
    } else {
      return { top: 0 }
    }
  },
})

모두를 위한 문서 한글화