post image

Time Gambit 블로그

프로젝트 소개

Time Gambit은 개발자 한진탁의 포트폴리오 및 블로그입니다. 수행한 프로젝트를 기록하고, 공부 및 분석한 내용들을 정리하고 공유하여, 저뿐만 아니라 다른 개발자들과 함께 성장하기 위해 개발하였습니다.

링크

Time Gambit BlogBlog GitHub

기술스택

Language

TypeScript

Front End

Next.JSReactTailwind CSS

Back End

MySQLPlanet Scale

Deployment

vercel

ETC

yarn berryMDXcontentlayer

블로그를 직접 만든 이유

지금까지 포스팅을 한 블로그가 없던 것은 아닙니다. 한 편뿐이긴 하지만 velog에 포스트를 적기도 했고, jekyll로 만든 github.io 블로그로 알고리즘 포스트를 작성하기도 했었습니다. 하지만 velog는 제 입맛대로 커스터마이징이 애초에 불가능했고, jekyll는 제가 잘 모르는 Ruby 기반 프레임워크 기반 블로그여서 커스터마이징을 할 수 없었습니다.

무엇보다 명색이 개발자라면 자신만의 사이트를 직접 개발해야하는 것 아닐까? 라는 생각도 들었습니다. 그래서 원하는 기능들을 모두 구현하고, 저만의 로고가 달린 블로그를 만들기로 결심했습니다.

포스트

마크다운 문법으로 작성하고 이를 파싱하여 기본적인 HTML 태그와 제가 정의한 컴포넌트들을 이용하여 렌더링을 해줍니다. 마크다운 파싱은 MDX bundler를 사용한 contentlayer 라이브러리로 구현했습니다. 개발을 하며 여러 마크다운 파싱 라이브러리를 비교했고, 높은 성능을 낼 수 있도록 노력하였습니다. 이와 관련하여 포스트도 작성하였습니다.

코드 하이라이팅

Axios.js
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  Axios.prototype[method] = function(url, config) {
    return this.request(mergeConfig(config || {}, {
      method,
      url,
      data: (config || {}).data
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  function generateHTTPMethod(isForm) {
    return function httpMethod(url, data, config) {
      return this.request(mergeConfig(config || {}, {
        method,
        headers: isForm ? {
          'Content-Type': 'multipart/form-data'
        } : {},
        url,
        data
      }));
    };
  }

  Axios.prototype[method] = generateHTTPMethod();
  Axios.prototype[method + 'Form'] = generateHTTPMethod(true);

  // code add, delete
+ console.log('add');
- console.log('delete');
});

저는 주로 드라큘라 테마를 이용해 코딩을 합니다. 제 코딩환경과 최대한 동일한 색상으로 코드를 보여주도록 하였습니다. 이를 위해 여러가지 코드 하이라이트 라이브러리를 비교하였습니다. 이와 관련하여 포스트도 작성하였습니다.

이미지 확대

이미지를 클릭해보세요
이미지를 클릭해보세요

이미지를 클릭하면 확대할 수 있습니다. 확대된 상태에서 스크롤을 움직이거나 이미지 바깥을 클릭하면 원래 크기로 돌아갑니다.

TOC

좌: 데스크탑 / 우: 모바일
좌: 데스크탑 / 우: 모바일

데스크탑뿐만 아니라 모바일 환경에서도 목차를 볼 수 있도록 구현하였습니다. 모바일 환경에서는 TOC 사이드 버튼 클릭시 TOC 사이드 바가 나옵니다.

주석 및 주석 풍선

주석을 달 수 있습니다.1

좋아요

데스크탑 환경
모바일 환경

블로그 포스트에 있는 좋아요 버튼을 총 4번까지 누를 수 있습니다. siteMetadata.mjs에서 횟수를 줄이거나 늘릴 수 있습니다. 좋아요를 빠르게 눌러도 한 번만 API 요청이 가도록 debounce를 이용한 batch를 구현했습니다.

Scroll Indicator

Scroll Indicator
Scroll Indicator

포스트 상단에 포스트를 어느정도 봤는지 알려주는 Scroll Indicator 있습니다.

기타 기능

헤더 및 사이드 TOC 버튼 숨기기

스크롤시 숨기기

스크롤을 아래로 내리면 헤더와 사이드 TOC가 숨습니다. 다시 스크롤을 올리면 나타납니다.

다크모드

다크모드

헤더의 버튼으로 다크모드와 라이트모드를 전환할 수 있습니다.

블로그 페이지들

블로그에 있는 페이지들을 소개합니다.

Home

Home 페이지
Home 페이지

간단한 자기소개와 최신 포스트 및 조회수가 많은 인기 포스트를 보여주는 Home 페이지입니다.

Blog

무한 스크롤을 적용한 Blog 페이지

작성한 블로그 포스트를 최근부터 내림차순으로 정렬하여 보여주는 Blog 페이지입니다. 한 번에 모든 포스트를 불러오는 것이 아니라, 스크롤을 내리면서 추가적인 포스트를 불러오도록 무한 스크롤을 구현하였습니다.

Tag

Tag 페이지
Tag 페이지
Tag 리스트 중 axios 클릭시
Tag 리스트 중 axios 클릭시

모든 블로그 포스트의 태그들을 모아서 보여주는 Tag 페이지입니다. 클릭시 해당 태그의 포스트만 모아서 보여줍니다.

Series

시리즈 페이지
시리즈 페이지
seriesData.mjs
const seriesData = {
  동작원리: {
    image: 'https://i.imgur.com/FKJ3GMT.png',
    slug: 'digging',
  },
  'HTTP 완벽 가이드': {
    image: 'https://i.imgur.com/FWceD2q.png',
    slug: 'http',
  },
  '블로그 제작일지': {
    image: 'https://i.imgur.com/Wigx54E.png',
    slug: 'blog-log',
  },
  '자바스크립트와 맞짱 뜨기': {
    image: 'https://i.imgur.com/zzh0DoW.png',
    slug: 'js'
  }
};

export default seriesData;

위와 같이 작성된 데이터를 이용해 Series 페이지를 만들어줍니다. 가장 최근에 작성된 시리즈부터 가장 마지막으로 작성된 시리즈까지 내림차순으로 정렬됩니다.

Guestbook

로그인 이전
로그인 이전
로그인 이후
로그인 이후
남겨진 방명록
남겨진 방명록

OAuth를 이용해 로그인을 하고 방명록을 남길 수 있습니다. 자신이 남긴 방명록의 경우 삭제할 수 있습니다.

Projects

Projects 페이지
Projects 페이지
projectData.ts
const projectData = [
  {
    title: 'Time Gambit 블로그',
    description: `공부한 내용을 기록하고 다른 개발자와 공유하기 위해 만든 블로그입니다.`,
    imgSrc: 'https://i.imgur.com/VIFO53Q.png',
    href: '/projects/timegambit-blog',
  },
  {
    title: '다크모드 vs 라이트모드',
    description:
      '다크모드와 라이트모드 중 더 적합한 모드를 찾아주는 사이트입니다.',
    imgSrc: 'https://i.imgur.com/4ZXZJCH.png',
    href: 'https://dark-vs-light.vercel.app/',
  },
  {
    title: '포도알 시뮬레이션',
    description:
      '모바일에서 버튼의 위치에 따라 사용자가 버튼의 어느 위치를 터치하는지 측정하기 위한 프로젝트입니다.',
    imgSrc: 'https://i.imgur.com/TXvd8r1.png',
    href: 'https://grape-simulation.vercel.app/',
  },
];

export default projectData;

위와 같이 작성된 데이터를 이용해 Projects 페이지를 만들어줍니다.

About

About 페이지
About 페이지
about.ts
const about = {
  ...,
  Contact: {
    Email: 'jintak0401@naver.com',
    GitHub: 'https://github.com/jintak0401',
    Blog: 'https://timegambit.com',
  },
  Skills: [
    {
      title: 'FrontEnd',
      info: ['Next JS', 'React', 'TypeScript', 'JavaScript', 'HTML/CSS'],
    },
    ...
  ],
  Experience: [
    {
      title: '우아한 테크캠프',
      date: '2022.07.04 ~ 2022.08.31',
      info: [
        '8주 동안 5개의 프로젝트 진행',
        '웹 프론트엔드 중심으로 백엔드를 함께 배우는 자기 주도형 풀스택 과정',
        '페어 프로그래밍, 팀 프로젝트를 통한 협업 경험',
        'Vanilla JS, TypeScript, React, Express, Nest JS, AWS 외 다수',
      ],
    },
    ...
  ],
  ...
};

export default about;

위와 같이 작성된 데이터를 이용해 About 페이지를 만들어줍니다.

성능

Lighthouse 성능
Lighthouse 성능

Time Gambit 블로그에서 가장 조회수가 높은 글인 axios interceptor 동작원리 포스트의 페이지를 Lighthouse로 측정한 결과입니다.

추가 목표

오픈소스화

data
  ├── blog
  │     └── 블로그 포스트들
  │     └── ...
  ├── logo
  │     └── 라이트모드 로고
  │     └── 다크모드 로고
  │     └── ...
  ├── project
  │     └── 프로젝트 포스트들
  │     └── ...
  ├── about.ts // About 페이지에서 사용할 데이터
  ├── navLinks.ts // 네비게이션 바에서 사용할 데이터
  ├── phrases.ts // 각 페이지들 문구
  ├── projectData.ts // Projects 페이지에서 사용할 데이터
  ├── seriesData.mjs // Series 페이지에서 사용할 데이터
  └── siteMetadata.mjs // 블로그 메타데이터

원하는 모든 사람들이 사용할 수 있는 오픈소스 템플릿으로 만들어서 배포하려고 합니다. 그래서 개발할 때 가장 신경쓴 것들 중 하나가 필요한 메타데이터 및 문구 데이터 등을 별도로 분리한 것입니다. 그래서 data 폴더의 데이터들만 수정하면 바로 사용할 수 있도로 구현하였습니다.

동작원리 시리즈

공부한 내용들을 계속해서 작성해 나가겠지만 동작원리 시리즈는 끊임없이 해나가고 싶습니다. 동작원리 시리즈는 라이브러리 혹은 자바스크립트의 내부적인 동작원리를 분석하는 시리즈입니다. 현재까지 axios의 동작원리 3편과 async/await 동작원리 3편을 작성하였습니다. 앞으로도 계속해서 작성해 나가려고 합니다.

Footnotes

  1. 주석을 클릭하거나 터치하면 글 하단으로 이동합니다. 데스크탑 환경인 경우, 주석 위에다가 마우스 커서를 올려놓으면 하단으로 내려가지 않고도 주석 풍선을 통해 주석을 읽을 수도 있습니다.

    주석이 스크린의 위쪽에 있다면 풍선은 아래쪽으로, 주석이 아래쪽에 있다면 풍선은 위쪽으로 나옵니다.

Table of Contents