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}개의 포스트
+
+ {posts.map((post) => (
+ -
+
+
{post.title}
+ {post.date}
+
+
+ ))}
+
+
+ ← 카테고리 목록
+
+
+ );
+}
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 (
+
+ 카테고리 모아보기
+
+ {categories.map(([category, count]) => (
+ -
+
+ {category}
+ {count}
+
+
+ ))}
+
+
+ );
+}
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 (
+
+
+
+
+ {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 (
-
-
-
그림과 음악이 좋은 프론트엔드 개발자
-
- )
-}
-
-export default Bio
diff --git a/src/components/@layout/bio/profile.gif b/src/components/@layout/bio/profile.gif
deleted file mode 100644
index 01bcc4c9..00000000
Binary files a/src/components/@layout/bio/profile.gif and /dev/null differ
diff --git a/src/components/@layout/categoryMenu/category-menu.module.css b/src/components/@layout/categoryMenu/category-menu.module.css
deleted file mode 100644
index 2f8aa59e..00000000
--- a/src/components/@layout/categoryMenu/category-menu.module.css
+++ /dev/null
@@ -1,60 +0,0 @@
-.wrapper {
- display: flex;
- flex-direction: column;
- margin-top: 32px;
-
- background: var(--color-theme-04);
- border: 1px solid var(--color-bg-surface-02);
- border-radius: 10px;
-}
-
-.header {
- padding: 16px 24px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid var(--color-bg-surface-02);
-
- p {
- margin: 0;
- color: var(--color-bg-surface-02);
-
- font-family: monospace;
- font-size: 16px;
- font-weight: 700;
- line-height: 16px;
- }
-}
-
-.wrapper ul {
- display: flex;
- flex-direction: column;
- gap: 14px;
-
- padding: 16px 24px;
- color: var(--color-neutral-01);
- margin: 0;
-}
-
-.wrapper li {
- font-size: 16px;
- line-height: 16px;
- font-weight: 400;
- list-style: none;
- margin: 0;
-
- transition: all var(--transition-duration) var(--transition-timing-function);
-}
-
-.wrapper li:hover,
-.wrapper li:active,
-.wrapper .active {
- color: var(--color-theme-01);
- font-weight: 700;
-}
-
-@media (max-width: 42rem) {
- .wrapper {
- grid-template-columns: repeat(3, 1fr);
- }
-}
diff --git a/src/components/@layout/categoryMenu/index.tsx b/src/components/@layout/categoryMenu/index.tsx
deleted file mode 100644
index 3279bc4d..00000000
--- a/src/components/@layout/categoryMenu/index.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Link } from "gatsby"
-import React, { useState } from "react"
-import { wrapper, header, active, inactive } from "./category-menu.module.css"
-import { HOME_URL } from "../../../constants/links"
-import { getCategoryPaths } from "../../../libs/paths"
-
-const CategoryMenu = ({ categories = {}, pathname = "" }) => {
- 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 (
-
-
-
- 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 (
-
- )
-}
-
-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}
-
-
- >
- )
-}
-
-export default Layout
diff --git a/src/components/@layout/main/index.tsx b/src/components/@layout/main/index.tsx
deleted file mode 100644
index 2aa7429c..00000000
--- a/src/components/@layout/main/index.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import React, { ReactNode } from "react"
-import { motion } from "framer-motion"
-
-const TransitionMain = ({
- children,
- className,
-}: {
- children: ReactNode
- className?: string
-}) => {
- return (
- //
- {children}
- //
- )
-}
-
-export default TransitionMain
diff --git a/src/components/@layout/navigation/index.tsx b/src/components/@layout/navigation/index.tsx
deleted file mode 100644
index b36db049..00000000
--- a/src/components/@layout/navigation/index.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import React, { memo, useState } from "react"
-import { wrapper, inner, hide, logo } from "./navigation.module.css"
-import { Link } from "gatsby"
-import { SplitText } from "../../split-text"
-import clsx from "clsx"
-// import throttle from "lodash/throttle"
-import { StaticImage } from "gatsby-plugin-image"
-import { HOME_URL } from "../../../constants/links"
-import ThemeBtn from "./themeBtn"
-
-const Navigation = () => {
- const [showNavigation, setShowNavigation] = useState(true)
- const [prevScrollPos, setPrevScrollPos] = useState(0)
-
- // useEffect(() => {
- // const handleScroll = throttle(() => {
- // const currentScrollPos = window.scrollY
-
- // if (prevScrollPos > currentScrollPos) {
- // setShowNavigation(true)
- // } else {
- // setShowNavigation(false)
- // }
-
- // setPrevScrollPos(currentScrollPos)
- // }, 300)
-
- // if (typeof window !== "undefined") {
- // setPrevScrollPos(window.scrollY)
- // window.addEventListener("scroll", handleScroll)
- // }
-
- // return () => {
- // if (typeof window !== "undefined") {
- // window.removeEventListener("scroll", handleScroll)
- // }
- // }
- // }, [prevScrollPos])
-
- return (
-
- )
-}
-
-export default memo(Navigation)
diff --git a/src/components/@layout/navigation/navigation.module.css b/src/components/@layout/navigation/navigation.module.css
deleted file mode 100644
index 8b2a5039..00000000
--- a/src/components/@layout/navigation/navigation.module.css
+++ /dev/null
@@ -1,70 +0,0 @@
-.hide {
- transform: translateY(calc(var(--navigation-height) * -1));
-}
-
-.wrapper {
- transform: 0;
-
- position: sticky;
- top: 0;
- left: 0;
- right: 0;
-
- z-index: var(--fixed-z-index);
- font-size: var(--fontSize-3);
- margin: 0;
-
- height: var(--navigation-height);
- background: var(--color-theme-04);
- /*backdrop-filter: blur(10px);*/
- border-bottom: 1px solid var(--color-bg-surface-02);
- /* box-shadow: rgba(0, 0, 0, 0.5) 0px 1px 3px, rgba(0, 0, 0, 1) 0px 1px 2px; */
- transition: transform var(--transition-duration)
- var(--transition-timing-function);
-
- /* background-color: #e5e5f7;
- opacity: 0.5;
- background-image: var(--transition-timing-function)-gradient(#444cf7 1px, transparent 1px),
- var(--transition-timing-function)-gradient(to right, #444cf7 1px, #e5e5f7 1px);
- background-size: 10px 10px; */
-}
-
-.inner {
- margin: auto;
- max-width: var(--maxWidth-wrapper);
-
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- width: 100%;
- height: 100%;
-}
-
-.inner li {
- list-style: none;
- /* display: inline-block; */
- margin: 0;
-
- font-weight: 700;
-
- > a {
- display: flex;
- align-items: center;
- }
-}
-
-@media screen and (max-width: 1300px) {
- .wrapper {
- padding: var(--spacing-3) 0;
- }
-
- .inner {
- padding: 0 var(--spacing-3);
- }
-}
-
-.logo {
- margin-right: 0.5rem;
- border-radius: 0.5rem;
-}
diff --git a/src/components/@layout/navigation/themeBtn/index.tsx b/src/components/@layout/navigation/themeBtn/index.tsx
deleted file mode 100644
index 6134aac8..00000000
--- a/src/components/@layout/navigation/themeBtn/index.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { motion } from "framer-motion"
-import React, { useEffect, useState } from "react"
-import { ReactComponent as Sun } from "/static/images/sun.svg"
-import { ReactComponent as Moon } from "/static/images/moon.svg"
-import { wrapper, inner, btn } from "./theme-btn.module.css"
-
-const ThemeBtn = () => {
- const [theme, setTheme] = useState("light")
-
- useEffect(() => {
- const storedTheme = localStorage.getItem("theme")
- if (storedTheme) {
- setTheme(storedTheme)
- } else {
- const prefersDarkMode = window.matchMedia(
- "(prefers-color-scheme: dark)"
- ).matches
-
- setTheme(prefersDarkMode ? "dark" : "light")
- }
- }, [])
-
- useEffect(() => {
- toggleClass(theme)
- }, [theme])
-
- const toggleClass = newTheme => {
- //toggle
- const body = document.querySelector("body")
- if (newTheme === "light") {
- body?.classList.add("light")
- body?.classList.remove("dark")
- } else {
- body?.classList.add("dark")
- body?.classList.remove("light")
- }
- }
- const toggleTheme = () => {
- const newTheme = theme === "light" ? "dark" : "light"
- setTheme(newTheme)
- localStorage.setItem("theme", newTheme)
- }
-
- const spring = {
- type: "spring",
-
- stiffness: 700,
- damping: 30,
- }
-
- return (
-
-
-
- {theme === "light" ? : }
-
-
-
- )
-}
-export default ThemeBtn
diff --git a/src/components/@layout/navigation/themeBtn/theme-btn.module.css b/src/components/@layout/navigation/themeBtn/theme-btn.module.css
deleted file mode 100644
index 0af7f043..00000000
--- a/src/components/@layout/navigation/themeBtn/theme-btn.module.css
+++ /dev/null
@@ -1,29 +0,0 @@
-.wrapper {
- box-shadow: 0 0 0 1px inset;
- width: 78px;
-
- border-radius: 99px;
- height: 46px;
- padding: 6px 8px;
-}
-
-.inner {
- border-radius: 99px;
- height: 100%;
- width: 100%;
-}
-
-.btn {
- box-shadow: none;
- width: 34px;
- height: 34px;
- border: none;
- box-shadow: 0 0 0 1px inset;
- border-radius: 50%;
-
- display: flex;
- align-items: center;
- justify-content: center;
-
- cursor: pointer;
-}
diff --git a/src/components/@layout/seo/index.tsx b/src/components/@layout/seo/index.tsx
deleted file mode 100644
index 25f20736..00000000
--- a/src/components/@layout/seo/index.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * SEO component that queries for data with
- * Gatsby's useStaticQuery React hook
- *
- * See: https://www.gatsbyjs.com/docs/how-to/querying-data/use-static-query/
- */
-
-import * as React from "react"
-import { useStaticQuery, graphql } from "gatsby"
-
-const Seo = ({ description, title, children }) => {
- const { site } = useStaticQuery(
- graphql`
- query {
- site {
- siteMetadata {
- title
- description
- social {
- twitter
- }
- }
- }
- }
- `
- )
-
- const metaDescription = description || site.siteMetadata.description
- const defaultTitle = site.siteMetadata?.title
-
- return (
- <>
- {defaultTitle ? `${title} | ${defaultTitle}` : title}
-
-
-
-
-
-
-
-
- {children}
- >
- )
-}
-
-export default Seo
diff --git a/src/components/@layout/tableOfContents/index.tsx b/src/components/@layout/tableOfContents/index.tsx
deleted file mode 100644
index 9de5abf0..00000000
--- a/src/components/@layout/tableOfContents/index.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React, { useEffect, useMemo } from "react"
-import { wrapper, inner, header, active } from "./table-contents.module.css"
-import { useActiveHash } from "../../../hooks/useActiveHash"
-
-interface TableContentsProps {
- content: string
-}
-
-const TableContents = ({ content = "" }: TableContentsProps) => {
- let targetedIds = useMemo(() => {
- if (typeof window === "undefined") return
- const dummyDOM = document.createElement("html")
- dummyDOM.innerHTML = content
- const justAnchors = dummyDOM.querySelectorAll(`a`)
-
- let anchorList = []
- justAnchors.forEach(a => {
- anchorList.push(decodeURI(a.hash.replace("#", "")))
- })
-
- return anchorList
- }, [])
-
- const activeHash = useActiveHash(targetedIds)
-
- useEffect(() => {
- const ToClinks = document.querySelectorAll(`.table-of-contents a`)
-
- ToClinks.forEach(a => {
- a.classList.remove(active)
- })
-
- const activeLink = document.querySelectorAll(
- `.table-of-contents a[href="${"#" + encodeURI(activeHash)}"]`
- )
-
- if (activeLink.length) {
- activeLink[0].classList.add(active)
- }
- }, [activeHash])
-
- if (!content) return null
- return (
-
- )
-}
-
-export default TableContents
diff --git a/src/components/@layout/tableOfContents/table-contents.module.css b/src/components/@layout/tableOfContents/table-contents.module.css
deleted file mode 100644
index f0549869..00000000
--- a/src/components/@layout/tableOfContents/table-contents.module.css
+++ /dev/null
@@ -1,90 +0,0 @@
-.wrapper {
- margin-bottom: 44px;
-}
-
-.inner {
- /* position: fixed; */
- min-width: 288px;
- max-height: calc(100vh - 200px);
- overflow: auto;
-
- list-style: none;
-
- /* box-shadow: 6px 6px 0px -1px var(--color-neutral-01); */
- /* border: 2px solid var(--color-bg-surface-02); */
-
- > div {
- font-weight: 700;
- padding: var(--spacing-4);
- }
-
- background-color: var(--color-theme-04);
- border: 1px solid var(--color-bg-surface-02);
- border-radius: 10px;
-}
-
-.inner a {
- color: var(--color-neutral-01);
- font-size: var(--fontSize-0);
-
- position: relative;
- display: inline-block;
- overflow: hidden;
-
- margin: 0;
- padding-bottom: var(--spacing-1);
-
- &.active,
- &:hover {
- color: var(--color-theme-01);
- font-weight: 700;
- }
-
- &.active {
- animation-name: pulse;
- animation-timing-function: var(--transition-timing-function);
- animation-duration: var(--transition-duration);
- animation-fill-mode: forwards;
- }
-}
-
-@keyframes pulse {
- from {
- transform: scale3d(1, 1, 1);
- }
-
- to {
- transform: scale3d(1.1, 1.1, 1.1);
- }
-}
-
-.inner p {
- margin-bottom: 0;
-}
-.inner ul,
-.inner li {
- margin: 0;
-}
-
-.inner li {
- list-style-type: none;
- margin-left: var(--spacing-3);
-}
-
-.inner ul:first-child {
- margin-left: 0;
-
- > li {
- margin-left: 0;
- }
-}
-.header {
- border-bottom: 1px solid var(--color-bg-surface-02);
- padding: 16px 24px;
- color: var(--color-bg-surface-02);
-
- font-family: MONOSPACE;
- font-size: 16px;
- line-height: 16px;
- font-weight: 700;
-}
diff --git a/src/components/@layout/utterances/index.tsx b/src/components/@layout/utterances/index.tsx
deleted file mode 100644
index e04e5afd..00000000
--- a/src/components/@layout/utterances/index.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React, { createRef, useLayoutEffect } from "react"
-
-const src = "https://utteranc.es/client.js"
-
-const Utterances = React.memo(() => {
- const containerRef = createRef()
-
- useLayoutEffect(() => {
- const utterances = document.createElement("script")
-
- const attributes = {
- src,
- repo: "suu3/blog-utterance",
- "issue-term": "pathname",
- label: "comment",
- theme: "github-light",
- crossOrigin: "anonymous",
- async: "true",
- }
-
- Object.entries(attributes).forEach(([key, value]) => {
- utterances.setAttribute(key, value)
- })
-
- if (containerRef.current) containerRef.current.appendChild(utterances)
- }, [])
-
- return
-})
-
-Utterances.displayName = "Utterances"
-
-export default Utterances
diff --git a/src/components/@templates/post-list-template.module.css b/src/components/@templates/post-list-template.module.css
deleted file mode 100644
index 503ce667..00000000
--- a/src/components/@templates/post-list-template.module.css
+++ /dev/null
@@ -1,54 +0,0 @@
-.main-wrapper {
- display: flex;
- justify-content: space-between;
- column-gap: 24px;
-}
-
-.main {
- width: 100%;
-
- h1 {
- margin: 0;
- padding: 20px 24px;
- font-family: MONOSPACE;
- font-size: 32px;
- line-height: 32px;
- font-weight: 700;
- color: var(--color-bg-surface-02);
- border: 1px solid var(--color-bg-surface-02);
- border-radius: 10px;
- }
-}
-
-.ol {
- > li {
- margin-bottom: 0;
- }
-
- list-style: none;
- display: grid;
- grid-template-columns: 1fr 1fr 1fr;
- row-gap: 34px;
- column-gap: 24px;
-
- margin-top: 32px;
- margin-bottom: 122px;
-
- @media (--desktop) {
- grid-template-columns: 1fr 1fr;
- }
-
- @media (max-width: 40rem) {
- grid-template-columns: 1fr;
- }
-}
-
-.side-menu {
- width: 100%;
- max-width: 288px;
-
- /* Media queries */
- @media (--desktop) {
- display: none;
- }
-}
diff --git a/src/components/@templates/post-list-template.tsx b/src/components/@templates/post-list-template.tsx
deleted file mode 100644
index 4bc2e123..00000000
--- a/src/components/@templates/post-list-template.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import * as React from "react"
-import { Link, navigate } from "gatsby"
-import Layout from "../@layout"
-import PostListItem from "../post-list-item"
-import Pagination from "../pagination"
-import CategoryMenu from "../@layout/categoryMenu"
-import TransitionMain from "../@layout/main"
-import { POST_PER_PAGE } from "../../constants/page"
-import {
- main,
- mainWrapper,
- ol,
- sideMenu,
-} from "./post-list-template.module.css"
-import Bio from "../@layout/bio"
-import { getCategoryPaths } from "../../libs/paths"
-
-const PostListTemplate = ({ data, location, pageContext }) => {
- const { currentPage, totalCount } = pageContext
- const categories = data.allCategoriesInfo
- const posts = data.allMarkdownRemark.nodes
- const tags = data.allTagsInfo
-
- const pathname = getCategoryPaths(location.pathname)
- const handlePageChange = (page: number) => {
- navigate(`${pathname}/${page}/`)
- }
-
- const renderPosts =
- posts.length === 0 ? (
- 게시물이 없습니다.
- ) : (
- posts.map(post => {
- const title = post.frontmatter.title || post.fields.slug
-
- return (
-
-
-
-
-
- )
- })
- )
-
- return (
-
-
-
-
- Latest
- {renderPosts}
-
-
-
-
-
- )
-}
-
-export default PostListTemplate
diff --git a/src/components/arrow.module.css b/src/components/arrow.module.css
deleted file mode 100644
index 207a5a65..00000000
--- a/src/components/arrow.module.css
+++ /dev/null
@@ -1,50 +0,0 @@
-.arrow {
- --arrow-width: 5px;
-
- display: inline-block;
-
- &.left {
- transform: rotate(0deg);
- }
- &.right {
- transform: rotate(180deg);
- }
-}
-
-.arrow svg {
- display: inline-block;
- /* width: calc(var(--arrow-width) * 3);
- height: calc(var(--arrow-width) * 3); */
- /* border-bottom: var(--arrow-width) solid var(--color-neutral-01);
- border-right: var(--arrow-width) solid var(--color-neutral-01);
- */
- transform: rotate(90deg);
- margin: calc(var(--arrow-width) * -2.2);
- animation: animate 2s infinite;
-}
-
-.arrow svg:nth-child(1) {
- animation-delay: 0s;
-}
-
-.arrow svg:nth-child(2) {
- animation-delay: -0.2s;
-}
-
-.arrow svg:nth-child(3) {
- animation-delay: -0.4s;
-}
-
-@keyframes animate {
- 0% {
- opacity: 0;
- transform: rotate(90deg) translate(-50%, -20px);
- }
- 50% {
- opacity: 1;
- }
- 100% {
- opacity: 0;
- transform: rotate(90deg) translate(-50%, 20px);
- }
-}
diff --git a/src/components/arrow.tsx b/src/components/arrow.tsx
deleted file mode 100644
index 11833ed0..00000000
--- a/src/components/arrow.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import clsx from "clsx"
-import * as styles from "./arrow.module.css"
-import React from "react"
-
-interface ArrowProps {
- direction?: "left" | "right"
- className?: string
- width?: number
- height?: number
- color?: string
-}
-const Arrow = ({
- direction = "left",
- className,
- width = 20,
- height = 20,
- color = "var(--color-neutral-01)",
-}: ArrowProps) => {
- return (
-
- {Array.from({ length: 3 }).map(() => (
-
- ))}
-
- )
-}
-
-export default Arrow
diff --git a/src/components/pagination.module.css b/src/components/pagination.module.css
deleted file mode 100644
index 192d6ade..00000000
--- a/src/components/pagination.module.css
+++ /dev/null
@@ -1,71 +0,0 @@
-.wrapper {
- display: flex;
- align-items: center;
- justify-content: center;
- margin-top: 86px;
-}
-
-.btn {
- width: 40px;
- height: 40px;
- border-radius: 10px;
- background-color: var(--color-theme-04);
- border: 1px solid var(--color-bg-surface-02);
- color: var(--color-bg-surface-02);
-
- font-family: IBM Plex Mono;
- font-size: 14px;
- font-weight: 400;
- line-height: 14px;
- cursor: pointer;
-
- &.prev-btn {
- margin-right: 35px;
- }
-
- &.next-btn {
- margin-left: 35px;
- }
-
- &.disabled {
- background-color: var(--color-neutral-03);
- border: 1px solid var(--color-neutral-02);
- path {
- stroke: var(--color-neutral-02);
- }
-
- pointer-events: none;
- }
-}
-
-.pages {
- display: flex;
- column-gap: 13px;
- margin: 0;
-}
-
-.page {
- display: flex;
- justify-content: center;
- align-items: center;
- border-radius: 10px;
- width: 40px;
- height: 40px;
- border: 1px solid var(--color-bg-surface-02);
- color: var(--color-bg-surface-02);
-
- font-family: IBM Plex Mono;
- font-size: 14px;
- font-weight: 400;
- line-height: 14px;
-
- cursor: pointer;
-
- &.active {
- background-color: var(--color-theme-01);
- }
-
- &.ellipsis {
- cursor: default;
- }
-}
diff --git a/src/components/pagination.tsx b/src/components/pagination.tsx
deleted file mode 100644
index c89a2ebf..00000000
--- a/src/components/pagination.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import React from "react"
-import clsx from "clsx"
-import * as styles from "./pagination.module.css"
-import { ReactComponent as LeftArrow } from "/static/images/left-arrow.svg"
-import { ReactComponent as RightArrow } from "/static/images/right-arrow.svg"
-
-type PaginationProps = {
- pagesToShow?: number
- currentPage: number
- totalPages: number
- onPageChange: (page: number) => void
-}
-
-const Pagination = ({
- pagesToShow = 10,
- currentPage,
- totalPages,
- onPageChange,
-}: PaginationProps) => {
- const isFirstPage = currentPage === 1
- const isLastPage = currentPage === totalPages
- const ellipsis = "..."
- const halfPagesToShow = Math.floor(pagesToShow / 2) // 5 => 2, 6 => 3
-
- const handlePrevPage = () => {
- if (currentPage > 1) onPageChange(currentPage - 1)
- }
-
- const handleNextPage = () => {
- if (currentPage < totalPages) onPageChange(currentPage + 1)
- }
-
- const getPageNumbers = () => {
- if (totalPages <= pagesToShow)
- // 1. 전체 페이지 수가 보여줄 페이지 개수보다 작거나 같을 때 : ...없이 모든 페이지를 보여준다.
- return Array.from({ length: totalPages }, (_, i) => i + 1)
-
- // 2. 페이지 수가 pageToShow를 넘었을 때
- if (currentPage <= halfPagesToShow)
- // 2-1. 첫 번째 그룹 ex) 1 2 3 4 5 ... 40
- return [
- ...Array.from({ length: pagesToShow }, (_, i) => i + 1),
- ellipsis,
- totalPages,
- ]
- else if (currentPage > totalPages - halfPagesToShow)
- // 2-2. 중간 그룹 ex) 1 ... 2 3 4 5 ... 40
- return [
- 1,
- ellipsis,
- ...Array.from(
- { length: pagesToShow },
- (_, i) => totalPages - pagesToShow + i + 1
- ),
- ]
- // 2-3. 마지막 그룹 ex) 1 ... 36 37 38 39 40
- else
- return [
- 1,
- ellipsis,
- ...Array.from(
- { length: pagesToShow },
- (_, i) => currentPage - halfPagesToShow + i
- ),
- ellipsis,
- totalPages,
- ]
- }
-
- const renderPages = getPageNumbers().map((page, i) => {
- const isActive = page === currentPage
- const isEllipsis = page === ellipsis
- return (
- {
- if (isEllipsis) return
- onPageChange(Number(page))
- }}
- className={clsx(styles["page"], {
- [styles["active"]]: isActive,
- [styles["ellipsis"]]: isEllipsis,
- })}
- >
- {page}
-
- )
- })
-
- return (
-
-
-
{renderPages}
-
-
- )
-}
-
-export default Pagination
diff --git a/src/components/post-list-item.module.css b/src/components/post-list-item.module.css
deleted file mode 100644
index 253983a0..00000000
--- a/src/components/post-list-item.module.css
+++ /dev/null
@@ -1,164 +0,0 @@
-.box {
- position: relative;
- height: 407px;
- overflow: hidden;
-
- display: flex;
- flex-direction: column;
- border: 1px solid var(--color-bg-surface-02);
-
- transition: all var(--transition-duration) var(--transition-timing-function);
-
- &:hover {
- background-color: var(--color-theme-04);
- box-shadow: 4px 4px 0px 0px var(--color-bg-surface-02);
-
- .badge {
- transition: all var(--transition-duration)
- var(--transition-timing-function);
- background: var(--color-theme-03);
- }
-
- .thumbnail-cls {
- .img {
- transform: scale(1.2);
- }
- }
- }
-
- > header {
- font-family: MONOSPACE;
- font-weight: 500;
- font-size: 16px;
- line-height: 12px;
-
- background-color: var(--color-theme-03);
- color: #000000;
- padding: 12px 16px;
- border-bottom: 1px solid var(--color-bg-surface-02);
- }
-
- /* padding: var(--spacing-8) 0; */
-
- &:not(:first-child) {
- margin: var(--spacing-10) 0;
- }
-
- border-radius: 10px;
-}
-
-.thumbnail-cls {
- position: relative;
-
- > span {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- z-index: 2;
- font-size: 24px;
- font-weight: 700;
- color: white;
- font-family: monospace;
- }
-
- &::after {
- content: "";
- position: absolute;
- z-index: 1;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- right: 0;
- border: 0;
- background-color: rgba(0, 0, 0, 0.5);
- }
- border: 1px solid var(--color-bg-surface-02);
- height: 157px;
- border-radius: 10px;
- overflow: hidden;
-
- margin-bottom: 16px;
-
- .img {
- width: 100%;
-
- transition: transform var(--transition-duration)
- var(--transition-timing-function);
- }
-}
-
-.inner {
- padding: 16px;
- height: 100%;
-}
-
-@keyframes move {
- 0% {
- transform: translate(20px, 0);
- }
- 50% {
- transform: translate(30px, 0);
- }
- 100% {
- transform: translate(20px, 0);
- }
-}
-.box small {
- font-family: MONOSPACE;
- font-weight: 400;
- font-size: 14px;
- line-height: 14px;
-
- color: var(--color-neutral-01);
- padding: 15px 26px;
- text-align: right;
-}
-.box h2 {
- font-size: 16px;
- font-weight: 600;
-
- color: var(--color-bg-surface-02);
- margin: 0;
- margin-bottom: 12px;
-
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.badge {
- padding: 4px;
-
- color: var(--color-neutral-01);
- font-family: MONOSPACE;
- border-radius: 6px;
- font-size: 12px;
- line-height: 12px;
- font-weight: 500;
- border: 1px solid var(--color-neutral-01);
-}
-
-.badges-cls {
- display: flex;
- align-items: center;
- gap: 10px;
- padding-bottom: 10px;
-}
-.description-cls {
- color: var(--color-neutral-02);
- font-weight: 400;
- font-size: 14px;
- line-height: 20px;
-
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- text-overflow: ellipsis;
-
- margin: 0;
-}
diff --git a/src/components/post-list-item.tsx b/src/components/post-list-item.tsx
deleted file mode 100644
index f213861b..00000000
--- a/src/components/post-list-item.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import React from "react"
-import {
- item,
- thumbnailCls,
- box,
- img,
- descriptionCls,
- badgesCls,
- inner,
- badge,
-} from "./post-list-item.module.css"
-import {
- GatsbyImage,
- IGatsbyImageData,
- StaticImage,
- getImage,
-} from "gatsby-plugin-image"
-// import Arrow from "./arrow"
-
-interface PostListItemProps {
- title: string
- date: string
- description: string | TrustedHTML
- tag: string[]
- category?: string
- thumbnail?: IGatsbyImageData
-}
-
-const PostListItem = ({
- title,
- date,
- description,
- category,
- tag = [],
- thumbnail,
-}: PostListItemProps) => {
- const thumbnailImg = getImage(thumbnail)
- const badges = tag?.map(item => (
-
- #{item}
-
- ))
-
- return (
-
- {category && }
-
-
- {category}
- {thumbnailImg ? (
-
- ) : (
-
- )}
-
- {badges &&
{badges}
}
-
-
- {date}
-
- )
-}
-
-export default PostListItem
diff --git a/src/components/split-text.module.css b/src/components/split-text.module.css
deleted file mode 100644
index f792825a..00000000
--- a/src/components/split-text.module.css
+++ /dev/null
@@ -1,24 +0,0 @@
-.wrapper {
- display: inline-block;
- overflow: hidden;
- color: var(--color-bg-surface-02);
-
- font-family: ibm-plex-mono;
- font-weight: 700;
- font-size: 24px;
- line-height: 31.2px;
-}
-
-.motion-div {
- display: inline-block;
- will-change: transform;
-}
-
-.container {
- display: flex;
- align-items: center;
-}
-
-.spacing {
- margin-left: var(--spacing-1);
-}
diff --git a/src/components/split-text.tsx b/src/components/split-text.tsx
deleted file mode 100644
index 1588e291..00000000
--- a/src/components/split-text.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from "react"
-import clsx from "clsx"
-import { AnimatePresence, motion } from "framer-motion"
-import { wrapper, motionDiv, container, spacing } from "./split-text.module.css"
-
-interface SplitTextProps {
- words: string
- className?: string
- style?: React.CSSProperties
-}
-
-export function SplitText({ words, ...rest }: SplitTextProps) {
- return words.split("").map((word: string, i) => (
-
-
-
- ({
- y: 0,
- transition: {
- delay: i * 0.1,
- },
- }),
- }}
- className={clsx(motionDiv, word === " " && spacing)}
- custom={i}
- >
- {word}
-
-
-
-
- ))
-}
diff --git a/src/constants/links.ts b/src/constants/links.ts
deleted file mode 100644
index 6b41d027..00000000
--- a/src/constants/links.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const HOME_URL = "/home/1"
diff --git a/src/constants/page.ts b/src/constants/page.ts
deleted file mode 100644
index 41c588cd..00000000
--- a/src/constants/page.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const POST_PER_PAGE = 6
diff --git a/src/hooks/useActiveHash.ts b/src/hooks/useActiveHash.ts
deleted file mode 100644
index 5952a401..00000000
--- a/src/hooks/useActiveHash.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { useEffect, useState } from "react"
-
-/**
- * @link https://stackoverflow.com/questions/23269951/how-to-get-all-elements-with-a-specified-href-attribute
- */
-export const useActiveHash = (itemIds, rootMargin = undefined) => {
- const [activeHash, setActiveHash] = useState(``)
-
- useEffect(() => {
- const observer = new IntersectionObserver(
- entries => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- setActiveHash(entry.target.id)
- }
- })
- },
- { rootMargin: rootMargin || `0% 0% -80% 0%` }
- )
-
- itemIds.forEach(id => {
- const element = document?.getElementById(id)
-
- if (element) observer?.observe(element)
- })
-
- return () => {
- itemIds?.forEach(id => {
- const element = document?.getElementById(id)
-
- if (element) observer?.unobserve(element)
- })
- }
- }, [])
-
- return activeHash
-}
diff --git a/src/libs/paths.ts b/src/libs/paths.ts
deleted file mode 100644
index cea969d0..00000000
--- a/src/libs/paths.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export const getCategoryPaths = pathname =>
- `/${decodeURI(pathname).split("/")[1]}`
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
deleted file mode 100644
index 12713a75..00000000
--- a/src/pages/404.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import * as React from "react"
-import { graphql } from "gatsby"
-
-import Layout from "../components/@layout"
-import Seo from "../components/@layout/seo"
-
-const NotFoundPage = ({ data, location }) => {
- const siteTitle = data.site.siteMetadata.title
-
- return (
-
- 404: Not Found
- 존재하지 않는 페이지입니다.
-
- )
-}
-
-export const Head = () =>
-
-export default NotFoundPage
-
-export const pageQuery = graphql`
- query {
- site {
- siteMetadata {
- title
- }
- }
- }
-`
diff --git a/src/styles/global.css b/src/styles/global.css
deleted file mode 100644
index ad85c843..00000000
--- a/src/styles/global.css
+++ /dev/null
@@ -1,402 +0,0 @@
-@import url("https://use.typekit.net/njr2div.css");
-@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css");
-
-@font-face {
- font-family: "FiraCode-Medium";
- src: url("/fonts/FiraCode-Medium.woff2") format("woff2");
- font-weight: 500;
- font-style: normal;
-}
-
-:root {
- --maxWidth-none: "none";
- --maxWidth-xs: 20rem;
- --maxWidth-sm: 24rem;
- --maxWidth-md: 28rem;
- --maxWidth-lg: 32rem;
- --maxWidth-xl: 36rem;
- --maxWidth-2xl: 42rem;
- --maxWidth-3xl: 48rem;
- --maxWidth-4xl: 56rem;
- --maxWidth-full: "100%";
- --maxWidth-wrapper: calc(288px + 912px + 24px);
- --spacing-px: "1px";
- --spacing-0: 0;
- --spacing-1: 0.25rem;
- --spacing-2: 0.5rem;
- --spacing-3: 0.75rem;
- --spacing-4: 1rem;
- --spacing-5: 1.25rem;
- --spacing-6: 1.5rem;
- --spacing-8: 2rem;
- --spacing-10: 2.5rem;
- --spacing-12: 3rem;
- --spacing-16: 4rem;
- --spacing-20: 5rem;
- --spacing-24: 6rem;
- --spacing-32: 8rem;
- --fontFamily-sans: "Pretendard Variable", system-ui, -apple-system,
- BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans",
- sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
- "Noto Color Emoji";
- --fontFamily-serif: "Pretendard Variable", "Georgia", Cambria,
- "Times New Roman", Times, serif;
- --font-body: var(--fontFamily-serif);
- --font-heading: var(--fontFamily-sans);
- --fontWeight-normal: 400;
- --fontWeight-medium: 500;
- --fontWeight-semibold: 600;
- --fontWeight-bold: 700;
- --fontWeight-extrabold: 800;
- --fontWeight-black: 900;
- --fontSize-root: 16px;
- --lineHeight-none: 1;
- --lineHeight-tight: 1.1;
- --lineHeight-normal: 1.5;
- --lineHeight-relaxed: 1.625;
- /* 1.200 Minor Third Type Scale */
- --fontSize-0: 0.833rem;
- --fontSize-1: 1rem;
- --fontSize-2: 1.2rem;
- --fontSize-3: 1.44rem;
- --fontSize-4: 1.728rem;
- --fontSize-5: 2.074rem;
- --fontSize-6: 2.488rem;
- --fontSize-7: 2.986rem;
-
- /* custom variables */
- --fixed-z-index: 100;
- --navigation-height: 60px;
- --transition-duration: 0.1s;
- --transition-timing-function: linear;
-}
-
-body {
- transition: color var(--transition-duration) var(--transition-timing-function),
- background-color var(--transition-duration)
- var(--transition-timing-function);
-}
-
-body.light {
- --color-theme-01: #ff6737;
- --color-theme-02: #fc8755;
- --color-theme-03: #ffddca;
- --color-theme-04: #f3efec;
- --color-bg-surface: #2a2b31;
- --color-bg-surface-02: #2a2b31;
- --color-neutral-01: #60616a;
- --color-neutral-02: #9597a1;
- --color-neutral-03: #ebebeb;
- --color-neutral-04: #fbfbfb;
-
- --color-background: #ffffff;
-
- /* prism */
- --code-color: #898989;
- --code-background: #ffffff;
- --code-dtype: #727272;
- --code-punctuation: #454545;
- --code-tag: #3d5e7d;
- --code-class-name: #e0e041;
- --code-constant: #528452;
- --code-deleted: #f92672;
- --code-number: #ff73fd;
- --code-inserted: #429200;
- --code-variable: #605fb1;
- --code-operator: #717171;
- --code-string: #598b5c;
- --code-function: #c0ad1f;
- --code-regex: #cfa135;
- --code-important: #c56900;
-}
-
-body.dark {
- --color-theme-01: #ff8b66;
- --color-theme-02: #957e76;
- --color-theme-03: #c29f94;
- --color-theme-04: #3d3937;
- --color-bg-surface: #2a2b31;
- --color-bg-surface-02: #ffffff;
- --color-neutral-01: #ebdfd9;
- --color-neutral-02: #948884;
- --color-neutral-03: #ebebeb;
- --color-neutral-04: #fbfbfb;
-
- --color-background: #2a2b31;
-
- /* prism */
- --code-background: #1d1f21;
- --code-color: #c5c8c6;
- --code-dtype: #7c7c7c;
- --code-punctuation: #c5c8c6;
- --code-tag: #96cbfe;
- --code-class-name: #ffffb6;
- --code-constant: #99cc99;
- --code-deleted: #f92672;
- --code-number: #ff73fd;
- --code-inserted: #a8ff60;
- --code-variable: #c6c5fe;
- --code-operator: #ededed;
- --code-string: #87c38a;
- --code-function: #dad085;
- --code-regex: #e9c062;
- --code-important: #fd971f;
-}
-/* HTML elements */
-
-*,
-:after,
-:before {
- box-sizing: border-box;
-}
-
-*::-webkit-scrollbar {
- width: 0.3rem;
- height: 0.3rem;
- background-color: var(--color-background);
-}
-*::-webkit-scrollbar-thumb {
- background-color: var(--color-neutral-01);
- border-radius: 0.5rem;
-}
-*::-webkit-scrollbar-track {
- background-color: var(--color-background);
-}
-
-html {
- overflow-x: hidden;
- line-height: var(--lineHeight-normal);
- font-size: var(--fontSize-root);
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-body {
- font-family: var(--font-body);
- font-size: var(--fontSize-1);
- color: var(--color-bg-surface-02);
- background-color: var(--color-background);
-
- /*grid
- background: url("/images/grid-vert.svg") repeat;
- padding: 50px;*/
-}
-
-header li {
- list-style: none;
-}
-
-footer {
- padding: var(--spacing-6) var(--spacing-0);
-}
-
-hr {
- background: var(--color-neutral-01);
- height: 1px;
- border: 0;
-}
-
-/* Heading */
-
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- font-family: var(--font-heading);
- margin-top: var(--spacing-12);
- margin-bottom: var(--spacing-6);
- line-height: var(--lineHeight-tight);
- letter-spacing: -0.025em;
-}
-
-h2,
-h3,
-h4,
-h5,
-h6 {
- font-weight: var(--fontWeight-bold);
- color: var(--color-heading);
-}
-
-h1 {
- font-weight: var(--fontWeight-black);
- font-size: var(--fontSize-6);
- color: var(--color-bg-surface-02);
-}
-
-h2 {
- font-size: var(--fontSize-4);
-}
-
-h3 {
- font-size: var(--fontSize-3);
-}
-
-h4 {
- font-size: var(--fontSize-2);
-}
-
-h5 {
- font-size: var(--fontSize-1);
-}
-
-h6 {
- font-size: var(--fontSize-1);
-}
-
-h1 > a {
- color: inherit;
- text-decoration: none;
-}
-
-h2 > a,
-h3 > a,
-h4 > a,
-h5 > a,
-h6 > a {
- text-decoration: none;
- color: inherit;
-}
-
-:not(pre) > code[class*="language-"].language-text {
- text-shadow: none;
- background: var(--color-theme-03);
- color: var(--color-black-01);
-}
-
-/* Prose */
-
-p {
- line-height: var(--lineHeight-relaxed);
- --baseline-multiplier: 0.179;
- --x-height-multiplier: 0.35;
- margin: var(--spacing-0) var(--spacing-0) var(--spacing-6) var(--spacing-0);
- padding: var(--spacing-0);
-}
-
-ul,
-ol {
- margin-left: var(--spacing-);
- margin-right: var(--spacing-0);
- padding: var(--spacing-0);
- margin-bottom: var(--spacing-6);
- list-style-position: outside;
- list-style-image: none;
-}
-
-ol > li > ol {
- list-style-type: lower-alpha;
-}
-
-ul li,
-ol li {
- /* 추가 */
- margin-top: calc(var(--spacing-6) / 4);
- margin-bottom: calc(var(--spacing-6) / 2);
-}
-
-li > p {
- margin-bottom: calc(var(--spacing-6) / 2);
-}
-
-li *:last-child {
- margin-bottom: var(--spacing-0);
-}
-
-li > ul,
-li > ol {
- margin-left: calc(var(--spacing-6) / 2);
- margin-top: calc(var(--spacing-6) / 4);
-}
-
-blockquote {
- color: var(--color-bg-surface-02);
- margin-left: calc(1 * var(--spacing-3));
- margin-right: var(--spacing-6);
- padding: var(--spacing-0) var(--spacing-0) var(--spacing-0) var(--spacing-6);
- border-left: var(--spacing-1) solid var(--color-neutral-01);
- font-size: var(--fontSize-2);
- font-style: italic;
- margin-bottom: var(--spacing-6);
-}
-
-blockquote > :last-child {
- margin-bottom: var(--spacing-0);
-}
-
-blockquote > ul,
-blockquote > ol {
- list-style-position: inside;
-}
-
-table {
- width: 100%;
- margin-bottom: var(--spacing-6);
- border-collapse: collapse;
- border-spacing: 0.25rem;
-}
-
-table thead tr th {
- border-bottom: 1px solid var(--color-neutral-01);
-}
-
-/* Link */
-a {
- color: var(--color-neutral-01);
-}
-
-a,
-a:hover,
-a:focus {
- text-decoration: none;
-}
-
-/* Custom classes */
-
-.global-wrapper {
- /* background-color: var(--color-background); */
- margin: auto;
- max-width: var(--maxWidth-wrapper);
- padding: 64px 0 var(--spacing-10) 0;
-
- position: relative;
-}
-
-/* .global-header {
- margin-bottom: var(--spacing-6);
-} */
-
-.header-link-home {
- font-weight: var(--fontWeight-bold);
- font-family: var(--font-heading);
- text-decoration: none;
- font-size: var(--fontSize-2);
-}
-
-.gatsby-highlight {
- margin-bottom: var(--spacing-6);
-}
-
-/* Media queries */
-@media screen and (max-width: 1300px) {
- .global-wrapper {
- padding: 0;
-
- main {
- padding: var(--spacing-3);
- }
- }
-}
-
-@media screen and (max-width: 42rem) {
- blockquote {
- padding: var(--spacing-0) var(--spacing-0) var(--spacing-0) var(--spacing-4);
- margin-left: var(--spacing-0);
- }
- ul,
- ol {
- list-style-position: inside;
- }
-}
diff --git a/src/styles/media-query.css b/src/styles/media-query.css
deleted file mode 100644
index 61b20ab7..00000000
--- a/src/styles/media-query.css
+++ /dev/null
@@ -1 +0,0 @@
-@custom-media --desktop (max-width: 1300px);
diff --git a/src/styles/normalize.css b/src/styles/normalize.css
deleted file mode 100644
index 5efdf4fa..00000000
--- a/src/styles/normalize.css
+++ /dev/null
@@ -1,343 +0,0 @@
-/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
-
-/* Document
- ========================================================================== */
-
-/**
- * 1. Correct the line height in all browsers.
- * 2. Prevent adjustments of font size after orientation changes in iOS.
- */
-
-html {
- line-height: 1.15; /* 1 */
- -webkit-text-size-adjust: 100%; /* 2 */
-}
-
-/* Sections
- ========================================================================== */
-
-/**
- * Remove the margin in all browsers.
- */
-
-body {
- margin: 0;
-}
-
-/**
- * Render the `main` element consistently in IE.
- */
-
-main {
- display: block;
-}
-
-/**
- * Correct the font size and margin on `h1` elements within `section` and
- * `article` contexts in Chrome, Firefox, and Safari.
- */
-
-h1 {
- font-size: 2em;
- margin: 0.67em 0;
-}
-
-/* Grouping content
- ========================================================================== */
-
-/**
- * 1. Add the correct box sizing in Firefox.
- * 2. Show the overflow in Edge and IE.
- */
-
-hr {
- box-sizing: content-box; /* 1 */
- height: 0; /* 1 */
- overflow: visible; /* 2 */
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-pre {
- font-family: monospace, monospace; /* 1 */
- font-size: 1em; /* 2 */
-}
-
-/* Text-level semantics
- ========================================================================== */
-
-/**
- * Remove the gray background on active links in IE 10.
- */
-
-a {
- background-color: transparent;
-}
-
-/**
- * 1. Remove the bottom border in Chrome 57-
- * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
- */
-
-abbr[title] {
- border-bottom: none; /* 1 */
- text-decoration: underline; /* 2 */
- text-decoration: underline dotted; /* 2 */
-}
-
-/**
- * Add the correct font weight in Chrome, Edge, and Safari.
- */
-
-b,
-strong {
- font-weight: bolder;
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-code,
-kbd,
-samp {
- font-family: monospace, monospace; /* 1 */
- font-size: 1em; /* 2 */
-}
-
-/**
- * Add the correct font size in all browsers.
- */
-
-small {
- font-size: 80%;
-}
-
-/**
- * Prevent `sub` and `sup` elements from affecting the line height in
- * all browsers.
- */
-
-sub,
-sup {
- font-size: 75%;
- line-height: 0;
- position: relative;
- vertical-align: baseline;
-}
-
-sub {
- bottom: -0.25em;
-}
-
-sup {
- top: -0.5em;
-}
-
-/* Embedded content
- ========================================================================== */
-
-/**
- * Remove the border on images inside links in IE 10.
- */
-
-img {
- border-style: none;
-}
-
-/* Forms
- ========================================================================== */
-
-/**
- * 1. Change the font styles in all browsers.
- * 2. Remove the margin in Firefox and Safari.
- */
-
-button,
-input,
-optgroup,
-select,
-textarea {
- font-family: inherit; /* 1 */
- font-size: 100%; /* 1 */
- line-height: 1.15; /* 1 */
- margin: 0; /* 2 */
-}
-
-/**
- * Show the overflow in IE.
- * 1. Show the overflow in Edge.
- */
-
-button,
-input {
- /* 1 */
- overflow: visible;
-}
-
-/**
- * Remove the inheritance of text transform in Edge, Firefox, and IE.
- * 1. Remove the inheritance of text transform in Firefox.
- */
-
-button,
-select {
- /* 1 */
- text-transform: none;
-}
-
-/**
- * Correct the inability to style clickable types in iOS and Safari.
- */
-
-button,
-[type="button"],
-[type="reset"],
-[type="submit"] {
- -webkit-appearance: button;
-}
-
-/**
- * Remove the inner border and padding in Firefox.
- */
-
-button::-moz-focus-inner,
-[type="button"]::-moz-focus-inner,
-[type="reset"]::-moz-focus-inner,
-[type="submit"]::-moz-focus-inner {
- border-style: none;
- padding: 0;
-}
-
-/**
- * Restore the focus styles unset by the previous rule.
- */
-
-button:-moz-focusring,
-[type="button"]:-moz-focusring,
-[type="reset"]:-moz-focusring,
-[type="submit"]:-moz-focusring {
- outline: 1px dotted ButtonText;
-}
-
-/**
- * Correct the padding in Firefox.
- */
-
-fieldset {
- padding: 0.35em 0.75em 0.625em;
-}
-
-/**
- * 1. Correct the text wrapping in Edge and IE.
- * 2. Correct the color inheritance from `fieldset` elements in IE.
- * 3. Remove the padding so developers are not caught out when they zero out
- * `fieldset` elements in all browsers.
- */
-
-legend {
- box-sizing: border-box; /* 1 */
- color: inherit; /* 2 */
- display: table; /* 1 */
- max-width: 100%; /* 1 */
- padding: 0; /* 3 */
- white-space: normal; /* 1 */
-}
-
-/**
- * Add the correct vertical alignment in Chrome, Firefox, and Opera.
- */
-
-progress {
- vertical-align: baseline;
-}
-
-/**
- * Remove the default vertical scrollbar in IE 10+.
- */
-
-textarea {
- overflow: auto;
-}
-
-/**
- * 1. Add the correct box sizing in IE 10.
- * 2. Remove the padding in IE 10.
- */
-
-[type="checkbox"],
-[type="radio"] {
- box-sizing: border-box; /* 1 */
- padding: 0; /* 2 */
-}
-
-/**
- * Correct the cursor style of increment and decrement buttons in Chrome.
- */
-
-[type="number"]::-webkit-inner-spin-button,
-[type="number"]::-webkit-outer-spin-button {
- height: auto;
-}
-
-/**
- * 1. Correct the odd appearance in Chrome and Safari.
- * 2. Correct the outline style in Safari.
- */
-
-[type="search"] {
- -webkit-appearance: textfield; /* 1 */
- outline-offset: -2px; /* 2 */
-}
-
-/**
- * Remove the inner padding in Chrome and Safari on macOS.
- */
-
-[type="search"]::-webkit-search-decoration {
- -webkit-appearance: none;
-}
-
-/**
- * 1. Correct the inability to style clickable types in iOS and Safari.
- * 2. Change font properties to `inherit` in Safari.
- */
-
-::-webkit-file-upload-button {
- -webkit-appearance: button; /* 1 */
- font: inherit; /* 2 */
-}
-
-/* Interactive
- ========================================================================== */
-
-/*
- * Add the correct display in Edge, IE 10+, and Firefox.
- */
-
-details {
- display: block;
-}
-
-/*
- * Add the correct display in all browsers.
- */
-
-summary {
- display: list-item;
-}
-
-/* Misc
- ========================================================================== */
-
-/**
- * Add the correct display in IE 10.
- */
-
-[hidden] {
- display: none;
-}
diff --git a/src/styles/prism-atom-dark.css b/src/styles/prism-atom-dark.css
deleted file mode 100644
index c3d68cc0..00000000
--- a/src/styles/prism-atom-dark.css
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * atom-dark theme for `prism.js`
- * Based on Atom's `atom-dark` theme: https://github.com/atom/atom-dark-syntax
- * @author Joe Gibson (@gibsjose)
- */
-
-.gatsby-highlight {
- /* padding-top: 40px; */
- /* background-color: black; */
- border: 1px solid var(--color-bg-surface-02);
- padding-bottom: 0;
-
- border-radius: 0.5em;
- overflow: hidden;
-}
-
-code[class*="language-"],
-pre[class*="language-"] {
- color: var(--code-color);
- font-family: "FiraCode-Medium", monospace, Consolas, "Courier New", Courier;
-
- font-size: 14px;
- direction: ltr;
- text-align: left;
- white-space: pre;
- word-spacing: normal;
- word-break: normal;
- line-height: 1.5;
-
- -moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
-
- -webkit-hyphens: none;
- -moz-hyphens: none;
- -ms-hyphens: none;
- hyphens: none;
-}
-
-/* Code blocks */
-pre[class*="language-"] {
- padding: 1em;
- margin: 0;
- overflow: auto;
-}
-
-:not(pre) > code[class*="language-"],
-pre[class*="language-"] {
- background: var(--code-background);
- /* border: 1px solid var(--color-bg-surface-02); */
- border-top: none;
-}
-
-/* Inline code */
-:not(pre) > code[class*="language-"] {
- padding: 0.1em;
- border-radius: 0.3em;
-}
-
-.token.comment,
-.token.prolog,
-.token.doctype,
-.token.cdata {
- color: var(--code-dtype);
-}
-
-.token.punctuation {
- color: var(--code-punctuation);
-}
-
-.namespace {
- opacity: 0.7;
-}
-
-.token.property,
-.token.keyword,
-.token.tag {
- color: var(--code-tag);
-}
-
-.token.class-name {
- color: var(--code-class-name);
- text-decoration: underline;
-}
-
-.token.boolean,
-.token.constant {
- color: var(--code-constant);
-}
-
-.token.symbol,
-.token.deleted {
- color: var(--code-deleted);
-}
-
-.token.number {
- color: var(--code-number);
-}
-
-.token.selector,
-.token.attr-name,
-.token.string,
-.token.char,
-.token.builtin,
-.token.inserted {
- color: var(--code-inserted);
-}
-
-.token.variable {
- color: var(--code-variable);
-}
-
-.token.operator {
- color: var(--code-operator);
-}
-
-.token.entity {
- color: var(--code-class-name);
- cursor: help;
-}
-
-.token.url {
- color: var(--code-tag);
-}
-
-.language-css .token.string,
-.style .token.string {
- color: var(--code-string);
-}
-
-.token.atrule,
-.token.attr-value {
- color: var(--code-class-name);
-}
-
-.token.function {
- color: var(--code-function);
-}
-
-.token.regex {
- color: var(--code-regex);
-}
-
-.token.important {
- color: var(--code-important);
-}
-
-.token.important,
-.token.bold {
- font-weight: bold;
-}
-
-.token.italic {
- font-style: italic;
-}
diff --git a/src/templates/blog-post.js b/src/templates/blog-post.js
deleted file mode 100644
index 0929e14e..00000000
--- a/src/templates/blog-post.js
+++ /dev/null
@@ -1,163 +0,0 @@
-import React from "react"
-import { Link, graphql } from "gatsby"
-import { ReactComponent as LeftArrow } from "/static/images/left-arrow.svg"
-
-import TransitionMain from "../components/@layout/main"
-import Utterances from "../components/@layout/utterances"
-// import Bio from "../components/@layout/bio"
-import Layout from "../components/@layout"
-import Seo from "../components/@layout/seo"
-import TableContents from "../components/@layout/tableOfContents"
-import { HOME_URL } from "../constants/links"
-import {
- wrapper,
- left,
- mainArticle,
- blogPost,
- blogPostNav,
- badgesCls,
- badgeCls,
- leftBox,
- rightBox,
-} from "./blog-post.module.css"
-import { navigate } from "gatsby"
-
-const BlogPostTemplate = ({
- data: { previous, next, site, markdownRemark: post },
- location,
-}) => {
- const siteTitle = site.siteMetadata?.title || `Title`
-
- const badges = post.frontmatter.tag?.map(tag => (
-
- #{tag}
-
- ))
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
-
-export const Head = ({ data: { markdownRemark: post } }) => {
- return (
-
- )
-}
-
-export default BlogPostTemplate
-
-export const pageQuery = graphql`
- query BlogPostBySlug(
- $id: String!
- $previousPostId: String
- $nextPostId: String
- ) {
- site {
- siteMetadata {
- title
- }
- }
- markdownRemark(id: { eq: $id }) {
- id
- excerpt(pruneLength: 160)
- html
- tableOfContents
- frontmatter {
- title
- date(formatString: "YYYY.MM.DD")
- description
- category
- tag
- }
- }
- previous: markdownRemark(id: { eq: $previousPostId }) {
- fields {
- slug
- }
- frontmatter {
- title
- }
- }
- next: markdownRemark(id: { eq: $nextPostId }) {
- fields {
- slug
- }
- frontmatter {
- title
- }
- }
- }
-`
diff --git a/src/templates/blog-post.module.css b/src/templates/blog-post.module.css
deleted file mode 100644
index 27b4c981..00000000
--- a/src/templates/blog-post.module.css
+++ /dev/null
@@ -1,232 +0,0 @@
-.blog-post {
- border-radius: 20px;
- border: 1px solid var(--color-bg-surface-02);
- margin: 0 0 50px 0;
-
- .wrapper {
- gap: 28px;
- display: flex;
- padding: 34px;
-
- .left {
- position: sticky;
- top: calc(60px + 10px);
- height: max-content;
-
- display: flex;
- flex-direction: column;
-
- button {
- min-width: 288px;
- cursor: pointer;
- width: 100%;
- display: flex;
- align-items: center;
-
- text-align: left;
- padding: 17px;
- background-color: var(--color-background);
- color: var(--color-bg-surface-02);
- font-size: 16px;
- font-weight: 400;
- line-height: 23.2px;
-
- border: 1px solid var(--color-bg-surface-02);
- border-radius: 10px;
- svg {
- margin-right: 22px;
- }
- }
- display: block;
- }
- }
-
- @media screen and (max-width: 1300px) {
- .wrapper .left {
- display: none;
- }
- margin: 16px 0;
- }
-}
-
-.main-article {
- > section {
- h2 {
- border: 1px solid var(--color-neutral-01);
- background: var(--color-theme-04);
- padding: 8px;
- border-radius: 8px;
- }
-
- a {
- color: var(--color-theme-02);
- }
-
- display: table;
- table-layout: fixed;
- width: 100%;
-
- border: 1px solid var(--color-bg-surface-02);
- padding: 32px;
- margin-bottom: 24px;
- border-radius: 10px;
-
- @media screen and (max-width: 1300px) {
- border: none;
- padding: 0;
- }
- }
-}
-
-header small {
- color: var(--color-neutral-01);
- font-size: 18px;
- font-weight: 500;
- line-height: 18px;
- display: flex;
- align-items: center;
-
- > div {
- display: inline-block;
- margin: 0 14px;
- width: 2px;
- height: 18px;
- background-color: var(--color-neutral-01);
- }
- > p {
- margin: 0;
- }
-}
-
-.blog-post > header {
- border-bottom: 1px solid var(--color-bg-surface-02);
- padding: 34px;
-
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
-}
-.blog-post header h1 {
- font-weight: 600;
- font-size: 32px;
- line-height: 41.6px;
- padding: 20px 0;
- margin: 0;
- /* margin: var(--spacing-0) var(--spacing-0) var(--spacing-4) var(--spacing-0); */
-}
-
-.blog-post li {
- margin-left: var(--spacing-4);
- padding-left: var(--spacing-0);
-}
-
-.badge-cls {
- border: 1px solid var(--color-bg-surface-02);
- border-radius: 6px;
- background-color: var(--color-theme-03);
- color: var(--color-neutral-01);
- padding: 6px;
-
- font-size: 16px;
- font-weight: 500;
- line-height: 12px;
-}
-.badges-cls {
- display: flex;
- align-items: center;
- gap: 10px;
- margin: 0;
- /* padding-bottom: var(--spacing-1); */
-}
-
-.blog-post header h1 {
- margin-bottom: 0;
-}
-
-.blog-post header hr {
- margin-bottom: var(--spacing-4);
-}
-
-.blog-post-nav ul {
- display: flex;
- flex-wrap: wrap;
- justify-content: space-between;
- list-style: none;
- margin: 0;
- padding: 0;
- margin-top: 24px;
-
- li {
- margin: 0;
- padding: 0;
- width: 40%;
- }
-}
-
-.blog-post p > img {
- display: block;
- margin: auto;
- max-width: 700px;
- @media (--desktop) {
- width: 100%;
- }
-}
-
-.blog-post-nav a {
- margin: 0;
- border-radius: 10px;
- overflow: hidden;
- border: 1px solid var(--color-bg-surface-02);
-
- /* padding: var(--spacing-4) var(--spacing-8); */
- display: flex;
- flex-direction: column;
-
- color: var(--color-bg-surface-02);
-
- > span {
- font-weight: var(--fontWeight-bold);
-
- border-bottom: 1px solid var(--color-bg-surface-02);
- padding: 10px 16px;
- background-color: var(--color-theme-03);
- }
-
- > p {
- padding: 10px 16px;
- height: 68px;
-
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-}
-.left-box {
- /* box-shadow: -6px 6px 0px -1px var(--color-neutral-01); */
-
- &:hover {
- animation: move-left 1s var(--transition-timing-function) infinite;
- }
-}
-
-.right-box {
- /* box-shadow: 6px 6px 0px -1px var(--color-neutral-01); */
- &:hover {
- animation: move-right 1s var(--transition-timing-function) infinite;
- }
-}
-
-@keyframes move-right {
- 0% {
- transform: translate(-10px, 0);
- }
- 50% {
- transform: translate(-20px, 0);
- }
- 100% {
- transform: translate(-10px, 0);
- }
-}
diff --git a/src/templates/category-posts.js b/src/templates/category-posts.js
deleted file mode 100644
index fa28801c..00000000
--- a/src/templates/category-posts.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import React from "react"
-import { graphql } from "gatsby"
-import Seo from "../components/@layout/seo"
-import PostListTemplate from "../components/@templates/post-list-template"
-
-const CategoryPost = ({ data, location, pageContext }) => {
- return
-}
-
-export default CategoryPost
-export const Head = () =>
-
-export const pageQuery = graphql`
- query ($category: String!, $skip: Int!, $limit: Int!) {
- site {
- siteMetadata {
- title
- }
- }
- allCategoriesInfo: allMarkdownRemark {
- totalCount
- group(field: { frontmatter: { category: SELECT } }) {
- fieldValue
- totalCount
- }
- }
- allTagsInfo: allMarkdownRemark(
- filter: { frontmatter: { category: { eq: $category } } }
- ) {
- totalCount
- group(field: { frontmatter: { tag: SELECT } }) {
- fieldValue
- totalCount
- }
- }
- allMarkdownRemark(
- sort: { frontmatter: { date: DESC } }
- limit: $limit
- skip: $skip
- filter: { frontmatter: { category: { eq: $category } } }
- ) {
- nodes {
- excerpt
- fields {
- slug
- }
- frontmatter {
- date(formatString: "YYYY.MM.DD")
- title
- description
- category
- tag
- thumbnail {
- childImageSharp {
- gatsbyImageData(
- width: 256
- placeholder: BLURRED
- formats: [AUTO, WEBP, AVIF]
- )
- }
- }
- }
- }
- }
- }
-`
diff --git a/src/templates/home.tsx b/src/templates/home.tsx
deleted file mode 100644
index f4ca5131..00000000
--- a/src/templates/home.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from "react"
-import { graphql } from "gatsby"
-import Seo from "../components/@layout/seo"
-import PostListTemplate from "../components/@templates/post-list-template"
-
-const Home = ({ data, location, pageContext }) => {
- return
-}
-
-export default Home
-
-/**
- * Head export to define metadata for the page
- *
- * See: https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-head/
- */
-export const Head = () =>
-
-export const pageQuery = graphql`
- query ($skip: Int!, $limit: Int!) {
- site {
- siteMetadata {
- title
- }
- }
- allCategoriesInfo: allMarkdownRemark {
- totalCount
- group(field: { frontmatter: { category: SELECT } }) {
- fieldValue
- totalCount
- }
- }
- allTagsInfo: allMarkdownRemark {
- totalCount
- group(field: { frontmatter: { tag: SELECT } }) {
- fieldValue
- totalCount
- }
- }
-
- allMarkdownRemark(
- sort: { frontmatter: { date: DESC } }
- limit: $limit
- skip: $skip
- ) {
- nodes {
- excerpt
- fields {
- slug
- }
- frontmatter {
- date(formatString: "YYYY.MM.DD")
- title
- description
- category
- tag
- thumbnail {
- childImageSharp {
- gatsbyImageData(
- width: 256
- placeholder: BLURRED
- formats: [AUTO, WEBP, AVIF]
- )
- }
- }
- }
- }
- }
- }
-`
diff --git a/tsconfig.json b/tsconfig.json
index 7c0951b5..7be34f1c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,30 +1,41 @@
{
"compilerOptions": {
- "target": "esnext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
+ "target": "ES2017",
"lib": [
"dom",
+ "dom.iterable",
"esnext"
- ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
- "jsx": "react" /* Specify what JSX code is generated. */,
- "module": "esnext" /* Specify what module code is generated. */,
- // "rootDir": "./", /* Specify the root folder within your source files. */
- "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
- "baseUrl": "./" /* Specify the base directory to resolve non-relative module names. */,
+ ],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "baseUrl": ".",
"paths": {
- "/*": ["./src/*"],
- "static/*": ["./static/*"]
- } /* Specify a set of entries that re-map imports to additional lookup locations. */,
- "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
- "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
- "strict": true /* Enable all strict type-checking options. */,
- "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ "@/*": [
+ "./*"
+ ]
+ }
},
- "files": ["./global.d.ts"],
"include": [
- "./src/**/*",
- "./gatsby-node.ts",
- "./gatsby-config.ts",
- "./plugins/**/*"
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts"
],
- "exclude": ["node_modules"]
+ "exclude": [
+ "node_modules"
+ ]
}