React 상태 트래핑 중단: 필터를 URL에 동기화 🔗

발행: (2026년 5월 4일 PM 01:54 GMT+9)
5 분 소요
원문: Dev.to

Source: Dev.to

Stop Trapping React State: Sync Your Filters to the URL 🔗의 커버 이미지

“공유 불가능한” 대시보드 문제

이 흔한 B2B SaaS 상황을 상상해 보세요: 한 임원이 여러분의 분석 대시보드를 엽니다. 그는 데이터를 설정하는 데 3분을 투자합니다—상태를 “Active” 로 필터링하고, 날짜 범위를 “Last 30 Days” 로 지정하고, 테이블을 “Highest Revenue” 로 정렬한 뒤 Page 4 로 이동합니다. 그런 다음 URL을 복사해 Slack으로 팀 리더에게 보냅니다.

팀 리더가 그 링크를 클릭하지만, Active High‑Revenue 클라이언트의 Page 4 를 보는 대신 기본, 필터가 적용되지 않은 대시보드만 보게 됩니다. 컨텍스트가 완전히 사라진 것이죠. 왜일까요? 원래 개발자가 모든 필터를 React의 useState 훅 안에 잡아두었기 때문입니다. 팀 리더가 페이지를 새로 고칠 때 그 로컬 상태가 사라져 버린 것입니다.

솔루션: URL이 진실의 단일 소스다

스마트 테크 개발자(Smart Tech Devs)에서 엔터프라이즈 급 프런트엔드 경험을 설계할 때 우리는 엄격한 규칙을 따릅니다: 화면에 표시되는 데이터가 변하는 상태는 반드시 URL에 존재해야 한다.

필터, 정렬, 페이지네이션을 URL 검색 파라미터(쿼리 문자열)와 동기화함으로써, 우리는 깊은 링크가 가능하고, 공유 가능하며, 새로 고침해도 유지되는 대시보드를 구현합니다.

Next.js (App Router)에서 URL 상태 설계하기

setFilter()를 사용하는 대신, Next.js 훅을 이용해 브라우저의 History API를 조작합니다. 아래는 URL을 안전하게 업데이트하는 필터 드롭다운을 만드는 방법입니다.

// app/components/StatusFilter.tsx
"use client";

import { useRouter, usePathname, useSearchParams } from 'next/navigation';

export default function StatusFilter() {
    const router = useRouter();
    const pathname = usePathname();
    const searchParams = useSearchParams();

    // Read the CURRENT state directly from the URL, defaulting to 'all'
    const currentStatus = searchParams.get('status') || 'all';

    const handleFilterChange = (newStatus: string) => {
        // 1. Create a fresh URLSearchParams object based on current URL
        const params = new URLSearchParams(searchParams.toString());

        // 2. Set the new parameter (or delete it if resetting)
        if (newStatus === 'all') {
            params.delete('status');
        } else {
            params.set('status', newStatus);
        }

        // 3. Reset pagination to page 1 whenever a filter changes!
        params.delete('page');

        // 4. Update the URL without triggering a full page reload
        router.push(`${pathname}?${params.toString()}`);
    };

    return (
         handleFilterChange(e.target.value)}
            className="filter-dropdown"
        >
            All Statuses
            Active Only
            Archived
        
    );
}

서버 컴포넌트에서 URL 상태 사용하기

이제 상태가 URL에 존재하기 때문에, Next.js 서버 컴포넌트는 초기 요청 시 즉시 이를 읽을 수 있습니다. 이는 서버에서 완벽하게 필터링된 데이터를 가져와 로딩 스피너 없이 즉시 표시하고, 뛰어난 SEO 효과를 제공합니다.

// app/dashboard/page.tsx
import { fetchClients } from '@/lib/db';
import StatusFilter from './components/StatusFilter';

// Next.js automatically passes searchParams to page components
export default async function DashboardPage({ searchParams }: { searchParams: { status?: string } }) {
    const currentStatus = searchParams.status || 'all';

    // Fetch directly from the DB using the URL state
    const clients = await fetchClients({ status: currentStatus });

    return (
        
            
                

클라이언트 명단

);

}

Conclusion

Local state (useState) should be reserved for transient UI elements like opening a modal or typing in a text field. For everything else—filters, tabs, search queries, and pagination—the URL must be your single source of truth. It is the defining line between a hobby project and a professional, collaborative SaaS platform.

0 조회
Back to Blog

관련 글

더 보기 »