Skip to content

Commit b6e81fc

Browse files
committed
feat: update Next.js configuration, add new Prisma extension, and enhance chess diagram component
1 parent f61886a commit b6e81fc

File tree

10 files changed

+258
-170
lines changed

10 files changed

+258
-170
lines changed

.github/workflows/vercel-deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
env:
2121
API_TOKEN_GITHUB: ${{ secrets.GIT_SECRET }}
2222
with:
23-
source-directory: 'output'
23+
source-directory: "output"
2424
destination-github-username: RedBe-an
2525
destination-repository-name: OpenChess
2626
user-email: redbean.of@gmail.com

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
## 🚀 소개
88

9-
체스 오프닝을 찾아볼 수 있는 사이트입니다. 스스로 수를 둬서 찾을 수도 있고, `PGN`, `FEN`을 사용해서 찾아볼 수도, 게임 링크로 찾아볼 수도 있습니다.
9+
체스 오프닝을 찾아볼 수 있는 사이트입니다. 스스로 수를 둬서 찾을 수도 있고, `PGN`, `FEN`을 사용해서 찾아볼 수도, 게임 링크로 찾아볼 수도 있습니다.
1010

1111
오프닝 이름이 무엇인지만을 알려주는 것이 아닌, `설명``이후 전개`, `바리에이션`, `유저 승률`, 해당 오프닝의 `유명 경기`, `관련 콘텐츠` 등을 제공합니다.
1212

@@ -18,7 +18,7 @@
1818
| :-----------: | :---------------------: |
1919
| **Front-end** | `TypeScript`, `Next.js` |
2020
| **Styling** | `TailwindCSS` |
21-
| **Database** | `prisma`, `supabase` |
21+
| **Database** | `prisma`, `supabase` |
2222
| **Backend** | `Rust` |
2323

2424
## 💻 설치 방법

bun.lockb

436 Bytes
Binary file not shown.

next.config.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import remarkToc from "remark-toc";
55
import remarkGfm from "remark-gfm";
66
import remarkBreaks from "remark-breaks";
77

8-
const nextConfig: import("next").NextConfig = {
8+
const nextConfig = {
99
pageExtensions: ["js", "jsx", "md", "mdx", "ts", "tsx"],
10-
10+
experimental: {
11+
mdxRs: true,
12+
},
1113
};
1214

1315
const withMDX = createMDX({
@@ -25,8 +27,8 @@ const withMDX = createMDX({
2527
},
2628
],
2729
],
30+
providerImportSource: "@mdx-js/react",
2831
},
2932
});
3033

31-
// Merge MDX config with Next.js config
3234
export default withMDX(nextConfig);

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@next/mdx": "^15.1.4",
2424
"@opentelemetry/api": "^1.9.0",
2525
"@prisma/client": "^6.2.1",
26+
"@prisma/extension-accelerate": "^1.2.1",
2627
"@prisma/extension-optimize": "^1.1.4",
2728
"@radix-ui/react-accordion": "^1.2.2",
2829
"@radix-ui/react-dialog": "^1.1.4",
@@ -31,7 +32,7 @@
3132
"@radix-ui/react-separator": "^1.1.1",
3233
"@radix-ui/react-slot": "^1.1.1",
3334
"@radix-ui/react-toast": "^1.2.4",
34-
"@supabase/supabase-js": "^2.47.14",
35+
"@supabase/supabase-js": "^2.47.15",
3536
"@types/mdx": "^2.0.13",
3637
"chess.js": "^1.0.0",
3738
"class-variance-authority": "^0.7.1",
@@ -52,7 +53,7 @@
5253
"devDependencies": {
5354
"@eslint/eslintrc": "^3.2.0",
5455
"@tailwindcss/typography": "^0.5.16",
55-
"@types/node": "^22.10.6",
56+
"@types/node": "^22.10.7",
5657
"@types/react": "^19.0.7",
5758
"@types/react-dom": "^19.0.3",
5859
"eslint": "^9.18.0",

src/app/globals.css

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,26 @@ body {
7575
::-webkit-scrollbar {
7676
display: none;
7777
}
78+
79+
.anchor-icon {
80+
visibility: hidden;
81+
margin-left: 0.5rem;
82+
color: var(--muted-foreground);
83+
}
84+
85+
h1:hover .anchor-icon,
86+
h2:hover .anchor-icon,
87+
h3:hover .anchor-icon,
88+
h4:hover .anchor-icon,
89+
h5:hover .anchor-icon,
90+
h6:hover .anchor-icon {
91+
visibility: visible;
92+
}
93+
94+
.anchor {
95+
text-decoration: none;
96+
}
97+
98+
.anchor:hover {
99+
text-decoration: underline;
100+
}
Lines changed: 84 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,109 @@
11
import { notFound } from "next/navigation";
22
import prisma from "@/lib/prisma";
3-
import { getMdxContent } from "@/lib/mdx";
3+
import { compileMDX } from "next-mdx-remote/rsc";
4+
import { supabase } from "@/lib/client";
5+
import ChessDiagram from "@/components/openings/ChessDiagram";
6+
import remarkToc from "remark-toc";
7+
import remarkGfm from "remark-gfm";
8+
import remarkBreaks from "remark-breaks";
9+
import rehypeSlug from "rehype-slug";
10+
import rehypeAutolinkHeadings from "rehype-autolink-headings";
411

512
type OpeningPageProps = {
613
params: Promise<{
714
slugs: string[];
815
}>;
916
};
1017

11-
export default async function OpeningPage({ params }: OpeningPageProps) {
12-
const resolvedParams = await params;
18+
async function getOpeningFromPath(openingPath: string) {
19+
if (!prisma?.opening) {
20+
console.error("Prisma or Opening model is not initialized.");
21+
return null;
22+
}
1323

14-
// 슬러그가 없으면 404
15-
if (!resolvedParams.slugs?.length) {
16-
console.log("No slugs found in params:", resolvedParams);
17-
return notFound();
24+
try {
25+
const opening = await prisma.opening.findFirst({
26+
where: { urlName: openingPath },
27+
});
28+
29+
console.log("DB query result:", opening);
30+
return opening;
31+
} catch (error) {
32+
console.error("Database query error:", error);
33+
return null;
1834
}
35+
}
1936

20-
const openingPath = resolvedParams.slugs.join("/").toLowerCase();
37+
async function getOpeningContent(mdxPath: string | null) {
38+
if (!mdxPath) {
39+
return <p>이 오프닝에 대한 설명이 없습니다.</p>;
40+
}
2141

2242
try {
23-
// 디버깅을 위한 로그 추가
24-
console.log("Attempting to find opening with path:", openingPath);
43+
const { data, error } = await supabase.storage
44+
.from("openings")
45+
.download(mdxPath);
2546

26-
// Check if Prisma is initialized
27-
if (!prisma || !prisma.opening) {
28-
console.error("Prisma or Opening model is not initialized.");
29-
return notFound();
47+
if (error) {
48+
console.error("MDX 파일을 가져오는데 실패했습니다:", error);
49+
return <p>오프닝 내용을 불러오는데 실패했습니다.</p>;
3050
}
3151

32-
// DB에서 오프닝 정보 조회
33-
const opening = await prisma.opening.findFirst({
34-
where: {
35-
urlName: openingPath,
52+
const content = await data.text();
53+
const { content: mdxContent } = await compileMDX({
54+
source: content,
55+
components: {
56+
ChessDiagram,
57+
},
58+
options: {
59+
mdxOptions: {
60+
remarkPlugins: [remarkToc, remarkGfm, remarkBreaks],
61+
rehypePlugins: [
62+
rehypeSlug,
63+
[
64+
rehypeAutolinkHeadings,
65+
{
66+
properties: {
67+
className: ["anchor"],
68+
},
69+
},
70+
],
71+
],
72+
},
3673
},
37-
}).catch((error) => {
38-
console.error("Database query error:", error);
39-
return null;
4074
});
4175

42-
console.log("DB query result:", opening);
76+
return mdxContent;
77+
} catch (error) {
78+
console.error("MDX 컨텐츠 로딩 에러:", error);
79+
return <p>오프닝 내용을 불러오는데 실패했습니다.</p>;
80+
}
81+
}
4382

44-
if (!opening) {
45-
console.error(`Opening not found in database: ${openingPath}`);
46-
return notFound();
47-
}
83+
export default async function OpeningPage({ params }: OpeningPageProps) {
84+
const resolvedParams = await params;
4885

49-
// MDX 컨텐츠 로드 (mdx 필드가 없을 경우 대체 로직 추가)
50-
let content = null;
51-
if (opening.mdx) {
52-
content = await getMdxContent(opening.mdx).catch((error) => {
53-
console.error("MDX content loading error:", error);
54-
return null;
55-
});
56-
} else {
57-
// mdx 필드가 없을 경우 기본 설명 표시
58-
content = <p>No description available for this opening.</p>;
59-
}
86+
if (!resolvedParams.slugs?.length) {
87+
console.log("슬러그를 찾을 수 없습니다:", resolvedParams);
88+
return notFound();
89+
}
6090

61-
return (
62-
<article className="max-w-3xl mx-auto py-8 px-4">
63-
<div className="prose prose-slate dark:prose-invert max-w-none">
64-
{content}
65-
</div>
66-
</article>
67-
);
68-
} catch (error) {
69-
console.error("Error fetching opening:", error);
91+
const openingPath = resolvedParams.slugs.join("/").toLowerCase();
92+
console.log("오프닝 경로 검색 중:", openingPath);
93+
94+
const opening = await getOpeningFromPath(openingPath);
95+
if (!opening) {
96+
console.error(`데이터베이스에서 오프닝을 찾을 수 없습니다: ${openingPath}`);
7097
return notFound();
7198
}
72-
}
99+
100+
const content = await getOpeningContent(opening.mdx);
101+
102+
return (
103+
<article className="max-w-3xl mx-auto py-8 px-4">
104+
<div className="prose prose-slate dark:prose-invert max-w-none">
105+
{content}
106+
</div>
107+
</article>
108+
);
109+
}

0 commit comments

Comments
 (0)