일상이 개발

React 상태 동기화 이슈 해결법 – Form, 전역 상태, URL 상태를 하나로 묶는 실전 전략

아빠고미 2025. 5. 3. 08:53
반응형

React 상태 동기화 이슈 해결법 – Form ↔ 전역 상태 ↔ URL 상태 간 데이터 일관성 유지 전략

React 프로젝트를 진행하다 보면 이런 경험, 한 번쯤 해보셨을 거예요.

  • 🔁 폼에서 입력한 값이 URL에 반영되지 않음
  • ❌ 전역 상태를 수정했는데 폼에는 반영이 안 됨
  • 🌀 페이지를 새로고침하면 상태가 초기화됨

이런 문제는 단순한 버그가 아니라, 상태 간 동기화 전략이 없어서 생기는 설계 부재 입니다.

React 상태 동기화 이슈 해결법 – Form ↔ 전역 상태 ↔ URL 상태 간 데이터 일관성 유지 전략

이번 글에서는 Form → 전역 상태 → URL 상태까지 React 앱 전반에서 상태 흐름을 통합하고 동기화하는 전략을 다룹니다.


1. 🔍 상태 동기화 이슈는 왜 발생할까?

📦 상태가 분산되어 있기 때문

  • Form은 useState 또는 React Hook Form이 관리
  • 전역 상태는 Zustand, Redux 등으로 따로 관리
  • URL 상태는 React Router에서 별도로 관리

이렇게 분리되어 있는 상태들이 **하나의 기준 없이 따로 노는 상태**가 되면, 동기화 불일치 문제가 발생합니다.

🧠 예: 필터 입력값 상태 흐름

[사용자 입력] → Form → 전역 상태 업데이트 → URL 변경
                       ↘ 페이지 새로고침 ↙
                URL 파싱 → 전역 상태 초기화 → Form 반영

이 전체 흐름을 설계하지 않으면, 각 상태 간 동기화가 꼬이게 됩니다.

2. 🔁 전역 상태와 URL 상태 동기화 설계

전역 상태와 URL 쿼리 파라미터는 자주 엮이는 조합입니다. 검색 조건, 필터, 페이지 번호 등은 공유 가능한 URL로 만들어야 하고, 동시에 앱 전체에서 참조할 수 있는 상태이기도 하죠.

📌 전역 상태 → URL 반영

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

const [searchParams, setSearchParams] = useSearchParams();
const { filter } = useStore(); // zustand 등에서 상태 가져오기

useEffect(() => {
  setSearchParams({ filter }); // 상태가 바뀌면 URL 반영
}, [filter]);

📌 URL → 전역 상태 초기화

useEffect(() => {
  const urlFilter = searchParams.get('filter');
  if (urlFilter) {
    setFilter(urlFilter); // URL에 있는 값을 상태로 반영
  }
}, []);

양방향 동기화가 필요할 때는 변경 시점을 정교하게 관리해야 해요.
예: 컴포넌트 마운트 시 1회만 적용 / 이후엔 상태 변경 시 URL 반영


3. ✍️ Form 입력과 상태 연결 – 세 가지 방식 비교

① useState 기반 제어 컴포넌트

const [email, setEmail] = useState('');

<input value={email} onChange={(e) => setEmail(e.target.value)} />

② useForm (React Hook Form)

const { register, watch } = useForm();

<input {...register('email')} />

const email = watch('email');

③ Form → 전역 상태 반영

const email = useWatch({ name: 'email' });

useEffect(() => {
  setEmailGlobal(email);
}, [email]);

➡️ 입력값이 바뀌면 전역 상태도 함께 갱신되도록 설계할 수 있어요.


4. 🧠 상태 변경 시점 관리 전략

여기서 중요한 건 상태를 언제 동기화하느냐입니다.

📌 상태 변경 타이밍 분류

  • ✅ 입력 즉시 반영 (onChange)
  • ✅ 디바운싱 후 반영 (200~300ms)
  • ✅ "검색" 버튼 클릭 시 일괄 반영

🎯 실무에서는 입력은 로컬, 반영은 전역 + URL 방식이 많습니다.

// useDebounce 훅 사용 예
const debounced = useDebounce(email, 300);

useEffect(() => {
  setEmailGlobal(debounced);
  setSearchParams({ email: debounced });
}, [debounced]);

5. 🛠️ 상태 동기화 리팩토링 패턴

프로젝트가 커질수록 다양한 입력 요소와 상태가 얽히게 됩니다. 이를 효율적으로 관리하려면 재사용 가능한 훅과 유틸로 추상화하는 것이 중요합니다.

✅ 커스텀 훅: useFormSync

function useFormSync(form, fieldName, globalSetter, queryKey) {
  const value = form.watch(fieldName);
  const [searchParams, setSearchParams] = useSearchParams();

  useEffect(() => {
    globalSetter(value);
    setSearchParams((prev) => {
      const params = new URLSearchParams(prev);
      params.set(queryKey, value);
      return params;
    });
  }, [value]);
}

💡 사용 예시

useFormSync(form, 'email', setGlobalEmail, 'email');

📦 한 줄로 Form ↔ 전역 ↔ URL 동기화를 동시에!


6. 💡 실전 예시 – 필터 UI 동기화 흐름

📋 시나리오

  1. 유저가 "카테고리"를 선택함 (드롭다운)
  2. 입력값은 Form 내부에서 관리됨
  3. 선택 즉시 전역 상태 + URL 동기화
  4. 다른 페이지로 이동 후 뒤로 가기 시, 선택값 유지

📁 구조 예시

src/
├── features/
│   └── product/
│       ├── ProductFilterForm.jsx
│       └── hooks/useProductFilterSync.js
├── stores/
│   └── useFilterStore.js
├── utils/
│   └── parseSearchParams.js

📦 결과

  • Form UI는 간결
  • 전역 상태는 다양한 페이지에서 공유 가능
  • URL로 상태 공유 및 새로고침 복원 가능

7. 🧠 마무리 – 세 상태의 흐름을 하나로 묶자

상태 동기화는 단순한 기술 구현이 아니라 앱의 UX, 유지보수, SEO에 영향을 주는 중요한 설계 포인트입니다.

✨ 핵심 요약

  • Form 상태는 로컬에서 제어하되, 필요한 시점에 전역 + URL로 반영하자
  • 전역 상태는 페이지 간 공유, URL 상태는 공유 및 새로고침 복원에 최적
  • useEffect, debounce, 커스텀 훅으로 상태 전환 시점 조절
  • 리팩토링을 통해 모든 입력 필드의 동기화를 일괄 관리 가능

이제 여러분의 앱에서도 Form, 전역 상태, URL 상태 간의 경계를 허물고, **일관된 사용자 경험**을 제공해보세요!

 

글이 도움 되셨다면 댓글, 공감, 공유로 응원 부탁드려요! 🙌

반응형