👉

URL에 React UI 상태 저장하기

Created
2024/02/19 00:24
Tags
안녕하세요. 볼드나인 프론트엔드 개발자 최송이입니다.
오늘은 URL과 UI 상태를 동기화하는 작업을 적용한 과정과, 이를 볼드나인 이지스토리지에 적용하며 느낀 바를 공유하고자 합니다.
[목차]

들어가며

브라우저를 새로고침할 때마다, 기존 UI 상태가 사라지면 어떨까요? 볼드나인의 이지스토리지 서비스 특성상 검색 필터가 많고, 대부분 페이지네이션 테이블이기 때문에 모두 초기화된다면 사용자는 큰 불편을 겪게 됩니다.
예로 테이블 페이지네이션 UI 상태를 살펴보겠습니다. 이전에는 React Router의 location.state에 페이지 인덱스와 페이지 사이즈 UI 상태를 저장해 URL에 노출되지 않고 숨겨진 상태로 유지됐습니다. 하지만 여기에 저장된 상태는 항상 불변성을 유지해야 하고, 이에 대한 코드의 복잡성도 증가했습니다. 이 문제를 해결하고 동시에 사용자 경험을 향상시키기 위해 URL에 UI 상태를 저장하기로 하였습니다.

URL에 UI 상태를 저장하면 얻을 수 있는 장점

1.
북마크 및 공유 가능한 링크: 사용자가 특정 UI 상태에 도달하면 해당 상태를 URL에 저장함으로써 사용자가 해당 상태를 쉽게 북마크하고 다른 사람과 공유할 수 있습니다.
2.
뒤로 가기 및 앞으로 가기 기능의 지원: URL에 UI 상태를 저장하면 사용자가 뒤로 가기 또는 앞으로 가기 버튼을 클릭했을 때 이전 또는 다음 상태로 즉시 이동할 수 있습니다. 사용자가 탐색하기 편리하게 하고, 애플리케이션 상태를 관리하는 데 유용한 기능을 제공합니다.
3.
상태 유지: 사용자가 페이지를 새로고침하거나 다시 방문할 때 이전 상태를 복원하여 사용자가 마지막에 떠난 상태로 애플리케이션을 복원할 수 있습니다. 이는 사용자 경험을 향상시키고 사용자들이 애플리케이션을 계속해서 사용할 수 있도록 도와줍니다.
4.
SEO (검색 엔진 최적화): 검색 엔진이 페이지를 색인화하여 애플리케이션의 검색 엔진 최적화를 향상시키고, 사용자는 검색 결과에서 특정 상태로 바로 이동할 수 있습니다.
5.
UI 상태 추적: 사용자의 상호작용을 추적하고 분석할 수 있습니다. 이를 통해 사용자의 행동 및 선호도를 이해하고 애플리케이션을 개선할 수 있습니다.

page를 이동할 때, URL과 동기화하자

페이지 인덱스나 페이지 사이즈 옵션을 변경할 때마다 URL에 해당 값을 넣어주어 페이지 UI 상태와 동기화시켜줍니다. 이 때, 쿼리 파라미터의 key의 네이밍은 일반적으로 권장되는 kebab case를 이용하였습니다. kebab case를 사용하면 -로 단어를 구분해주기 때문에 가독성이 좋고, 검색 엔진이 URL을 이해하기 쉽게 할 수 있으며, 링크 공유시 플랫폼에 상관없이 깨지지 않고 정확하게 전달되는 이점이 있습니다.
URL에 UI상태 값을 저장하기 위해 React Router에서 제공하는 useSearchParams 훅을 사용했습니다. 이 훅은 useState와 마찬가지로 URLSearchParams 객체를 반환하는 searchParams와 이를 업데이트하는 데 사용할 수 있는 함수 setSearchPaams을 반환합니다. 이를 이용해 간단히 현재 location에 대한 URL의 쿼리 스트링을 읽고 수정할 수 있습니다.
// 커스텀 훅 내부 const [searchParams, setSearchParams] = useSearchParams(); searchParams.set('page', (nextPage + 1).toString()); searchParams.set('page-size', nextPageSize.toString()); setSearchParams(Object.fromEntries(searchParams.entries()));
JavaScript
복사
페이지 상태가 변할 때, 커스텀 훅 내부에서 searchParamsset() 메서드로 반영한 후, setSearchParams를 이용해 변경된 쿼리 스트링을 현재 URL에 업데이트하는 방식입니다.
참고로 코드상 첫 페이지의 값은 0이지만 사용자 입장에서 첫 페이지는 1이 더 친숙하다고 판단해서 원래 페이지 인덱스 값에서 1을 더해주어 쿼리 스트링에 넣어줬습니다.

URL이 변경될 때, 값을 가져오자

URL이 변경될 때, URL 쿼리 스트링에 있는 page, page-size의 값을 가져와서 UI 상태에 반영합니다. 이렇게 값을 가져오면 새로고침 할 때도 잘 유지되겠죠? 이를 위해선 항상 문자열로 들어오는 쿼리 스트링을 원하는 타입으로 변형해야 합니다. 페이지 UI 상태값을 number 타입으로 변환해주고, 값이 없으면 원래 설정해주었던 default값이 되도록 했습니다. 이제 파싱된 값을 UI 상태에 업데이트 해줍니다.
// 커스텀 훅 내부 const usePageQueryParam = ( defaultPageValue = DEFAULT_INITIAL_PAGE_VALUE ): UsePageQueryParamReturn => { const location = useLocation(); const [pageParam, setPageParam] = useState( parsePageQueryParam( searchParams, defaultPageValue.page, defaultPageValue.pageSize ) ); ... useEffect(() => { setPageParam( parsePageQueryParam( searchParams, defaultPageValue.page, defaultPageValue.pageSize ) ); }, [location.search]); ... } // 쿼리 스트링 파싱 const parsePageQueryParam = ( searchParams: URLSearchParams, defaultPage: number, defaultPageSize: number ): PageQueryParam => { const page = Number(searchParams.get('page') ?? defaultPage); const pageSize = Number( searchParams.get('page-size') ?? defaultPageSize ); ... return { page, pageSize }; };
JavaScript
복사

URL에는 어떤 값이든 들어올 수 있다

URL에 UI 상태를 저장하면서 맞닥뜨린 문제는 URL에 어떤 값이든 들어올 수 있다는 점입니다. 이로 인해 사용자가 URL에 유효하지 않은 값을 넣어 UI 상태를 조작할 수 있습니다. 예를 들어 제공하는 페이지 사이즈의 옵션이 10, 100, 1000, 10000 까지인데, 쿼리 스트링에 ?page-size=1000000000이나 ?page-size=bold9을 넣었다면 예기치 못한 에러를 발생시킬 수 있습니다. 이를 해결하기 위해 파싱 과정에서 enum으로 유효하지 않은 값을 막아주었습니다.
enum PageSizeOptions { TEN = 10, TWENTY = 20, FIFTY = 50, HUNDRED = 100, TWOHUNDRED = 200, } // 쿼리 스트링 파싱 함수 내부 ... const pageSizeParam = Number( searchParams.get('page-size') ?? defaultPageSize ); if ( !Number.isNaN(pageSizeParam) && pageSizeParam in PageSizeOptions ) { return pageSizeParam; } else { return defaultPageSize; }
JavaScript
복사
정해져 있는 페이지 사이즈의 옵션을 숫자형 enum에 넣어줍니다. URL에서 값을 가져올 때, enum으로 옵션 이외의 값을 체크하고, 값이 NaN이면 default 값으로 변환해주어 해결하였습니다.

마치며

지금까지 페이지 UI상태를 예로 들어 URL에 UI상태를 저장하였던 경험에 대해 전해드렸습니다. 이번에 전부 소개하지는 못했지만, 다양한 형식의 검색 필터도 URL에 저장하기 위해 계속 개발 중입니다. 더 나아가 링크만으로 UI 상태를 공유할 수 있도록 할 예정입니다.
앞으로도 지속적으로 보완하여 이지스토리지에 편리한 서비스를 제공할 수 있도록 노력하겠습니다!
참고 문서