From b425d21678838dc23149f61ed5fc802689ade18e Mon Sep 17 00:00:00 2001 From: suu3 Date: Wed, 25 Feb 2026 20:08:10 +0900 Subject: [PATCH] Fix Next static assets, PWA metadata, and GitHub Pages deploy output --- .github/workflows/deploy.yml | 31 +- .gitignore | 4 +- README.md | 86 ++-- app/category/[category]/page.tsx | 40 ++ app/category/page.tsx | 26 ++ app/globals.css | 27 ++ app/layout.tsx | 54 +++ app/page.tsx | 16 + app/posts/[...slug]/page.tsx | 38 ++ components/post-search.tsx | 72 ++++ gatsby-browser.tsx | 5 - gatsby-config.ts | 203 --------- gatsby-node.ts | 193 --------- gatsby-ssr.ts | 12 - lib/posts.ts | 92 ++++ next-env.d.ts | 4 + next.config.ts | 10 + package.json | 80 +--- postcss.config.ts | 18 +- {static => public}/favicon.ico | Bin .../favicons/favicon-144x144.png | Bin .../favicons/favicon-192x192.png | Bin {static => public}/favicons/favicon-48x48.png | Bin .../favicons/favicon-512x512.png | Bin {static => public}/favicons/favicon-72x72.png | Bin {static => public}/favicons/favicon-96x96.png | Bin .../fonts/FiraCode-Medium.woff2 | Bin {static => public}/images/background.svg | 0 {static => public}/images/dummy.jpg | Bin {static => public}/images/github.svg | 0 {static => public}/images/grid-vert-2.svg | 0 {static => public}/images/grid-vert.svg | 0 {static => public}/images/home.svg | 0 {static => public}/images/icon.png | Bin {static => public}/images/left-arrow.svg | 0 {static => public}/images/moon.svg | 0 {static => public}/images/right-arrow.svg | 0 {static => public}/images/sun.svg | 0 {static => public}/robots.txt | 0 public/site.webmanifest | 21 + src/components/@layout/bio/bio.module.css | 49 --- src/components/@layout/bio/index.tsx | 44 -- src/components/@layout/bio/profile.gif | Bin 2284 -> 0 bytes .../categoryMenu/category-menu.module.css | 60 --- src/components/@layout/categoryMenu/index.tsx | 59 --- .../@layout/footer/footer.module.css | 34 -- src/components/@layout/footer/index.tsx | 28 -- src/components/@layout/index.tsx | 21 - src/components/@layout/main/index.tsx | 38 -- src/components/@layout/navigation/index.tsx | 67 --- .../@layout/navigation/navigation.module.css | 70 --- .../@layout/navigation/themeBtn/index.tsx | 97 ----- .../navigation/themeBtn/theme-btn.module.css | 29 -- src/components/@layout/seo/index.tsx | 50 --- .../@layout/tableOfContents/index.tsx | 57 --- .../tableOfContents/table-contents.module.css | 90 ---- src/components/@layout/utterances/index.tsx | 33 -- .../@templates/post-list-template.module.css | 54 --- .../@templates/post-list-template.tsx | 80 ---- src/components/arrow.module.css | 50 --- src/components/arrow.tsx | 40 -- src/components/pagination.module.css | 71 ---- src/components/pagination.tsx | 114 ----- src/components/post-list-item.module.css | 164 ------- src/components/post-list-item.tsx | 91 ---- src/components/split-text.module.css | 24 -- src/components/split-text.tsx | 42 -- src/constants/links.ts | 1 - src/constants/page.ts | 1 - src/hooks/useActiveHash.ts | 37 -- src/libs/paths.ts | 2 - src/pages/404.tsx | 30 -- src/styles/global.css | 402 ------------------ src/styles/media-query.css | 1 - src/styles/normalize.css | 343 --------------- src/styles/prism-atom-dark.css | 155 ------- src/templates/blog-post.js | 163 ------- src/templates/blog-post.module.css | 232 ---------- src/templates/category-posts.js | 66 --- src/templates/home.tsx | 70 --- tsconfig.json | 51 ++- 81 files changed, 521 insertions(+), 3621 deletions(-) create mode 100644 app/category/[category]/page.tsx create mode 100644 app/category/page.tsx create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/page.tsx create mode 100644 app/posts/[...slug]/page.tsx create mode 100644 components/post-search.tsx delete mode 100644 gatsby-browser.tsx delete mode 100644 gatsby-config.ts delete mode 100644 gatsby-node.ts delete mode 100644 gatsby-ssr.ts create mode 100644 lib/posts.ts create mode 100644 next-env.d.ts create mode 100644 next.config.ts rename {static => public}/favicon.ico (100%) rename {static => public}/favicons/favicon-144x144.png (100%) rename {static => public}/favicons/favicon-192x192.png (100%) rename {static => public}/favicons/favicon-48x48.png (100%) rename {static => public}/favicons/favicon-512x512.png (100%) rename {static => public}/favicons/favicon-72x72.png (100%) rename {static => public}/favicons/favicon-96x96.png (100%) rename {static => public}/fonts/FiraCode-Medium.woff2 (100%) rename {static => public}/images/background.svg (100%) rename {static => public}/images/dummy.jpg (100%) rename {static => public}/images/github.svg (100%) rename {static => public}/images/grid-vert-2.svg (100%) rename {static => public}/images/grid-vert.svg (100%) rename {static => public}/images/home.svg (100%) rename {static => public}/images/icon.png (100%) rename {static => public}/images/left-arrow.svg (100%) rename {static => public}/images/moon.svg (100%) rename {static => public}/images/right-arrow.svg (100%) rename {static => public}/images/sun.svg (100%) rename {static => public}/robots.txt (100%) create mode 100644 public/site.webmanifest delete mode 100644 src/components/@layout/bio/bio.module.css delete mode 100644 src/components/@layout/bio/index.tsx delete mode 100644 src/components/@layout/bio/profile.gif delete mode 100644 src/components/@layout/categoryMenu/category-menu.module.css delete mode 100644 src/components/@layout/categoryMenu/index.tsx delete mode 100644 src/components/@layout/footer/footer.module.css delete mode 100644 src/components/@layout/footer/index.tsx delete mode 100644 src/components/@layout/index.tsx delete mode 100644 src/components/@layout/main/index.tsx delete mode 100644 src/components/@layout/navigation/index.tsx delete mode 100644 src/components/@layout/navigation/navigation.module.css delete mode 100644 src/components/@layout/navigation/themeBtn/index.tsx delete mode 100644 src/components/@layout/navigation/themeBtn/theme-btn.module.css delete mode 100644 src/components/@layout/seo/index.tsx delete mode 100644 src/components/@layout/tableOfContents/index.tsx delete mode 100644 src/components/@layout/tableOfContents/table-contents.module.css delete mode 100644 src/components/@layout/utterances/index.tsx delete mode 100644 src/components/@templates/post-list-template.module.css delete mode 100644 src/components/@templates/post-list-template.tsx delete mode 100644 src/components/arrow.module.css delete mode 100644 src/components/arrow.tsx delete mode 100644 src/components/pagination.module.css delete mode 100644 src/components/pagination.tsx delete mode 100644 src/components/post-list-item.module.css delete mode 100644 src/components/post-list-item.tsx delete mode 100644 src/components/split-text.module.css delete mode 100644 src/components/split-text.tsx delete mode 100644 src/constants/links.ts delete mode 100644 src/constants/page.ts delete mode 100644 src/hooks/useActiveHash.ts delete mode 100644 src/libs/paths.ts delete mode 100644 src/pages/404.tsx delete mode 100644 src/styles/global.css delete mode 100644 src/styles/media-query.css delete mode 100644 src/styles/normalize.css delete mode 100644 src/styles/prism-atom-dark.css delete mode 100644 src/templates/blog-post.js delete mode 100644 src/templates/blog-post.module.css delete mode 100644 src/templates/category-posts.js delete mode 100644 src/templates/home.tsx diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f6301e5d..fa70be4d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,23 +8,28 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 + - name: Setup Node - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: - node-version: "18.x" - - uses: pnpm/action-setup@v2 + node-version: '20' + + - uses: pnpm/action-setup@v4 with: - version: 8 + version: 9 + - name: Install dependencies - run: pnpm install - - name: Build - run: pnpm clean && pnpm run build + run: pnpm install --frozen-lockfile + + - name: Build static export + run: pnpm run build env: NODE_ENV: production + - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v2 - env: - GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} - PUBLISH_BRANCH: deploy - PUBLISH_DIR: ./public + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.ACCESS_TOKEN }} + publish_branch: deploy + publish_dir: ./out diff --git a/.gitignore b/.gitignore index d14f1128..bff39e9d 100644 --- a/.gitignore +++ b/.gitignore @@ -56,10 +56,10 @@ typings/ # gatsby files .cache/ -public # Mac files .DS_Store # 비공개 게시물 -@Private \ No newline at end of file +@Private +.next diff --git a/README.md b/README.md index 44b57037..1eb54eb8 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,52 @@ -Start Template: gastby-starter [official and community-created starters](https://www.gatsbyjs.com/docs/gatsby-starters/) +# suu3.github.io (Next.js) -## 🚀 Quick start +기존 Gatsby 블로그를 Next.js App Router 기반으로 마이그레이션한 버전입니다. -Site: `http://localhost:8000` -Querying your data: `http://localhost:8000/___graphql` [Gatsby Tutorial](https://www.gatsbyjs.com/docs/tutorial/getting-started/part-4/#use-graphiql-to-explore-the-data-layer-and-write-graphql-queries). +## 실행 -```shell -# 실행 -pnpm start -# 배포 -pnpm run deploy +```bash +# 1) pnpm 활성화 +corepack enable + +# 2) (사내망/미러 환경이라면) npm registry 강제 지정 +pnpm config set registry https://registry.npmjs.org/ + +# 3) 설치 및 실행 +pnpm install +pnpm dev +``` + +- 접속: `http://localhost:3000` + +## 주요 기능 + +- 최신 Next.js(App Router) 구조 적용 +- Gatsby 레거시 컴포넌트/설정(`src/*`, `gatsby-*.ts(x)`) 제거로 Next.js 전용 구조 정리 +- Tailwind CSS v4 기반 UI 스타일링(기존 카드형 디자인 톤 유지) +- 전체 포스트 검색(제목/설명/카테고리/태그) +- 카테고리 모아보기 및 카테고리별 포스트 목록 +- PWA 메타데이터/manifest 및 favicon 연결 + +## 빌드 + +```bash +pnpm build ``` -## 🧐 What's inside? - -디렉터리 구조 - - . - ├── node_modules - └── blog - └── @Private :비공개 게시물 => 대신 포스트는 따로 노션에 관리. (어드민을 둘 생각이 없으므로) - └── ... - ├── src - ├── .gitignore - ├── gatsby-browser.js - ├── gatsby-config.js - ├── gatsby-node.js - ├── gatsby-ssr.js - ├── LICENSE - ├── package.json - └── README.md - -[Gatsby browser APIs](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-browser/) -[Gatsby Config](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-config/) -[Gatsby Node APIs](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/) -[Gatsby server-side rendering APIs](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-ssr/) -[Gatsby website](https://www.gatsbyjs.com/) -[Tutorial](https://www.gatsbyjs.com/docs/tutorial/getting-started/) -[Documentation](https://www.gatsbyjs.com/docs/) - -## 💫 Deploy - -gh-pages 배포 +정적 배포를 위해 `next.config.ts`에서 `output: 'export'`를 사용합니다. + +## 실행이 안 될 때 + +- `ERR_PNPM_FETCH_403`가 나면, 프로젝트 또는 전역 `.npmrc`에 사설 레지스트리 설정이 있는지 확인하세요. +- 아래 명령으로 현재 레지스트리를 확인/초기화할 수 있습니다. + +```bash +pnpm config get registry +pnpm config set registry https://registry.npmjs.org/ +``` + + +## 배포 + +GitHub Actions `deploy.yml`은 Next.js 정적 출력 폴더인 `out/`를 배포합니다. +정적 에셋은 Next.js 표준 위치인 `public/` 디렉터리를 사용합니다. diff --git a/app/category/[category]/page.tsx b/app/category/[category]/page.tsx new file mode 100644 index 00000000..e61e9df5 --- /dev/null +++ b/app/category/[category]/page.tsx @@ -0,0 +1,40 @@ +import Link from 'next/link'; +import { getAllPosts, splitSlugToSegments } from '@/lib/posts'; + +type Props = { + params: Promise<{ category: string }>; +}; + +export async function generateStaticParams() { + const categories = [...new Set(getAllPosts().map((post) => post.category))]; + return categories.map((category) => ({ category: encodeURIComponent(category) })); +} + +export default async function CategoryDetailPage({ params }: Props) { + const { category: categoryParam } = await params; + const category = decodeURIComponent(categoryParam); + const posts = getAllPosts().filter((post) => post.category === category); + + return ( +
+

{category}

+

{posts.length}개의 포스트

+ + + ← 카테고리 목록 + +
+ ); +} diff --git a/app/category/page.tsx b/app/category/page.tsx new file mode 100644 index 00000000..e7a0c6ec --- /dev/null +++ b/app/category/page.tsx @@ -0,0 +1,26 @@ +import Link from 'next/link'; +import { getAllPosts, getCategoriesWithCount } from '@/lib/posts'; + +export default function CategoryPage() { + const posts = getAllPosts(); + const categories = Object.entries(getCategoriesWithCount(posts)).sort((a, b) => b[1] - a[1]); + + return ( +
+

카테고리 모아보기

+ +
+ ); +} diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 00000000..282b9fa4 --- /dev/null +++ b/app/globals.css @@ -0,0 +1,27 @@ +@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"); +@import "tailwindcss"; + +:root { + --surface: #ffffff; + --text: #2a2b31; + --muted: #6b7280; + --line: #2a2b31; + --theme: #ff6737; + --theme-soft: #ffddca; + --bg: #f7f7f7; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + font-family: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + color: var(--text); + background: var(--bg); +} + +a { + color: inherit; +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 00000000..022c21a8 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,54 @@ +import type { Metadata, Viewport } from 'next'; +import Link from 'next/link'; +import './globals.css'; + +export const metadata: Metadata = { + title: 'Suu.Blog', + description: 'Suu3 기술 블로그', + metadataBase: new URL('https://suu3.github.io'), + manifest: '/site.webmanifest', + icons: { + icon: [ + { url: '/favicon.ico' }, + { url: '/favicons/favicon-48x48.png', sizes: '48x48', type: 'image/png' }, + { url: '/favicons/favicon-96x96.png', sizes: '96x96', type: 'image/png' }, + { url: '/favicons/favicon-144x144.png', sizes: '144x144', type: 'image/png' }, + { url: '/favicons/favicon-192x192.png', sizes: '192x192', type: 'image/png' }, + { url: '/favicons/favicon-512x512.png', sizes: '512x512', type: 'image/png' }, + ], + apple: [{ url: '/favicons/favicon-192x192.png' }], + }, +}; + +export const viewport: Viewport = { + themeColor: '#ff6737', +}; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + +
+
+ + Suu.Blog + + +
+
+ +
{children}
+ + + ); +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 00000000..6dbe577e --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,16 @@ +import PostSearch from '@/components/post-search'; +import { getAllPosts } from '@/lib/posts'; + +export default function HomePage() { + const posts = getAllPosts(); + + return ( +
+
+

전체 포스트

+

기록하고 공유합니다

+
+ +
+ ); +} diff --git a/app/posts/[...slug]/page.tsx b/app/posts/[...slug]/page.tsx new file mode 100644 index 00000000..b5620b18 --- /dev/null +++ b/app/posts/[...slug]/page.tsx @@ -0,0 +1,38 @@ +import Link from 'next/link'; +import { notFound } from 'next/navigation'; +import { getAllPosts, getPostBySlug } from '@/lib/posts'; + +type Props = { + params: Promise<{ slug: string[] }>; +}; + +export async function generateStaticParams() { + return getAllPosts().map((post) => ({ + slug: post.slug.split('/').map(encodeURIComponent), + })); +} + +export default async function PostDetailPage({ params }: Props) { + const { slug: slugSegments } = await params; + const slug = slugSegments.map(decodeURIComponent).join('/'); + const post = getPostBySlug(slug); + + if (!post) { + notFound(); + } + + return ( +
+

{post.category}

+

{post.title}

+

{post.date}

+

{post.description}

+

태그: {post.tags.join(', ') || '없음'}

+
+
{post.content}
+ + ← 목록으로 + +
+ ); +} diff --git a/components/post-search.tsx b/components/post-search.tsx new file mode 100644 index 00000000..67d8b6fd --- /dev/null +++ b/components/post-search.tsx @@ -0,0 +1,72 @@ +'use client'; + +import Link from 'next/link'; +import { useMemo, useState } from 'react'; +import type { PostSummary } from '@/lib/posts'; +import { splitSlugToSegments } from '@/lib/posts'; + +type Props = { + posts: PostSummary[]; +}; + +export default function PostSearch({ posts }: Props) { + const [query, setQuery] = useState(''); + + const filtered = useMemo(() => { + const normalized = query.trim().toLowerCase(); + + if (!normalized) { + return posts; + } + + return posts.filter((post) => + [post.title, post.description, post.category, post.tags.join(' ')].some((field) => + field.toLowerCase().includes(normalized), + ), + ); + }, [posts, query]); + + return ( +
+
+ + setQuery(event.target.value)} + className="w-full rounded-lg border border-[#2a2b31] px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-[#ff6737]" + /> +

총 {filtered.length}개의 포스트

+
+ + +
+ ); +} diff --git a/gatsby-browser.tsx b/gatsby-browser.tsx deleted file mode 100644 index 23376e18..00000000 --- a/gatsby-browser.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import "@fontsource-variable/montserrat" -import "@fontsource/merriweather" -import "./src/styles/normalize.css" -import "./src/styles/global.css" -import "./src/styles/prism-atom-dark.css" diff --git a/gatsby-config.ts b/gatsby-config.ts deleted file mode 100644 index 11ec7738..00000000 --- a/gatsby-config.ts +++ /dev/null @@ -1,203 +0,0 @@ -/** - * Configure your Gatsby site with this file. - * - * See: https://www.gatsbyjs.com/docs/reference/config-files/gatsby-config/ - * https://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/how-gatsby-works-with-github-pages/ - */ - -/** - * @type {import('gatsby').GatsbyConfig} - */ -module.exports = { - // pathPrefix: "/blog", - siteMetadata: { - title: `Suu.Blog`, - author: { - name: `suu3`, - summary: `Suu.Blog`, - }, - description: `@suu3의 기술 블로그`, - siteUrl: `https://suu3.github.io/home/1`, - favicon: "/favicon.ico", - // social: { - // twitter: `kylemathews`, - // }, - }, - plugins: [ - "gatsby-plugin-pnpm", - `gatsby-plugin-image`, - { - resolve: `gatsby-source-filesystem`, - options: { - path: `${__dirname}/content`, - name: `blog`, - }, - }, - { - resolve: `gatsby-source-filesystem`, - options: { - name: `images`, - path: `${__dirname}/static/images`, - }, - }, - { - resolve: `gatsby-transformer-remark`, - options: { - plugins: [ - { - resolve: `gatsby-remark-images`, - options: { - maxWidth: 630, - }, - }, - { - resolve: `gatsby-remark-responsive-iframe`, - options: { - wrapperStyle: `margin-bottom: 1.0725rem`, - }, - }, - "gatsby-remark-copy-relative-linked-files", - "gatsby-remark-autolink-headers", - "gatsby-remark-prismjs", // should be placed after `gatsby-remark-autolink-headers` - { - resolve: "gatsby-remark-codepen", - options: { - theme: "dark", - height: 400, - }, - }, - ], - }, - }, - `gatsby-transformer-sharp`, - `gatsby-plugin-sharp`, - { - resolve: `gatsby-plugin-feed`, - options: { - query: ` - { - site { - siteMetadata { - title - description - siteUrl - site_url: siteUrl - } - } - } - `, - feeds: [ - { - serialize: ({ query: { site, allMarkdownRemark } }) => { - return allMarkdownRemark.nodes.map(node => { - return Object.assign({}, node.frontmatter, { - description: node.excerpt, - date: node.frontmatter.date, - url: site.siteMetadata.siteUrl + node.fields.slug, - guid: site.siteMetadata.siteUrl + node.fields.slug, - custom_elements: [{ "content:encoded": node.html }], - }) - }) - }, - query: `{ - allMarkdownRemark(sort: {frontmatter: {date: DESC}}) { - nodes { - excerpt - html - fields { - slug - } - frontmatter { - title - date - } - } - } - }`, - output: "/rss.xml", - title: "Gatsby Starter Blog RSS Feed", - }, - ], - }, - }, - { - resolve: `gatsby-plugin-manifest`, //https://www.gatsbyjs.com/plugins/gatsby-plugin-manifest/ - options: { - name: `Suu3 Blog`, - short_name: `Suu3 Blog`, - start_url: `/home/1`, - background_color: `#ffffff`, - // This will impact how browsers show your PWA/website - // https://css-tricks.com/meta-theme-color-and-trickery/ - theme_color: `#ff6737`, - display: `minimal-ui`, - icon: `static/images/icon.png`, // This path is relative to the root of the site. - icons: [ - { - src: `static/favicons/favicon-48x48.png`, - sizes: `48x48`, - type: `image/png`, - }, - { - src: `static/favicons/favicon-72x72.png`, - sizes: `72x72`, - type: `image/png`, - }, - { - src: `static/favicons/favicon-96x96.png`, - sizes: `96x96`, - type: `image/png`, - }, - { - src: `static/favicons/favicon-144x144.png`, - sizes: `144x144`, - type: `image/png`, - }, - { - src: `static/favicons/favicon-192x192.png`, - sizes: `192x192`, - type: `image/png`, - }, - { - src: `static/favicons/favicon-512x512.png`, - sizes: `512x512`, - type: `image/png`, - }, - ], - }, - }, - { - resolve: "gatsby-plugin-postcss", - }, - { - resolve: "gatsby-plugin-svgr", - options: { - prettier: true, // use prettier to format JS code output (default) - svgo: true, // use svgo to optimize SVGs (default) - svgoConfig: { - plugins: [ - { - name: "removeViewBox", - active: false, - }, // remove viewBox when possible (default) - ], - }, - }, - }, - { - resolve: "gatsby-plugin-google-tagmanager", - options: { - id: "GTM-KZHNKFDT", - includeInDevelopment: false, - defaultDataLayer: { platform: "gatsby" }, - }, - }, - { - resolve: `gatsby-plugin-google-adsense`, - options: { - publisherId: `ca-pub-3886701004172830`, - }, - }, - ], - // trailingSlash: `never`, // url 뒤 행 슬래쉬 trailing slash -} diff --git a/gatsby-node.ts b/gatsby-node.ts deleted file mode 100644 index 49de3282..00000000 --- a/gatsby-node.ts +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Implement Gatsby's Node APIs in this file. - * - * See: https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/ - */ - -import { POST_PER_PAGE } from "./src/constants/page" - -const path = require(`path`) -const { createFilePath } = require(`gatsby-source-filesystem`) - -// Define the template for blog post -const blogPost = path.resolve(`./src/templates/blog-post.js`) - -/** - * @type {import('gatsby').GatsbyNode['createPages']} - */ -exports.createPages = async ({ graphql, actions, reporter }) => { - const { createRedirect } = actions - const { createPage } = actions - - // Get all markdown blog posts sorted by date - const result = await graphql(` - { - postsRemark: allMarkdownRemark( - sort: { frontmatter: { date: ASC } } - limit: 1000 - ) { - categoryList: distinct(field: { frontmatter: { category: SELECT } }) - nodes { - id - frontmatter { - category - } - fields { - slug - } - } - totalCount - } - } - `) - - if (result.errors) { - reporter.panicOnBuild( - `There was an error loading your blog posts`, - result.errors - ) - return - } - - // 카테고리 목록을 가져오고, 각 카테고리마다 페이지 생성 - const categories = result.data.postsRemark.categoryList - const posts = result.data.postsRemark.nodes - const postsPerPage = POST_PER_PAGE - - categories.forEach(category => { - const categoryPosts = posts.filter( - post => post.frontmatter.category === category - ) - const numPages = Math.ceil(categoryPosts.length / postsPerPage) - Array.from({ length: numPages }).forEach((_, i) => { - createPage({ - path: `/${category}/${i + 1}/`, - component: path.resolve(`./src/templates/category-posts.js`), // Just like `createPage()`\ - context: { - limit: postsPerPage, - skip: i * postsPerPage, - numPages, - currentPage: i + 1, - category, - totalCount: categoryPosts.length, - }, - }) - }) - }) - - //home = all - const numPages = Math.ceil(posts.length / postsPerPage) - Array.from({ length: numPages }).forEach((_, i) => { - createPage({ - path: `/home/${i + 1}/`, - component: path.resolve(`./src/templates/home.tsx`), // Just like `createPage()`\ - context: { - limit: postsPerPage, - skip: i * postsPerPage, - numPages, - currentPage: i + 1, - totalCount: posts.length, - }, - }) - }) - // Create blog posts pages - // But only if there's at least one markdown file found at "content/blog" (defined in gatsby-config.js) - // `context` is available in the template as a prop and as a variable in GraphQL - - if (posts.length > 0) { - posts.forEach((post, index) => { - const previousPostId = index === 0 ? null : posts[index - 1].id - const nextPostId = index === posts.length - 1 ? null : posts[index + 1].id - - createPage({ - path: post.fields.slug, - component: blogPost, - context: { - id: post.id, - previousPostId, - nextPostId, - }, - }) - }) - } - - // createRedirect({ - // fromPath: `/`, - // toPath: `/home/1`, - // redirectInBrowser: true, - // isPermanent: true, - // }) - - createRedirect({ - fromPath: `/home/`, - toPath: `/home/1`, - // redirectInBrowser: true, - isPermanent: true, - }) -} - -/** - * @type {import('gatsby').GatsbyNode['onCreateNode']} - */ -exports.onCreateNode = ({ node, actions, getNode }) => { - const { createNodeField } = actions - - if (node.internal.type === `MarkdownRemark`) { - const value = createFilePath({ node, getNode }) - - createNodeField({ - name: "slug", - node, - value, - }) - } -} - -/** - * @type {import('gatsby').GatsbyNode['createSchemaCustomization']} - */ -exports.createSchemaCustomization = ({ actions }) => { - const { createTypes } = actions - - // Explicitly define the siteMetadata {} object - // This way those will always be defined even if removed from gatsby-config.js - - // Also explicitly define the Markdown frontmatter - // This way the "MarkdownRemark" queries will return `null` even when no - // blog posts are stored inside "content/blog" instead of returning an error - - //@link https://github.com/gatsbyjs/gatsby/issues/4123 - createTypes(` - type SiteSiteMetadata { - author: Author - siteUrl: String - social: Social - } - - type Author { - name: String - summary: String - } - - type Social { - twitter: String - } - - type MarkdownRemark implements Node { - frontmatter: Frontmatter - fields: Fields - } - - type Frontmatter { - title: String - description: String - date: Date @dateformat - tag: [String] - thumbnail: File @fileByRelativePath - } - - type Fields { - slug: String - } - `) -} diff --git a/gatsby-ssr.ts b/gatsby-ssr.ts deleted file mode 100644 index 89b4985b..00000000 --- a/gatsby-ssr.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Implement Gatsby's SSR (Server Side Rendering) APIs in this file. - * - * See: https://www.gatsbyjs.com/docs/reference/config-files/gatsby-ssr/ - */ - -/** - * @type {import('gatsby').GatsbySSR['onRenderBody']} - */ -exports.onRenderBody = ({ setHtmlAttributes }) => { - setHtmlAttributes({ lang: `ko` }) -} diff --git a/lib/posts.ts b/lib/posts.ts new file mode 100644 index 00000000..b861508e --- /dev/null +++ b/lib/posts.ts @@ -0,0 +1,92 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import matter from 'gray-matter'; + +const CONTENT_DIR = path.join(process.cwd(), 'content'); + +type FrontMatter = { + title?: string; + date?: string; + description?: string; + category?: string; + tag?: string[]; +}; + +export type PostSummary = { + slug: string; + title: string; + date: string; + description: string; + category: string; + tags: string[]; +}; + +export type PostDetail = PostSummary & { + content: string; +}; + +function getMarkdownFiles(dir: string): string[] { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + return entries.flatMap((entry) => { + const resolvedPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + return getMarkdownFiles(resolvedPath); + } + + if (entry.isFile() && entry.name.endsWith('.md')) { + return [resolvedPath]; + } + + return []; + }); +} + +function createSlug(filePath: string): string { + return path.relative(CONTENT_DIR, filePath).replace(/\\/g, '/').replace(/\.md$/, ''); +} + +function parsePost(filePath: string): PostDetail { + const raw = fs.readFileSync(filePath, 'utf8'); + const { data, content } = matter(raw); + const frontmatter = data as FrontMatter; + + return { + slug: createSlug(filePath), + title: frontmatter.title ?? path.basename(filePath, '.md'), + date: frontmatter.date ?? '1970-01-01', + description: frontmatter.description ?? '', + category: frontmatter.category ?? '미분류', + tags: frontmatter.tag ?? [], + content, + } satisfies PostDetail; +} + +export function getAllPosts(): PostSummary[] { + return getMarkdownFiles(CONTENT_DIR) + .map(parsePost) + .map(({ content: _content, ...summary }) => summary) + .sort((a, b) => b.date.localeCompare(a.date)); +} + +export function getPostBySlug(slug: string): PostDetail | null { + const matchedPath = getMarkdownFiles(CONTENT_DIR).find((filePath) => createSlug(filePath) === slug); + + if (!matchedPath) { + return null; + } + + return parsePost(matchedPath); +} + +export function getCategoriesWithCount(posts: PostSummary[]) { + return posts.reduce>((acc, post) => { + acc[post.category] = (acc[post.category] ?? 0) + 1; + return acc; + }, {}); +} + +export function splitSlugToSegments(slug: string): string[] { + return slug.split('/').map(encodeURIComponent); +} diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 00000000..84ab714b --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,4 @@ +/// +/// + +// NOTE: This file should not be edited diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 00000000..55354d2e --- /dev/null +++ b/next.config.ts @@ -0,0 +1,10 @@ +import type { NextConfig } from 'next'; + +const nextConfig: NextConfig = { + output: 'export', + images: { + unoptimized: true, + }, +}; + +export default nextConfig; diff --git a/package.json b/package.json index 293c93b8..c1096cbc 100644 --- a/package.json +++ b/package.json @@ -1,73 +1,27 @@ { - "name": "gatsby-blog", + "name": "suu3-blog-next", "private": true, - "description": "A starter for a blog powered by Gatsby and Markdown", - "version": "1.5.0", + "description": "Next.js blog migrated from Gatsby", + "version": "2.0.0", "author": "suu3", "dependencies": { - "@fontsource-variable/montserrat": "^5.0.4", - "@fontsource/merriweather": "^5.0.3", - "@svgr/webpack": "^8.1.0", - "clsx": "^2.0.0", - "framer-motion": "^10.16.4", - "gatsby": "^5.11.0", - "gatsby-plugin-feed": "^5.11.0", - "gatsby-plugin-google-adsense": "^1.1.3", - "gatsby-plugin-google-tagmanager": "^5.13.1", - "gatsby-plugin-image": "^3.11.0", - "gatsby-plugin-manifest": "^5.11.0", - "gatsby-plugin-postcss": "^6.12.0", - "gatsby-plugin-sharp": "^5.11.0", - "gatsby-plugin-svgr": "3.0.0-beta.0", - "gatsby-remark-autolink-headers": "^6.12.0", - "gatsby-remark-codepen": "^0.1.1", - "gatsby-remark-copy-relative-linked-files": "^1.5.0", - "gatsby-remark-images": "^7.11.0", - "gatsby-remark-prismjs": "^7.11.0", - "gatsby-remark-responsive-iframe": "^6.11.1", - "gatsby-source-filesystem": "^5.11.0", - "gatsby-transformer-remark": "^6.11.0", - "gatsby-transformer-sharp": "^5.11.0", - "lodash": "^4.17.21", - "postcss": "^8.4.30", - "prismjs": "^1.29.0", - "react": "^18.1.0", - "react-dom": "^18.1.0" + "gray-matter": "^4.0.3", + "next": "^15.2.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { - "@babel/preset-typescript": "^7.21.5", - "@changesets/changelog-github": "^0.5.0", - "@changesets/cli": "^2.27.10", - "@csstools/postcss-global-data": "^3.0.0", - "@types/node": "^20.2.5", - "@types/react": "^18.2.8", - "@types/react-dom": "^18.2.4", - "eslint-plugin-flowtype": "^8.0.3", - "gatsby-plugin-pnpm": "^1.2.10", - "gh-pages": "^5.0.0", - "postcss-custom-media": "^11.0.5", - "prettier": "^2.8.8" - }, - "homepage": "https://suu3.github.io/", - "keywords": [ - "gatsby" - ], - "license": "0BSD", - "repository": { - "type": "git", - "url": "git+https://github.com/suu3/suu3.github.io.git" + "@types/node": "^22.13.10", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "typescript": "^5.8.2", + "tailwindcss": "^4.1.12", + "@tailwindcss/postcss": "^4.1.12" }, "scripts": { - "deploy": "gatsby clean && gatsby build && gh-pages -d public -b deploy", - "build": "gatsby build", - "develop": "gatsby develop", - "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"", - "start": "gatsby develop", - "serve": "gatsby serve", - "clean": "gatsby clean", - "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1" - }, - "peerDependencies": { - "gatsby": "^5.0.0 || ~4.x.x" + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" } } diff --git a/postcss.config.ts b/postcss.config.ts index 4ef64606..297374d8 100644 --- a/postcss.config.ts +++ b/postcss.config.ts @@ -1,13 +1,7 @@ -import postcssCustomMedia from "postcss-custom-media" -import postcssGlobalData from "@csstools/postcss-global-data" +const config = { + plugins: { + '@tailwindcss/postcss': {}, + }, +}; -module.exports = { - plugins: [ - postcssGlobalData({ - files: [ - "src/styles/media-query.css", // 커스텀 미디어 쿼리 파일 경로 - ], - }), - postcssCustomMedia(), // 커스텀 미디어 플러그인 - ], -} +export default config; diff --git a/static/favicon.ico b/public/favicon.ico similarity index 100% rename from static/favicon.ico rename to public/favicon.ico diff --git a/static/favicons/favicon-144x144.png b/public/favicons/favicon-144x144.png similarity index 100% rename from static/favicons/favicon-144x144.png rename to public/favicons/favicon-144x144.png diff --git a/static/favicons/favicon-192x192.png b/public/favicons/favicon-192x192.png similarity index 100% rename from static/favicons/favicon-192x192.png rename to public/favicons/favicon-192x192.png diff --git a/static/favicons/favicon-48x48.png b/public/favicons/favicon-48x48.png similarity index 100% rename from static/favicons/favicon-48x48.png rename to public/favicons/favicon-48x48.png diff --git a/static/favicons/favicon-512x512.png b/public/favicons/favicon-512x512.png similarity index 100% rename from static/favicons/favicon-512x512.png rename to public/favicons/favicon-512x512.png diff --git a/static/favicons/favicon-72x72.png b/public/favicons/favicon-72x72.png similarity index 100% rename from static/favicons/favicon-72x72.png rename to public/favicons/favicon-72x72.png diff --git a/static/favicons/favicon-96x96.png b/public/favicons/favicon-96x96.png similarity index 100% rename from static/favicons/favicon-96x96.png rename to public/favicons/favicon-96x96.png diff --git a/static/fonts/FiraCode-Medium.woff2 b/public/fonts/FiraCode-Medium.woff2 similarity index 100% rename from static/fonts/FiraCode-Medium.woff2 rename to public/fonts/FiraCode-Medium.woff2 diff --git a/static/images/background.svg b/public/images/background.svg similarity index 100% rename from static/images/background.svg rename to public/images/background.svg diff --git a/static/images/dummy.jpg b/public/images/dummy.jpg similarity index 100% rename from static/images/dummy.jpg rename to public/images/dummy.jpg diff --git a/static/images/github.svg b/public/images/github.svg similarity index 100% rename from static/images/github.svg rename to public/images/github.svg diff --git a/static/images/grid-vert-2.svg b/public/images/grid-vert-2.svg similarity index 100% rename from static/images/grid-vert-2.svg rename to public/images/grid-vert-2.svg diff --git a/static/images/grid-vert.svg b/public/images/grid-vert.svg similarity index 100% rename from static/images/grid-vert.svg rename to public/images/grid-vert.svg diff --git a/static/images/home.svg b/public/images/home.svg similarity index 100% rename from static/images/home.svg rename to public/images/home.svg diff --git a/static/images/icon.png b/public/images/icon.png similarity index 100% rename from static/images/icon.png rename to public/images/icon.png diff --git a/static/images/left-arrow.svg b/public/images/left-arrow.svg similarity index 100% rename from static/images/left-arrow.svg rename to public/images/left-arrow.svg diff --git a/static/images/moon.svg b/public/images/moon.svg similarity index 100% rename from static/images/moon.svg rename to public/images/moon.svg diff --git a/static/images/right-arrow.svg b/public/images/right-arrow.svg similarity index 100% rename from static/images/right-arrow.svg rename to public/images/right-arrow.svg diff --git a/static/images/sun.svg b/public/images/sun.svg similarity index 100% rename from static/images/sun.svg rename to public/images/sun.svg diff --git a/static/robots.txt b/public/robots.txt similarity index 100% rename from static/robots.txt rename to public/robots.txt diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 00000000..47ee756c --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "Suu.Blog", + "short_name": "Suu.Blog", + "description": "Suu3 기술 블로그", + "start_url": "/", + "display": "standalone", + "background_color": "#f7f7f7", + "theme_color": "#ff6737", + "icons": [ + { + "src": "/favicons/favicon-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/favicons/favicon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/src/components/@layout/bio/bio.module.css b/src/components/@layout/bio/bio.module.css deleted file mode 100644 index 1f3dd4c4..00000000 --- a/src/components/@layout/bio/bio.module.css +++ /dev/null @@ -1,49 +0,0 @@ -.profile { - border-radius: 10px; - border: 1px solid var(--color-bg-surface-02); - overflow: hidden; - - header { - padding: 16px; - display: flex; - align-items: center; - column-gap: 16px; - background: var(--color-theme-04); - - border-bottom: 1px solid var(--color-bg-surface-02); - - img { - border-radius: 50%; - border: 1px solid var(--color-bg-surface-02); - } - - > div { - display: flex; - flex-direction: column; - row-gap: 8px; - font-family: MONOSPACE; - - > span:first-child { - color: var(--color-bg-surface-02); - font-size: 16px; - font-weight: 700; - line-height: 16px; - } - > span:last-child { - color: var(--color-theme-02); - font-size: 14px; - font-weight: 700; - line-height: 14px; - } - } - } - - p { - padding: 16px 24px; - margin: 0; - color: var(--color-neutral-02); - font-size: 14px; - font-weight: 400; - line-height: 20.3px; - } -} diff --git a/src/components/@layout/bio/index.tsx b/src/components/@layout/bio/index.tsx deleted file mode 100644 index b6ee60e8..00000000 --- a/src/components/@layout/bio/index.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Bio component that queries for data - * with Gatsby's useStaticQuery component - * - * See: https://www.gatsbyjs.com/docs/how-to/querying-data/use-static-query/ - */ - -import * as React from "react" -import { useStaticQuery, graphql } from "gatsby" -import { profile } from "./bio.module.css" -import profileImage from "./profile.gif" - -const Bio = () => { - const data = useStaticQuery(graphql` - query BioQuery { - site { - siteMetadata { - author { - name - } - } - } - } - `) - - // Set these values by editing "siteMetadata" in gatsby-config.js - const author = data.site.siteMetadata?.author - - return ( -
-
- profile image - -
- @{author?.name} - Developer -
-
-

그림과 음악이 좋은 프론트엔드 개발자

-
- ) -} - -export default Bio diff --git a/src/components/@layout/bio/profile.gif b/src/components/@layout/bio/profile.gif deleted file mode 100644 index 01bcc4c9d984381cc497de6f4cc44d9bc06662a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2284 zcmciCX;72b8o=@QeaT7^3?K|rmXHK65ZMG1NA$i(fPhAJMUa9CC?KL%WET=b1O!<` zDT2^cD1s~sLRktJB3L#-uI#3SO=VFlT&#P!vD1FI_tX7yduGl&GtZp!?KgiXXGbRE zC=9?rGyoWE;0`!=dD`2#IarxlD1m@t^&WxPteAAgRXl=$6vYe;3p*PTHDzS<%-vw) zI3wLGxKr#k)gGUcm|9fR^tiQtW4^SY^5LVYnf@oAHa0fC{QaK~a$|;^6%<+$a;r9c zrQJ5N35-oa4-XwhojvFy~3jP}AkZkDWyhUlDG2%g$TsXWz-g z=yCgNw)@tI#E)|r35Gx0FGq)Y#?#J6+6#`Ph|&_1E+q;xi~_>4qe7GOw(|V!PsUy@ zq7;{KRFrQN0V?IS_LZg7A8y~Nt9Rm@Ym99w<^9y^S8*$ar(E9I-W}iI*K@O@w5F{@ z`JYwggT#rdM_h;6%7Kh0_mx3#;UOTutpcLgmYbb081U82RW$y0%t|ZJ9BuC3abj`q zy&v0xL|DyzEn2YH2)x4Z zin1oIK4qhano5?JA=Eu`q_>h_|`Z4KF;MXZ_dHvD^IF_?8)+LeDPA=QrvK2 z?8$GZ9bAuC&PQCNT2&{na`vlFrnE+$`w=1&dg+qh=+QQ|7#@yy< zKTU&xxC5>)ze8jnlcPRR9<1wty-FT8z@OL?wdB-(hm&C z;o=kHxPjzS9g>>rZU8E?o(r^eaIEYbevw{rq=xxMr)UUZYn&ItQ{lNhf<_BC8ewar zA1rk74fYUnlYHY~99zIh4|k7eWSHdgz--ei6cM91zz)aCqNx@fEP}zxuS+Q$b0@n) zcQ@f6D3wvwV6uM`gpuacT_D{4-d>56OxRt&4aAPPHRC3lI;n)wZV*Gk0<#O!rxXHu z1>0)1Ylt>GFIz<*(66YNRqFcEkV+;gkd>dIgu?607?Z)|Bmx;?!eV|7W&2vE&jbNZ zKCbrAo$4-z^9|_Ia+_VLGbQk4qmwdX%qB^DAx#ZR+rsJIr)d}su|xRAIq^gEe7x_B z#N&o$FjQ9o$usn@myVH_=t#S8pNpCatD(lD8dZ`#F+{$YKvr%*_!L#6!7aPZhigoG zhlH)Q>@Ho4GsU3yAzqq`D%{BjEx1}ng+zJlxlkqE84dp2!}wf{c=t0G9pXxJ4gJ53Mo{1v8a!gVDp5hB_D>qa6w<^S@?CNG^V4Zu zM?)NR)JEi7iJ3!SejKTdl2M9!vle%1s8-1q#zV%<{xN3S5|Z2f%gFCR=-Vh1Ww5Jdlsb)AqJh%@#zZ>x9J!7}Fwno! z*2uDT3erWVBG;8AM6A{7zDu>gkesSCp;7(xfLxk16LS4x;F1n-ZVB@ahar__y27un zey$piU@-xt?GHh-H4pj=wjLfaJ!~bD1c|tPUTgK;LJUq%W=UGlGgULsKrxjmutb2b z-(%B~wTx4jU3jJ=?qYeT12LhqM)qXCMqi((eLp3?z^gBN5i8#QI_w7A9QdY9z1~8g zZS?L_u@GAqn3V|LWS*m}8pHvI9E8S&S{!_-9s}=HSpSwgIXF#v(`PkP;M>D zUI~$HPo+V2f}`izF)$8Jf>V#V#WK=OvJZooOfU12O~0k#9In$;^S!Up!E%2wWxu%( zP+@2Ox(L-k_6%QJ$TT5Pf>{5ib4##HsVGbI;ezUtW=Q*nH;#+HwmE8 zGQ(X1H2Lx*8u2eQ1M`ylx}UH}L9C$hTFld~EC}&hdn=2S-JPjq0TD+~ByhWkoM=OL zgINLtbDsoHM-^~)wk}wbfcH41$B#90F0;O~=Gd;GyK{N7-C6I8u>;>cD~k=hDJit` z$Re@#&)G-S^u>zbynd*o-051Yd { - const [clicked, setClicked] = useState(getCategoryPaths(pathname)) - const { totalCount, group } = categories - - const listClassName = (category: string) => - clicked === category ? active : inactive - - const handleClickMenu = (category: string) => { - setClicked(category) - } - - return ( -
-
-

Category

- {/*
- - - -
*/} -
-
    - handleClickMenu("/home")}> -
  • 전체 보기 ({totalCount})
  • - - {group.map(({ fieldValue, totalCount }: string) => ( - handleClickMenu(`/${fieldValue}`)} - > -
  • - {fieldValue} ({totalCount}) -
  • - - ))} -
-
- ) -} - -export default CategoryMenu diff --git a/src/components/@layout/footer/footer.module.css b/src/components/@layout/footer/footer.module.css deleted file mode 100644 index 7b9f2621..00000000 --- a/src/components/@layout/footer/footer.module.css +++ /dev/null @@ -1,34 +0,0 @@ -.wrapper { - color: var(--color-neutral-01); - border-top: 1px solid var(--color-bg-surface-02); - margin-top: 50px; - display: flex; - flex-direction: column; - align-items: center; - gap: 13px; - justify-content: center; - - padding: 36px 10px 64px 10px; - font-family: monospace; - - font-size: 14px; - line-height: 14px; - font-weight: 500; - - ul, - p, - li { - list-style: none; - margin: 0; - padding: 0; - } - - a { - text-decoration: underline; - } -} - -.icon { - width: 24px; - height: 24px; -} diff --git a/src/components/@layout/footer/index.tsx b/src/components/@layout/footer/index.tsx deleted file mode 100644 index e8961905..00000000 --- a/src/components/@layout/footer/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react" -import { wrapper, icon } from "./footer.module.css" -import { ReactComponent as Github } from "/static/images/github.svg" - -const Footer = () => { - return ( -
-

@suu3

- -

- © {new Date().getFullYear()}, Built with{` `} - Gatsby -

-
- ) -} - -export default Footer diff --git a/src/components/@layout/index.tsx b/src/components/@layout/index.tsx deleted file mode 100644 index b5ac9794..00000000 --- a/src/components/@layout/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as React from "react" -import Navigation from "./navigation" -import Footer from "./footer" - -const Layout = ({ location, children }) => { - const rootPath = `${__PATH_PREFIX__}` - const isRootPath = location.pathname === rootPath - - return ( - <> - -
-
- {children} -
-