Next.js + Zustand에서 사용자 간 상태 공유 이슈 해결기
링크 드라퍼 초기 개발 당시, 어느 날 사용자 A가 제보를 해왔습니다.
“로그인했더니 다른 사람 이메일이 떠요…”
순간 등골이 서늘해졌습니다.
프라이버시가 중요한 서비스에서 다른 사용자의 정보가 보이는 건 치명적인 문제였죠.
확인해보니, Zustand로 만든 사용자 상태(`useUserStore`)가 서버에서 여러 사용자에게 공유되고 있었습니다.
Next.js는 기본적으로 SSR(서버사이드 렌더링)을 지원합니다.
페이지 요청이 들어오면 서버에서 HTML을 생성해 브라우저에 전달하죠.
그런데 문제가 생기는 포인트는 이겁니다.
이렇게 만든 store는 Node 서버의 메모리에 유지됩니다.
즉, 사용자 A가 로그인해서 상태가 설정되면, 사용자 B의 요청에도 A의 정보가 그대로 남아 있을 수 있다는 겁니다.
> 서버는 stateless해야 하는데, store는 stateful하게 공유되고 있었던 거죠.
Zustand만의 이슈는 아닙니다. 다음처럼 SSR 환경에서는 대부분의 상태관리 라이브러리가 주의가 필요합니다:
Zustand: SSR 시 store가 공유될 수 있어 분리 필요
Redux: 기본적으로 SSR 시 상태 공유 가능, createStore() 방식으로 해결
Jotai: atom이 공유될 수 있어 컨텍스트 분리 필요=
Recoil: 기본적으로 클라이언트 전용으로 SSR에서 공유 위험 낮음
Context API: 컴포넌트 내부에서만 동작하기 때문에 SSR 공유 위험이 낮음
즉, 클라이언트 전용 라이브러리를 제외하면 대부분 SSR에 맞는 설계가 필요합니다.
Zustand는 내부적으로 `createStore()`를 통해 새로운 store 인스턴스를 만들 수 있습니다.
이를 활용해 SSR에서도 사용자별 상태를 격리할 수 있죠.
이 방식은 간단하고 유용하지만, App Router 환경에서는 더 명확한 방법이 필요할 수도 있습니다.
`createUserStore()`로 생성한 store를 React Context로 컴포넌트 트리에 주입하는 방식입니다.
Redux의 Provider와 유사하지만 훨씬 가볍고 유연합니다.
요청별로 완전히 새로운 상태를 주입할 수 있기 때문에, 민감한 사용자 데이터에도 안전합니다.
두 가지 방법 모두 사용자 상태 분리에 효과적입니다.
getUserStore() 방식은 간단하고 CSR 중심의 앱에 적합합니다.
React Context 기반 방식은 SSR을 안전하게 다루고 싶을 때 가장 확실한 대안입니다.
우리가 겪은 이 문제는 단순한 개발 이슈가 아니라, 서비스 신뢰성과 사용자 경험의 근본을 건드리는 문제였습니다.
이런 고민 끝에 탄생한 서비스가 바로 링크 드라퍼입니다.
링크를 저장하면 제목, 설명, 썸네일이 자동으로 불러와지고, 폴더 단위로 정리하거나 공유도 가능합니다.
저희는 단순히 저장하는 것이 아니라 다시 꺼내보는 링크 경험을 만들고자 합니다.
사용해보시고 피드백도 남겨주시면 정말 큰 힘이 됩니다 :)
링크 드라퍼 초기에 겪었던 이슈는
단순한 상태관리 실수가 얼마나 큰 보안 문제로 이어질 수 있는지를 보여줬습니다.
Zustand는 정말 가볍고 좋은 도구지만,
Next.js SSR 환경에서는 반드시 안전한 store 설계를 동반해야 합니다.
이 글이 저처럼 이 문제를 몰라서 고생할 개발자분들에게
작은 경고이자 도움이 되기를 바랍니다.
궁금한 점이나 개선 아이디어가 있다면 댓글로 남겨주세요