Next.js와 ContentLayer로 블로그 제작기

21
목차

발단

아마 웹 개발자라면 자기가 직접 만든 블로그를 갖고 싶지 않을까요. 웹 개발을 처음 공부한 때부터 가슴 한구석에 그런 꿈을 갖고 있다가 우연히 Next.js를 잠깐 맛보고 이걸로 블로그 만들 수 있겠다 싶어서 냉큼 시작해 봤습니다.

사용한 기술

Next.js 13

22년 10월, Next.js 13 버전으로 바뀌면서 Pages 라우팅 대신 App 라우팅 방식으로 바뀌었습니다. 업데이트된 지 오래되지 않았기에 Next.js로 만들어진 블로그의 코드들 대부분이 pages 라우팅을 기반으로 한 코드였데, 문제는 저는 App 라우팅으로 코드 짜는 법만 막 배운 상태였습니다. 그래서 pages 라우팅을 기반으로 한 코드를 보고 이해하는 데 애를 좀 먹었습니다.

그래도 React 문법을 베이스로 하고 있어서 적응하는 데에는 큰 문제는 없었습니다.

ContentLayer

블로그 포스트의 경우 소스 코드와 같은 폴더에 있는 마크다운 파일을 파싱해 HTML로 변환한 뒤에 필요한 라우팅 경로에서 꺼내쓰는 구조로 되어있습니다.

예전까지만 해도 node.js 환경에서 fs 모듈을 사용해 마크다운의 경로를 찾아, 그 안에 있는 파일을 unified로 변환한 뒤 remark, rehype 라이브러리를 가지고 가공했으나, 최근에는 Contentlayer라는 도구의 등장으로 편리하게 마크다운을 관리할 수 있게 되었습니다.

contentlayer.config.ts(js)에서 데이터 소스의 경로를 설정하고 프론트 마터(Front-matter)에 들어가는 필드의 데이터 타입과 필수/옵션 정보를 삽입할 수 있고, 데이터 소스를 생성할 때 기존에 존재하는 remark, rehype 플러그인을 적용시킬 수 있습니다.

이에 매번 경로를 찾고, 불러오고, unified, remark, rehype 플러그인을 적용시키는 코드가 크게 줄어듭니다. 또 프론트 마터의 데이터 타입을 손쉽게 활용할 수 있어 프론트 마터의 정보를 조작하다가 타입 오류가 발생할 가능성이 크게 줄어듭니다. 또 프로덕션 환경에서 Live reloading이 되어 서버가 실행될 때 마크다운의 변경사항을 감지하면 알아서 업데이트가 되어 변화를 손쉽게 관찰할 수 있는 이점이 있습니다

이러한 이유로 인해 Contentlayer를 콘텐츠 관리 도구로 사용하게 되었습니다.

TypeScript

저번 프로젝트에서 자바스크립트로 코드를 짜니 타입 에러 때문에 오만가지 고생을 했던 경험이 있어서 이번에는 타입스크립트를 사용해 봤습니다. 기초적인 부분만 알고 들어왔던 터라 MouseEvent<HTMLDivElement>와 같은 타입 정의를 할 때 좀 헤맸었습니다. 그렇지만 undefined에 배열에 쓸 수 있는 함수를 낑궈넣어 타입 에러 나고, 그거 못 찾아서 두어 시간을 범인 색출하는 짓거리를 하지 않아도 되어 너무 좋았습니다.

Figma

230614_225339_blog-generation-review

유데미에서 할인하길래 덥석 샀던 피그마 강의를 빠르게 수강해서 레이아웃을 짜는 데 많은 도움을 받았습니다. 미리 디자인을 다 해놓고 들어가니 주로 사용하는 마진 간격, 색상, 트랜지션 값을 미리 정해두고 CSS 변수를 적용하는 데에도 많은 도움이 되었습니다. 또 중간에 만들다 디자인이 성에 안 차 수정하는 일이 적어서 빠르게 마무리할 수 있었습니다.

피그마에서도 컴포넌트를 만들어 공통 변경 사항을 쉽게 관리할 수 있었는데, 이를 보고 특정 요소를 어떤 식으로 컴포넌트화해야 할지 아이디어를 얻을 수 있었습니다.

CSS + CSS Moudle

보통 블로그 프로젝트에 Tailwind를 많이 사용하는 경향이 있었으나 당장 손에 익지 않은 기술들이 너무 많아 Tailwind까지 적용하기엔 무리라는 생각이 들어 일반적인 CSS를 사용했습니다. 다만 스타일링 변수의 겹침을 해소하고자 global.css와 module.css를 분리하여 컴포넌트별 스타일링을 별도로 관리하도록 했습니다.

느낀 점

생각 없는 구글링 지양

처음에는 기능 구현과 빠른 에러 탈출(?)에 급급해 무작정 구글링만 줄창 했었는데, 이러니까 비슷한 에러가 발생했을 때에(hydration 관련 에러), 그리고 조금만 생각하면 해결할 수 있는 문제가 발생했을 때에도 구글링에 의존하게 되어 실력이 정체된다는 느낌을 강하게 받았습니다.

그래서 무작정 구글링하는 것을 멈추고 먼저 잠시 생각을 한 뒤 구글링을 하거나, 복잡한 문제의 경우에는 종이에 로직을 정리해가면서 제가 뭘 아는지, 그리고 뭘 모르는지를 최대한 구체적으로 나눠놓고 그에 맞춰 차근히 검색하고 정리했습니다. 이 과정이 무지성 구글링보다 시간이 훨씬 더 오래 걸렸지만 이런 과정을 거치면서 기능 개발과 오류 해결을 하다 보니 정신적으로 쌓이는 피로감이 상당히 줄어들었습니다.

++ 그러나 이와는 반대 경우로 이슈로부터 너무 깊은 부분까지 이해하려고 하다 보면 공부를 위한 공부의 과정에 빠져 이게 개발을 하는건지, 단순 공부를 하는건지 헷갈리는 상황이 발생했습니다. 이슈와의 거리감을 어느정도 유지한 상태로 구글링을 하고 정리를 하는 것도 중요하단 생각이 들었습니다.

항상 확인하고 쓰기

지레짐작으로 코드 내용을 유추하지 말고 코드의 중간 과정을 수시로 확인한 상태로 코드를 작성해야 잘못된 코드 작성을 막을 수 있다는 걸 느꼈습니다. 예를 들어 Contenlayer가 생성하는 파일은 .contentlayer/generated 안에 있는데, contentlayer.config.ts의 사전 설정을 거쳐 생성된 파일의 형태를 직접 확인해야 어떤 프로퍼티를 사용해서 정보를 가공할지 감을 잡을 수 있었습니다.

이처럼 특정 변수를 사용할 때에도 의도대로 동작하고 값이 출력되는지 항상 확인하는 습관을 들여야 엉뚱한 곳에서 헤매고 애먼 곳에 삽질하는 경우를 줄일 수 있음을 많이 느꼈습니다.

기간 산정의 중요성: 별거 아닌 거 같은게 오히려 발목잡을 수 있음

다크모드를 구현할 때 일입니다. 테마 토글 버튼을 누를 때마다 theme이 'light'와 'dark'를 오가며 토글 되는 방식이고, 로컬 스토리지에 저장해 body 태그의 data-theme 속성에 심어주면 되겠다는 생각을 갖고 만만하게 접근했었는데.. 문제는 클라이언트의 로컬 스토리지에 접근하는 것은 어쩔 수 없이 클라이언트 측에서 코드를 실행해야 했기 때문에 서버 사이드에서 로드가 되고 클라이언트 측에서 로컬 스토리지 테마 정보를 접근하는 그 사이의 시간 동안 디폴트 테마(white)가 보여지는 현상이 생겼습니다.

그렇다면 서버 측에서 유저가 선택한 테마 정보를 받아서 그에 맞게 HTML을 전달해 주면 될 것 같아 로컬 스토리지 대신 쿠키를 이용해 다크모드를 구현해 보려고 했습니다. 프로덕션 단계에서는 아무 문제가 없이 잘 동작했으나, 빌드되고 배포가 되니 포스트 리스트에 접근이 안 되는 문제가 생겼습니다.

알고 보니 서버에서 cookie() 함수를 쓰면 정적 라우팅이 되어야 할 곳이 동적 라우팅이 되는 현상이 발생했던 것입니다. 저는 작성된 게시물들이 빌드 단계에서 모두 정적으로 생성되기를 바랬던 관계로 정적 생성을 유지하면서 테마를 토글할 수 있는 방법을 체크해 봤지만 실패했습니다.

결국 next-themes 라이브러리를 사용해서 해결은 했지만 이걸 해결하기 위해 이틀을 투자했습니다. 이런 식으로 별거 아닌 거라 생각했던 것이 실제로는 환장하게 만드는 경우가 있다는 걸 알았고, 기능 구현을 위한 기간을 산정할 때 조금 보수적으로 잡아야겠다..는 생각을 하게 됐습니다.

커밋의 생활화

진짜 부끄럽지만 저번 프로젝트까지만 하더라도 특정 기능이 구현될 때마다 커밋을 한다기보다는 Github에 구축된 CI/CD에 코드를 반영시키기 위해 커밋(과 푸시)을 했었습니다. 그래서 이번에도 커밋을 안하고 시작을 했었습니다만..

그러다 보름 동안 작업했던 코드가 실수로 싹 다 날아가 버렸습니다.

230614_225402_blog-generation-review

요렇게 된 것이었습니다. 어떻게든 복구해 보려고 여기저기 찾아봤는데 답이 안 보이길래 그냥 포기하고 그렇게 다시 처음부터 만들었습니다. 그런데 하다 보니 이건 진짜 아니다 싶어 방법을 찾다 찾다 결국 방법을 찾았는데, C:\Users\사용자명\Appdata\Roaming\Code\User\History 에 폴더별로 각 파일의 저장 히스토리를 조회하는 방법이었습니다. 정말 다행히 복구는 완료했지만, 사흘 동안 식음을 전폐하던 고통을 통해 두 번 다시 커밋 안하고 스트레이트로 코드짜는 짓은 안 하겠다 굳게 다짐했습니다.

다른 개발자들의 코드를 적극적으로 참고하자

여태까지는 다른 개발자의 repository를 참고할 생각을 못 했었는데, 기능을 구현하다 막혔을 때 문득 다른 분들의 repo를 보면 어떨까 싶어서 찾다 보니 굉장히 도움 되는 블로그들을 많이 발견했습니다. 단순히 지금 구현하고자 하는 기능들만 참고하는 걸 넘어, 어떤 식으로 hooks를 뺐는지, 폴더 구조를 어떻게 정리했는지와 같은 말로 표현하기 애매한 부분들을 피부로 느끼면서 다시 한 번 제가 햇병아리임을 느꼈습니다.

공식 문서를 제대로 이해할 수 있는 기초를 쌓자.

MDX 파일을 JSON으로 변환하고 그 과정에서 remark, rehype 플러그인을 적용하는 것은 공식문서를 참조하여 세팅하였으나, 그렇게 변환이 다 끝난 코드를 커스텀해서 건드리는 방법에 대한 정보를 얻기가 어려워 고생했습니다.

230614_223340_blog-generation-review

예를 들어 pre 태그로 감싸진 코드 블럭에 마우스를 호버하면 복사 버튼이 나오기를 원했는데, 이걸 어떻게 해야 하는 지 공식문서 찾아봐도 딱히 짚이는 부분이 없어서 머리가 깨질뻔 했습니다. 다행히 Time Gambit님 블로그에서 힌트를 얻어 어떻게 적용하는지 알아냈는데, 이게 알고보니 공식 문서에 있던 내용이었습니다.

/src/app/post/[slug]/page.tsx
export default function PostLayout({ params }: Props) {
  const MDXLayout = getMDXComponent(currentPost.body.code);
  const components = {
    pre: Pre, // Pre 컴포넌트에 커스텀하고 싶은 대로 만들면 mdx의 pre 태그에 그대로 반영됨.
  };
  // 생략

  return (
    // 생략
    <MDXLayout className={fontMono.className} components={components} />
    // 생략
  );
}

평소에 공식 문서의 Get Started의 예시나 다른 개발자들의 소스코드를 단순히 막 사용하면 놓치는 부분에서 꼭 중요한 걸 빠뜨리는 경우가 있었는데 이번이 바로 그 케이스였습니다.

분명히 저 설명을 봤음에도 불구하고 제게 필요한 정보였다는 걸 몰랐던 이유는 그 당시 Contentlayer 라이브러리의 컨셉을 제대로 이해하지 못한 상태로 사용했고, 용어에 대한 개념이 잡혀있지 않아서였습니다.

일단 예시 코드를 읽고 나서 공식 문서를 다시 보면 대충 무슨 뜻인지 이해는 되었으나, 문서 내용을 읽으면 너무 추상적으로 다가오는 탓에 그냥 넘기면서 읽고는 했었는데, 개발자들을 위해 가장 정제된 언어로 설명해주는 글을 제대로 읽지 못한다면 다른 개발자들과의 소통에 문제가 생길 것이라는 생각이 들었습니다.

configure, resolve, validate 등등 사전적 용어는 알지만 이게 실제 공식 문서에서 접하면 잡힐듯 말듯한 추상적 느낌을 영어단어 외우듯 체화시키는 것이 좋겠다는 생각이 들었습니다.

용어의 정확한 의미를 알고, 또 표현하고자 하는 것이 어떤 용어로 쓰이는 지를 알고 그 언어로 소통할 줄 알아야겠다는 생각이 들었습니다.

+ Notion을 컨텐츠 공급자로 쓰고 싶다면

Contentlayer의 컨텐츠 소스 공급원으로 노션을 지원하긴 하지만 현재는 제한적으로 지원하고 있습니다. 연결된 Notion 데이터베이스가 변경되어도 배포 환경에서는 변경 사항이 반영되지 못하기 때문에 변경을 마치고 난 뒤 매번 재배포해야 한다는 단점이 있습니다. 그리고 여러 플러그인들을 비벼서 여러 가지 서식을 지정할 수 있는 기존 방식에 비해서 제한적으로 꾸밀 수 있다는 단점 역시 존재합니다.

그로 인해 노션 DB와 Contentlayer를 연결하여 만든 블로그는 찾아보지 못했으나, Nobelium을 사용해 블로그와 노션 DB를 연결해서 쓰는 블로그는 찾아볼 수 있었습니다. 대강 훑어보니 노션과의 연동성을 원하시는 분은 이쪽을 참고해 블로그를 만들어 보는 것도 좋은 선택지로 생각됩니다.

레퍼런스

남은 거

업데이트 내역

2023-06-23

2023-06-27

댓글