diff --git a/src/components/Leaderboard/Leaderboard.stories.tsx b/src/components/Leaderboard/Leaderboard.stories.tsx
new file mode 100644
index 0000000..fbb5e57
--- /dev/null
+++ b/src/components/Leaderboard/Leaderboard.stories.tsx
@@ -0,0 +1,22 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import { MemoryRouter, Route, Routes } from "react-router-dom";
+import Leaderboard from "./Leaderboard";
+
+const pathname = `/leaderboard`;
+
+const meta = {
+ component: Leaderboard,
+ decorators: [
+ (Story) => (
+
+
+ } />
+
+
+ ),
+ ],
+} satisfies Meta;
+
+export default meta;
+
+export const Default: StoryObj = {};
diff --git a/src/components/Leaderboard/Leaderboard.test.tsx b/src/components/Leaderboard/Leaderboard.test.tsx
new file mode 100644
index 0000000..cd1752d
--- /dev/null
+++ b/src/components/Leaderboard/Leaderboard.test.tsx
@@ -0,0 +1,83 @@
+import { beforeEach, describe, expect, it } from "vitest";
+import { render, screen } from "@testing-library/react";
+import { MemoryRouter, Route, Routes } from "react-router-dom";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import Leaderboard from "./Leaderboard";
+
+const queryClient = new QueryClient();
+
+describe("", () => {
+ beforeEach(() =>
+ render(
+
+
+
+ } />
+
+
+ ,
+ ),
+ );
+
+ it("renders the members list section", () => {
+ expect(
+ screen.getByRole("region", { name: /members list/i }),
+ ).toBeInTheDocument();
+ });
+
+ it("renders the title", () => {
+ const heading = screen.getByRole("heading", { level: 1 });
+ expect(heading).toHaveTextContent("Leaderboard");
+ });
+
+ it("renders the member information", () => {
+ const members = [
+ { name: "DaleSeo", solved: 71, rank: "새싹" },
+ { name: "sounmind", solved: 69, rank: "나무" },
+ { name: "yolophg", solved: 65, rank: "새싹" },
+ { name: "Sunjae95", solved: 63, rank: "나무" },
+ { name: "HC-kang", solved: 62, rank: "나무" },
+ { name: "SamTheKorean", solved: 60, rank: "나무" },
+ ];
+
+ const memberItems = screen.getAllByRole("listitem");
+
+ expect(memberItems).toHaveLength(members.length);
+
+ members.forEach((member, index) => {
+ const memberItem = memberItems[index];
+ expect(memberItem).toHaveTextContent(`등급: ${member.rank}`);
+ expect(memberItem).toHaveTextContent(`진행 상황: ${member.solved}`);
+ });
+ });
+
+ it("renders the links for members", () => {
+ const members = [
+ { name: "DaleSeo" },
+ { name: "sounmind" },
+ { name: "yolophg" },
+ { name: "Sunjae95" },
+ { name: "HC-kang" },
+ { name: "SamTheKorean" },
+ ];
+
+ const progressLinks = screen.getAllByRole("link", { name: "풀이 보기" });
+ const certificateLinks = screen.getAllByRole("link", {
+ name: "수료증 보기",
+ });
+
+ expect(progressLinks).toHaveLength(members.length);
+ expect(certificateLinks).toHaveLength(members.length);
+
+ members.forEach((member, index) => {
+ expect(progressLinks[index]).toHaveAttribute(
+ "href",
+ `/progress?member=${member.name}`,
+ );
+ expect(certificateLinks[index]).toHaveAttribute(
+ "href",
+ `/certificate?member=${member.name}`,
+ );
+ });
+ });
+});
diff --git a/src/components/Leaderboard/Leaderboard.tsx b/src/components/Leaderboard/Leaderboard.tsx
new file mode 100644
index 0000000..cb3f95a
--- /dev/null
+++ b/src/components/Leaderboard/Leaderboard.tsx
@@ -0,0 +1,36 @@
+export default function Leaderboard() {
+ const members = [
+ { name: "DaleSeo", solved: 71, rank: "새싹" },
+ { name: "sounmind", solved: 69, rank: "나무" },
+ { name: "yolophg", solved: 65, rank: "새싹" },
+ { name: "Sunjae95", solved: 63, rank: "나무" },
+ { name: "HC-kang", solved: 62, rank: "나무" },
+ { name: "SamTheKorean", solved: 60, rank: "나무" },
+ ];
+
+ return (
+
+
Leaderboard
+
+
+ Members List
+
+ {members.map((member) => (
+ -
+
등급: {member.rank}
+ 진행 상황: {member.solved}
+
+
+ ))}
+
+
+
+ );
+}