diff --git a/apps/cs/package.json b/apps/cs/package.json
index 402f0d414..4b400a285 100644
--- a/apps/cs/package.json
+++ b/apps/cs/package.json
@@ -25,7 +25,8 @@
"react-chartjs-2": "^5.3.0",
"react-hook-form": "^7.54.2",
"tailwindcss-animate": "^1.0.7",
- "zod": "^3.24.2"
+ "zod": "^3.24.2",
+ "zustand": "^5.0.3"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
diff --git a/apps/cs/src/app/providers/app-provider.tsx b/apps/cs/src/app/providers/app-provider.tsx
index 7bd2ec683..cf477dcc0 100644
--- a/apps/cs/src/app/providers/app-provider.tsx
+++ b/apps/cs/src/app/providers/app-provider.tsx
@@ -1,19 +1,18 @@
"use client";
import { ConfirmProvider } from "@frontend-opensource/use-react-hooks";
-import { AuthProvider } from "@/entities/auth";
-import { CookieProvider } from "@/shared/lib";
-import { QueryProvider } from "./query-provider";
+import { AuthWrapper } from "@/entities/auth";
+import { QueryProvider, StoreProvider } from "@/shared/providers";
const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => {
return (
-
-
-
- {children}
-
-
-
+
+
+
+ {children}
+
+
+
);
};
diff --git a/apps/cs/src/entities/auth/index.ts b/apps/cs/src/entities/auth/index.ts
index 97579d0a5..dc3fb96c9 100644
--- a/apps/cs/src/entities/auth/index.ts
+++ b/apps/cs/src/entities/auth/index.ts
@@ -1,2 +1,2 @@
export { LoginApi } from "./api/login-service";
-export { AuthProvider } from "./lib/auth-provider";
+export { AuthWrapper } from "./ui/auth-wrapper";
diff --git a/apps/cs/src/entities/auth/lib/auth-provider.tsx b/apps/cs/src/entities/auth/lib/auth-provider.tsx
deleted file mode 100644
index 551cfa79d..000000000
--- a/apps/cs/src/entities/auth/lib/auth-provider.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-"use client";
-
-import { useRouter } from "next/navigation";
-import { useQuery } from "@tanstack/react-query";
-import { createContext, useState } from "react";
-import { client } from "@/shared/api";
-import { ENDPOINT, AUTH_USER_ID } from "@/shared/config";
-import { getCookie } from "@/shared/lib";
-import { LoginApi } from "../api/login-service";
-
-interface IUserInfo {
- regDt: string;
- mdfcnDt: string;
- rgtr: string;
- mdfr: string;
- userUnqId: string;
- userId: string;
- userName: string;
- email: string;
- encryptedPassword: string;
- refreshToken: string;
- userStateCode: string;
- lastLoginDate: string;
- loginFailCount: number;
- pswdHnt: string;
- pswdAns: string;
- mphn: string;
- telno: string;
- zip: string;
- addr: string;
- daddr: string;
- laeSeCd: string;
- ogdpSeCd: string;
- aprvYn: string;
- autzrId: string;
- expryYmd: string;
- lastPswdChgDt: string;
- googleId: string;
- kakaoId: string;
- naverId: string;
- isSocialUser: boolean;
- hasPassword: boolean;
-}
-
-export interface AuthContextType {
- userInfo: IUserInfo | null;
-}
-
-export const AuthContext = createContext(null);
-
-export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
- const router = useRouter();
- const [userInfo, setUserInfo] = useState(null);
-
- useQuery({
- queryKey: [ENDPOINT.USER_SERVICE.USERS],
- queryFn: async () => {
- try {
- const userId = getCookie(AUTH_USER_ID);
-
- if (!userId) {
- throw new Error("Missing required authentication tokens");
- }
-
- const response = await client.get(
- `${ENDPOINT.USER_SERVICE.USERS}/${userId}`,
- );
- setUserInfo(response.data);
- return response;
- } catch (error) {
- console.error(error);
- try {
- await LoginApi.silentRefresh();
- } catch (refreshError) {
- console.error("Token refresh failed:", refreshError);
- setUserInfo(null);
- router.push("/auth/login");
- }
- }
- },
- });
-
- const authContextValue: AuthContextType = {
- userInfo,
- };
-
- return (
-
- {children}
-
- );
-};
diff --git a/apps/cs/src/entities/auth/ui/auth-wrapper.tsx b/apps/cs/src/entities/auth/ui/auth-wrapper.tsx
new file mode 100644
index 000000000..ba175e697
--- /dev/null
+++ b/apps/cs/src/entities/auth/ui/auth-wrapper.tsx
@@ -0,0 +1,46 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { useQuery } from "@tanstack/react-query";
+import { client } from "@/shared/api";
+import { ENDPOINT, AUTH_USER_ID } from "@/shared/config";
+import { getCookie, useUserStore } from "@/shared/lib";
+import type { IUserInfo } from "@/shared/store";
+import { LoginApi } from "../api/login-service";
+
+const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
+ const router = useRouter();
+ const { setUserInfo } = useUserStore((state) => state);
+
+ useQuery({
+ queryKey: [ENDPOINT.USER_SERVICE.USERS],
+ queryFn: async () => {
+ try {
+ const userId = getCookie(AUTH_USER_ID);
+
+ if (!userId) {
+ throw new Error("Missing required authentication tokens");
+ }
+
+ const response = await client.get(
+ `${ENDPOINT.USER_SERVICE.USERS}/${userId}`,
+ );
+ setUserInfo(response.data);
+ return response;
+ } catch (error) {
+ console.error(error);
+ try {
+ await LoginApi.silentRefresh();
+ } catch (refreshError) {
+ console.error("Token refresh failed:", refreshError);
+ setUserInfo(null);
+ router.push("/auth/login");
+ }
+ }
+ },
+ });
+
+ return <>{children}>;
+};
+
+export { AuthWrapper };
diff --git a/apps/cs/src/shared/lib/cookie-provider.tsx b/apps/cs/src/shared/lib/cookie-provider.tsx
deleted file mode 100644
index f844d4f8a..000000000
--- a/apps/cs/src/shared/lib/cookie-provider.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-"use client";
-
-import { createContext } from "react";
-import { getCookie, setCookie, removeCookie } from "./cookie";
-
-export interface CookieContextType {
- getCookie: (name: string) => string | undefined;
- setCookie: (
- name: string,
- value: string,
- options: Cookies.CookieAttributes,
- ) => void;
- removeCookie: (name: string, options: Cookies.CookieAttributes) => void;
-}
-
-export const CookieContext = createContext(null);
-
-export function CookieProvider({ children }: { children: React.ReactNode }) {
- const cookieContextValue: CookieContextType = {
- getCookie,
- setCookie,
- removeCookie,
- };
-
- return (
-
- {children}
-
- );
-}
diff --git a/apps/cs/src/shared/lib/index.ts b/apps/cs/src/shared/lib/index.ts
index 0f4be9c9b..259525de6 100644
--- a/apps/cs/src/shared/lib/index.ts
+++ b/apps/cs/src/shared/lib/index.ts
@@ -1,2 +1,2 @@
-export { CookieProvider } from "./cookie-provider";
export { getCookie, setCookie, removeCookie } from "./cookie";
+export { useUserStore } from "./use-user-store";
diff --git a/apps/cs/src/shared/lib/use-user-store.ts b/apps/cs/src/shared/lib/use-user-store.ts
new file mode 100644
index 000000000..f4bf9763a
--- /dev/null
+++ b/apps/cs/src/shared/lib/use-user-store.ts
@@ -0,0 +1,14 @@
+import { useStore } from "zustand";
+import { useContext } from "react";
+import { type UserStore } from "@/shared/store/user-store";
+import { StoreContext } from "../providers/store-provider";
+
+export const useUserStore = (selector: (store: UserStore) => T): T => {
+ const storeContext = useContext(StoreContext);
+
+ if (!storeContext) {
+ throw new Error("useUserStore must be used within StoreProvider");
+ }
+
+ return useStore(storeContext.userStore, selector);
+};
diff --git a/apps/cs/src/shared/providers/index.ts b/apps/cs/src/shared/providers/index.ts
new file mode 100644
index 000000000..5d9f096a3
--- /dev/null
+++ b/apps/cs/src/shared/providers/index.ts
@@ -0,0 +1,2 @@
+export { QueryProvider } from "./query-provider";
+export { StoreProvider } from "./store-provider";
diff --git a/apps/cs/src/app/providers/query-provider.tsx b/apps/cs/src/shared/providers/query-provider.tsx
similarity index 100%
rename from apps/cs/src/app/providers/query-provider.tsx
rename to apps/cs/src/shared/providers/query-provider.tsx
diff --git a/apps/cs/src/shared/providers/store-provider.tsx b/apps/cs/src/shared/providers/store-provider.tsx
new file mode 100644
index 000000000..ec913f235
--- /dev/null
+++ b/apps/cs/src/shared/providers/store-provider.tsx
@@ -0,0 +1,27 @@
+"use client";
+
+import { createContext, useRef } from "react";
+import { createUserStore } from "@/shared/store/user-store";
+
+type StoreContextType = {
+ userStore: ReturnType;
+};
+
+export const StoreContext = createContext(
+ undefined,
+);
+
+export const StoreProvider = ({ children }: { children: React.ReactNode }) => {
+ const storeRef = useRef(null);
+ if (storeRef.current === null) {
+ storeRef.current = {
+ userStore: createUserStore(),
+ };
+ }
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/apps/cs/src/shared/store/index.ts b/apps/cs/src/shared/store/index.ts
new file mode 100644
index 000000000..4dae19ced
--- /dev/null
+++ b/apps/cs/src/shared/store/index.ts
@@ -0,0 +1 @@
+export { createUserStore, type IUserInfo } from "./user-store";
diff --git a/apps/cs/src/shared/store/user-store.ts b/apps/cs/src/shared/store/user-store.ts
new file mode 100644
index 000000000..00379fb1f
--- /dev/null
+++ b/apps/cs/src/shared/store/user-store.ts
@@ -0,0 +1,56 @@
+import { createStore } from "zustand/vanilla";
+
+export interface IUserInfo {
+ regDt: string;
+ mdfcnDt: string;
+ rgtr: string;
+ mdfr: string;
+ userUnqId: string;
+ userId: string;
+ userName: string;
+ email: string;
+ encryptedPassword: string;
+ refreshToken: string;
+ userStateCode: string;
+ lastLoginDate: string;
+ loginFailCount: number;
+ pswdHnt: string;
+ pswdAns: string;
+ mphn: string;
+ telno: string;
+ zip: string;
+ addr: string;
+ daddr: string;
+ laeSeCd: string;
+ ogdpSeCd: string;
+ aprvYn: string;
+ autzrId: string;
+ expryYmd: string;
+ lastPswdChgDt: string;
+ googleId: string;
+ kakaoId: string;
+ naverId: string;
+ isSocialUser: boolean;
+ hasPassword: boolean;
+}
+
+export type UserState = {
+ userInfo: IUserInfo | null;
+};
+
+export type UserActions = {
+ setUserInfo: (userInfo: IUserInfo | null) => void;
+};
+
+export type UserStore = UserState & UserActions;
+
+export const defaultInitState: UserState = {
+ userInfo: null,
+};
+
+export const createUserStore = (initState: UserState = defaultInitState) => {
+ return createStore()((set) => ({
+ ...initState,
+ setUserInfo: (userInfo: IUserInfo | null) => set(() => ({ userInfo })),
+ }));
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 58b9fe203..484efdd1d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -87,6 +87,9 @@ importers:
zod:
specifier: ^3.24.2
version: 3.24.2
+ zustand:
+ specifier: ^5.0.3
+ version: 5.0.3(@types/react@18.3.18)(react@18.3.1)
devDependencies:
'@eslint/eslintrc':
specifier: ^3
@@ -7982,3 +7985,25 @@ packages:
/zod@3.24.2:
resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==}
+
+ /zustand@5.0.3(@types/react@18.3.18)(react@18.3.1):
+ resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': ^18.3.18
+ immer: '>=9.0.6'
+ react: ^18.3.1
+ use-sync-external-store: '>=1.2.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+ dependencies:
+ '@types/react': 18.3.18
+ react: 18.3.1
+ dev: false