[React] 뒤로가기 시 상태 유지하기 ( useNavigationType, popstate )

2024. 11. 15. 18:15·React

TLDR

이번에는 Tab Navigation을 서비스를 개발하며 개선한 부분에 대해서 얘기해보려고 합니다. 그저 "탭의 상태" 에 따른 표시되는 UI가 변경되는 정도의 컴포넌트로 인식하고 있었지만, 사용자측면을 고려하고 베타테스트를 진행하면서 저 "상태"를 자연스럽게 관리하는 것에 대해 고민하고 개선한 내용과 그 로직에 대해 공유하려고 합니다.

 

초기 로직 : UI와 그 기능에 집중

처음 개발한 Tab Navigation은 UI와 Highligt Animation 등에 더 신경을 많이 썼습니다. 다른 페이지에서도 활용 될 수 있게끔, 탭의 갯수와 상관없이 UI를 적용할 수 있게끔 고민했고, 탭의 상태는 크게 신경쓰지 않았습니다.

컴포넌트의 Props를 통해 tab의 정보를 배열로 받았으며, tab navigation에서 선택된 탭의 상태를 useState를 통해 관리 했으며, Props를 통해 공유했습니다.

 

문제 1 : 활성화 된 탭의 상태가 계속해서 초기화 된다

useState로 활성화 된 탭을 관리하니 당연한 일이지만, 다른 페이지로 이동하거나 해당페이지 내 상세페이지로 들어갔다가 나올 때면 해당 탭이 초기화 되어있어서 사용자 불편함을 유발했습니다. 웹에서도 불편하겠지만, 웹뷰를 통해 앱으로 서비스를 지원하는 경우 그 불편함과 어색함이 더 부각되어 빠르게 개선이 필요했습니다.

그래서 URL params에 활성화된 tab을 기록하여 뒤로가기 stack에 남게끔 수정했습니다. URL params에 남아있다면 다른 어딘가로 이동했다가 와도 그 기록을 통해 기존에 활성화 되어 있던 탭으로 돌아 갈 수 있게끔 하여, 사용자의 불편함을 해결하였습니다.

 

import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';

function TabNavigation() {
	const [ query, setQuery ] = useSearchParams();
    
    useEffect(() => {
    	query.set('tab', tab상태);
        setQuery(query);
    }, [tab 상태])
    
    ...
}

 

문제 2 : 뒤로가기 stack이 너무 많이 쌓임

위 방법을 통해 해결한건 사용자 입장에서 다른 불편함을 유발했습니다. 탭이 변경될 때 마다 뒤로가기 stack이 쌓이게 된 것 입니다. 뒤로가기를 눌러도 이전 탭, 화면으로 돌아가는게 아닌 tab navigation 화면그대로 URL 만 되돌아 가는 현상이 발생했고 URL이 보이지 않는 앱 입장에서는 더욱 치명적으로 다가왔습니다.

위 사진은 쌓여있는 뒤로가기 stack의 예시입니다. 사용자 입장에서 tab Navigation을 다룬후 pageC로 이동했다가 다시 뒤로가기를 통해 pageA로 가려면? pageB?tab=3 -> tab=2 -> tab=1 -> pageB 라는 과정을 통해 pageB에 뒤로가기 4번 시도하는 동안 갖혀있게 됩니다.

 

문제 재 정의

URL params를 활용하는 방식의 한계를 알았으니 다른 방식으로의 수정이 필요했습니다. 기존 useState방식은 상태가 유지되지 못하니, 전역상태와 같은 방법을 고민하기 이전에 해결해야하는 문제에 대해 구체적으로 정의가 필요했습니다.

 

Tab Navigation 의 상태저장 예시

  1. tab navigation page로 정상적으로 이동했다면? "탭의 상태가 초기화" 되야 한다.
  2. 다른 페이지 / 심지어 외부페이지 이동했다가 "뒤로가기"를 해서 페이지로 돌아온다면 "탭의 상태 유지"가 되야한다
  3. 새로고침 시, 탭 유지

 

LocalStorge

서비스 내부에는 아예 외부 다른 페이지로 이동하는 링크가 있습니다. 이런 부분까지 고려한다면, 상태 관리는 localStorage에서 하는 게 맞다고 판단했습니다.

 

뒤로가기 상태 구분하기

tab navigation이 있는 페이지에서 위 문제를 해결하기 위해서는? 페이지를 접근할때 뒤로가기로 접근했는지, 아니면 페이지로 새롭게 접근했는지를 구분할 수 있어야 합니다.

  • window.addEventListener('popstate') : 뒤로가기 할 때 실행
  • useNavigationType(): react-router-dom 내 hooks로 어떤 동작을 통해 페이지로 접근했는지 확인

해당 페이지에 어떤 동작으로 접근했는지 확인하기 위해 window.history.state를 사용하려고 한적이 있으나, React SPA 때문인지 예상대로 동작하지 않았습니다. performance.navigation.type 도 마찬가지로 원하는데로 분류할 수 없어서 위 두가지 방법을 엮어서 뒤로가기 상태를 구분했습니다.

 

코드

import React, { useState, useEffect } from 'react';
import { useLocation, useNavigationType } from 'react-router-dom';

const TabContent = ({ tab }) => (
  <div>
    <h2>{tab} 페이지 내용</h2>
  </div>
);

const TabNavigation = () => {
  const isValidTab = (tab) => ['tab1', 'tab2', 'tab3'].includes(tab);

  // 현재 네비게이션 타입과 위치를 가져옵니다.
  const navigationType = useNavigationType();
  const location = useLocation();

  const initialTab =
    navigationType === 'POP'
      ? localStorage.getItem('activeTab') || 'tab1' // 뒤로가기인 경우 저장된 탭 유지
      : 'tab1'; // 새로고침 또는 직접 접근 시 기본값 설정

  const [activeTab, setActiveTab] = useState(
    isValidTab(initialTab) ? initialTab : 'tab1'
  );
  
   useEffect(() => {
    if (navigationType !== 'POP') {
      setActiveTab('tab1');
      localStorage.removeItem('activeTab');
    }
  }, []);

  useEffect(() => {
    const restoredTab = localStorage.getItem('activeTab');
    if (navigationType === 'POP' && isValidTab(restoredTab)) {
      setActiveTab(restoredTab);
    }

    const handleTabChange = (tab) => {
      setActiveTab(tab);
      localStorage.setItem('activeTab', tab);
    };

    return () => {
      handleTabChange(activeTab); // 컴포넌트 언마운트 시 현재 탭 저장
    };
  }, [location, navigationType]);

  const handleTabClick = (tab) => {
    setActiveTab(tab);
    localStorage.setItem('activeTab', tab); // 탭 상태를 localStorage에 저장
  };

  return (
    <div>
      <div>
        <button
          onClick={() => handleTabClick('tab1')}
          className={activeTab === 'tab1' ? 'active' : ''}
        >
          탭 1
        </button>
        <button
          onClick={() => handleTabClick('tab2')}
          className={activeTab === 'tab2' ? 'active' : ''}
        >
          탭 2
        </button>
        <button
          onClick={() => handleTabClick('tab3')}
          className={activeTab === 'tab3' ? 'active' : ''}
        >
          탭 3
        </button>
      </div>
      <div>
        <TabContent tab={activeTab} />
      </div>
    </div>
  );
};

const App = () => (
  <div>
    <h1>탭 네비게이션 예제</h1>
    <TabNavigation />
  </div>
);

export default App;

 

마무리

지금까지 TabNavigation 의 상태유지를 위해 개선한 내용에 대해 소개해 드렸습니다. 이번 문제를 해결하기 위해 React에서 페이지 상태를 분류할 수 있는 다양한 이벤트타입과 hooks, 로직 등에 대해서 새롭게 알게된 부분이 많았던 것 같습니다.

저작자표시 변경금지 (새창열림)

'React' 카테고리의 다른 글

React에서 안전하게 HTML 렌더링하기: dangerouslySetInnerHTML의 대안  (0) 2025.02.26
[React] 비즈니스 / 도메인 로직을 통해 코드 응집도 높이기  (3) 2024.12.03
[React] 웹뷰로 연결된 Android Stuiod / IOS 에 데이터 주고받기 ( 브릿지 통신 )  (0) 2024.11.11
[AWS] Sub Domain 연결하기  (5) 2024.10.31
[React + Vite + Nginx + Docker + AWS] 를 활용하여 배포 해봅시다!  (0) 2024.06.05
'React' 카테고리의 다른 글
  • React에서 안전하게 HTML 렌더링하기: dangerouslySetInnerHTML의 대안
  • [React] 비즈니스 / 도메인 로직을 통해 코드 응집도 높이기
  • [React] 웹뷰로 연결된 Android Stuiod / IOS 에 데이터 주고받기 ( 브릿지 통신 )
  • [AWS] Sub Domain 연결하기
설탕시럽
설탕시럽
설탕시럽의 Web Tech Blog 입니다.
  • 설탕시럽
    SugarSyrup Tech Blog
    설탕시럽
  • 전체
    오늘
    어제
    • 분류 전체보기 (46)
      • HTML & CSS (2)
      • JavaScript (6)
      • React (15)
      • BackEnd (1)
      • 회고 (5)
        • 엄브렐라 개발일지 (1)
        • 인턴 회고!!! (2)
      • 컴퓨터 기초 (4)
        • 운영체제 (2)
        • SICP (1)
      • 코딩테스트 (10)
      • 짧은 글 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    html-react-parser
    에러
    Element
    document
    React
    level1
    AWS
    오블완
    운영체제
    css
    php
    배포
    리액트
    SQL
    TypeScript
    consistent return
    회고
    티스토리챌린지
    인턴
    타입스크립트
    프로그래머스
    EC2
    자바스크립트
    코딩테스트
    CodingTest
    JavaScript
    nginx
    프론트엔드
    JS
    docker
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
설탕시럽
[React] 뒤로가기 시 상태 유지하기 ( useNavigationType, popstate )
상단으로

티스토리툴바