본문 바로가기
개발 기록

바닥부터 만드는 블로그 개발기

by 유세지 2022. 10. 2.

드디어 미루고 미루던 블로그 프로젝트의 첫 삽을 떴습니다. 그동안 "직접 만든 블로그 하나쯤은 있어야지!" 라고 막연히 생각만 하고 있었는데, 이런 저런 핑계만 대고 있으면 평생 시작도 못할 것 같아서 일단 무작정 시작해보기로 했습니다.

 

마침 감정 회고나 특별한 이벤트가 없는 근황을 원래 사용하던 이 기술 블로그에 그대로 싣기에는 좀 부끄러워서 새로운 공간이 하나 필요하다 느꼈던 차여서, 이번에 만들 블로그를 그러한 글들을 기록하는 목적의 공간으로 사용해보기로 했습니다.

 

무작정 레포지토리부터 만들고 있으려니 어떤 분께서 DDD(도메인 주도 개발) 를 "일단 도메인을 사면 개발하게 된다" 로 해석하신 글이 문득 생각났는데, 나중에 재미 붙여서 새벽 시간 쪼개가며 개발하던 걸 보면 정말 시작이 중요한 것 같습니다 😊

 

 

기획 단계

 

노션으로 간단하게 적은 기획서

 

새 프로젝트이니 먼저 기획을 해보기로 했습니다. 만들게 될 블로그에는 어떤 기능들이 필요할지 구체적인 내용은 빼고 정말 간단하게만 정리했습니다.

 

  • 마크다운 파일(.md)을 이용한 글 내용과 글 목록을 보여주는 핵심적인 기능
  • 간단한 자기소개를 담을 about 페이지
  • 모바일이나 태블릿에서도 이용할 수 있도록 하는 반응형 레이아웃
  • 눈 건강(?)을 위한 다크 모드

 

이걸 한 번에 전부 구현하겠다는건 당연히 아니고, 일단 배포를 하고 조금씩 업데이트를 할 목적으로 부담없이 적었습니다.

 

 

다음은 프로젝트에 사용할 기술 스택인데, 이 부분도 굉장히 많은 고민을 했었습니다.

 

조그만한 토이 프로젝트에 리덕스 붙이고, 리액트 쿼리 붙이고... 큼지막한 시스템을 만들어 놓는건 오버 엔지니어링일뿐 아니라 오히려 기능 구현과 유지 보수가 불편해진다고 생각했기 때문에, 최대한 기성 라이브러리의 사용을 자제하여 개발하기로 했습니다. 이렇게 해서 완성하게 되면 개발이 어느정도 된 뒤에 좋다는 다른 기술들을 이것저것 시도하는 플레이그라운드 용도로 사용하기 딱 좋아 보였습니다.

 

  • React + Typescript
  • Context API (이 외에 별도 상태관리 라이브러리는 사용하지 않음)
  • Emotion
  • Yarn berry
  • Webpack
  • Vercel (배포)

 

패키지 매니저의 경우 기존에 주로 사용했었던 npm 대신 yarn berry를 사용하기로 했습니다. yarn berry는 커녕 그냥 yarn도 사용해본적이 없어 도전이 되겠지만... 장점들은 익히 들어왔던터라 직접 느껴보고 싶었습니다.

 

피그마를 활용한 예시 디자인

 

이렇게 만든 기획을 바탕으로 피그마를 이용하여 대략적인 디자인도 해보았습니다. 막상 디자인을 하고 보니 언뜻 노션 문서처럼 보이기도 하지만, 마크다운 문서를 사용하는 이상 그 스타일에서 나오는 특유의 느낌을 지우기는 쉽지 않았습니다. 물론 개인적으로 마크다운이 주는 이런 느낌도 나쁘지 않다고 생각하기 때문에 굳이 바꾸지는 않기로 했습니다.

 

 

개발 시작

얼기설기 만든 디자인이 끝났으니 바로 레포지토리를 파고, 개발 환경부터 세팅하기 시작했습니다.

 

공식 문서를 참고하여 yarn을 설치하고 eslint와 prettier를 적용해 기본적인 개발 준비를 마쳤습니다. yarn을 처음 사용했더니 잘 안되는 부분이 많아서 삽질을 한참 했습니다. 덕분에 이것저것 찾아보고 만져보며 지식이 늘었다는게 다행이었네요. 이때 공부한 내용은 npm, yarn, 그리고 yarn berry 포스트에 정리해 두었습니다.

 

여기까지 구현한 내용들은 나중에 새로운 프로젝트를 시작할때 공통적으로 많이 사용하겠다 싶어서 보일러 플레이트로 올려두고 템플릿 레포지토리로 변경해 두었습니다. 앞으로 요긴하게 쓸 수 있을 것 같네요.

 

준비가 완료된 후, 블로그의 주요 기능들을 구현하기 시작했습니다. 몇 가지를 살펴보겠습니다.

 

 

글 목록 보여주기

글 목록을 보여주려면 우선 마크다운 문서 형태로 되어있는 파일들을 가져와야 합니다. 저는 src의 내부 디렉토리 안에 포스팅 파일들을 위치시켜 주었는데, 한 가지 문제가 있습니다.

 

보통 node.js를 사용한다면 fs 모듈을 이용해 파일 시스템에 접근할텐데, 지금 만드는 블로그는 100% 프론트만 구현하기 때문에 해당 모듈을 사용할 수 없다는 점입니다. 지난 프로젝트였던 한 줄 번역기에서 했던 것처럼 서버를 하나 만들면 되는 일이지만 더 쉽게 해결할 수 있는 방법이 있습니다. 해당 파일이 있는지 없는지는 중요하지 않습니다. 일단 접근을 시도하여 파일에 매칭되기만 한다면 데이터를 받아올 수 있습니다.

 

posts 디렉토리 내부에 파일들을 위치시켰습니다.

 

프로젝트 파일 구조를 보시면 특이하게 포스트 파일 이름이 숫자로 되어있습니다. 모든 포스트 파일의 이름이 하나씩 올라가는 숫자라면 직접 디렉토리 내부를 확인해보지 않아도 해당 파일을 가져올 수 있다고 생각했습니다. 파일명 규칙을 알았으니, 이제 import 키워드를 통해 접근해보겠습니다.

 

const [postNumber, setPostNumber] = useState<number>(1);
const [list, setList] = useState<Post[]>([]);
const [isError, setIsError] = useState<boolean>(false);

const loadAllPost = () => {
  if (isError) return;

  import(`posts/${postNumber}.md`)
    .then(data => {
      setList([parseDocument(postNumber, data.default), ...list]);
      setPostNumber(postNumber + 1);
    })
    .catch(() => {
      setIsError(true);
    });
};

 

파일 이름이 1부터 시작하니 하나씩 올려가며 import를 해주고, 그 결과가 존재한다면 list state에 추가해주는 간단한 로직입니다. 단순히 에러가 날때까지 임포트를 해보는 무식하고도 용감한 방식이지만 효과가 나쁘지 않습니다.

 

 

글 내용 보여주기

글 목록을 불러오는데 성공했으니, 이제 불러온 글들을 깔끔하게 파싱해서 보여줄 차례입니다. 그 전에 먼저 파일의 형식을 살펴보도록 하겠습니다.

 

1.md

---
title: Hello, World!
subTitle: 안녕하세요, 유세지입니다.
date: 2022-09-14
---

여기서부터는 글 내용입니다! 반가워요!

 

두 개의 구분선 (---) 을 이용해 파일의 정보를 먼저 담아주었습니다. 구분선 내부에는 title, subTitle, date 세 가지 정보가 담겨있고, 두 번째 구분선 이후부터는 글 내용이 담겨있는 상태입니다.

 

interface Post {
  id: number;
  title: string;
  subTitle: string;
  date: string;
  content: string;
}

const [content, setContent] = useState<Post>(undefined);

const loadCurrentPost = () => {
  import(`posts/${id}.md`).then(data => {
    setContent(parseDocument(id, data.default));
  });
};

 

단순 문자열 파싱 부분이라 상세한 구현은 넣지 않았고, 대략 '이런 형태로 사용했구나' 정도로만 봐주시면 좋을 것 같습니다.

 

src/pages/Post/index.tsx

function Post() {
  const { id } = useParams();
  const { title, date, content } = usePost(Number(id));

  if (!content) return <></>;

  return (
    <S.Container>
      <S.Title>{title}</S.Title>
      <S.Date>{date}</S.Date>
      <S.Content dangerouslySetInnerHTML={content} />
    </S.Container>
  );
}

export default Post;

 

위에 있었던 글 목록을 불러오는 부분과 글 내용을 보여주는 부분은 각각 useList, usePost 라는 훅으로 분리해주었습니다. 이렇게 해서 파일을 불러오는 코드까지 완료했지만, 당장 화면에 표시되진 않습니다. .md 확장자의 파일을 불러와야하니 웹팩에서 설정도 해주어야겠죠. 저는 markdown-loader를 이용하였습니다.

 

webpack.config.js

module: {
  rules: [
    {
      test: /\.md$/,
      use: [
        {
          loader: 'html-loader',
        },
        {
          loader: 'markdown-loader',
        },
      ],
    },
  ],
},

 

markdown 파일의 출력값이 html이니 html-loader도 use에 함께 추가해주었습니다. (라이브러리의 권장사항이기도 합니다.) 여기에 typescript를 사용했으니 declare module ".md" 까지 해주는것으로, 드디어 포스트 관련 핵심 기능들의 구현이 끝나게 됩니다.

 

포스트가 아주 잘 표시되고 있습니다.

 

 

배포하기

이제 만든 프로젝트를 배포할 차례입니다. 지금껏 배포 서비스로 heroku, Chromatic 등을 이용해보았는데, 이번엔 Vercel이 그렇게 좋다고 해서 한 번 사용해 보기로 마음 먹었습니다.

 

빌드 및 배포 설정 화면

 

Vercel에 가입하고, 레포지토리를 연결하여 간단히 배포 설정을 하면 됩니다. 한 가지 단점은, vercel이 yarn berry의 pnp를 지원하지 않는 것 같습니다. Install Command를 오버라이드 하도록 설정하고 비워두면 pnp로 설정된 프로젝트라 바로 빌드할 수 있어야 하는데 오류를 발생시키고, 빌드에 실패합니다.

 

오버라이드 옵션을 꺼두면 빌드에는 성공하는데, 로그를 확인해보면 node_modules 폴더를 찾고 있습니다. 로그에 정확히 표시되진 않지만, 아무래도 yarn classic 버젼으로 동작하는 것 같습니다. 이 부분은 나중에 차차 찾아봐야 할 것 같네요.

 

yarn pnp가 정상적으로 적용되지 않습니다.

 

 

 

마무리

여러 우여곡절들을 거쳐 저만의 블로그를 만들어 보았습니다. 사실 개발자들의 블로그 하면 Gatsby에 테마를 사용해서 배포하는 경우가 많이 보였는데, 별도의 템플릿 없이 이렇게 만들고 배포까지 하고 나니 굉장히 뿌듯했습니다.

 

지금은 시간 날 때마다 이것저것 기능들을 추가하며 관리중입니다. 주중에 시간내기가 쉽지 않아서 주말에나 가끔 한 번씩 커밋이 올라오는데, 혹시 코드 구현이 궁금하신 분들은 깃허브 레포지토리를 참고하시면 될 것 같습니다.

 

배포된 블로그는 유세지의 식물원 에서 확인하실 수 있습니다. 어느정도 기능들이 더 갖춰지면 도메인도 사서 연결해둬야겠네요. 오랜만에 즐거운 토이 프로젝트였습니다!

반응형

댓글