From 19f808bc0d32c7d8fd1ec2790461ec0001555bf1 Mon Sep 17 00:00:00 2001
From: Kim0426 <706shin1728@naver.com>
Date: Fri, 22 Dec 2023 01:42:21 +0900
Subject: [PATCH 1/5] chore: init upload project
---
.eslintrc.json | 68 +++++++++
.gitignore | 67 +++++++++
next.config.js | 29 ++++
package.json | 73 ++++++++++
src copy/@types/AboutProps.ts | 6 +
src copy/@types/ButtonType.ts | 10 ++
src copy/@types/CategoryType.ts | 4 +
src copy/@types/GuestBookType.ts | 3 +
src copy/@types/PostType.ts | 0
src copy/@types/ThemeType.ts | 1 +
src copy/Component/About/Content.tsx | 22 +++
.../Component/About/ProfileImgWrapper.tsx | 18 +++
src copy/Component/About/Title.tsx | 20 +++
src copy/Component/Blog/CodeBlock.tsx | 35 +++++
src copy/Component/Blog/Exterct.tsx | 20 +++
src copy/Component/Blog/LayOut.tsx | 78 +++++++++++
src copy/Component/Blog/PostSearchModal.tsx | 94 +++++++++++++
src copy/Component/Blog/SearchPost.tsx | 35 +++++
.../Component/CategoryList/CategoryList.tsx | 43 ++++++
src copy/Component/Common/Avatar.tsx | 0
src copy/Component/Common/Button.tsx | 43 ++++++
src copy/Component/Common/Footer.tsx | 51 +++++++
src copy/Component/Common/Navbar.tsx | 61 ++++++++
src copy/Component/Common/PostLayout.tsx | 32 +++++
src copy/Component/DarkMode/ToggoeButton.tsx | 34 +++++
src copy/Component/GA/GA.tsx | 37 +++++
src copy/Component/Giscus/Gitcus.tsx | 22 +++
src copy/Component/GuestBook/GuestBook.tsx | 36 +++++
src copy/Component/Input/Input.tsx | 17 +++
src copy/Component/Input/InputBox.tsx | 21 +++
src copy/Component/Input/index.tsx | 4 +
.../Component/Notion/NotionresumeClient.tsx | 40 ++++++
src copy/Component/Post/PostItem.tsx | 40 ++++++
src copy/Component/Post/PostList.tsx | 24 ++++
src copy/Component/TOC/index.tsx | 58 ++++++++
src copy/app/(root)/(routes)/(home)/page.tsx | 35 +++++
src copy/app/(root)/(routes)/about/page.tsx | 46 ++++++
.../app/(root)/(routes)/blog/[slug]/page.tsx | 132 ++++++++++++++++++
.../category/[categoryId]/[slug]/page.tsx | 101 ++++++++++++++
.../(routes)/category/[categoryId]/page.tsx | 63 +++++++++
.../(routes)/guestbook/HydratedGuestBook.tsx | 16 +++
.../app/(root)/(routes)/guestbook/page.tsx | 5 +
src copy/app/(root)/(routes)/notion/page.tsx | 27 ++++
.../(root)/(routes)/notion/resume/page.tsx | 12 ++
src copy/app/(root)/layout.tsx | 96 +++++++++++++
src copy/app/(root)/not-found.tsx | 3 +
src copy/app/Recoil.tsx | 7 +
src copy/app/api/guestbook/route.ts | 58 ++++++++
src copy/app/api/slugs/[id]/route.ts | 29 ++++
src copy/app/api/slugs/route.ts | 19 +++
src copy/app/firebase.js | 17 +++
src copy/app/globalAtom.ts | 20 +++
src copy/app/globals.css | 25 ++++
src copy/app/queryClientProvider.tsx | 28 ++++
src copy/app/registry.tsx | 28 ++++
src copy/app/sitemap.ts | 27 ++++
src copy/app/sitemap.xml.tsx | 1 +
src copy/app/themeWrapper.tsx | 22 +++
src copy/constants/POST.ts | 7 +
src copy/hooks/useGetGuestBook.ts | 20 +++
src copy/hooks/useInput.ts | 21 +++
src copy/hooks/useNaverInit.ts | 29 ++++
src copy/hooks/useObserver.ts | 42 ++++++
src copy/hooks/usePostGuestBook.ts | 17 +++
src copy/hooks/usePostQuery.ts | 40 ++++++
src copy/hooks/useScroll.tsx | 34 +++++
src copy/hooks/useSearchPost.tsx | 38 +++++
src copy/hooks/useTheme.ts | 13 ++
src copy/style/globalStyle.tsx | 48 +++++++
src copy/style/theme/darkMode.ts | 26 ++++
src copy/style/theme/media.ts | 17 +++
src copy/utils/axiosClient.ts | 46 ++++++
src copy/utils/getQueryClient.ts | 8 ++
tsconfig.json | 34 +++++
74 files changed, 2403 insertions(+)
create mode 100644 .eslintrc.json
create mode 100644 .gitignore
create mode 100644 next.config.js
create mode 100644 package.json
create mode 100644 src copy/@types/AboutProps.ts
create mode 100644 src copy/@types/ButtonType.ts
create mode 100644 src copy/@types/CategoryType.ts
create mode 100644 src copy/@types/GuestBookType.ts
create mode 100644 src copy/@types/PostType.ts
create mode 100644 src copy/@types/ThemeType.ts
create mode 100644 src copy/Component/About/Content.tsx
create mode 100644 src copy/Component/About/ProfileImgWrapper.tsx
create mode 100644 src copy/Component/About/Title.tsx
create mode 100644 src copy/Component/Blog/CodeBlock.tsx
create mode 100644 src copy/Component/Blog/Exterct.tsx
create mode 100644 src copy/Component/Blog/LayOut.tsx
create mode 100644 src copy/Component/Blog/PostSearchModal.tsx
create mode 100644 src copy/Component/Blog/SearchPost.tsx
create mode 100644 src copy/Component/CategoryList/CategoryList.tsx
create mode 100644 src copy/Component/Common/Avatar.tsx
create mode 100644 src copy/Component/Common/Button.tsx
create mode 100644 src copy/Component/Common/Footer.tsx
create mode 100644 src copy/Component/Common/Navbar.tsx
create mode 100644 src copy/Component/Common/PostLayout.tsx
create mode 100644 src copy/Component/DarkMode/ToggoeButton.tsx
create mode 100644 src copy/Component/GA/GA.tsx
create mode 100644 src copy/Component/Giscus/Gitcus.tsx
create mode 100644 src copy/Component/GuestBook/GuestBook.tsx
create mode 100644 src copy/Component/Input/Input.tsx
create mode 100644 src copy/Component/Input/InputBox.tsx
create mode 100644 src copy/Component/Input/index.tsx
create mode 100644 src copy/Component/Notion/NotionresumeClient.tsx
create mode 100644 src copy/Component/Post/PostItem.tsx
create mode 100644 src copy/Component/Post/PostList.tsx
create mode 100644 src copy/Component/TOC/index.tsx
create mode 100644 src copy/app/(root)/(routes)/(home)/page.tsx
create mode 100644 src copy/app/(root)/(routes)/about/page.tsx
create mode 100644 src copy/app/(root)/(routes)/blog/[slug]/page.tsx
create mode 100644 src copy/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx
create mode 100644 src copy/app/(root)/(routes)/category/[categoryId]/page.tsx
create mode 100644 src copy/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx
create mode 100644 src copy/app/(root)/(routes)/guestbook/page.tsx
create mode 100644 src copy/app/(root)/(routes)/notion/page.tsx
create mode 100644 src copy/app/(root)/(routes)/notion/resume/page.tsx
create mode 100644 src copy/app/(root)/layout.tsx
create mode 100644 src copy/app/(root)/not-found.tsx
create mode 100644 src copy/app/Recoil.tsx
create mode 100644 src copy/app/api/guestbook/route.ts
create mode 100644 src copy/app/api/slugs/[id]/route.ts
create mode 100644 src copy/app/api/slugs/route.ts
create mode 100644 src copy/app/firebase.js
create mode 100644 src copy/app/globalAtom.ts
create mode 100644 src copy/app/globals.css
create mode 100644 src copy/app/queryClientProvider.tsx
create mode 100644 src copy/app/registry.tsx
create mode 100644 src copy/app/sitemap.ts
create mode 100644 src copy/app/sitemap.xml.tsx
create mode 100644 src copy/app/themeWrapper.tsx
create mode 100644 src copy/constants/POST.ts
create mode 100644 src copy/hooks/useGetGuestBook.ts
create mode 100644 src copy/hooks/useInput.ts
create mode 100644 src copy/hooks/useNaverInit.ts
create mode 100644 src copy/hooks/useObserver.ts
create mode 100644 src copy/hooks/usePostGuestBook.ts
create mode 100644 src copy/hooks/usePostQuery.ts
create mode 100644 src copy/hooks/useScroll.tsx
create mode 100644 src copy/hooks/useSearchPost.tsx
create mode 100644 src copy/hooks/useTheme.ts
create mode 100644 src copy/style/globalStyle.tsx
create mode 100644 src copy/style/theme/darkMode.ts
create mode 100644 src copy/style/theme/media.ts
create mode 100644 src copy/utils/axiosClient.ts
create mode 100644 src copy/utils/getQueryClient.ts
create mode 100644 tsconfig.json
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..6db3279
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,68 @@
+{
+ "plugins": ["import"],
+ "extends": ["next/core-web-vitals", "plugin:import/recommended"],
+ "rules": {
+ "no-unused-vars": [
+ "warn",
+ {
+ "argsIgnorePattern": "^_",
+ "varsIgnorePattern": "^_"
+ }
+ ],
+ "import/named": 0,
+ "import/order": [
+ "error",
+ {
+ "newlines-between": "always",
+ "groups": [
+ "type",
+ "builtin",
+ "external",
+ "internal",
+ "parent",
+ "sibling",
+ "index",
+ "unknown"
+ ],
+ "pathGroups": [
+ {
+ "pattern": "next*",
+ "group": "external",
+ "position": "before"
+ },
+ {
+ "pattern": "react*",
+ "group": "external",
+ "position": "before"
+ },
+ {
+ "pattern": "src/@hooks/**",
+ "group": "internal",
+ "position": "after"
+ },
+ {
+ "pattern": "src/@Component/**",
+ "group": "internal",
+ "position": "after"
+ },
+ {
+ "pattern": "src/@utils/**",
+ "group": "internal",
+ "position": "after"
+ }
+ ],
+ "pathGroupsExcludedImportTypes": [
+ "next*",
+ "react*",
+ "src/@hooks/**",
+ "src/@Component/**",
+ "src/@utils/**"
+ ],
+ "alphabetize": {
+ "order": "asc",
+ "caseInsensitive": true
+ }
+ }
+ ]
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b848d1d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,67 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# lighthouseci
+.lighthouseci
+/lhci_reports/
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+.env
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+.lighthouserc.js
+.commitlint.config.js
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+# Sentry Auth Token
+.sentryclirc
+.sentry.client.config.ts
+.sentry.edge.config.ts
+.sentry.server.config.ts
+
+.tsconfig.json
+
+public
+
+posts
+.next
+.vercel
+.lighthouseci
+.husky
+
+lhci_reports
+.vscode
+# package.json
+package-lock.json
+
+public
diff --git a/next.config.js b/next.config.js
new file mode 100644
index 0000000..25e8879
--- /dev/null
+++ b/next.config.js
@@ -0,0 +1,29 @@
+const withBundleAnalyzer = require('@next/bundle-analyzer')({
+ enabled: process.env.ANALYZE === 'true',
+});
+
+const nextConfig = {
+ experimental: {
+ optimizePackageImports: ['styled-components,sentry,lodash,react-markdown'],
+ },
+ reactStrictMode: true,
+ compiler: {
+ styledComponents: true,
+ },
+ images: {
+ remotePatterns: [
+ {
+ protocol: 'https',
+ hostname: '**',
+ },
+ ],
+ formats: ['image/avif', 'image/webp'],
+ },
+};
+
+const bundleAnalyzerConfig = withBundleAnalyzer({
+ ...nextConfig,
+ compress: true,
+});
+
+module.exports = bundleAnalyzerConfig;
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..3d18104
--- /dev/null
+++ b/package.json
@@ -0,0 +1,73 @@
+{
+ "name": "hj-devlog",
+ "version": "0.1.0",
+ "private": true,
+ "homepage": "https://github.com/khj0426/HJ_Devlog",
+ "scripts": {
+ "lhci": "next build && lhci autorun",
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint",
+ "lint:fix": "next lint --fix",
+ "deploy": "next build && npx gh-pages -d out",
+ "analyze": "cross-env ANALYZE=true next build",
+ "analyze:server": "cross-env BUNDLE_ANALYZE=server next build",
+ "analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build"
+ },
+ "dependencies": {
+ "@giscus/react": "^2.2.8",
+ "@next/bundle-analyzer": "^13.4.7",
+ "@sentry/nextjs": "^7.64.0",
+ "@tanstack/react-query": "^4.33.0",
+ "@types/node": "20.2.5",
+ "@types/react": "18.2.8",
+ "@types/react-dom": "18.2.4",
+ "@types/react-syntax-highlighter": "^15.5.7",
+ "@types/recoil": "^0.0.9",
+ "@types/styled-components": "^5.1.26",
+ "@types/uuid": "^9.0.1",
+ "autoprefixer": "10.4.14",
+ "axios": "^1.6.2",
+ "cross-env": "^7.0.3",
+ "date-fns": "^2.30.0",
+ "eslint": "8.42.0",
+ "eslint-config-next": "13.4.4",
+ "firebase": "^10.7.1",
+ "github-label-sync": "^2.3.1",
+ "gray-matter": "^4.0.3",
+ "js-cookie": "^3.0.5",
+ "next": "^13.5.0",
+ "notion-client": "^6.16.0",
+ "postcss": "8.4.24",
+ "prism-react-renderer": "^2.0.5",
+ "react": "18.2.0",
+ "react-cookie": "^6.1.1",
+ "react-dom": "18.2.0",
+ "react-markdown": "^8.0.7",
+ "react-notion-x": "^6.16.0",
+ "react-secure-storage": "^1.3.0",
+ "react-syntax-highlighter": "^15.5.0",
+ "recoil": "^0.7.7",
+ "rehype-raw": "^6.1.1",
+ "remark": "^14.0.3",
+ "remark-html": "^15.0.2",
+ "styled-components": "^6.0.0-rc.3",
+ "tailwindcss": "3.3.2",
+ "typescript": "5.1.3",
+ "uuid": "^9.0.0"
+ },
+ "devDependencies": {
+ "@commitlint/cli": "^17.7.2",
+ "@commitlint/config-conventional": "^17.7.0",
+ "@lhci/cli": "^0.12.0",
+ "@sentry/utils": "^7.64.0",
+ "@types/glob": "^8.1.0",
+ "@typescript-eslint/parser": "^6.7.5",
+ "eslint-import-resolver-typescript": "^3.6.1",
+ "eslint-plugin-import": "^2.28.1",
+ "gh-pages": "^5.0.0",
+ "glob": "^10.2.7",
+ "husky": "^8.0.3"
+ }
+}
diff --git a/src copy/@types/AboutProps.ts b/src copy/@types/AboutProps.ts
new file mode 100644
index 0000000..9ae002c
--- /dev/null
+++ b/src copy/@types/AboutProps.ts
@@ -0,0 +1,6 @@
+type AboutProps = {
+ title: string;
+ imgurl: string;
+ content: string | string[];
+};
+export default AboutProps;
diff --git a/src copy/@types/ButtonType.ts b/src copy/@types/ButtonType.ts
new file mode 100644
index 0000000..a3dc9a7
--- /dev/null
+++ b/src copy/@types/ButtonType.ts
@@ -0,0 +1,10 @@
+import { CSSProperties } from 'react';
+
+export type ButtonProps = {
+ style?: CSSProperties;
+ disabled?: boolean;
+ variant?: 'default' | 'outlined';
+ label?: string;
+ icon?: React.ReactNode;
+ onClick?: () => void;
+};
diff --git a/src copy/@types/CategoryType.ts b/src copy/@types/CategoryType.ts
new file mode 100644
index 0000000..18cac1d
--- /dev/null
+++ b/src copy/@types/CategoryType.ts
@@ -0,0 +1,4 @@
+export type CategoryItem = {
+ category: string;
+ categoryCount: string;
+};
diff --git a/src copy/@types/GuestBookType.ts b/src copy/@types/GuestBookType.ts
new file mode 100644
index 0000000..bcf688e
--- /dev/null
+++ b/src copy/@types/GuestBookType.ts
@@ -0,0 +1,3 @@
+export type GuestBook = {
+ comments: {};
+};
diff --git a/src copy/@types/PostType.ts b/src copy/@types/PostType.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src copy/@types/ThemeType.ts b/src copy/@types/ThemeType.ts
new file mode 100644
index 0000000..d2c2a51
--- /dev/null
+++ b/src copy/@types/ThemeType.ts
@@ -0,0 +1 @@
+export type ThemeType = 'light' | 'dark';
diff --git a/src copy/Component/About/Content.tsx b/src copy/Component/About/Content.tsx
new file mode 100644
index 0000000..8f05252
--- /dev/null
+++ b/src copy/Component/About/Content.tsx
@@ -0,0 +1,22 @@
+import styled from 'styled-components';
+import { v4 as uuidv4 } from 'uuid';
+
+const StyledContent = styled.section`
+ display: flex;
+ min-width: 300px;
+ min-height: 400px;
+ max-width: 400px;
+ max-height: 500px;
+ display: flex;
+ font-size: 18px;
+ flex-direction: column;
+`;
+export default function Content({ content }: { content: string | string[] }) {
+ return (
+
+ {Array.from(content).map((eachContent) => (
+ {eachContent}
+ ))}
+
+ );
+}
diff --git a/src copy/Component/About/ProfileImgWrapper.tsx b/src copy/Component/About/ProfileImgWrapper.tsx
new file mode 100644
index 0000000..405c361
--- /dev/null
+++ b/src copy/Component/About/ProfileImgWrapper.tsx
@@ -0,0 +1,18 @@
+'use client';
+
+import Image from 'next/image';
+export default function ProfileImageWrapper({ imgurl }: { imgurl: string }) {
+ return (
+
+
+
+ );
+}
diff --git a/src copy/Component/About/Title.tsx b/src copy/Component/About/Title.tsx
new file mode 100644
index 0000000..f0d00e8
--- /dev/null
+++ b/src copy/Component/About/Title.tsx
@@ -0,0 +1,20 @@
+'use client';
+
+import styled from 'styled-components';
+
+const StyledTitle = styled.h3`
+ font-weight: 700;
+ display: flex;
+ flex-wrap: wrap;
+
+ @media ${({ theme }) => theme.device.tablet} {
+ font-size: 24px;
+ }
+ @media ${({ theme }) => theme.device.mobile} {
+ font-size: 20px;
+ }
+`;
+
+export default function Title({ title }: { title: string }) {
+ return {title};
+}
diff --git a/src copy/Component/Blog/CodeBlock.tsx b/src copy/Component/Blog/CodeBlock.tsx
new file mode 100644
index 0000000..e161c0d
--- /dev/null
+++ b/src copy/Component/Blog/CodeBlock.tsx
@@ -0,0 +1,35 @@
+'use client';
+import dynamic from 'next/dynamic';
+const PrismLight = dynamic(
+ () => import('react-syntax-highlighter/dist/cjs/prism-light')
+);
+import { materialDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
+
+
+export default function CodeBlock({
+ children
+}: {
+ children: string | string[];
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src copy/Component/Blog/Exterct.tsx b/src copy/Component/Blog/Exterct.tsx
new file mode 100644
index 0000000..d7e1b5a
--- /dev/null
+++ b/src copy/Component/Blog/Exterct.tsx
@@ -0,0 +1,20 @@
+'use client';
+import styled from 'styled-components';
+
+const StyledPostExterct = styled.h5`
+ color: #808080;
+ @media ${({ theme }) => theme.device.laptop} {
+ font-size: 14px;
+ }
+
+ @media ${({ theme }) => theme.device.tablet} {
+ font-size: 13px;
+ }
+ @media ${({ theme }) => theme.device.mobile} {
+ font-size: 11px;
+ }
+`;
+
+export default function PostExterct({ exterct }: { exterct: string }) {
+ return {exterct};
+}
diff --git a/src copy/Component/Blog/LayOut.tsx b/src copy/Component/Blog/LayOut.tsx
new file mode 100644
index 0000000..224db2f
--- /dev/null
+++ b/src copy/Component/Blog/LayOut.tsx
@@ -0,0 +1,78 @@
+'use client';
+import { useEffect } from 'react';
+
+import styled, { css } from 'styled-components';
+
+const PostLayOutPC = css`
+ min-width: 60%;
+ max-width: 60%;
+`;
+const PostLayOutMobile = css`
+ min-width: 80%;
+ max-width: 80%;
+`;
+
+const StyledPostLayOut = styled.article`
+ ${PostLayOutPC}
+ display: flex;
+ margin: 20px auto;
+ flex-direction: column;
+ margin-bottom: 50px;
+ font-size: 20px;
+
+ @media ${({ theme }) => theme.device.laptop} {
+ ${PostLayOutPC}
+ }
+
+ @media ${({ theme }) => theme.device.tablet} {
+ ${PostLayOutPC}
+ }
+
+ @media ${({ theme }) => theme.device.mobile} {
+ ${PostLayOutMobile}
+ }
+`;
+
+export default function BlogLayOut({
+ children,
+}: {
+ children: React.ReactNode[];
+}) {
+ useEffect(() => {
+ const getH2Element = Array.from(document.querySelectorAll('h2'));
+ const getH3Element = Array.from(document.querySelectorAll('h3'));
+
+ const getAllHeaderElement = [...getH2Element, ...getH3Element];
+
+ const io = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ const getTOC = entry.target.textContent as string;
+ const getTOCElement = document.getElementsByClassName(
+ entry.target.textContent as string
+ );
+ if (!getTOCElement[0]) {
+ return;
+ }
+ if (getTOCElement && entry.isIntersecting) {
+ getTOCElement[0].classList.add('active');
+ } else if (getTOCElement && !entry.isIntersecting) {
+ getTOCElement[0].classList.remove('active');
+ }
+ });
+ },
+ {
+ threshold: 0.4,
+ rootMargin: '-50px 0px 0px 0px',
+ }
+ );
+
+ getAllHeaderElement.forEach((eachHeaderElement) => {
+ io.observe(eachHeaderElement);
+ });
+
+ return () => io.disconnect();
+ }, []);
+
+ return {children};
+}
diff --git a/src copy/Component/Blog/PostSearchModal.tsx b/src copy/Component/Blog/PostSearchModal.tsx
new file mode 100644
index 0000000..f2f85d1
--- /dev/null
+++ b/src copy/Component/Blog/PostSearchModal.tsx
@@ -0,0 +1,94 @@
+import { ChangeEvent, useState } from 'react';
+
+import Link from 'next/link';
+import styled from 'styled-components';
+
+import useSearchPost from '@/hooks/useSearchPost';
+
+const StyledPostSearchModalWrapper = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ backdrop-filter: blur(1px);
+ width: 100%;
+ height: 100%;
+`;
+
+const StyledPostSearchInput = styled.input`
+ padding: 14px 24px;
+ width: 100%;
+ height: 100px;
+ box-sizing: border-box;
+ outline: none;
+ border: none;
+ background-color: inherit;
+ color: rgb(255, 255, 255);
+ font-family: inherit;
+`;
+
+const StyledPostSearchModal = styled.div`
+ width: 350px;
+ min-height: 450px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ height: auto;
+ position: absolute;
+ background-color: rgb(38, 41, 43);
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%) scale(1);
+ color: rgb(236, 237, 238);
+ box-shadow: 0 15px 30px 0 rgba(#000, 0.25);
+ border-radius: 15px;
+ overflow: auto;
+`;
+
+export default function PostSearchModal({
+ onCloseModal,
+}: {
+ onCloseModal: () => void;
+}) {
+ const [querySearch, setQuerySearch] = useState(null);
+ const { posts } = useSearchPost(querySearch as string);
+
+ const handleChangSearchQuery = (e: ChangeEvent) => {
+ if (e.target.value.length === 0) {
+ return;
+ }
+
+ setQuerySearch(e.target.value);
+ };
+
+ return (
+ onCloseModal()}>
+
+ onCloseModal()}
+ style={{
+ cursor: 'pointer',
+ }}
+ >
+ X
+
+
+ {posts.map((post) => (
+ onCloseModal()}
+ style={{
+ color: 'inherit',
+ }}
+ >
+ {post.title}
+
+ ))}
+
+
+ );
+}
diff --git a/src copy/Component/Blog/SearchPost.tsx b/src copy/Component/Blog/SearchPost.tsx
new file mode 100644
index 0000000..b2837d9
--- /dev/null
+++ b/src copy/Component/Blog/SearchPost.tsx
@@ -0,0 +1,35 @@
+import Image from 'next/image';
+import { useRecoilState } from 'recoil';
+
+import { postSearchModalState } from '@/app/globalAtom';
+
+import SearchImage from '../../.././public/images/search.webp';
+
+import PostSearchModal from './PostSearchModal';
+
+export default function SearchPostButton() {
+ const [modalState, setPostSearchModal] = useRecoilState(postSearchModalState);
+
+ return (
+ <>
+ {
+ setPostSearchModal(!modalState);
+ }}
+ >
+ {modalState && (
+ setPostSearchModal(!modalState)} />
+ )}
+ >
+ );
+}
diff --git a/src copy/Component/CategoryList/CategoryList.tsx b/src copy/Component/CategoryList/CategoryList.tsx
new file mode 100644
index 0000000..56f1a6d
--- /dev/null
+++ b/src copy/Component/CategoryList/CategoryList.tsx
@@ -0,0 +1,43 @@
+'use client';
+
+import type { CategoryItem } from '@/@types/CategoryType';
+
+import Link from 'next/link';
+import styled from 'styled-components';
+
+const CategoryListStyle = styled.span`
+ color: ${({ theme }) => theme.text};
+ font-size: 20px;
+ cursor: pointer;
+ &:hover {
+ text-decoration: underline ${({ theme }) => theme.text};
+ }
+`;
+
+const CategoryListWrapper = styled.div`
+ gap: 15px;
+ display: flex;
+ width: 100%;
+ flex-wrap: wrap;
+ flex-direction: row;
+ justify-content: center;
+`;
+export default function CategoryList({
+ category,
+}: {
+ category: CategoryItem[];
+}) {
+ return (
+
+ {category.map(({ category, categoryCount }) => {
+ return (
+
+
+ # {category + categoryCount}
+
+
+ );
+ })}
+
+ );
+}
diff --git a/src copy/Component/Common/Avatar.tsx b/src copy/Component/Common/Avatar.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/src copy/Component/Common/Button.tsx b/src copy/Component/Common/Button.tsx
new file mode 100644
index 0000000..afe9a2f
--- /dev/null
+++ b/src copy/Component/Common/Button.tsx
@@ -0,0 +1,43 @@
+import type { ButtonProps } from '@/@types/ButtonType';
+
+import styled from 'styled-components';
+
+const StyledBaseButton = styled.button`
+ background-color: ${({ theme }) => theme.backgroundPost};
+ font-weight: bold;
+ border-radius: 0.25rem;
+ cursor: pointer;
+ &:hover {
+ background-color: rgba(66, 153, 225, 0.7);
+ }
+ &:disabled {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+ color: ${({ theme }) => theme.text};
+ transition: icon 0.3s ease-in-out;
+ border: ${(props) =>
+ props.variant === 'outlined' ? '1px rgba(255,255,255,1)' : 'none'};
+`;
+
+export default function Button({
+ variant,
+ style,
+ disabled,
+ icon,
+ onClick,
+ label,
+}: ButtonProps) {
+ return (
+
+ {icon}
+ {label}
+
+ );
+}
diff --git a/src copy/Component/Common/Footer.tsx b/src copy/Component/Common/Footer.tsx
new file mode 100644
index 0000000..6e55496
--- /dev/null
+++ b/src copy/Component/Common/Footer.tsx
@@ -0,0 +1,51 @@
+'use client';
+
+import Image from 'next/image';
+import Link from 'next/link';
+import styled from 'styled-components';
+
+import InstagramImage from '../../../public/images/Instagramgram.webp';
+
+const StyledFooter = styled.footer`
+ height: 30px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin: 60px auto;
+`;
+
+const StyledLinkIconArea = styled.div`
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+ margin: 0 auto;
+`;
+
+export default function Footer() {
+ return (
+
+
+
+
+
+
+
+
+
+ HJ DevLog
+ ©{new Date().getFullYear()} 효중킴의 블로그, Powered By Next.js
+
+ );
+}
diff --git a/src copy/Component/Common/Navbar.tsx b/src copy/Component/Common/Navbar.tsx
new file mode 100644
index 0000000..07b0808
--- /dev/null
+++ b/src copy/Component/Common/Navbar.tsx
@@ -0,0 +1,61 @@
+'use client';
+import Link from 'next/link';
+import styled, { css } from 'styled-components';
+
+import SearchPostButton from '../Blog/SearchPost';
+import ToggleDarkModeButton from '../DarkMode/ToggoeButton';
+
+const StyledNavBarLayout = styled.nav`
+ position: sticky;
+ top: 0;
+ left: 0;
+ width: 100%;
+ display: flex;
+ font-size: 20px;
+ color: ${({ theme }) => theme.text};
+ gap: 15px;
+ margin: 0 auto;
+ background-color: ${({ theme }) => theme.body};
+ align-items: center;
+ justify-content: space-around;
+ z-index: 1;
+`;
+
+const StyledNavBarTitle = styled(Link)`
+ font-weight: 600;
+ text-decoration: none;
+ color: inherit;
+ ${({ href }) =>
+ href === '/notion/resume' &&
+ css`
+ @media (max-width: 1024px) {
+ opacity: 0;
+ }
+ `}
+`;
+
+export default function Navbar() {
+ return (
+
+
+ Blog
+ About
+ Resume
+
+
+
+
+
+
+ );
+}
diff --git a/src copy/Component/Common/PostLayout.tsx b/src copy/Component/Common/PostLayout.tsx
new file mode 100644
index 0000000..559ef01
--- /dev/null
+++ b/src copy/Component/Common/PostLayout.tsx
@@ -0,0 +1,32 @@
+'use client';
+import styled from 'styled-components';
+const StyledPostLayOut = styled.section`
+ display: flex;
+ justify-content: space-between;
+ padding-left: 5px;
+ border-radius: 25px;
+ margin: 25px auto;
+ background: ${({ theme }) => theme.backgroundPost};
+ max-width: 800px;
+ width: 60%;
+
+ @media ${({ theme }) => theme.device.laptop} {
+ width: 80%;
+ }
+
+ @media ${({ theme }) => theme.device.tablet} {
+ width: 90%;
+ }
+
+ @media ${({ theme }) => theme.device.mobile} {
+ width: 90%;
+ }
+`;
+
+export default function PostLayout({
+ children,
+}: {
+ children: React.ReactNode | React.ReactNode[];
+}) {
+ return {children};
+}
diff --git a/src copy/Component/DarkMode/ToggoeButton.tsx b/src copy/Component/DarkMode/ToggoeButton.tsx
new file mode 100644
index 0000000..99b2357
--- /dev/null
+++ b/src copy/Component/DarkMode/ToggoeButton.tsx
@@ -0,0 +1,34 @@
+import Image from 'next/image';
+import { useRecoilState } from 'recoil';
+
+import { themeState } from '@/app/globalAtom';
+
+import darkModeImage from '../../../public/images/darkmode.webp';
+import lightModeImage from '../../../public/images/lightmode.webp';
+
+export default function ToggleDarkModeButton() {
+ const [currentTheme, setCurrentTheme] = useRecoilState(themeState);
+ const modeImageSrc =
+ currentTheme === 'light' ? darkModeImage : lightModeImage;
+
+ const handleClickToggleImage = () => {
+ if (currentTheme === 'light') {
+ setCurrentTheme('dark');
+ return;
+ }
+ setCurrentTheme('light');
+ };
+ return (
+
+ );
+}
diff --git a/src copy/Component/GA/GA.tsx b/src copy/Component/GA/GA.tsx
new file mode 100644
index 0000000..f9b261d
--- /dev/null
+++ b/src copy/Component/GA/GA.tsx
@@ -0,0 +1,37 @@
+'use client';
+import Script from 'next/script';
+
+const GoogleAnalytics = ({
+ GA_MEASUREMENT_ID,
+}: {
+ GA_MEASUREMENT_ID: string;
+}) => {
+ return (
+ <>
+
+
+ >
+ );
+};
+export default GoogleAnalytics;
diff --git a/src copy/Component/Giscus/Gitcus.tsx b/src copy/Component/Giscus/Gitcus.tsx
new file mode 100644
index 0000000..609780d
--- /dev/null
+++ b/src copy/Component/Giscus/Gitcus.tsx
@@ -0,0 +1,22 @@
+'use client';
+
+import dynamic from 'next/dynamic';
+const DynamicGiscus = dynamic(() => import('@giscus/react'));
+
+export default function Comments() {
+ return (
+
+ );
+}
diff --git a/src copy/Component/GuestBook/GuestBook.tsx b/src copy/Component/GuestBook/GuestBook.tsx
new file mode 100644
index 0000000..f0a1f91
--- /dev/null
+++ b/src copy/Component/GuestBook/GuestBook.tsx
@@ -0,0 +1,36 @@
+'use client';
+
+import styled from 'styled-components';
+
+import { Input, InputBox } from '@/Component/Input';
+import useGetGuestBook from '@/hooks/useGetGuestBook';
+import useInput from '@/hooks/useInput';
+import usePostGuestBook from '@/hooks/usePostGuestBook';
+
+const GuestBookList = styled.section`
+ min-height: 700px;
+`;
+
+export default function GuestBook() {
+ const guestBookInput = useInput('', (e) => e.target.value.length <= 150);
+ const { data } = useGetGuestBook();
+ const { mutate } = usePostGuestBook();
+
+ const handleSubmitGuestBook = () => {
+ mutate({
+ comment: guestBookInput.value,
+ });
+ };
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src copy/Component/Input/Input.tsx b/src copy/Component/Input/Input.tsx
new file mode 100644
index 0000000..9000628
--- /dev/null
+++ b/src copy/Component/Input/Input.tsx
@@ -0,0 +1,17 @@
+import styled from 'styled-components';
+
+const Input = styled.input<{ textAlign?: string }>`
+ width: 100%;
+ height: 100%;
+
+ border: none;
+ background: none;
+
+ font-weight: 600;
+ font-size: 18px;
+
+ outline: none;
+ text-align: ${({ textAlign }) => textAlign ?? 'center'};
+`;
+
+export default Input;
diff --git a/src copy/Component/Input/InputBox.tsx b/src copy/Component/Input/InputBox.tsx
new file mode 100644
index 0000000..15f50ef
--- /dev/null
+++ b/src copy/Component/Input/InputBox.tsx
@@ -0,0 +1,21 @@
+import styled from 'styled-components';
+
+const InputBox = styled.div<{
+ width?: string;
+ isError?: boolean;
+ color?: string;
+}>`
+ display: flex;
+ height: 45px;
+ width: ${({ width }) => width ?? '100%'};
+
+ padding: 12px;
+
+ box-sizing: border-box;
+ background: ${({ color }) => color ?? 'gray'};
+
+ border: 1px solid ${({ isError }) => (isError ? '#ec2f1b' : '#ecebf1')};
+ border-radius: 7px;
+`;
+
+export default InputBox;
diff --git a/src copy/Component/Input/index.tsx b/src copy/Component/Input/index.tsx
new file mode 100644
index 0000000..646da70
--- /dev/null
+++ b/src copy/Component/Input/index.tsx
@@ -0,0 +1,4 @@
+import Input from './Input';
+import InputBox from './InputBox';
+
+export { Input, InputBox };
diff --git a/src copy/Component/Notion/NotionresumeClient.tsx b/src copy/Component/Notion/NotionresumeClient.tsx
new file mode 100644
index 0000000..a2749f1
--- /dev/null
+++ b/src copy/Component/Notion/NotionresumeClient.tsx
@@ -0,0 +1,40 @@
+'use client';
+
+import 'react-notion-x/src/styles.css';
+
+import 'prismjs/themes/prism-tomorrow.css';
+import type { ReturnTypeofNotionRecord } from '@/app/(root)/(routes)/notion/resume/page';
+
+import { NotionRenderer } from 'react-notion-x';
+
+import { Collection } from 'react-notion-x/build/third-party/collection';
+import { Modal } from 'react-notion-x/build/third-party/modal';
+import { useRecoilState } from 'recoil';
+
+import { themeState } from '@/app/globalAtom';
+
+type PromiseType> = T extends Promise
+ ? U
+ : never;
+
+export default function ResumeClient({
+ recordMap,
+}: {
+ recordMap: PromiseType;
+}) {
+ const [modeState] = useRecoilState(themeState);
+ return (
+
+
+
+ );
+}
diff --git a/src copy/Component/Post/PostItem.tsx b/src copy/Component/Post/PostItem.tsx
new file mode 100644
index 0000000..01d791d
--- /dev/null
+++ b/src copy/Component/Post/PostItem.tsx
@@ -0,0 +1,40 @@
+'use client';
+
+import Image from 'next/image';
+import Link from 'next/link';
+
+import Title from '@/Component/About/Title';
+import PostLayout from '@/Component/Common/PostLayout';
+
+
+export default function PostItem({
+ post,
+}: {
+ post: { [key: string]: string };
+}) {
+ return (
+
+
+
+
+ {post.date}
+
+ {post.content}
+
+
+
+ );
+}
diff --git a/src copy/Component/Post/PostList.tsx b/src copy/Component/Post/PostList.tsx
new file mode 100644
index 0000000..1b23c87
--- /dev/null
+++ b/src copy/Component/Post/PostList.tsx
@@ -0,0 +1,24 @@
+'use client';
+
+import useObserver from '@/hooks/useObserver';
+import usePostQuery from '@/hooks/usePostQuery';
+
+import PostItem from './PostItem';
+
+export default function PostServiceLayer() {
+ const { data, fetchNextPage, hasNextPage } = usePostQuery();
+ const { target } = useObserver({
+ threshold: 0.1,
+ hasNextPage,
+ fetchNextPage,
+ });
+
+ return (
+ <>
+ {data?.pages?.map((page) =>
+ page.posts.map((post) => )
+ )}
+
+ >
+ );
+}
diff --git a/src copy/Component/TOC/index.tsx b/src copy/Component/TOC/index.tsx
new file mode 100644
index 0000000..7935cc0
--- /dev/null
+++ b/src copy/Component/TOC/index.tsx
@@ -0,0 +1,58 @@
+'use client';
+
+import { uuid4 } from '@sentry/utils';
+import styled from 'styled-components';
+
+import replaceStrWithBlank from '../../../lib/replaceStr';
+
+const StyledTOCList = styled.ul`
+ position: fixed;
+ max-width: 235px;
+ height: auto;
+ max-height: 75vh;
+ word-wrap: break-word;
+ text-align: justify;
+ width: 235px;
+ margin-top: 100px;
+ right: 0;
+ opacity: 1;
+ font-size: 15px;
+ overflow-y: scroll;
+
+ @media ${({ theme }) => theme.device.laptop} {
+ opacity: 0;
+ }
+
+ @media ${({ theme }) => theme.device.tablet} {
+ opacity: 0;
+ }
+
+ @media ${({ theme }) => theme.device.mobile} {
+ opacity: 0;
+ }
+`;
+
+const StyledTOCLink = styled.a`
+ &:hover {
+ color: rgb(0, 131, 120);
+ }
+`;
+
+export default function TOC({ toc }: { toc: string[] }) {
+ const TOC = toc.map((eachToc) => {
+ const makeTOC = replaceStrWithBlank([eachToc, ['#', '##', '###', '####']]);
+ return (
+ <>
+
+
+
+ {makeTOC}
+
+
+
+ >
+ );
+ });
+
+ return {TOC};
+}
diff --git a/src copy/app/(root)/(routes)/(home)/page.tsx b/src copy/app/(root)/(routes)/(home)/page.tsx
new file mode 100644
index 0000000..ea99ff4
--- /dev/null
+++ b/src copy/app/(root)/(routes)/(home)/page.tsx
@@ -0,0 +1,35 @@
+import CategoryList from '@/Component/CategoryList/CategoryList';
+import PostItem from '@/Component/Post/PostItem';
+import PostList from '@/Component/Post/PostList';
+
+import { getAllCategories, getInitPosts } from '../../../../../lib/api';
+
+export default function Home() {
+ const allCategory = getAllCategories();
+ const initPosts = getInitPosts([
+ 'title',
+ 'data',
+ 'slug',
+ 'category',
+ 'excerpt',
+ 'date',
+ 'image',
+ ]);
+
+ return (
+ <>
+
+
+ {initPosts.map((post) => (
+
+ ))}
+
+
+
+ >
+ );
+}
diff --git a/src copy/app/(root)/(routes)/about/page.tsx b/src copy/app/(root)/(routes)/about/page.tsx
new file mode 100644
index 0000000..c8fec99
--- /dev/null
+++ b/src copy/app/(root)/(routes)/about/page.tsx
@@ -0,0 +1,46 @@
+'use client';
+
+import type AboutProps from '@/@types/AboutProps';
+
+import styled from 'styled-components';
+
+import Content from '@/Component/About/Content';
+import ProfileImageWrapper from '@/Component/About/ProfileImgWrapper';
+import Title from '@/Component/About/Title';
+
+const AboutContent: AboutProps = {
+ title: '안녕하세요! 항상 팀에 기여하고 싶은 개발자 김효중입니다',
+ imgurl: '/images/Profile.jpg',
+ content: [
+ '새로운 기술이 왜 등장했고, 어떤 문제를 해결하는 지 공부하는 것에 즐거움을 느낍니다',
+ '몰입할 수 있는 환경에서 개발하는 것을 즐깁니다',
+ '기록하면서 공부할 때 가장 성취감을 느낍니다.항상 제가 배운 내용을 기록하고자 합니다.',
+ '항상 소속된 팀에서 작은 것들이라도 기여하는 사람이 되고 싶습니다',
+ ],
+};
+
+const StyledContentLayOut = styled.article`
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ gap: 25px;
+ flex-wrap: wrap;
+`;
+
+export default function About() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/src copy/app/(root)/(routes)/blog/[slug]/page.tsx b/src copy/app/(root)/(routes)/blog/[slug]/page.tsx
new file mode 100644
index 0000000..2c25e7e
--- /dev/null
+++ b/src copy/app/(root)/(routes)/blog/[slug]/page.tsx
@@ -0,0 +1,132 @@
+import { Metadata } from 'next';
+
+import ReactMarkdown from 'react-markdown';
+
+import Image from 'next/image';
+import rehypeRaw from 'rehype-raw';
+
+import CodeBlock from '@/Component/Blog/CodeBlock';
+import PostExterct from '@/Component/Blog/Exterct';
+import BlogLayOut from '@/Component/Blog/LayOut';
+import Comments from '@/Component/Giscus/Gitcus';
+import TOC from '@/Component/TOC';
+
+import { getPostBySlug } from '../../../../../../lib/api';
+import makeToc from '../../../../../../lib/makeToc';
+
+export async function generateMetadata({
+ params,
+}: {
+ params: {
+ slug: string;
+ };
+}): Promise {
+ const post = getPostBySlug(params.slug, [
+ 'title',
+ 'content',
+ 'excerpt',
+ 'date',
+ 'author',
+ 'image',
+ ]);
+
+ const dynamicMetaTag: Metadata = {
+ title: post.title,
+ description: post.excerpt,
+ openGraph: {
+ images: [
+ {
+ url: `${post.image}`,
+ width: 800,
+ height: 600,
+ alt: '블로그 대표 이미지',
+ },
+ ],
+ type: 'website',
+ siteName: `${post.title}`,
+ locale: 'ko-KR',
+ },
+ keywords: post.title,
+
+ robots: {
+ index: true,
+ follow: true,
+ googleBot: {
+ index: true,
+ follow: true,
+ },
+ },
+ viewport: {
+ width: 'device-width',
+ initialScale: 1,
+ },
+ verification: {
+ google: 'g3Daim29whdK1ZzL1CE6pvkYyvSgM5-6C898-TVjiz0',
+ },
+ };
+ return dynamicMetaTag;
+}
+
+export default function Post({
+ params,
+}: {
+ params: {
+ slug: string;
+ };
+}) {
+ const post = getPostBySlug(decodeURIComponent(params.slug), [
+ 'title',
+ 'content',
+ 'excerpt',
+ 'date',
+ 'author',
+ 'image',
+ ]);
+
+ return (
+ <>
+
+ {post.title}
+
+
+ {post.date}
+
+
+ (
+
+ ),
+ code: ({ children }) => {children as string},
+ h2: ({ children }) => {
+ return {children}
;
+ },
+ h3: ({ children }) => {
+ return {children}
;
+ },
+ }}
+ >
+ {post.content}
+
+
+
+
+
+ >
+ );
+}
diff --git a/src copy/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx b/src copy/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx
new file mode 100644
index 0000000..7664713
--- /dev/null
+++ b/src copy/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx
@@ -0,0 +1,101 @@
+import { Metadata } from 'next';
+
+import ReactMarkdown from 'react-markdown';
+
+import rehypeRaw from 'rehype-raw';
+
+import CodeBlock from '@/Component/Blog/CodeBlock';
+import PostExterct from '@/Component/Blog/Exterct';
+import BlogLayOut from '@/Component/Blog/LayOut';
+import Comments from '@/Component/Giscus/Gitcus';
+
+import { getPostBySlug } from '../../../../../../../lib/api';
+
+export default function Post({
+ params,
+}: {
+ params: {
+ categoryId: string;
+ slug: string;
+ };
+}) {
+ const post = getPostBySlug(decodeURIComponent(params.slug), [
+ 'title',
+ 'content',
+ 'excerpt',
+ 'date',
+ 'author',
+ 'image',
+ ]);
+
+ const dynamicMetaData: Metadata = {
+ title: post.title,
+ description: post.excerpt,
+ keywords: post.title,
+ openGraph: {
+ images: [
+ {
+ url: `${post.image}`,
+ width: 800,
+ height: 600,
+ },
+ ],
+ },
+
+ robots: {
+ index: true,
+ follow: true,
+ googleBot: {
+ index: true,
+ follow: true,
+ },
+ },
+ viewport: {
+ width: 'device-width',
+ initialScale: 1,
+ },
+ verification: {
+ google: 'g3Daim29whdK1ZzL1CE6pvkYyvSgM5-6C898-TVjiz0',
+ },
+ };
+
+ return (
+ <>
+
+ {post.title}
+
+
+ {post.date}
+
+
+ (
+
+ ),
+ code: ({ node, inline, children, ...props }) => (
+ {children as string}
+ ),
+ }}
+ >
+ {post.content}
+
+
+
+ >
+ );
+}
diff --git a/src copy/app/(root)/(routes)/category/[categoryId]/page.tsx b/src copy/app/(root)/(routes)/category/[categoryId]/page.tsx
new file mode 100644
index 0000000..a5b70ed
--- /dev/null
+++ b/src copy/app/(root)/(routes)/category/[categoryId]/page.tsx
@@ -0,0 +1,63 @@
+import Image from 'next/image';
+import Link from 'next/link';
+
+import Title from '@/Component/About/Title';
+import PostLayout from '@/Component/Common/PostLayout';
+
+import { getCategoryFilteredPosts } from '../../../../../../lib/api';
+
+export default function Home({
+ params,
+}: {
+ params: {
+ categoryId: string;
+ };
+}) {
+ params.categoryId = decodeURIComponent(params.categoryId);
+
+ const posts = getCategoryFilteredPosts(
+ ['title', 'data', 'slug', 'category', 'excerpt', 'date', 'image'],
+ params.categoryId
+ );
+
+ return (
+ <>
+ {params.categoryId}
+
+ {posts.map((post) => (
+
+
+
+ {post.category}
+ {post.content}
+
+
+
+
+
+
+ ))}
+
+ >
+ );
+}
diff --git a/src copy/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx b/src copy/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx
new file mode 100644
index 0000000..b359422
--- /dev/null
+++ b/src copy/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx
@@ -0,0 +1,16 @@
+import { dehydrate, Hydrate } from '@tanstack/react-query';
+
+import GuestBook from '@/Component/GuestBook/GuestBook';
+import { getGuestBook } from '@/hooks/useGetGuestBook';
+import getQueryClient from '@/utils/getQueryClient';
+
+export default async function HydratedGuestBook() {
+ const queryClient = getQueryClient();
+ await queryClient.prefetchQuery(['guestBook'], getGuestBook);
+ const dehydratedState = dehydrate(queryClient);
+ return (
+
+
+
+ );
+}
diff --git a/src copy/app/(root)/(routes)/guestbook/page.tsx b/src copy/app/(root)/(routes)/guestbook/page.tsx
new file mode 100644
index 0000000..e8e3df5
--- /dev/null
+++ b/src copy/app/(root)/(routes)/guestbook/page.tsx
@@ -0,0 +1,5 @@
+import HydratedGuestBook from './HydratedGuestBook';
+
+export default function GuestBookPage() {
+ return ;
+}
diff --git a/src copy/app/(root)/(routes)/notion/page.tsx b/src copy/app/(root)/(routes)/notion/page.tsx
new file mode 100644
index 0000000..3de8153
--- /dev/null
+++ b/src copy/app/(root)/(routes)/notion/page.tsx
@@ -0,0 +1,27 @@
+import Link from 'next/link';
+export default function allPage() {
+ const allNotionPage = [
+ {
+ name: '주하님',
+ id: '68507fb471144ed6b86a92c8a51284b7',
+ },
+ {
+ name: '효리님',
+ id: '9c13c482e5da4cf984d929f4234c3b59',
+ },
+ {
+ name: '효중님',
+ id: 'f6a44883acb343feb2f3c32d602609b2',
+ },
+ ] as const;
+ return (
+
+ 동근동근팀
+ {allNotionPage.map((page) => (
+
+ {page.name}
+
+ ))}
+
+ );
+}
diff --git a/src copy/app/(root)/(routes)/notion/resume/page.tsx b/src copy/app/(root)/(routes)/notion/resume/page.tsx
new file mode 100644
index 0000000..5c96449
--- /dev/null
+++ b/src copy/app/(root)/(routes)/notion/resume/page.tsx
@@ -0,0 +1,12 @@
+import { NotionAPI } from 'notion-client';
+
+import ResumeClient from '@/Component/Notion/NotionresumeClient';
+export type ReturnTypeofNotionRecord = ReturnType;
+const notion = new NotionAPI();
+export default async function ResumePage() {
+ const recordMap = await notion.getPage(
+ `https://notion-api.splitbee.io/v1/page/${process.env.NOTION_PAGE_ID}`
+ );
+
+ return ;
+}
diff --git a/src copy/app/(root)/layout.tsx b/src copy/app/(root)/layout.tsx
new file mode 100644
index 0000000..94ee905
--- /dev/null
+++ b/src copy/app/(root)/layout.tsx
@@ -0,0 +1,96 @@
+import '../globals.css';
+import { Metadata } from 'next';
+
+import { Lora } from 'next/font/google';
+import Head from 'next/head';
+
+import Providers from '@/app/queryClientProvider';
+import Footer from '@/Component/Common/Footer';
+import Navbar from '@/Component/Common/Navbar';
+import GoogleAnalytics from '@/Component/GA/GA';
+import GlobalStyle from '@/style/globalStyle';
+
+import Recoil from '../Recoil';
+import { ThemeWrapper } from '../themeWrapper';
+
+declare global {
+ interface Window {
+ naver: any;
+ }
+}
+
+const baseFont = Lora({
+ subsets: ['latin'],
+ preload: true,
+ display: 'swap',
+ weight: '400',
+});
+
+export const metadata: Metadata = {
+ title: 'HJ`s Blog',
+ description: '개발관련 여러 지식을 기록하는 공간',
+ icons: {
+ icon: '/images/favicon.webp',
+ },
+ metadataBase: new URL('https://hj-devlog.vercel.app/'),
+ openGraph: {
+ url: 'https://hj-devlog.vercel.app',
+ title: 'HJ`s Blog',
+ description: '개발 관련 여러 지식을 기록하고, 정리하는 공간입니다',
+ images: [
+ {
+ url: 'https://avatars.githubusercontent.com/u/59411107?v=4',
+ width: 800,
+ height: 600,
+ },
+ {
+ url: 'https://avatars.githubusercontent.com/u/59411107?v=4',
+ width: 1800,
+ height: 1600,
+ alt: 'My custom alt',
+ },
+ ],
+ type: 'website',
+ },
+ robots: {
+ index: true,
+ follow: true,
+ googleBot: {
+ index: true,
+ follow: true,
+ },
+ },
+ viewport: {
+ width: 'device-width',
+ initialScale: 1,
+ },
+ verification: {
+ google: 'g3Daim29whdK1ZzL1CE6pvkYyvSgM5-6C898-TVjiz0',
+ },
+};
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+ );
+}
diff --git a/src copy/app/(root)/not-found.tsx b/src copy/app/(root)/not-found.tsx
new file mode 100644
index 0000000..f5af2c9
--- /dev/null
+++ b/src copy/app/(root)/not-found.tsx
@@ -0,0 +1,3 @@
+export default function NotFound() {
+ return 404
;
+}
diff --git a/src copy/app/Recoil.tsx b/src copy/app/Recoil.tsx
new file mode 100644
index 0000000..aef56e0
--- /dev/null
+++ b/src copy/app/Recoil.tsx
@@ -0,0 +1,7 @@
+'use client';
+
+import { RecoilRoot } from 'recoil';
+
+export default function Recoil({ children }: { children: React.ReactNode }) {
+ return {children};
+}
diff --git a/src copy/app/api/guestbook/route.ts b/src copy/app/api/guestbook/route.ts
new file mode 100644
index 0000000..7a26530
--- /dev/null
+++ b/src copy/app/api/guestbook/route.ts
@@ -0,0 +1,58 @@
+import { ref, get, push } from 'firebase/database';
+import { NextResponse, NextRequest } from 'next/server';
+
+import { DB } from '../../firebase';
+
+export async function GET() {
+ const guestbookRef = ref(DB, '/guestbook');
+ const snapShot = await get(guestbookRef);
+
+ if (snapShot.exists()) {
+ return NextResponse.json(
+ {
+ guestbook: await snapShot.val().comments,
+ },
+ {
+ status: 200,
+ }
+ );
+ }
+
+ return new Response(
+ JSON.stringify({
+ error: 'firebase 설정 에러',
+ }),
+ {
+ status: 502,
+ }
+ );
+}
+
+export async function POST(req: NextRequest) {
+ const body = await req.json();
+
+ await push(ref(DB, '/guestbook'), {
+ comment: body.comment,
+ commentTime: body.time,
+ })
+ .then((val) => {
+ return NextResponse.json(
+ {
+ guestBook: val,
+ },
+ {
+ status: 200,
+ }
+ );
+ })
+ .catch((e) => {
+ return NextResponse.json(
+ {
+ e,
+ },
+ {
+ status: 502,
+ }
+ );
+ });
+}
diff --git a/src copy/app/api/slugs/[id]/route.ts b/src copy/app/api/slugs/[id]/route.ts
new file mode 100644
index 0000000..362751c
--- /dev/null
+++ b/src copy/app/api/slugs/[id]/route.ts
@@ -0,0 +1,29 @@
+import { getAllPosts } from '../../../../../lib/api';
+
+export async function GET(
+ req: Request,
+ {
+ params,
+ }: {
+ params: {
+ id: string;
+ };
+ }
+) {
+ const allPosts = getAllPosts([
+ 'title',
+ 'data',
+ 'slug',
+ 'category',
+ 'excerpt',
+ 'date',
+ 'image',
+ ]).filter((posts) => posts.slug === params.id);
+
+ return new Response(JSON.stringify(allPosts), {
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+}
diff --git a/src copy/app/api/slugs/route.ts b/src copy/app/api/slugs/route.ts
new file mode 100644
index 0000000..d141000
--- /dev/null
+++ b/src copy/app/api/slugs/route.ts
@@ -0,0 +1,19 @@
+import { getAllPosts } from '../../../../lib/api';
+
+export async function GET(req: Request) {
+ const allPosts = getAllPosts([
+ 'title',
+ 'data',
+ 'slug',
+ 'category',
+ 'excerpt',
+ 'date',
+ 'image',
+ ]);
+ return new Response(JSON.stringify(allPosts), {
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+}
diff --git a/src copy/app/firebase.js b/src copy/app/firebase.js
new file mode 100644
index 0000000..75ed49d
--- /dev/null
+++ b/src copy/app/firebase.js
@@ -0,0 +1,17 @@
+import { initializeApp } from 'firebase/app';
+import { getDatabase } from 'firebase/database';
+
+const firebaseConfig = {
+ apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
+ authDomain: process.env.NEXT_PUBLIC_FIREWAREBASE_AUTH_DOMAIN,
+ projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
+ storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
+ messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
+ appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
+ measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
+ databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE,
+};
+
+const app = initializeApp(firebaseConfig);
+const DB = getDatabase(app);
+export { app, DB };
diff --git a/src copy/app/globalAtom.ts b/src copy/app/globalAtom.ts
new file mode 100644
index 0000000..f578aae
--- /dev/null
+++ b/src copy/app/globalAtom.ts
@@ -0,0 +1,20 @@
+import type { ThemeType } from '@/@types/ThemeType';
+
+import { atom } from 'recoil';
+
+import POST_CONSTANT from '@/constants/POST';
+
+export const themeState = atom({
+ key: 'THEME_STATE',
+ default: 'light',
+});
+
+export const postSearchModalState = atom({
+ key: 'POST_SEARCH_MODAL_STATE',
+ default: false,
+});
+
+export const postCurPage = atom({
+ key: 'POST_PAGE_STATE',
+ default: POST_CONSTANT.end,
+});
diff --git a/src copy/app/globals.css b/src copy/app/globals.css
new file mode 100644
index 0000000..d1a9c98
--- /dev/null
+++ b/src copy/app/globals.css
@@ -0,0 +1,25 @@
+footer {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin: 5px auto;
+}
+.tocItem:hover {
+ color: rgb(0, 131, 120);
+}
+.active {
+ color: rgb(0, 131, 120);
+}
+main {
+ font-family: inherit;
+ min-height: calc(100vh - 120px);
+}
+
+ul,
+li {
+ list-style-type: none;
+}
+
+.TOC {
+ text-shadow: -0.01em -0.01em 0.01em #000;
+}
diff --git a/src copy/app/queryClientProvider.tsx b/src copy/app/queryClientProvider.tsx
new file mode 100644
index 0000000..3967ff4
--- /dev/null
+++ b/src copy/app/queryClientProvider.tsx
@@ -0,0 +1,28 @@
+// Provider.tsx
+
+'use client';
+
+import React from 'react';
+
+import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
+
+type Props = {
+ children: React.ReactNode;
+};
+
+function Providers({ children }: Props) {
+ const [client] = React.useState(
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ refetchOnWindowFocus: false,
+ retry: false,
+ },
+ },
+ })
+ );
+
+ return {children};
+}
+
+export default Providers;
diff --git a/src copy/app/registry.tsx b/src copy/app/registry.tsx
new file mode 100644
index 0000000..f7f08ae
--- /dev/null
+++ b/src copy/app/registry.tsx
@@ -0,0 +1,28 @@
+'use client';
+
+import { useState } from 'react';
+
+import { useServerInsertedHTML } from 'next/navigation';
+import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
+
+export default function StyledComponentsRegistry({
+ children,
+}: {
+ children: React.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 copy/app/sitemap.ts b/src copy/app/sitemap.ts
new file mode 100644
index 0000000..66a93ca
--- /dev/null
+++ b/src copy/app/sitemap.ts
@@ -0,0 +1,27 @@
+import { MetadataRoute } from 'next';
+
+import { getAllPosts } from '../../lib/api';
+
+export default function sitemap(): MetadataRoute.Sitemap {
+ const SITE_SUFFIX = 'https://hj-devlog.vercel.app';
+ const posts = getAllPosts(['title', 'date']);
+
+ const postsSiteMap = posts.map((post) => {
+ return {
+ url: `${SITE_SUFFIX}/blog/${post.title}`,
+ lastModified: new Date(post.date),
+ };
+ });
+
+ return [
+ {
+ url: `${SITE_SUFFIX}`,
+ lastModified: new Date(),
+ },
+ {
+ url: `${SITE_SUFFIX}/about`,
+ lastModified: new Date(),
+ },
+ ...postsSiteMap,
+ ];
+}
diff --git a/src copy/app/sitemap.xml.tsx b/src copy/app/sitemap.xml.tsx
new file mode 100644
index 0000000..d3b7807
--- /dev/null
+++ b/src copy/app/sitemap.xml.tsx
@@ -0,0 +1 @@
+import { GetServerSidePropsContext } from 'next';
diff --git a/src copy/app/themeWrapper.tsx b/src copy/app/themeWrapper.tsx
new file mode 100644
index 0000000..3578c49
--- /dev/null
+++ b/src copy/app/themeWrapper.tsx
@@ -0,0 +1,22 @@
+'use client';
+
+import { useRecoilState } from 'recoil';
+import { ThemeProvider } from 'styled-components';
+
+import { darkTheme, lightTheme } from '@/style/theme/darkMode';
+import mediatheme from '@/style/theme/media';
+
+import { themeState } from './globalAtom';
+import StyledComponentsRegistry from './registry';
+
+
+export function ThemeWrapper({ children }: { children: React.ReactNode }) {
+ const [currentTheme, setCurrentTheme] = useRecoilState(themeState);
+ const themeObj = currentTheme === 'light' ? lightTheme : darkTheme;
+ const theme = { ...themeObj, ...mediatheme };
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src copy/constants/POST.ts b/src copy/constants/POST.ts
new file mode 100644
index 0000000..5d85a95
--- /dev/null
+++ b/src copy/constants/POST.ts
@@ -0,0 +1,7 @@
+const POST_CONSTANT = {
+ start: 5,
+ end: 5,
+};
+
+Object.freeze(POST_CONSTANT);
+export default POST_CONSTANT;
diff --git a/src copy/hooks/useGetGuestBook.ts b/src copy/hooks/useGetGuestBook.ts
new file mode 100644
index 0000000..456c48c
--- /dev/null
+++ b/src copy/hooks/useGetGuestBook.ts
@@ -0,0 +1,20 @@
+import type { GuestBook } from '@/@types/GuestBookType';
+
+import { useQuery } from '@tanstack/react-query';
+
+export const getGuestBook = async () => {
+ const guestBook = await fetch('/api/guestbook', {
+ cache: 'no-store',
+ });
+ return guestBook.json() as Promise;
+};
+
+export default function useGetGuestBook() {
+ return useQuery({
+ queryFn: getGuestBook,
+ queryKey: ['guestBook'],
+ refetchInterval: false,
+ retry: 0,
+ refetchOnMount: false,
+ });
+}
diff --git a/src copy/hooks/useInput.ts b/src copy/hooks/useInput.ts
new file mode 100644
index 0000000..96f8a6f
--- /dev/null
+++ b/src copy/hooks/useInput.ts
@@ -0,0 +1,21 @@
+import React, { useState } from 'react';
+
+const useInput = (
+ initialState: string,
+ prevConditionCallback: (e: React.ChangeEvent) => boolean
+) => {
+ const [value, setValue] = useState(initialState);
+
+ const onChange = (e: React.ChangeEvent) => {
+ if (!prevConditionCallback(e)) return;
+
+ setValue(e.target.value);
+ };
+
+ return {
+ value,
+ onChange,
+ };
+};
+
+export default useInput;
diff --git a/src copy/hooks/useNaverInit.ts b/src copy/hooks/useNaverInit.ts
new file mode 100644
index 0000000..6c45e61
--- /dev/null
+++ b/src copy/hooks/useNaverInit.ts
@@ -0,0 +1,29 @@
+'use client';
+
+import { useCallback, useEffect } from 'react';
+
+import { Environment } from '@/utils/envorinments';
+
+const useNaverInit = () => {
+ const handleNaverInit = useCallback(() => {
+ const naver = window.naver;
+
+ const naverLogin = new naver.LoginWithNaverId({
+ clientId: Environment.naverID, //ClientID
+ callbackUrl: Environment.naverCallback,
+ callbackHandle: true,
+ isPopup: false, // 팝업 형태로 인증 여부
+ loginButton: {
+ color: 'green', // 색상
+ type: 1, // 버튼 크기
+ height: '60', // 버튼 높이
+ }, // 로그인 버튼 설정
+ });
+ }, []);
+
+ useEffect(() => {
+ handleNaverInit();
+ }, [handleNaverInit]);
+};
+
+export default useNaverInit;
diff --git a/src copy/hooks/useObserver.ts b/src copy/hooks/useObserver.ts
new file mode 100644
index 0000000..fe00f87
--- /dev/null
+++ b/src copy/hooks/useObserver.ts
@@ -0,0 +1,42 @@
+import { useEffect, useRef } from 'react';
+
+import { InfiniteQueryObserverResult } from '@tanstack/react-query';
+
+type observerProps = {
+ threshold: number;
+ hasNextPage: boolean | undefined;
+ fetchNextPage: () => Promise;
+};
+
+export default function useObserver({
+ threshold = 0.1,
+ hasNextPage,
+ fetchNextPage,
+}: observerProps) {
+ const target = useRef(null);
+
+ const observerCallback: IntersectionObserverCallback = (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting && hasNextPage) {
+ fetchNextPage();
+ }
+ });
+ };
+
+ useEffect(() => {
+ if (!target || !target.current) {
+ return;
+ }
+
+ const observer = new IntersectionObserver(observerCallback, {
+ threshold,
+ });
+ observer.observe(target?.current);
+ return () => observer.disconnect();
+ }),
+ [target, threshold, observerCallback];
+
+ return {
+ target,
+ };
+}
diff --git a/src copy/hooks/usePostGuestBook.ts b/src copy/hooks/usePostGuestBook.ts
new file mode 100644
index 0000000..cf87dc1
--- /dev/null
+++ b/src copy/hooks/usePostGuestBook.ts
@@ -0,0 +1,17 @@
+import { useMutation } from '@tanstack/react-query';
+
+import { post } from '@/utils/axiosClient';
+export const postGuestBook = async ({ comment }: { comment: string }) => {
+ return await post('api/guestbook', {
+ comment,
+ time: new Date().toUTCString(),
+ });
+};
+
+const usePostGuestBook = () => {
+ return useMutation({
+ mutationFn: postGuestBook,
+ });
+};
+
+export default usePostGuestBook;
diff --git a/src copy/hooks/usePostQuery.ts b/src copy/hooks/usePostQuery.ts
new file mode 100644
index 0000000..96297b8
--- /dev/null
+++ b/src copy/hooks/usePostQuery.ts
@@ -0,0 +1,40 @@
+import { useInfiniteQuery } from '@tanstack/react-query';
+
+import { get } from '@/utils/axiosClient';
+
+type Post = {
+ title: string;
+ date: string;
+ slug: string;
+ category: string;
+ excerpt: string;
+ image: string;
+};
+
+type PageParams = {
+ start: number;
+ end: number;
+};
+
+type PostsResponse = {
+ posts: Post[];
+ page: number;
+};
+
+const getPosts = async ({ pageParams }: { pageParams: number }) => {
+ const start = pageParams * 5;
+ const end = start + 5;
+ return (await get(`/api/posts?start=${start}&end=${end}`))
+ .data;
+};
+
+export default function usePostQuery() {
+ return useInfiniteQuery({
+ queryKey: ['getPosts'],
+ queryFn: ({ pageParam = 1 }) => getPosts({ pageParams: pageParam }),
+ getNextPageParam: (lastPage) => {
+ const nextPage = Math.floor(lastPage.page);
+ return nextPage + 1;
+ },
+ });
+}
diff --git a/src copy/hooks/useScroll.tsx b/src copy/hooks/useScroll.tsx
new file mode 100644
index 0000000..75aacae
--- /dev/null
+++ b/src copy/hooks/useScroll.tsx
@@ -0,0 +1,34 @@
+import { useState, useEffect } from 'react';
+
+export default function useScroll({
+ target,
+ options,
+}: {
+ target: HTMLElement | null | HTMLElement[];
+ options: IntersectionObserverInit;
+}) {
+ const [isFetching, setIsFetching] = useState(false);
+ const intersectionCallback = (entries: IntersectionObserverEntry[]) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ setIsFetching(true);
+ }
+ });
+ };
+
+ useEffect(() => {
+ let observer = new IntersectionObserver(intersectionCallback, options);
+ if (target instanceof HTMLElement) {
+ observer.observe(target);
+ return;
+ }
+
+ if (target instanceof Array) {
+ target.forEach((eachTarget) => {
+ observer.observe(eachTarget);
+ });
+ }
+ }, [options, target]);
+
+ return { isFetching };
+}
diff --git a/src copy/hooks/useSearchPost.tsx b/src copy/hooks/useSearchPost.tsx
new file mode 100644
index 0000000..4a90a33
--- /dev/null
+++ b/src copy/hooks/useSearchPost.tsx
@@ -0,0 +1,38 @@
+import { useState, useEffect } from 'react';
+
+type Item = {
+ [key: string]: string;
+};
+
+export default function useSearchPost(searchInput: string) {
+ const [posts, setPosts] = useState- ([]);
+
+ useEffect(() => {
+ const debounceHandler = setTimeout(() => {
+ fetchAllPosts();
+ }, 1000);
+
+ const fetchAllPosts = async () => {
+ const allPostResponse = await fetch('/api/slugs');
+ const allPosts: Item[] = await allPostResponse.json();
+ if (allPostResponse.ok) {
+ setPosts(
+ allPosts.filter((post) =>
+ post.title
+ .trim()
+ .toLowerCase()
+ .includes(searchInput.trim().toLowerCase())
+ )
+ );
+ }
+ };
+
+ return () => {
+ clearTimeout(debounceHandler);
+ };
+ }, [searchInput]);
+
+ return {
+ posts,
+ };
+}
diff --git a/src copy/hooks/useTheme.ts b/src copy/hooks/useTheme.ts
new file mode 100644
index 0000000..722204c
--- /dev/null
+++ b/src copy/hooks/useTheme.ts
@@ -0,0 +1,13 @@
+import { useState } from 'react';
+
+export default function useTheme() {
+ const [theme, setTheme] = useState<'light' | 'dark'>('light');
+ const onChangeTheme = () => {
+ setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
+ };
+
+ return {
+ theme,
+ onChangeTheme,
+ };
+}
diff --git a/src copy/style/globalStyle.tsx b/src copy/style/globalStyle.tsx
new file mode 100644
index 0000000..409afb9
--- /dev/null
+++ b/src copy/style/globalStyle.tsx
@@ -0,0 +1,48 @@
+'use client';
+import { createGlobalStyle } from 'styled-components';
+
+const GlobalStyle = createGlobalStyle`
+
+ *,
+ *::before,
+ *::after {
+ box-sizing: border-box;
+ }
+
+ a {
+ text-decoration: none;
+ color: ${({ theme }) => theme.text};
+ cursor: pointer;
+ }
+
+ main{
+ min-height: calc(100vh - 120px);
+ }
+
+
+ body {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ min-height: 100vh;
+ line-height: 1.5;
+ max-width: 100%;
+ background-color: ${({ theme }) => theme.background};
+ color: ${({ theme }) => theme.text};
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ font-size: 20px;
+ transition: background 0.2s ease-in, color 0.2s ease-in;
+ }
+
+ pre {
+ border-radius: 10px;
+ padding: 0 1em;
+ white-space: pre-wrap;
+ font-family: inherit;
+ word-wrap: break-word;
+ }
+
+`;
+export default GlobalStyle;
diff --git a/src copy/style/theme/darkMode.ts b/src copy/style/theme/darkMode.ts
new file mode 100644
index 0000000..007a4e3
--- /dev/null
+++ b/src copy/style/theme/darkMode.ts
@@ -0,0 +1,26 @@
+'use client';
+
+//121212
+//d22427
+
+const lightTheme = {
+ body: '#eaeaea',
+ text: '#1c1e21',
+ toggleBorder: '#d1d1d1',
+ background: '#EEEEEE',
+ codeblock: '#f6f8fa',
+ backgroundPost: '#E4E6E8',
+ blue: '#1976d2',
+};
+
+const darkTheme = {
+ body: '#2f3136',
+ text: '#fafafa',
+ toggleBorder: '#6b8096',
+ background: '#363537',
+ codeblock: '#fafafa',
+ backgroundPost: 'rgba(31, 46, 61,0.3)',
+ blue: '#1976d2',
+};
+
+export { darkTheme, lightTheme };
diff --git a/src copy/style/theme/media.ts b/src copy/style/theme/media.ts
new file mode 100644
index 0000000..0c0ed0f
--- /dev/null
+++ b/src copy/style/theme/media.ts
@@ -0,0 +1,17 @@
+const deviceSizes = {
+ mobile: '450px',
+ tablet: '768px',
+ laptop: '1024px',
+};
+
+const device = {
+ mobile: `screen and (max-width:${deviceSizes.mobile})`,
+ tablet: `screen and (max-width:${deviceSizes.tablet})`,
+ laptop: `screen and (max-width:${deviceSizes.laptop})`,
+};
+
+const mediatheme = {
+ device,
+};
+
+export default mediatheme;
diff --git a/src copy/utils/axiosClient.ts b/src copy/utils/axiosClient.ts
new file mode 100644
index 0000000..9eef0a1
--- /dev/null
+++ b/src copy/utils/axiosClient.ts
@@ -0,0 +1,46 @@
+import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
+
+const axiosClient = axios.create({
+ baseURL:
+ process.env.NODE_ENV === 'production'
+ ? process.env.NEXT_PUBLIC_PRODUCT_URL
+ : process.env.NEXT_PUBLIC_LOCAL_URL,
+});
+
+export const get = async (
+ url: string,
+ config?: AxiosRequestConfig
+): Promise> => {
+ return await axiosClient.get(url, config);
+};
+
+export const post = async (
+ url: string,
+ data?: any,
+ config?: AxiosRequestConfig
+): Promise> => {
+ return await axiosClient.post(url, data, config);
+};
+
+export const patch = async (
+ url: string,
+ data?: any,
+ config?: AxiosRequestConfig
+): Promise> => {
+ return await axiosClient.patch(url, data, config);
+};
+
+export const put = async (
+ url: string,
+ data?: any,
+ config?: AxiosRequestConfig
+): Promise> => {
+ return await axiosClient.put(url, data, config);
+};
+
+export const del = async (
+ url: string,
+ config?: AxiosRequestConfig
+): Promise> => {
+ return await axiosClient.delete(url, config);
+};
diff --git a/src copy/utils/getQueryClient.ts b/src copy/utils/getQueryClient.ts
new file mode 100644
index 0000000..8b5fda3
--- /dev/null
+++ b/src copy/utils/getQueryClient.ts
@@ -0,0 +1,8 @@
+// app/getQueryClient.jsx
+
+import { cache } from 'react';
+
+import { QueryClient } from '@tanstack/react-query';
+
+const getQueryClient = cache(() => new QueryClient());
+export default getQueryClient;
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..95b999e
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ "next-sitemap.config.js"
+ ],
+ "exclude": ["node_modules"]
+}
From 1ccaf42825d6f433110f721ea8d58ca65f1f365d Mon Sep 17 00:00:00 2001
From: Kim0426 <706shin1728@naver.com>
Date: Fri, 22 Dec 2023 02:14:09 +0900
Subject: [PATCH 2/5] =?UTF-8?q?:chore:=20src=20=ED=8F=B4=EB=8D=94=20?=
=?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
{src copy => src}/@types/AboutProps.ts | 0
{src copy => src}/@types/ButtonType.ts | 0
{src copy => src}/@types/CategoryType.ts | 0
{src copy => src}/@types/GuestBookType.ts | 0
{src copy => src}/@types/PostType.ts | 0
{src copy => src}/@types/ThemeType.ts | 0
6 files changed, 0 insertions(+), 0 deletions(-)
rename {src copy => src}/@types/AboutProps.ts (100%)
rename {src copy => src}/@types/ButtonType.ts (100%)
rename {src copy => src}/@types/CategoryType.ts (100%)
rename {src copy => src}/@types/GuestBookType.ts (100%)
rename {src copy => src}/@types/PostType.ts (100%)
rename {src copy => src}/@types/ThemeType.ts (100%)
diff --git a/src copy/@types/AboutProps.ts b/src/@types/AboutProps.ts
similarity index 100%
rename from src copy/@types/AboutProps.ts
rename to src/@types/AboutProps.ts
diff --git a/src copy/@types/ButtonType.ts b/src/@types/ButtonType.ts
similarity index 100%
rename from src copy/@types/ButtonType.ts
rename to src/@types/ButtonType.ts
diff --git a/src copy/@types/CategoryType.ts b/src/@types/CategoryType.ts
similarity index 100%
rename from src copy/@types/CategoryType.ts
rename to src/@types/CategoryType.ts
diff --git a/src copy/@types/GuestBookType.ts b/src/@types/GuestBookType.ts
similarity index 100%
rename from src copy/@types/GuestBookType.ts
rename to src/@types/GuestBookType.ts
diff --git a/src copy/@types/PostType.ts b/src/@types/PostType.ts
similarity index 100%
rename from src copy/@types/PostType.ts
rename to src/@types/PostType.ts
diff --git a/src copy/@types/ThemeType.ts b/src/@types/ThemeType.ts
similarity index 100%
rename from src copy/@types/ThemeType.ts
rename to src/@types/ThemeType.ts
From f1dd309862f91ca8c5121fc16a62a64e7e4d72b1 Mon Sep 17 00:00:00 2001
From: Kim0426 <706shin1728@naver.com>
Date: Fri, 22 Dec 2023 02:18:04 +0900
Subject: [PATCH 3/5] =?UTF-8?q?chore:=20=EC=B5=9C=EC=A2=85=20=ED=8F=B4?=
=?UTF-8?q?=EB=8D=94=20=EC=97=85=EB=A1=9C=EB=93=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lib/api.ts | 108 ++++++++++++++++++
lib/makeToc.tsx | 3 +
lib/markdowntoHTML.ts | 13 +++
lib/replaceStr.ts | 13 +++
{src copy => src}/Component/About/Content.tsx | 0
.../Component/About/ProfileImgWrapper.tsx | 0
{src copy => src}/Component/About/Title.tsx | 0
.../Component/Blog/CodeBlock.tsx | 0
{src copy => src}/Component/Blog/Exterct.tsx | 0
{src copy => src}/Component/Blog/LayOut.tsx | 0
.../Component/Blog/PostSearchModal.tsx | 0
.../Component/Blog/SearchPost.tsx | 0
.../Component/CategoryList/CategoryList.tsx | 0
{src copy => src}/Component/Common/Avatar.tsx | 0
{src copy => src}/Component/Common/Button.tsx | 0
{src copy => src}/Component/Common/Footer.tsx | 0
{src copy => src}/Component/Common/Navbar.tsx | 0
.../Component/Common/PostLayout.tsx | 0
.../Component/DarkMode/ToggoeButton.tsx | 0
{src copy => src}/Component/GA/GA.tsx | 0
{src copy => src}/Component/Giscus/Gitcus.tsx | 0
.../Component/GuestBook/GuestBook.tsx | 0
{src copy => src}/Component/Input/Input.tsx | 0
.../Component/Input/InputBox.tsx | 0
{src copy => src}/Component/Input/index.tsx | 0
.../Component/Notion/NotionresumeClient.tsx | 0
{src copy => src}/Component/Post/PostItem.tsx | 0
{src copy => src}/Component/Post/PostList.tsx | 0
{src copy => src}/Component/TOC/index.tsx | 0
.../app/(root)/(routes)/(home)/page.tsx | 0
.../app/(root)/(routes)/about/page.tsx | 0
.../app/(root)/(routes)/blog/[slug]/page.tsx | 0
.../category/[categoryId]/[slug]/page.tsx | 0
.../(routes)/category/[categoryId]/page.tsx | 0
.../(routes)/guestbook/HydratedGuestBook.tsx | 0
.../app/(root)/(routes)/guestbook/page.tsx | 0
.../app/(root)/(routes)/notion/page.tsx | 0
.../(root)/(routes)/notion/resume/page.tsx | 0
{src copy => src}/app/(root)/layout.tsx | 0
{src copy => src}/app/(root)/not-found.tsx | 0
{src copy => src}/app/Recoil.tsx | 0
{src copy => src}/app/api/guestbook/route.ts | 0
{src copy => src}/app/api/slugs/[id]/route.ts | 0
{src copy => src}/app/api/slugs/route.ts | 0
{src copy => src}/app/firebase.js | 0
src/app/fonts/KyoboHandwriting2022khn.ttf | Bin 0 -> 8645056 bytes
{src copy => src}/app/globalAtom.ts | 0
{src copy => src}/app/globals.css | 0
{src copy => src}/app/queryClientProvider.tsx | 0
{src copy => src}/app/registry.tsx | 0
{src copy => src}/app/sitemap.ts | 0
{src copy => src}/app/sitemap.xml.tsx | 0
{src copy => src}/app/themeWrapper.tsx | 0
{src copy => src}/constants/POST.ts | 0
{src copy => src}/hooks/useGetGuestBook.ts | 0
{src copy => src}/hooks/useInput.ts | 0
{src copy => src}/hooks/useNaverInit.ts | 0
{src copy => src}/hooks/useObserver.ts | 0
{src copy => src}/hooks/usePostGuestBook.ts | 0
{src copy => src}/hooks/usePostQuery.ts | 0
{src copy => src}/hooks/useScroll.tsx | 0
{src copy => src}/hooks/useSearchPost.tsx | 0
{src copy => src}/hooks/useTheme.ts | 0
{src copy => src}/style/globalStyle.tsx | 0
{src copy => src}/style/theme/darkMode.ts | 0
{src copy => src}/style/theme/media.ts | 0
{src copy => src}/utils/axiosClient.ts | 0
{src copy => src}/utils/getQueryClient.ts | 0
68 files changed, 137 insertions(+)
create mode 100644 lib/api.ts
create mode 100644 lib/makeToc.tsx
create mode 100644 lib/markdowntoHTML.ts
create mode 100644 lib/replaceStr.ts
rename {src copy => src}/Component/About/Content.tsx (100%)
rename {src copy => src}/Component/About/ProfileImgWrapper.tsx (100%)
rename {src copy => src}/Component/About/Title.tsx (100%)
rename {src copy => src}/Component/Blog/CodeBlock.tsx (100%)
rename {src copy => src}/Component/Blog/Exterct.tsx (100%)
rename {src copy => src}/Component/Blog/LayOut.tsx (100%)
rename {src copy => src}/Component/Blog/PostSearchModal.tsx (100%)
rename {src copy => src}/Component/Blog/SearchPost.tsx (100%)
rename {src copy => src}/Component/CategoryList/CategoryList.tsx (100%)
rename {src copy => src}/Component/Common/Avatar.tsx (100%)
rename {src copy => src}/Component/Common/Button.tsx (100%)
rename {src copy => src}/Component/Common/Footer.tsx (100%)
rename {src copy => src}/Component/Common/Navbar.tsx (100%)
rename {src copy => src}/Component/Common/PostLayout.tsx (100%)
rename {src copy => src}/Component/DarkMode/ToggoeButton.tsx (100%)
rename {src copy => src}/Component/GA/GA.tsx (100%)
rename {src copy => src}/Component/Giscus/Gitcus.tsx (100%)
rename {src copy => src}/Component/GuestBook/GuestBook.tsx (100%)
rename {src copy => src}/Component/Input/Input.tsx (100%)
rename {src copy => src}/Component/Input/InputBox.tsx (100%)
rename {src copy => src}/Component/Input/index.tsx (100%)
rename {src copy => src}/Component/Notion/NotionresumeClient.tsx (100%)
rename {src copy => src}/Component/Post/PostItem.tsx (100%)
rename {src copy => src}/Component/Post/PostList.tsx (100%)
rename {src copy => src}/Component/TOC/index.tsx (100%)
rename {src copy => src}/app/(root)/(routes)/(home)/page.tsx (100%)
rename {src copy => src}/app/(root)/(routes)/about/page.tsx (100%)
rename {src copy => src}/app/(root)/(routes)/blog/[slug]/page.tsx (100%)
rename {src copy => src}/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx (100%)
rename {src copy => src}/app/(root)/(routes)/category/[categoryId]/page.tsx (100%)
rename {src copy => src}/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx (100%)
rename {src copy => src}/app/(root)/(routes)/guestbook/page.tsx (100%)
rename {src copy => src}/app/(root)/(routes)/notion/page.tsx (100%)
rename {src copy => src}/app/(root)/(routes)/notion/resume/page.tsx (100%)
rename {src copy => src}/app/(root)/layout.tsx (100%)
rename {src copy => src}/app/(root)/not-found.tsx (100%)
rename {src copy => src}/app/Recoil.tsx (100%)
rename {src copy => src}/app/api/guestbook/route.ts (100%)
rename {src copy => src}/app/api/slugs/[id]/route.ts (100%)
rename {src copy => src}/app/api/slugs/route.ts (100%)
rename {src copy => src}/app/firebase.js (100%)
create mode 100644 src/app/fonts/KyoboHandwriting2022khn.ttf
rename {src copy => src}/app/globalAtom.ts (100%)
rename {src copy => src}/app/globals.css (100%)
rename {src copy => src}/app/queryClientProvider.tsx (100%)
rename {src copy => src}/app/registry.tsx (100%)
rename {src copy => src}/app/sitemap.ts (100%)
rename {src copy => src}/app/sitemap.xml.tsx (100%)
rename {src copy => src}/app/themeWrapper.tsx (100%)
rename {src copy => src}/constants/POST.ts (100%)
rename {src copy => src}/hooks/useGetGuestBook.ts (100%)
rename {src copy => src}/hooks/useInput.ts (100%)
rename {src copy => src}/hooks/useNaverInit.ts (100%)
rename {src copy => src}/hooks/useObserver.ts (100%)
rename {src copy => src}/hooks/usePostGuestBook.ts (100%)
rename {src copy => src}/hooks/usePostQuery.ts (100%)
rename {src copy => src}/hooks/useScroll.tsx (100%)
rename {src copy => src}/hooks/useSearchPost.tsx (100%)
rename {src copy => src}/hooks/useTheme.ts (100%)
rename {src copy => src}/style/globalStyle.tsx (100%)
rename {src copy => src}/style/theme/darkMode.ts (100%)
rename {src copy => src}/style/theme/media.ts (100%)
rename {src copy => src}/utils/axiosClient.ts (100%)
rename {src copy => src}/utils/getQueryClient.ts (100%)
diff --git a/lib/api.ts b/lib/api.ts
new file mode 100644
index 0000000..2579c96
--- /dev/null
+++ b/lib/api.ts
@@ -0,0 +1,108 @@
+import fs from 'fs';
+import { join, basename } from 'path';
+
+import matter from 'gray-matter';
+
+const PostDirectory = join(process.cwd(), 'posts');
+const ImageDirectory = join(process.cwd(), 'public/images');
+
+export function getPostSlugs() {
+ return fs.readdirSync(PostDirectory);
+}
+
+export function getPostBySlug(slug: string, fields: string[] = []) {
+ const realSlug = decodeURIComponent(slug.replace(/\.md/, ''));
+ const fullPath = join(PostDirectory, `${realSlug}.md`);
+ const fileContent = fs.readFileSync(fullPath, 'utf-8');
+ const { data, content } = matter(fileContent);
+
+ type Item = {
+ [key: string]: string;
+ };
+ const items: Item = {};
+
+ fields.forEach((field) => {
+ if (field === 'slug') {
+ items[field] = realSlug;
+ }
+ if (field === 'content') {
+ items[field] = content;
+ }
+ if (typeof data[field] !== 'undefined') {
+ items[field] = data[field];
+ }
+ if (field === 'image') {
+ const imagePath = join(ImageDirectory, data[field]);
+ }
+ });
+
+ return items;
+}
+
+export function getAllPosts(fields: string[] = []) {
+ const slugs = getPostSlugs();
+ const posts = slugs
+ .map((slug) => getPostBySlug(slug, fields))
+ // sort posts by date in descending order
+ .sort((post1, post2) => (post1.date > post2.date ? -1 : 1));
+
+ return posts;
+}
+
+export function getInitPosts(fields: string[] = []) {
+ const slugs = getPostSlugs();
+ const posts = slugs
+ .map((slug) => getPostBySlug(slug, fields))
+ .sort((post1, post2) => (post1.date > post2.date ? -1 : 1))
+ .slice(0, 5);
+
+ return posts;
+}
+
+export function getAllPostRequest(fields: string[] = []) {
+ const slugs = getPostSlugs();
+ const posts = slugs
+ .map((slug) => getPostBySlug(slug, fields))
+ // sort posts by date in descending order
+ .sort((post1, post2) => (post1.date > post2.date ? -1 : 1));
+
+ return {};
+}
+
+//리팩토링 필요한 부분
+export function getAllCategories() {
+ const allPosts = getAllPosts(['category']);
+
+ const allCategory = new Map();
+
+ allPosts.map((post) => {
+ const getCategory = allCategory.get(post.category);
+ if (getCategory) {
+ allCategory.set(post.category, getCategory + 1);
+ return;
+ }
+
+ allCategory.set(post.category, 1);
+ });
+
+ return Array.from(allCategory).map(([category, categoryCount]) => {
+ return {
+ category: category,
+ categoryCount: categoryCount + '',
+ };
+ });
+}
+
+export function getCategoryFilteredPosts(
+ fields: string[] = [],
+ category: string
+) {
+ const slugs = getPostSlugs();
+ const posts = slugs
+ .map((slug) => getPostBySlug(slug, fields))
+ // sort posts by date in descending order
+ .sort((post1, post2) => (post1.date > post2.date ? -1 : 1))
+ .filter((post) => post.category === category);
+
+ return posts;
+}
diff --git a/lib/makeToc.tsx b/lib/makeToc.tsx
new file mode 100644
index 0000000..7ac8c43
--- /dev/null
+++ b/lib/makeToc.tsx
@@ -0,0 +1,3 @@
+export default function makeToc({ children }: { children: string }) {
+ return children.match(/(?:##|###)(.*)/g);
+}
diff --git a/lib/markdowntoHTML.ts b/lib/markdowntoHTML.ts
new file mode 100644
index 0000000..6730420
--- /dev/null
+++ b/lib/markdowntoHTML.ts
@@ -0,0 +1,13 @@
+import { remark } from 'remark';
+import html from 'remark-html';
+
+export default async function markdownToHtml(markdown: string) {
+ const result = await remark()
+ .use(html, {
+ sanitize: false,
+ })
+
+ .process(markdown);
+
+ return result.toString();
+}
diff --git a/lib/replaceStr.ts b/lib/replaceStr.ts
new file mode 100644
index 0000000..839d748
--- /dev/null
+++ b/lib/replaceStr.ts
@@ -0,0 +1,13 @@
+export default function replaceStrWithBlank([input, target]: [
+ string,
+ string | string[]
+]) {
+ if (target instanceof Array) {
+ const result = target.map((eachTarget) => {
+ return input.replaceAll(eachTarget, '');
+ });
+ return result[0].trim();
+ }
+
+ return target.replaceAll(input, '');
+}
diff --git a/src copy/Component/About/Content.tsx b/src/Component/About/Content.tsx
similarity index 100%
rename from src copy/Component/About/Content.tsx
rename to src/Component/About/Content.tsx
diff --git a/src copy/Component/About/ProfileImgWrapper.tsx b/src/Component/About/ProfileImgWrapper.tsx
similarity index 100%
rename from src copy/Component/About/ProfileImgWrapper.tsx
rename to src/Component/About/ProfileImgWrapper.tsx
diff --git a/src copy/Component/About/Title.tsx b/src/Component/About/Title.tsx
similarity index 100%
rename from src copy/Component/About/Title.tsx
rename to src/Component/About/Title.tsx
diff --git a/src copy/Component/Blog/CodeBlock.tsx b/src/Component/Blog/CodeBlock.tsx
similarity index 100%
rename from src copy/Component/Blog/CodeBlock.tsx
rename to src/Component/Blog/CodeBlock.tsx
diff --git a/src copy/Component/Blog/Exterct.tsx b/src/Component/Blog/Exterct.tsx
similarity index 100%
rename from src copy/Component/Blog/Exterct.tsx
rename to src/Component/Blog/Exterct.tsx
diff --git a/src copy/Component/Blog/LayOut.tsx b/src/Component/Blog/LayOut.tsx
similarity index 100%
rename from src copy/Component/Blog/LayOut.tsx
rename to src/Component/Blog/LayOut.tsx
diff --git a/src copy/Component/Blog/PostSearchModal.tsx b/src/Component/Blog/PostSearchModal.tsx
similarity index 100%
rename from src copy/Component/Blog/PostSearchModal.tsx
rename to src/Component/Blog/PostSearchModal.tsx
diff --git a/src copy/Component/Blog/SearchPost.tsx b/src/Component/Blog/SearchPost.tsx
similarity index 100%
rename from src copy/Component/Blog/SearchPost.tsx
rename to src/Component/Blog/SearchPost.tsx
diff --git a/src copy/Component/CategoryList/CategoryList.tsx b/src/Component/CategoryList/CategoryList.tsx
similarity index 100%
rename from src copy/Component/CategoryList/CategoryList.tsx
rename to src/Component/CategoryList/CategoryList.tsx
diff --git a/src copy/Component/Common/Avatar.tsx b/src/Component/Common/Avatar.tsx
similarity index 100%
rename from src copy/Component/Common/Avatar.tsx
rename to src/Component/Common/Avatar.tsx
diff --git a/src copy/Component/Common/Button.tsx b/src/Component/Common/Button.tsx
similarity index 100%
rename from src copy/Component/Common/Button.tsx
rename to src/Component/Common/Button.tsx
diff --git a/src copy/Component/Common/Footer.tsx b/src/Component/Common/Footer.tsx
similarity index 100%
rename from src copy/Component/Common/Footer.tsx
rename to src/Component/Common/Footer.tsx
diff --git a/src copy/Component/Common/Navbar.tsx b/src/Component/Common/Navbar.tsx
similarity index 100%
rename from src copy/Component/Common/Navbar.tsx
rename to src/Component/Common/Navbar.tsx
diff --git a/src copy/Component/Common/PostLayout.tsx b/src/Component/Common/PostLayout.tsx
similarity index 100%
rename from src copy/Component/Common/PostLayout.tsx
rename to src/Component/Common/PostLayout.tsx
diff --git a/src copy/Component/DarkMode/ToggoeButton.tsx b/src/Component/DarkMode/ToggoeButton.tsx
similarity index 100%
rename from src copy/Component/DarkMode/ToggoeButton.tsx
rename to src/Component/DarkMode/ToggoeButton.tsx
diff --git a/src copy/Component/GA/GA.tsx b/src/Component/GA/GA.tsx
similarity index 100%
rename from src copy/Component/GA/GA.tsx
rename to src/Component/GA/GA.tsx
diff --git a/src copy/Component/Giscus/Gitcus.tsx b/src/Component/Giscus/Gitcus.tsx
similarity index 100%
rename from src copy/Component/Giscus/Gitcus.tsx
rename to src/Component/Giscus/Gitcus.tsx
diff --git a/src copy/Component/GuestBook/GuestBook.tsx b/src/Component/GuestBook/GuestBook.tsx
similarity index 100%
rename from src copy/Component/GuestBook/GuestBook.tsx
rename to src/Component/GuestBook/GuestBook.tsx
diff --git a/src copy/Component/Input/Input.tsx b/src/Component/Input/Input.tsx
similarity index 100%
rename from src copy/Component/Input/Input.tsx
rename to src/Component/Input/Input.tsx
diff --git a/src copy/Component/Input/InputBox.tsx b/src/Component/Input/InputBox.tsx
similarity index 100%
rename from src copy/Component/Input/InputBox.tsx
rename to src/Component/Input/InputBox.tsx
diff --git a/src copy/Component/Input/index.tsx b/src/Component/Input/index.tsx
similarity index 100%
rename from src copy/Component/Input/index.tsx
rename to src/Component/Input/index.tsx
diff --git a/src copy/Component/Notion/NotionresumeClient.tsx b/src/Component/Notion/NotionresumeClient.tsx
similarity index 100%
rename from src copy/Component/Notion/NotionresumeClient.tsx
rename to src/Component/Notion/NotionresumeClient.tsx
diff --git a/src copy/Component/Post/PostItem.tsx b/src/Component/Post/PostItem.tsx
similarity index 100%
rename from src copy/Component/Post/PostItem.tsx
rename to src/Component/Post/PostItem.tsx
diff --git a/src copy/Component/Post/PostList.tsx b/src/Component/Post/PostList.tsx
similarity index 100%
rename from src copy/Component/Post/PostList.tsx
rename to src/Component/Post/PostList.tsx
diff --git a/src copy/Component/TOC/index.tsx b/src/Component/TOC/index.tsx
similarity index 100%
rename from src copy/Component/TOC/index.tsx
rename to src/Component/TOC/index.tsx
diff --git a/src copy/app/(root)/(routes)/(home)/page.tsx b/src/app/(root)/(routes)/(home)/page.tsx
similarity index 100%
rename from src copy/app/(root)/(routes)/(home)/page.tsx
rename to src/app/(root)/(routes)/(home)/page.tsx
diff --git a/src copy/app/(root)/(routes)/about/page.tsx b/src/app/(root)/(routes)/about/page.tsx
similarity index 100%
rename from src copy/app/(root)/(routes)/about/page.tsx
rename to src/app/(root)/(routes)/about/page.tsx
diff --git a/src copy/app/(root)/(routes)/blog/[slug]/page.tsx b/src/app/(root)/(routes)/blog/[slug]/page.tsx
similarity index 100%
rename from src copy/app/(root)/(routes)/blog/[slug]/page.tsx
rename to src/app/(root)/(routes)/blog/[slug]/page.tsx
diff --git a/src copy/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx b/src/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx
similarity index 100%
rename from src copy/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx
rename to src/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx
diff --git a/src copy/app/(root)/(routes)/category/[categoryId]/page.tsx b/src/app/(root)/(routes)/category/[categoryId]/page.tsx
similarity index 100%
rename from src copy/app/(root)/(routes)/category/[categoryId]/page.tsx
rename to src/app/(root)/(routes)/category/[categoryId]/page.tsx
diff --git a/src copy/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx b/src/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx
similarity index 100%
rename from src copy/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx
rename to src/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx
diff --git a/src copy/app/(root)/(routes)/guestbook/page.tsx b/src/app/(root)/(routes)/guestbook/page.tsx
similarity index 100%
rename from src copy/app/(root)/(routes)/guestbook/page.tsx
rename to src/app/(root)/(routes)/guestbook/page.tsx
diff --git a/src copy/app/(root)/(routes)/notion/page.tsx b/src/app/(root)/(routes)/notion/page.tsx
similarity index 100%
rename from src copy/app/(root)/(routes)/notion/page.tsx
rename to src/app/(root)/(routes)/notion/page.tsx
diff --git a/src copy/app/(root)/(routes)/notion/resume/page.tsx b/src/app/(root)/(routes)/notion/resume/page.tsx
similarity index 100%
rename from src copy/app/(root)/(routes)/notion/resume/page.tsx
rename to src/app/(root)/(routes)/notion/resume/page.tsx
diff --git a/src copy/app/(root)/layout.tsx b/src/app/(root)/layout.tsx
similarity index 100%
rename from src copy/app/(root)/layout.tsx
rename to src/app/(root)/layout.tsx
diff --git a/src copy/app/(root)/not-found.tsx b/src/app/(root)/not-found.tsx
similarity index 100%
rename from src copy/app/(root)/not-found.tsx
rename to src/app/(root)/not-found.tsx
diff --git a/src copy/app/Recoil.tsx b/src/app/Recoil.tsx
similarity index 100%
rename from src copy/app/Recoil.tsx
rename to src/app/Recoil.tsx
diff --git a/src copy/app/api/guestbook/route.ts b/src/app/api/guestbook/route.ts
similarity index 100%
rename from src copy/app/api/guestbook/route.ts
rename to src/app/api/guestbook/route.ts
diff --git a/src copy/app/api/slugs/[id]/route.ts b/src/app/api/slugs/[id]/route.ts
similarity index 100%
rename from src copy/app/api/slugs/[id]/route.ts
rename to src/app/api/slugs/[id]/route.ts
diff --git a/src copy/app/api/slugs/route.ts b/src/app/api/slugs/route.ts
similarity index 100%
rename from src copy/app/api/slugs/route.ts
rename to src/app/api/slugs/route.ts
diff --git a/src copy/app/firebase.js b/src/app/firebase.js
similarity index 100%
rename from src copy/app/firebase.js
rename to src/app/firebase.js
diff --git a/src/app/fonts/KyoboHandwriting2022khn.ttf b/src/app/fonts/KyoboHandwriting2022khn.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..03d0bbb527864a3c2362d54140b3d4a8510a17ae
GIT binary patch
literal 8645056
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
qFzDoK^4D%@D9s#Hy?F4d4~O0}fg+&FGL
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