From 6d740d5e27abcd88f169ffa5d28b7492d3885705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=98=88=EC=A7=84?= Date: Sat, 18 Nov 2023 23:16:27 +0900 Subject: [PATCH 1/6] feat: add app directory --- src/app/AnalyticsScript.tsx | 16 ++++++++ src/app/Providers.tsx | 39 +++++++++++++++++++ src/app/layout.tsx | 30 +++++++++++++++ src/app/new/page.tsx | 3 ++ src/components/Header.tsx | 24 +++--------- src/components/header/ThemeToggleButton.tsx | 17 +++++++++ src/components/mdx/CodeBlock.tsx | 8 ++-- src/registry/StyledComponentsRegistry.tsx | 27 +++++++++++++ src/styles/theme.ts | 42 ++++++++++++++------- src/utils/greeting.ts | 27 +++++++++++++ 10 files changed, 196 insertions(+), 37 deletions(-) create mode 100644 src/app/AnalyticsScript.tsx create mode 100644 src/app/Providers.tsx create mode 100644 src/app/layout.tsx create mode 100644 src/app/new/page.tsx create mode 100644 src/components/header/ThemeToggleButton.tsx create mode 100644 src/registry/StyledComponentsRegistry.tsx create mode 100644 src/utils/greeting.ts diff --git a/src/app/AnalyticsScript.tsx b/src/app/AnalyticsScript.tsx new file mode 100644 index 0000000..6a72944 --- /dev/null +++ b/src/app/AnalyticsScript.tsx @@ -0,0 +1,16 @@ +import Script from "next/script"; + +export const AnalyticsScript = () => ( + <> + + +); diff --git a/src/app/Providers.tsx b/src/app/Providers.tsx new file mode 100644 index 0000000..309cc9c --- /dev/null +++ b/src/app/Providers.tsx @@ -0,0 +1,39 @@ +"use client"; + +import React, { ReactNode, useState } from "react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { MDXProvider } from "@mdx-js/react"; +import ThemeProvider from "@src/utils/context/ThemeProvider"; +import DarkModeProvider from "@src/utils/context/DarkModeContext"; +import components from "@src/components/mdx/MDXComponents"; +import StyledComponentsRegistry from "@src/registry/StyledComponentsRegistry"; +import GlobalStyle from "@src/styles/GlobalStyle"; + +export const Providers = ({ children }: { children: ReactNode }) => { + const [queryClient] = useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + refetchOnMount: false, + refetchOnReconnect: false, + refetchInterval: false, + }, + }, + }), + ); + + return ( + + + + + + {children} + + + + + ); +}; diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..95bb430 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,30 @@ +import { PropsWithChildren } from "react"; +import { Metadata } from "next"; // import "data://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css"; +import Header from "@src/components/Header"; +import { Providers } from "@src/app/Providers"; +import { AnalyticsScript } from "@src/app/AnalyticsScript"; + +export const metadata: Metadata = { + title: { template: "%s | oooooroblog", default: "oooooroblog" }, + description: "웹 프론트엔드 개발 블로그", + icons: { + icon: "/favicon-32x32.png", + shortcut: "/favicon-32x32.png", + apple: "/apple-touch-icon.png", + }, + manifest: "/site.webmanifest", +}; + +export default function RootLayout({ children }: PropsWithChildren) { + return ( + + + +
+ {children} + + + + + ); +} diff --git a/src/app/new/page.tsx b/src/app/new/page.tsx new file mode 100644 index 0000000..3738375 --- /dev/null +++ b/src/app/new/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return

app page

; +} diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 0e13af4..0145ab1 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,34 +1,20 @@ +"use client"; import styled from "styled-components"; import Link from "next/link"; -import { useContext } from "react"; -import { DarkModeContext } from "@src/utils/context/DarkModeContext"; +import { ThemeToggleButton } from "@src/components/header/ThemeToggleButton"; export default function Header() { - const { isDarkMode, toggleDarkMode } = useContext(DarkModeContext); - return ( -
- - ooooorobo - -
-
- -
+ ooooorobo +
); } -const Background = styled.div` +const Background = styled.nav` background-color: ${(props) => props.theme.colors.bg.secondary}; `; diff --git a/src/components/header/ThemeToggleButton.tsx b/src/components/header/ThemeToggleButton.tsx new file mode 100644 index 0000000..0482336 --- /dev/null +++ b/src/components/header/ThemeToggleButton.tsx @@ -0,0 +1,17 @@ +"use client"; +import { useContext } from "react"; +import { DarkModeContext } from "@src/utils/context/DarkModeContext"; + +export const ThemeToggleButton = () => { + const { isDarkMode, toggleDarkMode } = useContext(DarkModeContext); + + return ( + + ); +}; diff --git a/src/components/mdx/CodeBlock.tsx b/src/components/mdx/CodeBlock.tsx index a0ced7b..60f26ad 100644 --- a/src/components/mdx/CodeBlock.tsx +++ b/src/components/mdx/CodeBlock.tsx @@ -1,5 +1,4 @@ -import Highlight, { defaultProps } from "prism-react-renderer"; -import theme from "prism-react-renderer/themes/oceanicNext"; +import { Highlight, themes } from "prism-react-renderer"; import styled from "styled-components"; export default function CodeBlock(props: any) { @@ -7,14 +6,13 @@ export default function CodeBlock(props: any) { const matches = className.match(/language-(?.*)/); return ( {({ className, style, tokens, getLineProps, getTokenProps }) => (
@@ -30,7 +28,7 @@ export default function CodeBlock(props: any) {
                     ))}
                   
                 
-              )
+              ),
           )}
         
)} diff --git a/src/registry/StyledComponentsRegistry.tsx b/src/registry/StyledComponentsRegistry.tsx new file mode 100644 index 0000000..a4865af --- /dev/null +++ b/src/registry/StyledComponentsRegistry.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { ReactNode, useState } from "react"; +import { useServerInsertedHTML } from "next/navigation"; +import { ServerStyleSheet, StyleSheetManager } from "styled-components"; + +export default function StyledComponentsRegistry({ + children, +}: { + children: ReactNode; +}) { + const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()); + + useServerInsertedHTML(() => { + const styles = styledComponentsStyleSheet.getStyleElement(); + styledComponentsStyleSheet.instance.clearTag(); + return <>{styles}; + }); + + if (typeof window !== "undefined") return <>{children}; + + return ( + + {children} + + ); +} diff --git a/src/styles/theme.ts b/src/styles/theme.ts index b53aab6..2d1e384 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -1,4 +1,5 @@ -import { DefaultTheme, css, CSSProp } from "styled-components"; +import { css, CSSProp, DefaultTheme } from "styled-components"; +import { CSSProperties } from "react"; declare module "styled-components" { export interface DefaultTheme { @@ -90,18 +91,16 @@ const defaultTheme: Omit = { code: 1.6, }, media: { - mobile: (...args) => - css` - @media only screen and (max-width: 800px) { - ${args} - } - `, - desktop: (...args) => - css` - @media only screen and (min-width: 800px) { - ${args} - } - `, + mobile: (...args) => css` + @media only screen and (max-width: 800px) { + ${args} + } + `, + desktop: (...args) => css` + @media only screen and (min-width: 800px) { + ${args} + } + `, }, }; @@ -123,3 +122,20 @@ interface ColorPreset { primaryLight: string; secondary: string; } + +const theme = { + light: lightTheme, + dark: darkTheme, +}; + +export const theming = (cb: (theme: DefaultTheme) => CSSProperties) => + Object.keys(theme).reduce( + (acc, name) => + Object.assign(acc, { [`.theme-${name} &`]: cb(darkTheme[name]) }), + {}, + ); + +export const media = { + mobile: `@media only screen and (max-width: 800px)`, + desktop: `@media only screen and (min-width: 800px)`, +}; diff --git a/src/utils/greeting.ts b/src/utils/greeting.ts new file mode 100644 index 0000000..0418dce --- /dev/null +++ b/src/utils/greeting.ts @@ -0,0 +1,27 @@ +export const greeting = () => { + console.log( + ` +%c + _ _ + | | | | + ___ ___ ___ ___ ___ _ __ ___ | |__ | | ___ __ _ + / _ \\ / _ \\ / _ \\ / _ \\ / _ \\| '__/ _ \\| '_ \\| |/ _ \\ / _\` | +| (_) | (_) | (_) | (_) | (_) | | | (_) | |_) | | (_) | (_| | + \\___/ \\___/ \\___/ \\___/ \\___/|_| \\___/|_.__/|_|\\___/ \\__, | + __/ | + |___/ +` + + ` + _ _ +| | | | +| |__ _ _ ___ ___ ___ ___ ___ _ __ ___ | |__ ___ +| '_ \\| | | | / _ \\ / _ \\ / _ \\ / _ \\ / _ \\| '__/ _ \\| '_ \\ / _ \\ +| |_) | |_| | | (_) | (_) | (_) | (_) | (_) | | | (_) | |_) | (_) | +|_.__/ \\__, | \\___/ \\___/ \\___/ \\___/ \\___/|_| \\___/|_.__/ \\___/ + __/ | + |___/ + +방문해 주셔서 감사합니다 :>`, + "color: #fe5000", + ); +}; From dfb135c8a442d1a06ec176509156f1cfc0b3633f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=98=88=EC=A7=84?= Date: Sat, 18 Nov 2023 23:32:51 +0900 Subject: [PATCH 2/6] feat: migrate main page --- src/app/Providers.tsx | 8 +-- src/app/layout.tsx | 6 +++ src/app/new/page.tsx | 3 -- src/app/page.tsx | 40 ++++++++++++++ src/business/post.ts | 28 ++++++++++ src/pages/index.tsx | 82 ----------------------------- src/styles/theme.ts | 18 ------- src/utils/context/ThemeProvider.tsx | 6 +-- 8 files changed, 81 insertions(+), 110 deletions(-) delete mode 100644 src/app/new/page.tsx create mode 100644 src/app/page.tsx create mode 100644 src/business/post.ts delete mode 100644 src/pages/index.tsx diff --git a/src/app/Providers.tsx b/src/app/Providers.tsx index 309cc9c..f18a517 100644 --- a/src/app/Providers.tsx +++ b/src/app/Providers.tsx @@ -27,12 +27,12 @@ export const Providers = ({ children }: { children: ReactNode }) => { return ( - - + + {children} - - + + ); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 95bb430..3bc5cf1 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -13,6 +13,12 @@ export const metadata: Metadata = { apple: "/apple-touch-icon.png", }, manifest: "/site.webmanifest", + openGraph: { + type: "website", + url: "https://www.oooooroblog.com", + title: "oooooroblog", + description: "웹 프론트엔드 개발 블로그", + }, }; export default function RootLayout({ children }: PropsWithChildren) { diff --git a/src/app/new/page.tsx b/src/app/new/page.tsx deleted file mode 100644 index 3738375..0000000 --- a/src/app/new/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return

app page

; -} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..675a0e9 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,40 @@ +"use client"; +import styled from "styled-components"; +import { useEffect } from "react"; +import { getAllPostMeta } from "@src/business/post"; +import PostList from "@src/components/main/PostList"; +import { StorageKey } from "@src/constants/constants"; +import Profile from "@src/components/main/Profile"; + +export default function Page() { + const posts = getAllPostMeta(); + + useEffect(() => { + const scroll = parseInt( + sessionStorage.getItem(StorageKey.MAIN_SCROLL_Y) ?? "0", + ); + window.scrollTo({ top: scroll, behavior: "auto" }); + }, []); + + const onClickPost = () => { + sessionStorage.setItem(StorageKey.MAIN_SCROLL_Y, window.scrollY.toString()); + }; + + return ( + + +
+ +
+
+ ); +} + +const Wrapper = styled.div` + margin: 2.5rem auto; + max-width: 760px; + padding: 0 1rem; + display: flex; + flex-direction: column; + row-gap: 20px; +`; diff --git a/src/business/post.ts b/src/business/post.ts new file mode 100644 index 0000000..00cffb0 --- /dev/null +++ b/src/business/post.ts @@ -0,0 +1,28 @@ +import { compareDesc } from "date-fns"; +import { allPosts } from "contentlayer/generated"; +import { PostListElement } from "@src/types/post"; + +export const getAllPostMeta = (): PostListElement[] => { + return allPosts + .sort((a, b) => { + const compareByDate = compareDesc(new Date(a.date), new Date(b.date)); + if (compareByDate !== 0) return compareByDate; + return b.id - a.id; + }) + .map( + (meta) => + ({ + slug: meta.url, + meta: { + index: meta.id, + title: meta.title, + description: meta.description || "", + postedAt: meta.date, + category: "", + series: "", + tags: [], + keywords: [], + }, + }) satisfies PostListElement, + ); +}; diff --git a/src/pages/index.tsx b/src/pages/index.tsx deleted file mode 100644 index 105a242..0000000 --- a/src/pages/index.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import type { NextPage } from "next"; -import { useEffect } from "react"; -import { NextSeo } from "next-seo"; -import styled from "styled-components"; -import { compareDesc } from "date-fns"; -import { allPosts } from "contentlayer/generated"; - -import { PostListElement } from "@src/types/post"; -import Profile from "@src/components/main/Profile"; -import PostList from "@src/components/main/PostList"; -import { StorageKey } from "@src/constants/constants"; - -type HomeProps = { posts: PostListElement[] }; - -const Home: NextPage = ({ posts }: HomeProps) => { - useEffect(() => { - const scroll = parseInt( - sessionStorage.getItem(StorageKey.MAIN_SCROLL_Y) ?? "0" - ); - window.scrollTo({ top: scroll, behavior: "auto" }); - }, []); - - const onClickPost = () => { - sessionStorage.setItem(StorageKey.MAIN_SCROLL_Y, window.scrollY.toString()); - }; - - return ( - - - -
- -
-
- ); -}; - -export const getStaticProps = () => { - const posts = allPosts - .sort((a, b) => { - const compareByDate = compareDesc(new Date(a.date), new Date(b.date)); - if (compareByDate !== 0) return compareByDate; - return b.id - a.id; - }) - .map( - (meta) => - ({ - slug: meta.url, - meta: { - index: meta.id, - title: meta.title, - description: meta.description || "", - postedAt: meta.date, - category: "", - series: "", - tags: [] as string[], - }, - } as PostListElement) - ); - return { props: { posts } }; -}; - -export default Home; - -const Wrapper = styled.div` - margin: 2.5rem auto; - max-width: 760px; - padding: 0 1rem; - display: flex; - flex-direction: column; - row-gap: 20px; -`; diff --git a/src/styles/theme.ts b/src/styles/theme.ts index 2d1e384..05dd0af 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -1,5 +1,4 @@ import { css, CSSProp, DefaultTheme } from "styled-components"; -import { CSSProperties } from "react"; declare module "styled-components" { export interface DefaultTheme { @@ -122,20 +121,3 @@ interface ColorPreset { primaryLight: string; secondary: string; } - -const theme = { - light: lightTheme, - dark: darkTheme, -}; - -export const theming = (cb: (theme: DefaultTheme) => CSSProperties) => - Object.keys(theme).reduce( - (acc, name) => - Object.assign(acc, { [`.theme-${name} &`]: cb(darkTheme[name]) }), - {}, - ); - -export const media = { - mobile: `@media only screen and (max-width: 800px)`, - desktop: `@media only screen and (min-width: 800px)`, -}; diff --git a/src/utils/context/ThemeProvider.tsx b/src/utils/context/ThemeProvider.tsx index 11b10fa..40db05d 100644 --- a/src/utils/context/ThemeProvider.tsx +++ b/src/utils/context/ThemeProvider.tsx @@ -1,12 +1,12 @@ -import { ReactElement, useContext, useEffect, useState } from "react"; +import { ReactNode, useContext, useEffect, useState } from "react"; import { ThemeProvider as _ThemeProvider } from "styled-components"; import { darkTheme, lightTheme } from "@src/styles/theme"; import { DarkModeContext } from "./DarkModeContext"; -const ThemeProvider = ({ children }: { children: ReactElement }) => { +const ThemeProvider = ({ children }: { children: ReactNode }) => { const { isDarkMode } = useContext(DarkModeContext); const [themePreset, setThemePreset] = useState( - isDarkMode ? darkTheme : lightTheme + isDarkMode ? darkTheme : lightTheme, ); useEffect(() => { From 03888462b3abad94686a952fec33399ee385fbd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=98=88=EC=A7=84?= Date: Sun, 19 Nov 2023 00:28:13 +0900 Subject: [PATCH 3/6] feat: migrate post page to app router --- package.json | 1 + src/app/posts/[slug]/layout.tsx | 18 ++++ .../[slug].tsx => app/posts/[slug]/page.tsx} | 74 ++++----------- src/business/post.ts | 59 +++++++----- src/components/mdx/CodeBlock.tsx | 7 +- src/components/post/Comment.tsx | 33 +++---- src/components/post/SidePost.tsx | 14 ++- src/pages/_app.tsx | 91 ------------------- src/pages/_document.tsx | 89 ------------------ yarn.lock | 56 ++++++++++++ 10 files changed, 153 insertions(+), 289 deletions(-) create mode 100644 src/app/posts/[slug]/layout.tsx rename src/{pages/posts/[slug].tsx => app/posts/[slug]/page.tsx} (51%) delete mode 100644 src/pages/_app.tsx delete mode 100644 src/pages/_document.tsx diff --git a/package.json b/package.json index 8bffbc8..33f6df7 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@actions/core": "^1.10.1", + "@giscus/react": "^2.3.0", "@mdx-js/loader": "3.0.0", "@mdx-js/react": "3.0.0", "@next/mdx": "14.0.3", diff --git a/src/app/posts/[slug]/layout.tsx b/src/app/posts/[slug]/layout.tsx new file mode 100644 index 0000000..7937e01 --- /dev/null +++ b/src/app/posts/[slug]/layout.tsx @@ -0,0 +1,18 @@ +import { PropsWithChildren } from "react"; +import { Metadata } from "next"; +import { getPostDetail } from "@src/business/post"; +import { PostPageProps } from "@src/app/posts/[slug]/page"; + +export function generateMetadata({ + params: { slug }, +}: PostPageProps): Metadata { + const { detail: post } = getPostDetail(slug); + return { + title: post.title, + description: post.description, + }; +} + +export default function PostLayout({ children }: PropsWithChildren) { + return <>{children}; +} diff --git a/src/pages/posts/[slug].tsx b/src/app/posts/[slug]/page.tsx similarity index 51% rename from src/pages/posts/[slug].tsx rename to src/app/posts/[slug]/page.tsx index 11e397d..85ba960 100644 --- a/src/pages/posts/[slug].tsx +++ b/src/app/posts/[slug]/page.tsx @@ -1,76 +1,32 @@ -import { GetStaticPropsContext } from "next"; -import { useRouter } from "next/router"; -import { NextSeo } from "next-seo"; -import styled from "styled-components"; +"use client"; import { useMDXComponent } from "next-contentlayer/hooks"; -import { allPosts, type Post } from "contentlayer/generated"; - -import components from "@src/components/mdx/MDXComponents"; -import WavyLine from "@src/components/WavyLine"; +import styled from "styled-components"; import Meta from "@src/components/post/Meta"; import PostTitle from "@src/components/post/PostTitle"; +import WavyLine from "@src/components/WavyLine"; +import components from "@src/components/mdx/MDXComponents"; import Profile from "@src/components/main/Profile"; +import SidePost from "@src/components/post/SidePost"; import Comment from "@src/components/post/Comment"; +import { getPostDetail, getPostMeta } from "@src/business/post"; import { FadeIn } from "@src/styles/animation"; -import SidePost from "@src/components/post/SidePost"; - -export async function getStaticPaths() { - // Get a list of valid post paths. - const paths = allPosts.map((post) => ({ - params: { slug: post._raw.flattenedPath }, - })); - - return { paths, fallback: false }; -} - -export async function getStaticProps(context: GetStaticPropsContext) { - // Find the post for the current page. - const postIdx = allPosts.findIndex( - (post) => post._raw.flattenedPath === context.params?.slug - ); - const post = allPosts[postIdx]; - const prevPost = allPosts[postIdx - 1] ?? null, - nextPost = allPosts[postIdx + 1] ?? null; - - // Return notFound if the post does not exist. - if (!post) return { notFound: true }; - - // Return the post as page props. - return { props: { post, prevPost, nextPost } }; -} +export type PostPageProps = { params: { slug: string } }; const codePrefix = ` if (typeof process === 'undefined') { globalThis.process = { env: {} } } `; -export default function Page({ - post, - prevPost, - nextPost, -}: { - post: Post; - prevPost: Post; - nextPost: Post; -}) { - // Parse the MDX file via the useMDXComponent hook. +export default function PostPage({ params: { slug } }: PostPageProps) { + const { postIdx, detail: post } = getPostDetail(slug); + const prevPost = getPostMeta(postIdx - 1), + nextPost = getPostMeta(postIdx + 1); + const MDXContent = useMDXComponent(codePrefix + post.body.code); - const router = useRouter(); return ( <> - - +
+ +
); } @@ -105,7 +63,7 @@ const PostHeader = styled.div` margin-bottom: 3rem; `; -export const Article = styled.article` +const Article = styled.article` animation: ${FadeIn("0%")} 1.4s; max-width: 760px; diff --git a/src/business/post.ts b/src/business/post.ts index 00cffb0..e8f306d 100644 --- a/src/business/post.ts +++ b/src/business/post.ts @@ -2,27 +2,42 @@ import { compareDesc } from "date-fns"; import { allPosts } from "contentlayer/generated"; import { PostListElement } from "@src/types/post"; +const getSortedPosts = () => { + return allPosts.sort((a, b) => { + const compareByDate = compareDesc(new Date(a.date), new Date(b.date)); + if (compareByDate !== 0) return compareByDate; + return b.id - a.id; + }); +}; + export const getAllPostMeta = (): PostListElement[] => { - return allPosts - .sort((a, b) => { - const compareByDate = compareDesc(new Date(a.date), new Date(b.date)); - if (compareByDate !== 0) return compareByDate; - return b.id - a.id; - }) - .map( - (meta) => - ({ - slug: meta.url, - meta: { - index: meta.id, - title: meta.title, - description: meta.description || "", - postedAt: meta.date, - category: "", - series: "", - tags: [], - keywords: [], - }, - }) satisfies PostListElement, - ); + return getSortedPosts().map( + (meta) => + ({ + slug: meta.url, + meta: { + index: meta.id, + title: meta.title, + description: meta.description || "", + postedAt: meta.date, + category: "", + series: "", + tags: [], + keywords: [], + }, + }) satisfies PostListElement, + ); +}; + +export const getPostDetail = (slug: string) => { + const sorted = getSortedPosts(); + const postIdx = sorted.findIndex((post) => post._raw.flattenedPath === slug); + return { + postIdx, + detail: sorted[postIdx], + }; +}; + +export const getPostMeta = (idx: number) => { + return getSortedPosts()[idx]; }; diff --git a/src/components/mdx/CodeBlock.tsx b/src/components/mdx/CodeBlock.tsx index 60f26ad..b5a0692 100644 --- a/src/components/mdx/CodeBlock.tsx +++ b/src/components/mdx/CodeBlock.tsx @@ -1,7 +1,8 @@ import { Highlight, themes } from "prism-react-renderer"; import styled from "styled-components"; +import { ReactElement } from "react"; -export default function CodeBlock(props: any) { +export default function CodeBlock(props: { children: ReactElement }) { const className = props.children.props.className || ""; const matches = className.match(/language-(?.*)/); return ( @@ -20,11 +21,11 @@ export default function CodeBlock(props: any) { (line, i) => // 마지막 줄은 무조건 \n만 있음 i < tokens.length - 1 && ( - + {i + 1} {line.map((token, key) => ( - + ))} diff --git a/src/components/post/Comment.tsx b/src/components/post/Comment.tsx index e7cb107..5ad4c01 100644 --- a/src/components/post/Comment.tsx +++ b/src/components/post/Comment.tsx @@ -1,26 +1,23 @@ -import Script from "next/script"; +import Giscus from "@giscus/react"; /** * Giscus comment */ export default function Comment() { return ( - + ); } diff --git a/src/components/post/SidePost.tsx b/src/components/post/SidePost.tsx index 21a6e37..f0d2098 100644 --- a/src/components/post/SidePost.tsx +++ b/src/components/post/SidePost.tsx @@ -5,11 +5,9 @@ import { type Post } from "contentlayer/generated"; const Post = ({ title, url }: { title: string; url: string }) => { return ( - ( - + {title} - - ) + ); }; export default function SidePost({ @@ -21,7 +19,7 @@ export default function SidePost({ }) { return ( - + {prevPost && ( <> 이전 포스트 @@ -29,7 +27,7 @@ export default function SidePost({ )} - + {nextPost && ( <> 다음 포스트 @@ -54,9 +52,9 @@ const Wrapper = styled.div` `)} `; -const PostWrapper = styled.div<{ align: string }>` +const PostWrapper = styled.div<{ $align: string }>` width: 50%; - text-align: ${({ align }) => align}; + text-align: ${({ $align }) => $align}; ${({ theme }) => theme.media.mobile(` width: auto; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx deleted file mode 100644 index 1a0abba..0000000 --- a/src/pages/_app.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import type { AppProps } from "next/app"; -import { useEffect, useState } from "react"; -import { MDXProvider } from "@mdx-js/react"; -import { - Hydrate, - QueryClient, - QueryClientProvider, -} from "@tanstack/react-query"; -import { Nanum_Gothic_Coding, Noto_Sans_KR } from "next/font/google"; - -import ThemeProvider from "@src/utils/context/ThemeProvider"; -import DarkModeProvider from "@src/utils/context/DarkModeContext"; -import Header from "@src/components/Header"; -import components from "@src/components/mdx/MDXComponents"; -import GlobalStyle from "@src/styles/GlobalStyle"; - -const defaultFont = Noto_Sans_KR({ - subsets: ["latin"], - display: "swap", - weight: ["300", "400", "700"], - variable: "--font-default", -}); - -const codingFont = Nanum_Gothic_Coding({ - subsets: ["latin"], - display: "swap", - weight: ["400", "700"], - variable: "--font-coding", -}); - -function MyApp({ Component, pageProps }: AppProps) { - const [queryClient] = useState( - () => - new QueryClient({ - defaultOptions: { - queries: { - refetchOnWindowFocus: false, - refetchOnMount: false, - refetchOnReconnect: false, - refetchInterval: false, - }, - }, - }), - ); - useEffect(() => { - console.log( - ` -%c - _ _ - | | | | - ___ ___ ___ ___ ___ _ __ ___ | |__ | | ___ __ _ - / _ \\ / _ \\ / _ \\ / _ \\ / _ \\| '__/ _ \\| '_ \\| |/ _ \\ / _\` | -| (_) | (_) | (_) | (_) | (_) | | | (_) | |_) | | (_) | (_| | - \\___/ \\___/ \\___/ \\___/ \\___/|_| \\___/|_.__/|_|\\___/ \\__, | - __/ | - |___/ -` + - ` - _ _ -| | | | -| |__ _ _ ___ ___ ___ ___ ___ _ __ ___ | |__ ___ -| '_ \\| | | | / _ \\ / _ \\ / _ \\ / _ \\ / _ \\| '__/ _ \\| '_ \\ / _ \\ -| |_) | |_| | | (_) | (_) | (_) | (_) | (_) | | | (_) | |_) | (_) | -|_.__/ \\__, | \\___/ \\___/ \\___/ \\___/ \\___/|_| \\___/|_.__/ \\___/ - __/ | - |___/ - -방문해 주셔서 감사합니다 :>`, - "color: #fe5000", - ); - }, []); - return ( - - - - -
- -
- - - -
-
-
-
-
- ); -} - -export default MyApp; diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx deleted file mode 100644 index 840fde5..0000000 --- a/src/pages/_document.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import Document, { - DocumentContext, - DocumentInitialProps, - Head, - Html, - Main, - NextScript, -} from "next/document"; -import { ServerStyleSheet } from "styled-components"; -import Script from "next/script"; - -export default class MyDocument extends Document { - static async getInitialProps( - ctx: DocumentContext - ): Promise { - const sheet = new ServerStyleSheet(); - const originalRenderPage = ctx.renderPage; - - try { - ctx.renderPage = () => - originalRenderPage({ - enhanceApp: (App) => (props) => - sheet.collectStyles(), - }); - - const initialProps = await Document.getInitialProps(ctx); - return { - ...initialProps, - styles: [ - <> - {initialProps.styles} - {sheet.getStyleElement()} - , - ], - }; - } finally { - sheet.seal(); - } - } - - render() { - return ( - - - - - - - - {process.env.NODE_ENV === "production" && ( - <> - - - )} - - -
- - - - ); - } -} diff --git a/yarn.lock b/yarn.lock index d500db9..d250c63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -327,6 +327,13 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== +"@giscus/react@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@giscus/react/-/react-2.3.0.tgz#1c13f2f96bb67684d4f5288dc1ed3155ff307ce4" + integrity sha512-tj79B+NNBfidhPdXJqWoqRm5Jhoc6CBhXMYwBR9nwTwsrdaB/spcQXmHpKcUuOdXZtlYSwMfCFcBogMNbD+gKQ== + dependencies: + giscus "^1.3.0" + "@grpc/grpc-js@^1.7.1": version "1.9.11" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.11.tgz#7b21195c910a49c0bb5d0df21d28a30c4e174851" @@ -372,6 +379,18 @@ jsbi "^4.3.0" tslib "^2.4.1" +"@lit-labs/ssr-dom-shim@^1.0.0", "@lit-labs/ssr-dom-shim@^1.1.0": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.2.tgz#d693d972974a354034454ec1317eb6afd0b00312" + integrity sha512-jnOD+/+dSrfTWYfSXBXlo5l5f0q1UuJo3tkbMDCYA2lKUYq79jaxqtGEvnRoh049nt1vdo1+45RinipU6FGY2g== + +"@lit/reactive-element@^1.3.0", "@lit/reactive-element@^1.6.0": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-1.6.3.tgz#25b4eece2592132845d303e091bad9b04cdcfe03" + integrity sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.0.0" + "@mdx-js/esbuild@^2.0.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@mdx-js/esbuild/-/esbuild-2.3.0.tgz#97f2f1b854d904c50bcd0a219b3664657f4fe8c3" @@ -935,6 +954,11 @@ resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.3.tgz#0dff504fc23487a02a29209b162249070e83a0da" integrity sha512-86XLCVEmWagiUEbr2AjSbeY4qHN9jMm3pgM3PuBYfLIbT0MpDSnA3GA/4W7KoH/C/eeK77kNaeIxZzjhKYIBgw== +"@types/trusted-types@^2.0.2": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.6.tgz#d12451beaeb9c3838f12024580dc500b7e88b0ad" + integrity sha512-HYtNooPvUY9WAVRBr4u+4Qa9fYD1ze2IUlAD3HoA6oehn1taGwBx3Oa52U4mTslTS+GAExKpaFu39Y5xUEwfjg== + "@types/unist@*", "@types/unist@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.2.tgz#6dd61e43ef60b34086287f83683a5c1b2dc53d20" @@ -2275,6 +2299,13 @@ get-tsconfig@^4.5.0: dependencies: resolve-pkg-maps "^1.0.0" +giscus@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/giscus/-/giscus-1.3.0.tgz#b413e6e39b7c3aa96c2d2838df99bbf75fd4709d" + integrity sha512-A3tVLgSmpnh2sX9uGjo9MbzmTTEJirSyFUPRvkipvy37y9rhxUYDoh9kO37QVrP7Sc7QuJ+gihB6apkO0yDyTw== + dependencies: + lit "^2.7.5" + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3033,6 +3064,31 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lit-element@^3.3.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-3.3.3.tgz#10bc19702b96ef5416cf7a70177255bfb17b3209" + integrity sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.1.0" + "@lit/reactive-element" "^1.3.0" + lit-html "^2.8.0" + +lit-html@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-2.8.0.tgz#96456a4bb4ee717b9a7d2f94562a16509d39bffa" + integrity sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q== + dependencies: + "@types/trusted-types" "^2.0.2" + +lit@^2.7.5: + version "2.8.0" + resolved "https://registry.yarnpkg.com/lit/-/lit-2.8.0.tgz#4d838ae03059bf9cafa06e5c61d8acc0081e974e" + integrity sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA== + dependencies: + "@lit/reactive-element" "^1.6.0" + lit-element "^3.3.0" + lit-html "^2.8.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" From c99da5cd447417474babd43ac80caeffd68d5b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=98=88=EC=A7=84?= Date: Sun, 19 Nov 2023 00:36:11 +0900 Subject: [PATCH 4/6] fix: fix mdx errors --- src/posts/60-starting-geultto-8.mdx | 12 ++++++++---- src/posts/63-test-async-with-jest.mdx | 5 +---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/posts/60-starting-geultto-8.mdx b/src/posts/60-starting-geultto-8.mdx index 42273d2..ba4b066 100644 --- a/src/posts/60-starting-geultto-8.mdx +++ b/src/posts/60-starting-geultto-8.mdx @@ -7,13 +7,17 @@ date: 2023-02-10 --- + + + + + + + import WavyLine from "../components/WavyLine"; > 네트워크를 통해 내가 얻을 수 있는 것과 줄 수 있는 것이 무엇인지를 구분하는 것이 중요하다.
-우미영, \ - <나를 믿고 일한다는 것> - - + 우미영, \<나를 믿고 일한다는 것> 최근 다양한 커뮤니티에 참여해서 네트워킹을 하고 있는데, 내가 얻을 수 있는 것은 생각해 본 적이 있지만 줄 수 있는 것을 생각해 본 적은 없었던 것 같다. diff --git a/src/posts/63-test-async-with-jest.mdx b/src/posts/63-test-async-with-jest.mdx index 92984d9..fd47b15 100644 --- a/src/posts/63-test-async-with-jest.mdx +++ b/src/posts/63-test-async-with-jest.mdx @@ -298,10 +298,7 @@ test("비동기 작업이 완료되기 이전에는 onBeforeExecute는 실행되 1. execute를 실행하면, 자바스크립트 콜 스택에 execute 함수가 쌓이고, 주도권이 execute 함수로 넘어갑니다. 2. 이때 `await fetchList`를 만나면, WebAPI가 setTimeout의 처리를 맡게 되고 콜 스택에서 execute는 빠져나옵니다. 3. 주도권이 테스트로 넘어옵니다. -4. (여기까지 동일) `jest.advanceTimersByTime`으로 3.1초가 흐르고, execute는 태스크 큐로 이동해 실행 대기 상태가 됩니다. +4. (여기까지 동일) `jest.advanceTimersByTime`으로 3.1초가 흐르고, execute는 태스크 큐로 이동해 실행 대기 상태가 됩니다. 5. `await flushPromises()` 처리로 인해, 테스트도 콜 스택에서 빠져나와 태스크 큐로 이동합니다. 6. 콜 스택이 비었기 때문에, 태스크 큐에서 더 앞에 있던 `execute`가 콜 스택으로 들어가 마저 실행됩니다. 이 시점에 `onAfterExecute`가 실행됩니다. 7. execute 실행이 모두 끝나면, 다시 테스트를 실행합니다. 이때는 `expect(onAfterExecute).toHaveBeenCalledTimes(1);`를 통과할 수 있습니다. From f2f3ba8ca50cef9a3dfe46f322b5e5e0741a18e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=98=88=EC=A7=84?= Date: Sun, 19 Nov 2023 00:44:51 +0900 Subject: [PATCH 5/6] fix: fix type errors --- src/components/mdx/CodeBlock.tsx | 10 +++++----- src/styles/theme.ts | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/mdx/CodeBlock.tsx b/src/components/mdx/CodeBlock.tsx index b5a0692..bc482f9 100644 --- a/src/components/mdx/CodeBlock.tsx +++ b/src/components/mdx/CodeBlock.tsx @@ -1,14 +1,14 @@ -import { Highlight, themes } from "prism-react-renderer"; +import {Highlight, themes} from "prism-react-renderer"; import styled from "styled-components"; -import { ReactElement } from "react"; -export default function CodeBlock(props: { children: ReactElement }) { - const className = props.children.props.className || ""; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default function CodeBlock(props: any) { + const className = props.children?.props.className || ""; const matches = className.match(/language-(?.*)/); return ( = { +const defaultTheme = { fontSizes: { tiny: "12px", s: "14px", @@ -90,12 +90,12 @@ const defaultTheme: Omit = { code: 1.6, }, media: { - mobile: (...args) => css` + mobile: (...args: RuleSet) => css` @media only screen and (max-width: 800px) { ${args} } `, - desktop: (...args) => css` + desktop: (...args: RuleSet) => css` @media only screen and (min-width: 800px) { ${args} } From f3305adf197642bd652c8f4267855fa49748ce81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=98=88=EC=A7=84?= Date: Sun, 19 Nov 2023 00:48:02 +0900 Subject: [PATCH 6/6] fix: fix side post order --- src/app/posts/[slug]/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/posts/[slug]/page.tsx b/src/app/posts/[slug]/page.tsx index 85ba960..7705773 100644 --- a/src/app/posts/[slug]/page.tsx +++ b/src/app/posts/[slug]/page.tsx @@ -20,8 +20,8 @@ if (typeof process === 'undefined') { export default function PostPage({ params: { slug } }: PostPageProps) { const { postIdx, detail: post } = getPostDetail(slug); - const prevPost = getPostMeta(postIdx - 1), - nextPost = getPostMeta(postIdx + 1); + const prevPost = getPostMeta(postIdx + 1), + nextPost = getPostMeta(postIdx - 1); const MDXContent = useMDXComponent(codePrefix + post.body.code);