# React Server Components와 싸우다 만든 나만의 상태 관리 라이브러리

*(React Server Components, 상태 관리 라이브러리, React 상태 관리)*

---

# # 서론 – “서버와 클라이언트 사이의 데이터 공유, 왜 이렇게 어려울까?”

React Server Components(RSC)를 도입한 뒤, 복잡한 페이지를 만들면서 가장 크게 부딪힌 문제는 **서버와 클라이언트 간에 상태를 어떻게 공유하느냐**였습니다.

RSC는 서버에서 렌더링된 UI를 그대로 클라이언트에 전달해 성능을 높여 주지만, 그 과정에서 기존에 사용하던 Redux·Recoil·Zustand 같은 클라이언트‑전용 상태 관리 도구는 제 역할을 못하게 됩니다.

저는 6개월 동안 여러 프로젝트를 진행하면서 “서버‑사이드에서 만든 데이터를 클라이언트에서도 즉시 활용하고 싶다”는 요구에 부딪혔고, 결국 **직접 상태 관리 라이브러리를 만들기로 결심**했습니다. 이번 포스트에서는 그 과정을 초보자도 이해하기 쉽도록 단계별로 정리합니다.

---

# # 1️⃣ React Server Components의 기본 동작 원리

1. **서버에서 컴포넌트 실행**

   - `export default async function Component() { … }` 형태로 선언하면 서버에서만 실행됩니다.

2. **클라이언트에 스트리밍 전송**

   - 서버가 만든 HTML/JSX가 스트리밍 방식으로 클라이언트에 전달됩니다.

3. **클라이언트는 “읽기 전용” UI만 받음**

   - 클라이언트는 해당 컴포넌트를 다시 렌더링하거나 상태를 직접 바꿀 수 없습니다.

> 👉 **핵심 포인트**: RSC는 **데이터를 가져오는 역할**은 하지만, **데이터를 수정하거나 유지하는 역할은 클라이언트가 담당**한다는 점입니다.

---

# # 2️⃣ 기존 상태 관리 도구가 RSC와 맞지 않았던 이유

| 도구 | 서버 사이드 지원 | 클라이언트와의 동기화 방식 | 문제점 |

|------|----------------|--------------------------|--------|

| Redux | 제한적 (middleware 필요) | 전역 스토어를 직접 공유 | 서버에서 만든 상태를 클라이언트에 자동 전파 불가 |

| Recoil | 클라이언트 전용 | Atom/Selector 기반 | RSC와 결합 시 서버에서 만든 atom을 초기화할 방법이 없음 |

| Zustand | 경량 | Hook 기반 | 서버 렌더링 시 상태 초기값을 전달하는 메커니즘 부재 |

결과적으로 **서버에서 만든 데이터를 클라이언트가 그대로 사용할 방법이 없었고**, 매번 API 호출을 중복해서 수행하거나, 복잡한 프록시 로직을 구현해야 했습니다.

---

# # 3️⃣ 내가 만든 상태 관리 라이브러리의 설계 원칙

## # 3.1 서버‑클라이언트 “동기화 파이프라인” 구축

1. **서버에서 `serializeState` 함수 호출**

   
```
ts

   // server/state.ts

   export function serializeState(data) {

     return JSON.stringify(data);

   }

   
```

2. **클라이언트에서 `hydrateState` 로 복구**

   
```
ts

   // client/state.ts

   export function hydrateState(serialized) {

     return JSON.parse(serialized);

   }

   
```

이 파이프라인을 통해 서버가 만든 **JSON 형태의 상태**를 클라이언트가 최초 렌더링 시 그대로 복원합니다.

## # 3.2 React Context와 Hook을 결합

```
tsx

const StateContext = React.createContext<any>(null);

export const StateProvider = ({initialState, children}) => {

  const [state, setState] = React.useState(initialState);

  return (

    <StateContext.Provider value={{state, setState}}>

      {children}

    </StateContext.Provider>

  );

};

export const useAppState = () => React.useContext(StateContext);

```

- **Context**는 전역 접근을 보장하고,

- **Hook**은 사용 편의성을 제공해 초보자도 `const {state, setState} = useAppState();` 한 줄로 사용할 수 있습니다.

## # 3.3 자동 “서버‑클라이언트 재동기화”

서버에서 상태가 변하면 `serializeState`를 다시 호출하고, 클라이언트는 `EventSource`(SSE) 혹은 `WebSocket`을 통해 변화를 감지해 `hydrateState`를 실행합니다. 이렇게 하면 **실시간으로 상태가 동기화**됩니다.

---

# # 4️⃣ 실제 적용 예시 – 간단한 Todo 앱

```
tsx

// server/todo.ts (RSC)

export async function TodoList() {

  const todos = await db.todo.findMany();

  const serialized = serializeState(todos);

  return (

    <>

      <script dangerouslySetInnerHTML={{

        __html: `window.__INITIAL_TODOS__ = ${serialized};`

      }}/>

      <TodoListClient />

    </>

  );

}

```

```
tsx

// client/TodoListClient.tsx

import {useEffect} from 'react';

import {StateProvider, useAppState} from '../state';

function TodoListClient() {

  const {state: todos, setState: setTodos} = useAppState();

  useEffect(() => {

    // 최초 로드 시 서버에서 전달된 상태 복원

    if (window.__INITIAL_TODOS__) {

      setTodos(hydrateState(window.__INITIAL_TODOS__));

    }

  }, []);

  // UI 렌더링 …

}

```

- **서버**는 데이터를 가져와 직렬화하고,

- **클라이언트**는 페이지 로드 시 `hydrateState` 로 복구해 즉시 UI에 반영합니다.

이 구조 덕분에 **중복 API 호출 없이** 서버와 클라이언트가 동일한 데이터를 공유하게 됩니다.

---

# # 5️⃣ 배포와 성능 최적화 팁

1. **JSON 압축** – `gzip` 혹은 `brotli` 로 직렬화된 상태를 압축하면 네트워크 비용이 크게 감소합니다.

2. **부분 상태 전송** – 전체 상태가 아닌 **변경된 부분만** 전송하도록 diff 알고리즘을 적용하면 실시간 동기화가 더욱 가볍게 동작합니다.

3. **SSR 캐시 활용** – 동일한 요청에 대해 `serializeState` 결과를 캐시하면 서버 부하를 크게 낮출 수 있습니다.

---

# # 결론 – 직접 만든 상태 관리 라이브러리로 RSC를 자유롭게!

React Server Components와 기존 상태 관리 도구 사이의 격차는 **서버‑클라이언트 상태 동기화 파이프라인**을 직접 설계함으로써 해소할 수 있습니다.

- **서버**에서는 데이터를 직렬화해 전달하고,

- **클라이언트**에서는 `hydrateState` 로 복원해 전역 Context에 저장한다.

이 방식을 적용하면 **중복 API 호출을 없애고, 초기 로딩 속도를 크게 개선**할 수 있습니다.

> **지금 바로** 여러분의 프로젝트에 위와 같은 간단한 상태 관리 구조를 도입해 보세요. 직접 구현해 보면 React Server Components와의 궁합이 얼마나 좋아지는지 체감하실 수 있을 겁니다.

궁금한 점이나 구현 중 마주친 문제는 댓글로 남겨 주세요! 여러분의 피드백이 다음 개선에 큰 도움이 됩니다. 🚀
