diff --git a/tests/components/notes/NoteList.test.tsx b/tests/components/notes/NoteList.test.tsx
new file mode 100644
index 0000000..ff2b988
--- /dev/null
+++ b/tests/components/notes/NoteList.test.tsx
@@ -0,0 +1,95 @@
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import NoteList from "@/app/(protected)/notes/_components/NoteList";
+import { Note } from "@/hooks/queries/notes";
+
+describe("NoteList", () => {
+ const mockNotes: Note[] = [
+ {
+ id: 1,
+ title: "첫 번째 노트",
+ todo: { id: 1, title: "할 일 1", done: false },
+ updatedAt: "2026-01-15T10:00:00Z",
+ },
+ {
+ id: 2,
+ title: "두 번째 노트",
+ todo: { id: 2, title: "할 일 2", done: true },
+ updatedAt: "2026-01-16T10:00:00Z",
+ },
+ ];
+
+ const mockOnEditNote = jest.fn();
+ const mockOnDeleteNote = jest.fn();
+
+ const renderNoteList = () => {
+ return render(
+ ,
+ );
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe("렌더링", () => {
+ it("노트 목록이 화면에 렌더링된다", () => {
+ renderNoteList();
+
+ expect(screen.getByText("첫 번째 노트")).toBeInTheDocument();
+ expect(screen.getByText("두 번째 노트")).toBeInTheDocument();
+ });
+
+ it("노트가 최신순으로 정렬된다", () => {
+ renderNoteList();
+
+ const articles = screen.getAllByRole("article");
+ expect(articles[0]).toHaveTextContent("두 번째 노트");
+ expect(articles[1]).toHaveTextContent("첫 번째 노트");
+ });
+
+ it("노트 클릭 시 상세 페이지로 이동하는 링크가 있다", () => {
+ renderNoteList();
+
+ const links = screen.getAllByRole("link");
+ expect(links[0]).toHaveAttribute("href", "/notes/2");
+ expect(links[1]).toHaveAttribute("href", "/notes/1");
+ });
+ });
+
+ describe("사용자 인터랙션", () => {
+ it("수정 버튼 클릭 시 onEditNote가 호출된다", async () => {
+ const user = userEvent.setup();
+
+ renderNoteList();
+
+ const dropdownButtons = screen.getAllByLabelText("노트 옵션 메뉴");
+ await user.click(dropdownButtons[0]);
+
+ const editButton = screen.getByText("수정하기");
+ await user.click(editButton);
+
+ expect(mockOnEditNote).toHaveBeenCalledWith(2);
+ expect(mockOnEditNote).toHaveBeenCalledTimes(1);
+ });
+
+ it("삭제 버튼 클릭 시 onDeleteNote가 호출된다", async () => {
+ const user = userEvent.setup();
+
+ renderNoteList();
+
+ const dropdownButtons = screen.getAllByLabelText("노트 옵션 메뉴");
+ await user.click(dropdownButtons[0]);
+
+ const deleteButton = screen.getByText("삭제하기");
+ await user.click(deleteButton);
+
+ expect(mockOnDeleteNote).toHaveBeenCalledWith(2);
+ expect(mockOnDeleteNote).toHaveBeenCalledTimes(1);
+ });
+ });
+});
diff --git a/tests/components/notes/NoteListContainer.test.tsx b/tests/components/notes/NoteListContainer.test.tsx
new file mode 100644
index 0000000..a228f90
--- /dev/null
+++ b/tests/components/notes/NoteListContainer.test.tsx
@@ -0,0 +1,66 @@
+import { screen } from "@testing-library/react";
+import { useNotesQuery, useDeleteNoteMutation } from "@/hooks/queries/notes";
+import NoteListContainer from "@/app/(protected)/notes/_components/NoteListContainer";
+import { renderWithQueryClient } from "tests/test-utils";
+
+jest.mock("@/hooks/queries/notes");
+
+jest.mock("next/navigation", () => ({
+ useRouter: () => ({
+ push: jest.fn(),
+ }),
+}));
+
+describe("NoteListContainer", () => {
+ const goalId = 1;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ jest.mocked(useDeleteNoteMutation).mockReturnValue({
+ mutate: jest.fn(),
+ } as unknown as ReturnType);
+ });
+
+ describe("노트가 없을 때", () => {
+ it("노트가 없으면 EmptyState를 렌더링한다", () => {
+ jest.mocked(useNotesQuery).mockReturnValue({
+ data: {
+ totalCount: 0,
+ nextCursor: null,
+ goal: { id: goalId, title: "목표 1" },
+ notes: [],
+ },
+ } as unknown as ReturnType);
+
+ renderWithQueryClient();
+
+ expect(screen.getByText("아직 등록된 노트가 없어요")).toBeInTheDocument();
+ });
+ });
+
+ describe("노트가 있을 때", () => {
+ it("GoalBanner와 NoteList를 렌더링한다", () => {
+ jest.mocked(useNotesQuery).mockReturnValue({
+ data: {
+ totalCount: 1,
+ nextCursor: null,
+ goal: { id: goalId, title: "목표 1" },
+ notes: [
+ {
+ id: 1,
+ title: "첫 번째 노트",
+ todo: { id: 1, title: "할 일 1", done: false },
+ updatedAt: "2026-01-17T10:00:00Z",
+ },
+ ],
+ },
+ } as unknown as ReturnType);
+
+ renderWithQueryClient();
+
+ expect(screen.getByText("목표 1")).toBeInTheDocument();
+ expect(screen.getByText("첫 번째 노트")).toBeInTheDocument();
+ });
+ });
+});