Remix는 서버 중심 아키텍처(Server-first) 를 철학으로 삼는 웹 프레임워크입니다.
데이터 패칭, 폼 처리, 전환 상태 관리까지 대부분 서버를 통해 처리하고, 클라이언트 코드는 가능한 한 얇게 유지하자는 접근이죠.
그렇다면 이런 Remix에서 클라이언트 상태는 어떻게 관리해야 할까요?
Redux, Zustand, React Query 같은 기존 상태관리 도구를 써야 할까요? 아니면 Remix만으로 충분할까요?
이 글에서는 Remix에서 클라이언트 상태를 다루는 3가지 범주로 나눠서 설명합니다:
1. 💾 UI 상태 (로컬 상태)
✅ 언제나 useState가 기본
UI 상의 일시적인 상태 — 예: 모달 열림 여부, 탭 선택, 인풋 값 등 — 은 기존 React와 동일하게 useState로 처리합니다.
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button onClick={() => setIsOpen(true)}>열기</button>
{isOpen && <Modal />}
</>
);
Remix는 React 기반이므로 기본적인 컴포넌트 로컬 상태는 그대로 사용하면 됩니다.
2. 🔁 폼 상태와 요청 흐름
Remix의 진정한 힘은 서버와의 통신을 UI 상태처럼 제어할 수 있는 구조에 있습니다.
📦 useFetcher()로 낙관적 UI + 서버 상태 처리
const fetcher = useFetcher();
return (
<fetcher.Form method="post" action="/api/toggle">
<button type="submit">
{fetcher.state === "submitting" ? "변경 중..." : "상태 변경"}
</button>
</fetcher.Form>
);
- fetcher.state로 요청 상태 추적 가능
- 폼 데이터를 서버로 보내면서도 전체 페이지 리렌더 없이 부분 전환 가능
- 서버 응답 결과는 fetcher.data로 받음
Remix는 fetcher를 통해 서버 상태를 마치 클라이언트 상태처럼 다루게 해줍니다.
3. 🧠 전역 상태 (글로벌 상태 공유) 👇 사용하는 방식은 크게 2가지
3-1. Context API (Remix 친화적)
createContext와 useContext를 통해 전역 상태를 구성할 수 있습니다.
예: 테마 설정, 로그인 사용자 정보, 글로벌 알림 등
// context/theme.ts
const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => useContext(ThemeContext);
// root.tsx
<ThemeProvider>
<Outlet />
</ThemeProvider>
이 구조는 Remix의 중첩 라우팅(Outlet) 과도 잘 어울립니다.
3-2. 상태 관리 라이브러리 (Zustand, Redux 등)
- 복잡한 전역 상태가 많거나, API 응답 캐싱/동기화가 필요하다면 사용
- Remix는 SSR이므로 상태 초기화를 클라이언트에서 분리해야 함
- 대부분의 라이브러리는 클라이언트 전용으로 useEffect 안에서만 동작하도록 제한
예: Zustand
import create from "zustand";
const useStore = create((set) => ({
count: 0,
inc: () => set((state) => ({ count: state.count + 1 })),
}));
function Counter() {
const { count, inc } = useStore();
return <button onClick={inc}>{count}</button>;
}
Zustand는 SSR 환경에서도 안전하게 작동하며, Remix와 궁합이 좋은 상태 라이브러리 중 하나입니다.
4. 🧵 React Query? TanStack Query?
Remix는 React Query 같은 클라이언트 사이드 데이터 패칭 라이브러리와도 함께 사용할 수는 있지만, 권장되진 않습니다.
왜냐하면 Remix는 이미:
- 서버에서 loader()로 데이터를 패칭하고
- useLoaderData()로 클라이언트에 전달하므로
- React Query의 역할을 중복할 필요가 없음
다만 대시보드나 비동기 UI가 많은 복잡한 SPA 구조에서는 사용할 수 있으며, 이때는 hydration + Suspense 구조를 잘 설계해야 합니다.
5. ☑️ Remix 스타일의 상태관리 철학 요약
상태 유형 권장 방식 설명
UI 상태 | useState, useReducer | 일반 React 방식 그대로 사용 |
서버 통신 결과 | useFetcher, useLoaderData | 클라이언트-서버 연결을 자연스럽게 처리 |
글로벌 상태 | Context, Zustand | 사용자 정보, 테마, 알림 등 |
복잡한 비동기 데이터 | 필요 시 React Query | 데이터 동기화와 캐싱이 중요할 경우만 |
✅ 마무리: Remix에서의 상태 관리는 더 “웹스럽게”
Remix는 상태를 클라이언트에서 모두 관리하지 말고, 웹의 구조에 맞게 분산하자는 철학을 지향합니다.
즉:
- 가능한 상태는 서버에서 관리하고
- 필요한 경우에만 클라이언트에서 최소한으로 관리하되
- fetcher, Form, useLoaderData 등을 활용해 상태 전환을 URL과 연결하자
이는 기존의 SPA 중심 상태관리 전략과는 다른 방향이지만,
결과적으로 더 예측 가능하고, 접근성(A11y)과 성능(Performance)에 유리한 구조로 이어집니다.
'소프트웨어 > ReactRemix' 카테고리의 다른 글
React Server Components: Next.js에서 실제로 써보며 느낀 점 (0) | 2025.05.31 |
---|---|
Remix에서 RSC가 없는 건 약점일까? (1) | 2025.04.20 |
OpenAI는 왜 Next에서 Remix로 갈아탔을까 (4) | 2025.03.30 |
Next.js vs Remix 2025 완전 비교 - 선택이 고민될때 (2) | 2025.03.30 |
Remix와 React Router의 병합: FE 개발자들이 주목해야하는 이유 (3) | 2025.03.29 |