diff --git a/src/components/UserList.tsx b/src/components/UserList.tsx
new file mode 100644
index 0000000..d09d561
--- /dev/null
+++ b/src/components/UserList.tsx
@@ -0,0 +1,54 @@
+import { ChevronDown } from "lucide-react";
+import UserListCard from "./UserListCard";
+import { useEffect, useState } from "react";
+import { useUserStore } from "../stores/userListStore";
+import { useLocation } from "react-router";
+
+export default function UserList() {
+ const [isOpen, setIsOpen] = useState(false);
+ const location = useLocation();
+ const { userList, fetchUsers } = useUserStore();
+
+ // 페이지 이동 시 자동으로 닫기
+ useEffect(() => {
+ setIsOpen(false);
+ }, [location.pathname]);
+
+ useEffect(() => {
+ fetchUsers();
+ }, [fetchUsers]);
+
+ /* 필요한 정보 */
+ /* 프로필 이미지, 이름, 소속, 학년(전공 과목), auth_id */
+ return (
+
+ {/* 헤더 */}
+
setIsOpen(!isOpen)}
+ >
+
온라인 사용자
+
+
+
+ {/* 슬라이딩 리스트 */}
+
+
+ {userList.map((user) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/UserListCard.tsx b/src/components/UserListCard.tsx
new file mode 100644
index 0000000..d894ff8
--- /dev/null
+++ b/src/components/UserListCard.tsx
@@ -0,0 +1,53 @@
+import { Link } from "react-router";
+import { getAge } from "../utils/getAge";
+import { getGrade } from "../utils/getGrade";
+
+export default function UserListCard({ user }: { user: User }) {
+ const age = user.birth_date ? getAge(user.birth_date) : 0;
+ const grade = user.role === "student" && getGrade(age);
+
+ const roleMap: Record = {
+ student: "학생",
+ teacher: "선생님",
+ parent: "학부모",
+ };
+
+ return (
+ <>
+
+
+ {/* left */}
+
+ {/* 이미지 */}
+
+ {/* 온라인 */}
+
+ {/* 오프라인 */}
+ {/*
*/}
+
+ {/* 정보 */}
+
+ {/* 이름 */}
+
{user.nickname}
+ {/* 소속, 학년 (선생님은 전공) */}
+
+ {roleMap[user.role] || "알 수 없음"}
+ {user.role === "student" && `, ${grade}`}{" "}
+ {user.role === "teacher" && `, ${user.major}`}
+
+
+
+ {/* right */}
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/layouts/MainLayout.tsx b/src/layouts/MainLayout.tsx
index b2f57ed..fd89b18 100644
--- a/src/layouts/MainLayout.tsx
+++ b/src/layouts/MainLayout.tsx
@@ -1,6 +1,7 @@
import { Outlet } from "react-router";
import Header from "../components/layout/Header";
import Footer from "../components/layout/Footer";
+import UserList from "../components/UserList";
export default function MainLayout() {
return (
@@ -11,13 +12,15 @@ export default function MainLayout() {
{/* 스크롤 영역 */}
-
+
+ {/* 유저 리스트 모달 */}
+
>
diff --git a/src/stores/userListStore.ts b/src/stores/userListStore.ts
new file mode 100644
index 0000000..a13d42f
--- /dev/null
+++ b/src/stores/userListStore.ts
@@ -0,0 +1,28 @@
+import { create } from "zustand";
+import { immer } from "zustand/middleware/immer";
+import supabase from "../utils/supabase";
+
+type UserListState = {
+ userList: User[];
+ // fetchUsers: 전체 유저 리스트 가져오기
+ fetchUsers: () => Promise;
+};
+
+export const useUserStore = create()(
+ immer((set) => ({
+ userList: [],
+ // 전체 유저 리스트 가져오기
+ fetchUsers: async () => {
+ try {
+ const { data, error } = await supabase
+ .from("users")
+ .select("*");
+ if (error) throw error;
+
+ set({ userList: data || [] });
+ } catch (err) {
+ console.error("유저 목록 로딩 오류:", err);
+ }
+ },
+ })),
+);
diff --git a/src/types/user.d.ts b/src/types/user.d.ts
new file mode 100644
index 0000000..e12050f
--- /dev/null
+++ b/src/types/user.d.ts
@@ -0,0 +1,8 @@
+type User = {
+ auth_id: string;
+ nickname: string;
+ online?: string;
+ role: string;
+ birth_date?: Date;
+ major?: string;
+};