Skip to content

페이지가 렌더링 된 후 스크롤을 최상단으로 올리기

juhyojeong edited this page Aug 4, 2024 · 1 revision

✏️ 구현하고 싶었던 것

랜딩 페이지나 이벤트 페이지의 경우 화면에 위아래로 길고, 스크롤을 내릴때마다 해당 위치 컴포넌트들이 나타나는 스크롤 애니메이션이 적용되어야하는 스펙이었다. 이때 중간에서 새로고침을 하면 해당 위치에서 새로고침이 되는 것보다는 맨 첫 위치로 이동해서 다시 스크롤 애니메이션을 볼 수 있도록 하는 것이 더 자연스러운 UX라고 생각했다.

그래서 렌더링이 될 때마다 scroll을 top으로 끌어올리는 hook을 만들어서 스크롤 애니메이션이 들어가는 페이지마다 선언을 해주려고 했다.

1. useEffect() 활용

import { useEffect } from "react";
import { useLocation } from "react-router-dom";

export default function useScrollTop() {
    const { pathname } = useLocation();

    useEffect(() => {
        window.scroll({ top: 0 });
    }, [pathname]);
}

useEffect를 사용해서 첫 렌더링 시 / 새로고침 시 / pathname이 바뀔 시 스크롤을 맨 처음으로 올리도록 hook을 구현했다. 그런데 이 코드는 동작하지 않는다. 디버깅을 해보면 코드가 호출되기는 하는데 결정적으로 scroll이 top: 0의 위치로 이동하지 않는다. 화면이 다 로드되지 않고 실행되서 그런가 싶어서 load 이벤트와 함께 동작하게 수정했다.

2. load 이벤트 활용

  • load 이벤트 : 페이지의 모든 요소(HTML, CSS, JavaScript, 이미지 등)가 완전히 로드된 후 발생한다
import { useEffect } from "react";
import { useLocation } from "react-router-dom";

export default function useScrollTop() {
    const { pathname } = useLocation();

    useEffect(() => {
        const handleScroll = () => {
            window.scroll({ top: 0 });
        };

        handleScroll();
        window.addEventListener("load", handleScroll);

        return () => {
            window.removeEventListener("load", handleScroll);
        };
    }, [pathname]);
}

useEffect 내에서 load 이벤트 리스너를 부착해서, 자원이 모두 로드되었을 때 스크롤 top 이벤트가 발생할 수 있도록 했다. 그런데 여전히 실행 안된다. 그래서 좀 찾아보니 load 이벤트에서 scroll을 호출할 때, 자원은 모두 로드되었을지라도 페이지가 완전히 렌더링되지 않았거나 브라우저의 최적화 과정 중에 있기 때문에, 스크롤 위치가 즉시 업데이트되지 않을 수 있다고 한다.

3. beforeunload 이벤트 활용

  • beforeunload 이벤트 : 페이지가 언로드되기 전에 발생한다
import { useEffect } from "react";
import { useLocation } from "react-router-dom";

export default function useScrollTop() {
    const { pathname } = useLocation();

    useEffect(() => {
        const handleScroll = () => {
            window.scroll({ top: 0 });
        };

        handleScroll();
        window.addEventListener("beforeunload", handleScroll);

        return () => {
            window.removeEventListener("beforeunload", handleScroll);
        };
    }, [pathname]);
}

beforeunload 이벤트는 페이지가 완전히 렌더링된 상태에서 호출된다. 기존에 load 이벤트를 사용하는 경우에는 렌더링을 하고 스크롤을 top으로 올려주려고 했다면, beforeunload의 경우 리렌더링 전 스크롤을 위로 올린 후 리렌더링이 된다. 그래서 스크롤 위치 조정이 올바르게 반영되고 다음 렌더가 실행돼서 원하는 대로 동작한다.

참고) scrollRestoration

추가로 찾아보다가 알게된 history API(웹 브라우저의 세션 기록을 조작하고 관리할 수 있는 JavaScript API)의 속성인데, 새로고침 시 스크롤의 위치를 어떻게 할 것인가에 대한 결정을 하는 속성이다.

  • "auto": 디폴트 값으로 페이지의 이전 스크롤 위치를 자동으로 복원한다
  • "manual": 스크롤 위치를 자동으로 복원하지 않는다

이 속성을 manual로 설정하면 원하는 대로 동작하지 않을까 했는데, 이 API는 새로고침 시(페이지 전체가 새로 로드)에만 동작하고 react-router-dom의 Link 태그로 SPA 라우팅이 발생했을 때는 동작하지 않는다. 그래서 이 속성을 사용하지는 못하고 위에서 정의한 hook을 사용했다.

📚 학습 정리

🗂️ 멘토링

Clone this wiki locally