feat(ai-character): 관리자 라우트 지연 로딩 적용

This commit is contained in:
Yu Sung
2026-08-06 21:38:12 +09:00
parent 68959cb33b
commit 2a04955bd7
10 changed files with 849 additions and 48 deletions

View File

@@ -5,7 +5,7 @@ import { build } from "vite";
import { describe, expect, test } from "vitest";
describe("production mock graph", () => {
test("excludes the browser mock module from the production bundle", async () => {
test("splits production chunks and excludes the browser mock module from the production bundle", async () => {
// Given
const outDir = mkdtempSync(join(tmpdir(), "ai-character-admin-prod-"));
const previousNodeEnv = process.env.NODE_ENV;
@@ -21,12 +21,16 @@ describe("production mock graph", () => {
mode: "production",
});
const outputFiles = collectFiles(outDir);
const output = outputFiles
.filter((filePath) => filePath.endsWith(".js"))
const jsFiles = outputFiles.filter((filePath) => filePath.endsWith(".js"));
const output = jsFiles
.map((filePath) => readFileSync(filePath, "utf8"))
.join("\n");
// Then
expect(jsFiles.length).toBeGreaterThanOrEqual(2);
for (const jsFile of jsFiles) {
expect(statSync(jsFile).size).toBeLessThanOrEqual(500_000);
}
expect(outputFiles.some((filePath) => filePath.endsWith("mockServiceWorker.js"))).toBe(false);
expect(output).not.toContain("mockServiceWorker.js");
expect(output).not.toContain("startMockWorker");

View File

@@ -57,7 +57,7 @@ test("ResourcePagination connects each page size label to a unique select", () =
expect(labels.map((label) => label.control)).toEqual(selects);
});
test("ResourcePagination separates page size from an equal-width mobile movement row", () => {
test("ResourcePagination separates page size from stacked mobile movement controls", () => {
render(<ResourcePagination data={pageData} onPageChange={vi.fn()} onSizeChange={vi.fn()} />);
const sizeControls = screen.getByRole("group", { name: "페이지 크기 설정" });
@@ -67,7 +67,7 @@ test("ResourcePagination separates page size from an equal-width mobile movement
expect(within(sizeControls).getByLabelText("페이지 크기")).toBeInTheDocument();
expect(sizeControls).not.toContainElement(previous);
expect(movementControls).toHaveClass("grid-cols-2");
expect(previous).toHaveClass("min-h-11", "w-full");
expect(next).toHaveClass("min-h-11", "w-full");
expect(movementControls).toHaveClass("grid-cols-1");
expect(previous).toHaveClass("min-h-11", "w-full", "whitespace-nowrap");
expect(next).toHaveClass("min-h-11", "w-full", "whitespace-nowrap");
});

View File

@@ -13,10 +13,10 @@ export function ResourcePagination({ data, onPageChange, onSizeChange, sizeOptio
return (
<nav aria-label="페이지" className="flex flex-col gap-3 rounded-lg border border-border bg-card p-4 sm:flex-row sm:items-center sm:justify-between">
<p className="text-sm font-semibold text-muted-foreground"> {data.totalCount.toLocaleString("ko-KR")} · {data.page + 1}</p>
<p className="break-keep text-sm font-semibold text-muted-foreground"> {data.totalCount.toLocaleString("ko-KR")} · {data.page + 1}</p>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
<div aria-label="페이지 크기 설정" className="flex items-center gap-2" role="group">
<label className="text-sm font-semibold" htmlFor={pageSizeId}>
<label className="break-keep text-sm font-semibold" htmlFor={pageSizeId}>
</label>
<select className="min-h-11 rounded-md border border-input bg-card px-3 py-2 text-base" id={pageSizeId} onChange={(event) => onSizeChange(Number(event.currentTarget.value))} value={data.size}>
@@ -25,11 +25,11 @@ export function ResourcePagination({ data, onPageChange, onSizeChange, sizeOptio
))}
</select>
</div>
<div aria-label="페이지 이동" className="grid grid-cols-2 gap-2 sm:flex" role="group">
<button className="min-h-11 w-full rounded-md border border-input bg-card px-3 py-2 font-semibold hover:bg-accent disabled:opacity-60 sm:w-auto" disabled={data.page <= 0} onClick={() => onPageChange(data.page - 1)} type="button">
<div aria-label="페이지 이동" className="grid grid-cols-1 gap-2 sm:flex" role="group">
<button className="min-h-11 w-full whitespace-nowrap rounded-md border border-input bg-card px-3 py-2 font-semibold hover:bg-accent disabled:opacity-60 sm:w-auto" disabled={data.page <= 0} onClick={() => onPageChange(data.page - 1)} type="button">
</button>
<button className="min-h-11 w-full rounded-md border border-input bg-card px-3 py-2 font-semibold hover:bg-accent disabled:opacity-60 sm:w-auto" disabled={!data.hasNext} onClick={() => onPageChange(data.page + 1)} type="button">
<button className="min-h-11 w-full whitespace-nowrap rounded-md border border-input bg-card px-3 py-2 font-semibold hover:bg-accent disabled:opacity-60 sm:w-auto" disabled={!data.hasNext} onClick={() => onPageChange(data.page + 1)} type="button">
</button>
</div>