블로그 프로젝트 회고
2024-06-27 오후 08시 48분
2024-06-27 오후 08시 48분
Blog 구현 프로젝트 05/31 ~ 06/27
프로젝트 목적
복잡한 정적 사이트를 직접 제작해보며 다양한 기능의 구현 방법에 대해
이해하고 확장성있는 서비스를 직접 운영해보고자 시작하게 된 개인 블로그 프로젝트
입니다.
많은 서비스가 존재한다, 왜 블로그를 직접 만들었는가?

누구도 가지지못한 독창적인 개인블로그를 가지고싶었던 마음도 컷고, 무엇보다
프론트엔드의 포르폴리오로서 다른사람들에게 노출되는 프로젝트를 구현한다면
저는 블로그를 가장크게 생각할 것 같습니다.
내 생각을 정리하고 내 정보를 공유하며 이야기한다면 블로그만한게 없다고 생각하기 때문입니다.
처음에는 Nextra
를 활용한 간단한 블로그를 운영중에 있었습니다.
개발은 불편함을 느끼는 부분을 직접 제작해서 해결하는 것
이 중요하다고 생각했습니다.
이러한 프레임워크는 여러 문제점이 존재했습니다.
개발된 코드가 오래된 경우가 아주 많았습니다.
ContentLayer
의 경우 마지막 업데이트가 2023년이며
App-router
를 지원하지 않습니다.
또한 둘러보면 대체로 gatsby
를 활용해서 제작한 블로그가 많은데
graphQL
에 의존적이며 불필요한 모듈이 지나치게 많아 무겁게 작동하는 등의 문제가 있었습니다.
• 라이브러리가 버전업에 취약하다는 등 문제점이 많았다.
아무래도 직접 만들게 된다면 모든 요소에 대한 커스텀마이징과 나만의 스타일을
설계할 수 있습니다.
무엇보다 큰 강점이자 단점인데, 다시말하자면 모든 스타일을 내손으로
설계 해야한다는 문제점도 존재합니다. 관련 스타일링을 도와주는 라이브러리가 존재하지만,
그럼에도 직접 커스텀해야하는 부분이 아주많기에 설계과정에 꽤나 난해합니다.
• 직접 경험해보며 스타일을 넣어보고 기능을 만들면서 시행착오를 겪어보고 싶었다.
프레임워크를 통해 모든 형태가 정해져있다는 뜻은 다시말해 내가 해당 블로그의 작동방식도
알수 없다는 점입니다. 이런 부분은 개발자로서 직접만든 블로그라고 할 수 없다고 생각합니다.
물론 단순 글쓰기에 치중을 둔다면 직접 블로그를 제작할 이유가 없습니다.
velog
, tistory
등 좋은 플랫폼이 많이 존재하고
해당플랫폼을 기반으로 작성하면 글작성은 훨씬 간단하기 때문입니다.
• 서비스에 종속되지 않은 나만의 서비스를 직접 제작해보고 싶었다.
왜 Next.js를 사용했는가?
리액트는 CSRClient Side Rendering
를 이용해 웹에서 아주빠르게 구동되는 서비스를 제공합니다.
하지만 SEOSearch Engine Optimization
가 취약하고 초기구동이 느려질수 있는 등 단점이 존재합니다.
이러한 단점을 해결하는데 있어서 Next.js
는 좋은 선택이 되었습니다.
처음 사용하는 Next14
버전이고, 인터넷에서 정보도 빈약해서 다루기 어려웠습니다.
하지만 프론트엔드 개발자로서 이러한 기술을 익히는것은 당연한 것이고, 도전심을 자극하는 요소 였습니다.
Next.js
는 SSRServer Side Rendering
와 SSGStatic Site Generation
를 지원해주어
정적 사이트를 운영하는데 있어서 필요한 요소를 제공해주었습니다.
또한, 동적 라우팅 생성을 손쉽게 제공해주어 제가 원하는 형태의 라우팅을 구현할 수 있었습니다.
• SEO, SSG와 동적 라우팅을 중요하게 생각하여 Next.js를 채택하였다.
왜 Typescript를 사용했는가?
개발에서 가장 중요한 부분은 사람들이 많이 사용하는 것
이라고 생각합니다.
사람들이 많이 사용한다는 점은 많은 의미를 합축하기 때문입니다.
저는 현업에서 JavaScript
의 타입 안정성을 위해 채택하여 사용하는 TypeScript
에 더욱 정진하고 싶었고
이 프로젝트는 많은 컨텐츠 데이터를 다루기 때문에 Typescript
를 더욱 사용해야한다고 생각했습니다.
• 타입 안정성, 높은 생산성을 유지하기위해 TypeScript를 채택하였다.
왜 Supabase를 사용했는가?
프론트엔드 개발자로서 백엔드에서의 협업을 고려해봐야 합니다. 물론 항상 백엔드가 존재하는게 아니기 때문에,
백엔드가 없을때의 작업도 혼자 해낼수 있는가를 고민하게 되었습니다.
알아보던중, 요새 No_SQL
백엔드 서비스로 유명한 Supabase
에 대해 알게 되었습니다.
Supabase
는 간단한 Table CRUD
부터 RealTime DB
, Authorization
까지 무료로 제공해주는
오픈소스 서비스입니다. 저의 서비스가 복잡한 백엔드 구조를 가지진 않을거라고 생각했고
저번 프로젝트에서 Server - DB
연동 또한 연습해보았기에 이번에는 Supabase
를 통해 진행 했습니다.
• NO_SQL 서비스를 직접 연습해보고자 Supabase를 채택하였다.
왜 TailwindCSS를 사용했는가?
현업에서 가장 자주사용되는건 Styled-Component
로 직접 사용해본적이 있습니다.
하지만 블로그는 많은 유지보수가 필요하고 저에게 익숙한 Js-In-CSS
를 사용하고 싶었습니다.
그래서 최근에 트렌드하고, shadcn/ui
와 함께 사용하는 등 스타일링이 간편한 TailwindCSS
를
사용했습니다.
아무래도 기능구현에 좀더 중점을 두고싶었기에 스타일에 시간을 크게 사용하고 싶지 않았습니다.
• 스타일을 빠르게 작성하고 기능구현에 중점을 두기위해 TailwindCSS를 채택하였다.
왜 Zustand를 사용했는가?
현업에서 가장 주로 사용되는 전역상태 라이브러리는 Redux
입니다.
최근에도 사용하는 기업이 많지만, Kakao
, 배달의민족
등 선두테크기업에서는
최근에는 Redux
의 복잡한 구조를 탈피하고, Zustand
와 React-Query
를 활용하는
상태 관리를 더 선호하고 이미 마이그레이션을 진행중인걸로 알고있습니다.
왜냐면 리덕스는 toolkit
을 사용하더라도 긴 보일러플레이트를 작성해야 하는데,
많은양의 전역상태를 사용하지 않을경우 과하기 때문입니다.
그런 점에서 저는 Zustand
를 사용했습니다.
• 전역상태가 많이 필요하지 않고, 간편하게 작성하기위해 Zustand를 채택하였다.
아키텍처 구조

Github
에서 Github Action
을 통해 자동으로 Next.js
를 Vercel
에 배포하고
배포된 페이지에서 필요한 요청을 No_SQL SERVICE
인 Supabase
에서 전달해줍니다.
디렉토리 구조
├── app/
│ ├── api/
│ │ ├── comments
│ │ ├── playdetail
│ │ └── playlist
│ ├── music
│ └── posts/
│ └── [category]/
│ └── [slug]
├── components/
│ ├── header
│ ├── home
│ ├── music
│ ├── posts
│ └── mdx/
│ ├── footer
│ ├── header
│ ├── toc
│ └── modules
├── config
├── hook
├── lib/
│ └── supabase
└── utils/
└── mdx
공용 컴포넌트는 Components
폴더의 최상위에 존재하고, 특정 페이지에서 사용되는 경우
페이지명에 맞춰 컴포넌트를 작성하였습니다.
데이터를 처리해주는 함수의 경우 utils
폴더에 작성하고
백엔드와 소통하거나 데이터를 파싱해주는 함수의 경우 lib
폴더에 작성했습니다.
배우게 된 점
Node.js
의 fs
기능과 Next.js V14
에서의 동적 라우팅
Next.js
환경에서의 SEO 최적화와 컴포넌트 최적화, 이미지 최적화
Intersection Observer API
의 직접적인 사용법
MDX
에서의 커스텀 마크다운 데이터 처리
API
통신 과정에서의 에러발생 예외처리
Supabase
에서 Client 측 데이터 CRUD
구현
Zustand
전역 상태관리와 상태호출
Framer-motion
를 통한 효과적인 애니메이션 호출과 최적화
잘 구현했다고 생각하는 점
블로그의 MDX파싱부분은 총 3가지의 주요컴포넌트로 구성되어있습니다.
app/posts/[category]/slug/page.tsx
export default async function Page({
params,
}: {
params: { category: string; slug: string };
}) {
const { category, slug } = params;
const { frontMatter, content } = await getPost(category, slug);
const footMatter = (await getCategoryPost(category)) as FootMatterTypes[];
const tocControl = frontMatter?.toc === undefined && true;
const footControl = frontMatter?.comment === undefined && true;
return (
<PageContainer>
{tocControl && <Mdx_Toc footControl={footControl} />}
<div className='m-auto w-[95%] md:w-[75%] 3xl:w-[50%]'>
<Mdx_Header frontMatter={{ ...frontMatter, category: category }} />
<Mdx_Body content={content} />
{footControl && <Mdx_Footer footMatter={footMatter} />}
</div>
</PageContainer>
);
}
MDX_TOC
- 목차(TOC)정보와 intersectionObserverAPI를 다룹니다.
- frontMatter에서 toc가
false
일 경우 제공하지 않습니다.
MDX_HEADER
- frontMatter 정보를 가공하여 게시글 헤더 및 썸네일 이미지를 설정합니다.
MDX_BODY
- Content 정보를 가공하여 mdx 컨텐츠를 제공합니다.
MDX_FOOTER
- frontMatter 정보중 필요한 정보를 따로 제공받아 게시글 댓글 정보를 제공합니다.
- frontMatter에서 comment가
false
일 경우 제공하지 않습니다.
하나의 [slug]
에서 수많은 정보를 적당히 가공하여 형태로 보여주는 부분의 대한
설계를 치밀하게 해냈다고 생각합니다.
문제가 발생하였던 요소
많은 우여곡절이 존재했습니다. 해당내용은 아래 포스트에 정리해두겠습니다.
내용 정리 포스트
다른 사용자가 이해하기쉽게 정리한 게시글도 존재합니다. 참고해주시면 좋겠습니다.
댓글은 포스팅에 도움이됩니다. 적극적인 의견 감사드립니다.