Skip to content

Commit 60a883a

Browse files
committed
feat: MDX 렌더링 실패 시 재시도가 가능하도록 버튼 추가
1 parent a563382 commit 60a883a

File tree

3 files changed

+50
-22
lines changed

3 files changed

+50
-22
lines changed

apps/pyconkr/src/main.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ const queryClient = new QueryClient({
3535
});
3636

3737
const CommonOptions: Common.Contexts.ContextOptions = {
38-
baseUrl: import.meta.env.VITE_API_BASE_URL,
38+
debug: import.meta.env.MODE === "development",
39+
baseUrl: '.',
3940
};
4041

4142
const ShopOptions: Shop.Contexts.ContextOptions = {
Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import * as React from "react";
22
import * as runtime from "react/jsx-runtime";
3+
import * as R from "remeda";
34

45
import { evaluate, EvaluateOptions } from "@mdx-js/mdx";
5-
import { CircularProgress, Typography } from "@mui/material";
6+
import { Button, CircularProgress, Typography } from "@mui/material";
67
import { ErrorBoundary, Suspense } from "@suspensive/react";
78
import { useSuspenseQuery } from "@tanstack/react-query";
89
import components, { MuiMdxComponentsOptions } from 'mui-mdx-components';
9-
import * as R from "remeda";
1010

1111
import { useCommonContext } from '../hooks/useCommonContext';
1212

@@ -26,11 +26,45 @@ const MDXComponents: MuiMdxComponentsOptions = {
2626
}
2727
}
2828

29-
const useMDX = (text: string) => {
30-
const { baseUrl } = useCommonContext();
31-
const options: EvaluateOptions = { ...runtime, baseUrl }
29+
const SimplifiedMDXErrorFallback: React.FC<{ reset: () => void }> = ({ reset }) => {
30+
return <>
31+
<Typography variant="body2" color="error">
32+
페이지를 그리던 중 문제가 발생했습니다, 잠시 후 다시 시도해주세요.<br />
33+
만약 문제가 계속 발생한다면, 파이콘 한국 준비 위원회에게 알려주세요!<br />
34+
<br />
35+
Problem occurred while drawing the page, please try again later.<br />
36+
If the problem persists, please let the PyCon Korea organizing committee know!
37+
</Typography>
38+
<br />
39+
<Button variant="outlined" onClick={reset}>다시 시도 | Retry</Button>
40+
</>;
41+
}
42+
43+
const DetailedMDXErrorFallback: React.FC<{ error: Error, reset: () => void }> = ({ error, reset }) => {
44+
const errorObject = Object.getOwnPropertyNames(error).reduce((acc, key) => ({ ...acc, [key]: (error as unknown as { [key: string]: unknown })[key] }), {});
45+
return <>
46+
<Typography variant="body2" color="error">MDX 변환 오류: {error.message}</Typography>
47+
<details open>
48+
<summary>오류 상세</summary>
49+
<pre style={{
50+
whiteSpace: "pre-wrap",
51+
backgroundColor: "#f5f5f5",
52+
padding: "1em",
53+
borderRadius: "4px",
54+
userSelect: "text",
55+
}}>
56+
<code>{JSON.stringify(errorObject, null, 2)}</code>
57+
</pre>
58+
</details>
59+
<br />
60+
<Button variant="outlined" onClick={reset}>다시 시도</Button>
61+
</>;
62+
};
63+
64+
const InnerMDXRenderer: React.FC<{ text: string, baseUrl: string }> = ({ text, baseUrl }) => {
65+
const options: EvaluateOptions = { ...runtime, baseUrl };
3266

33-
return useSuspenseQuery({
67+
const { data } = useSuspenseQuery({
3468
queryKey: ["mdx", text],
3569
queryFn: async () => {
3670
const { default: RenderResult } = await evaluate(text, options);
@@ -39,30 +73,21 @@ const useMDX = (text: string) => {
3973
</div>
4074
},
4175
});
42-
}
4376

44-
const MDXErrorFallback: React.FC<{ error: Error }> = ({ error }) => {
45-
console.error(error);
46-
return (
47-
<Typography variant="body2" color="error">
48-
MDX 변환 오류: {error.message}
49-
</Typography>
50-
);
51-
};
52-
53-
const InnerMDXRenderer: React.FC<{ text: string }> = ({ text }) => {
54-
const { data } = useMDX(text);
5577
return <>{data}</>;
5678
}
5779

5880
export const MDXRenderer: React.FC<{ text: string }> = ({ text }) => {
5981
// 원래 MDX는 각 줄의 마지막에 공백 2개가 있어야 줄바꿈이 되고, 또 연속 줄바꿈은 무시되지만,
6082
// 편의성을 위해 렌더러 단에서 공백 2개를 추가하고 연속 줄바꿈을 <br />로 변환합니다.
61-
let processedText = text.split("\n").map((line) => R.isEmpty(line.trim()) ? "" : `${line.trim()} `).join("\n").replaceAll("\n\n", "\n<br />\n");
83+
const { baseUrl, debug } = useCommonContext();
84+
85+
const ErrorHandler = debug ? DetailedMDXErrorFallback : SimplifiedMDXErrorFallback;
86+
const processedText = text.split("\n").map((line) => R.isEmpty(line.trim()) ? "" : `${line.trim()} `).join("\n").replaceAll("\n\n", "\n<br />\n");
6287

63-
return <ErrorBoundary fallback={MDXErrorFallback}>
88+
return <ErrorBoundary fallback={ErrorHandler}>
6489
<Suspense fallback={<CircularProgress />}>
65-
<InnerMDXRenderer text={processedText} />
90+
<InnerMDXRenderer text={processedText} baseUrl={baseUrl} />
6691
</Suspense>
6792
</ErrorBoundary>
6893
};

packages/common/src/contexts/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import * as React from "react";
33
namespace GlobalContext {
44
export type ContextOptions = {
55
baseUrl: string;
6+
debug?: boolean;
67
}
78

89
export const context = React.createContext<ContextOptions>({
910
baseUrl: "",
11+
debug: false,
1012
});
1113
}
1214

0 commit comments

Comments
 (0)