나만의 콘텐츠 플랫폼 구축기: Notion과 GitHub, 그 완벽한 동기화를 향한 엔지니어의 여정

나만의 콘텐츠 플랫폼 구축기: Notion과 GitHub, 그 완벽한 동기화를 향한 엔지니어의 여정

회고 6분 읽기

Notion을 Headless CMS로, Hugo와 GitHub Actions를 자동화 엔진으로 사용하여 마찰 없는 글쓰기 환경을 구축한 엔지니어링 회고록입니다.

#Notion #GitHub Actions #Hugo #자동화 #Engineering #DevOps

[회고] 나만의 콘텐츠 플랫폼 구축기: Notion과 GitHub, 그 완벽한 동기화를 향한 엔지니어의 여정

결론부터 말하자면, 나는 나에게 가장 완벽한 글쓰기 환경을 구축했다. 이제 나는 Notion의 유려한 편집기에서 자유롭게 생각을 기록하고, 데이터베이스로 글을 관리한다. 그리고 내가 ‘발행(Published)’ 상태로 바꾸는 것만으로, 그 모든 콘텐츠는 고도로 최적화된 정적 블로그로 자동 배포된다. 이 글은 단순히 Notion과 GitHub Pages를 ‘연결’하는 방법을 나열한 튜토리얼이 아니다. 이것은 최고의 창작 도구와 최고의 배포 시스템을 결합하여, 오직 글쓰기에만 집중할 수 있는 나만의 플랫폼을 집요하게 만들어나간 한 엔지니어의 사투와 의사결정에 대한 기록이다.

왜 시작했는가: Notion에 대한 애증, 그리고 숙명적 과제

나는 Notion을 사랑한다. Notion은 무겁지만 강력한 도구다. 생각을 구조화하는 데 최적화된 블록 기반 편집 방식, 여러 페이지를 유기적으로 연결하는 유연함, 그리고 데스크톱과 모바일을 넘나들며 언제 어디서든 아이디어를 기록할 수 있는 멀티플랫폼 지원까지. 간단한 협업이나 글 공유의 측면에서는, 마크다운 파일 기반의 Obsidian보다 훨씬 강력한 도구라고 개인적으로 생각한다. 내 모든 생각의 파편과 지식의 체계는 Notion 안에서 자라나고 있었다.

하지만 애정이 깊어질수록, 엔지니어로서의 고민도 함께 깊어졌다. Notion은 치명적인 단점을 안고 있었다.

  1. SEO(검색 엔진 최적화)의 한계: Notion 페이지를 웹에 직접 공유할 수는 있지만, 복잡한 구조 탓에 검색 엔진이 콘텐츠를 제대로 수집하고 이해하기 어렵다. 이는 내 지식이 더 넓은 세상에 닿을 수 없다는 의미였다.
  2. 데이터 소유권의 문제: 내 모든 콘텐츠는 결국 Notion의 서버에 종속된다. 만약 Notion의 정책이 바뀌거나, 서비스가 중단된다면 내 지식 자산 전체가 위험에 처할 수 있다. 데이터는 온전히 나의 통제 하에 있어야 했다.
  3. 웹 성능의 문제: Notion 페이지는 그 자체로 하나의 무거운 웹 애플리케이션이다. 순수한 콘텐츠를 소비하기에는 너무 많은 자바스크립트와 데이터를 불러온다. 이는 독자에게 쾌적한 읽기 경험을 제공하기 어렵다는 것을 의미했다.

Notion은 분명 ‘편집’과 ‘협업’, ‘공유’에는 최강의 도구였지만, ‘배포’에는 너무나도 약했다. 이 문제를 해결하는 것은 언젠가 반드시 넘어야 할 산처럼, 하나의 숙명처럼 다가왔다.

그래서 처음에는 Notion API를 활용해 Tistory 같은 기존 블로그 플랫폼에 자동으로 글을 올리는 방법을 고민했다. 하지만 이는 또 다른 플랫폼에 종속되는 것일 뿐, 근본적인 해결책이 아니었다. Notion 사이트 자체를 어떻게든 최적화해서 배포해볼까도 생각했지만, 똑같이 Notion에 의존되는 것이었다. 그러던 중, 익숙한 도구들의 조합 속에서 한 줄기 빛이 보였다. Notion API, GitHub Actions, 그리고 GitHub Pages. 이 세 가지를 엮는다면, Notion의 장점은 그대로 취하면서 단점은 완벽하게 보완하는 나만의 솔루션을 만들 수 있지 않을까? 그 번뜩임이 이 프로젝트의 시작이었다.

어떻게 구축했는가: 자동화의 뼈대부터 디테일의 완성까지

1단계: 보이지 않는 기반 공사 (자동화 코어 설계)

프로젝트의 심장은 단연 sync-notion.js 스크립트와 이를 실행하는 GitHub Actions 워크플로우다. 이 스크립트는 단순한 변환기가 아니라, 두 시스템 간의 데이터를 안전하고 효율적으로 동기화하는 ‘데이터 파이프라인’으로 설계되어야 했다.

처음에는 notion-to-md 같은 기존 라이브러리를 검토했다. 하지만 이내 한계를 깨달았다. 내가 원하는 것은 단순한 마크다운 변환이 아니었다. Notion의 특정 블록(Callout, Code 등)을 내가 정의한 HTML 구조로 변환하고, 페이지 속성을 Hugo의 Front Matter로 정교하게 가공하려면 더 깊은 수준의 제어가 필요했다. 이는 편의성(라이브러리 사용)과 제어력(직접 구현) 사이의 전형적인 엔지니어링 트레이드오프였고, 나는 장기적인 확장성을 위해 제어력을 택했다.

이 결정은 곧바로 세 가지 핵심 과제로 이어졌다.

  1. 데이터 무결성(Data Integrity): 파이프라인의 가장 기본적인 덕목은 데이터의 무결성을 해치지 않는 것이다. Notion에서 입력된 텍스트가 Hugo의 Front Matter로 변환될 때, YAML 문법을 깨뜨리는 특수문자가 포함될 수 있었다. 나는 입력값을 신뢰하지 않는다는 원칙 아래, 모든 문자열을 안전하게 이스케이프하고 따옴표로 감싸는 처리 로직을 추가했다. 이는 사소한 버그 수정이 아니라, 파이프라인의 안정성을 보장하는 근본적인 조치였다. (fix: Notion 동기화 스크립트 개선: 안전한 YAML 처리 강화)

  2. 효율성(Efficiency): 콘텐츠가 수백 개로 늘어날 미래를 가정해야 했다. 매번 모든 글을 API로 가져오는 것은 명백한 낭비다. 나는 상태 기반의 캐싱(Stateful Caching) 메커니즘을 도입했다. notion_cache.json이라는 상태 저장소를 만들어, 각 페이지의 마지막 수정 시간을 기록했다. 스크립트 실행 시, 이 캐시와 Notion의 실제 데이터를 비교하여 변경된 페이지만 다시 가져오도록 설계했다. 이로 인해 시스템은 복잡성이 약간 증가했지만, 장기적인 성능과 확장성을 확보할 수 있었다. (feat: Notion 동기화 스크립트 개선: 캐싱 기능 추가)

  3. 내결함성(Fault Tolerance): “만약 스크립트가 중간에 실패하면?” 이 질문에 답할 수 있어야 했다. API 호출 실패, 네트워크 오류 등 어떤 이유로든 동기화가 중단될 경우, 기존 콘텐츠가 모두 삭제된 채로 빌드가 진행되어 블로그가 통째로 사라지는 최악의 시나리오를 막아야 했다. 나는 원자적 연산(Atomic Operation) 개념을 차용했다. 동기화 시작 전, 기존 콘텐츠를 임시 디렉터리로 백업한다. 모든 Notion 페이지를 성공적으로 가져와 파일로 변환한 후에야, 비로소 기존 콘텐츠를 새로운 내용으로 교체한다. 만약 중간에 실패하면, 백업된 원본을 복원하여 시스템은 항상 일관된 상태를 유지한다. 이는 내 자동화 시스템을 단순한 스크립트에서 신뢰할 수 있는 서비스로 격상시킨 핵심적인 설계였다. (feat: Notion 동기화 스크립트 개선: 콘텐츠 백업 및 복원 기능 추가)

이 보이지 않는 기반 공사는 앞으로 쌓아 올릴 모든 사용자 경험 개선의 단단한 토대가 되었다.

2단계: 사용자 경험(UX)에 대한 집착 (기능를 넘어 감성으로)

견고한 백엔드 파이프라인이 완성되자, 나의 관심은 자연스럽게 최종 사용자인 ‘독자’에게로 향했다. 엔지니어로서 나는 기능이 ‘작동’하는 것을 넘어, 그것이 ‘어떻게’ 경험되는지에 대해 고민해야 했다.

“목록은 지루하다.” 이 한 문장은 글 목록 페이지의 전면적인 재설계를 이끌었다. 텍스트 목록과 썸네일 중심의 갤러리 뷰를 사용자가 선택할 수 있도록 토글 기능을 추가했다. 하지만 이내 Hugo의 복잡한 페이지네이션 로직과 씨름해야 했다. 뷰를 전환할 때 페이지 상태가 유지되지 않는 문제를 해결하기 위해, 나는 Hugo의 템플릿을 단순한 HTML 생성기가 아닌, 상태와 데이터를 다루는 ‘뷰 레이어’로 취급하며 접근했다. 수많은 fixrevert 커밋 끝에, 안정적으로 상태를 유지하는 페이지네이션 로직을 완성하며, 단순한 기능 구현을 넘어선 시스템에 대한 깊은 이해를 얻게 되었다. (refactor(posts): Re-implement view toggle with stable pagination and improved UI)

“긴 글을 읽는 독자를 위한 배려.” 기술 콘텐츠의 특성상 글의 길이가 길어질 수밖에 없다. 독자의 가독성을 돕는 목차(TOC)는 필수였다. 여기서 나는 한 걸음 더 나아갔다.

  • 동적 스크롤 추적: 현재 읽고 있는 섹션을 목차에 실시간으로 하이라이트하여, 독자가 숲과 나무를 동시에 볼 수 있도록 했다.
  • 사용자 제어권 부여: 화면을 가리는 목차를 독자가 직접 원하는 위치로 드래그할 수 있게 하여, 인터페이스의 제어권을 사용자에게 돌려주었다.
  • 반응형 설계: 모바일 환경에서는 토글 버튼으로 목차를 숨기고, 필요 시에만 오버레이로 표시하여 좁은 화면의 한계를 극복했다.

이 모든 TOC 개선 작업은 외부 라이브러리 없이 순수 JavaScript로 구현했다. 사이트의 속도를 저하 시킬 수 있는 불필요한 의존성을 추가하지 않겠다는 엔지니어링 원칙 때문이었다. 이는 성능과 사용자 경험 사이의 균형을 맞추려는 나의 의지였다.

3단계: 디테일의 미학 (엔지니어링의 완성도)

이제 플랫폼은 튼튼한 뼈대와 아름다운 얼굴을 갖추었다. 마지막은 디테일을 연마하여 완성도를 끌어올리는 작업이었다. 이 디테일들은 플랫폼의 정체성을 정의하고, 사용자와의 신뢰를 구축하는 중요한 요소다.

“코드 블록은 엔지니어의 언어다.” 나는 내 블로그의 주된 독자가 될 동료 엔지니어들에게 가장 친숙한 경험을 제공하고 싶었다. 그래서 VS Code의 UI를 웹에 재현하기로 했다. 단순한 스타일링을 넘어, 코드 블록 상단에 파일명을 표시하고, 터미널과 유사한 헤더를 디자인했다. (feat: VS Code 스타일 코드블록 완전 재설계 및 터미널 UX 구현)

“소통과 탐색의 가치.” 블로그는 소통의 공간이다. 나는 광고나 사용자 추적에서 자유로운 Giscus를 댓글 시스템으로 선택했다. GitHub 저장소의 Discussions를 그대로 활용하는 방식은, 내 블로그의 기술 스택과 철학에 완벽하게 부합했다. 독자들은 익숙한 GitHub 계정으로 편하게 의견을 나눌 수 있게 되었다. (feat: GitHub Discussions 기반 Giscus 댓글 시스템 구현) 또한, 외부 링크에 마우스를 올리면 해당 페이지의 정보를 미리 보여주는 ‘URL 프리뷰’ 기능을 구현하여, 독자의 불필요한 클릭을 줄이고 정보 탐색의 효율을 높였다.

이 모든 디테일은 sync-notion.js 스크립트와 Hugo 템플릿의 유기적인 협력을 통해 구현되었다. 백엔드 파이프라인이 데이터를 풍부하게 가공하고, 프론트엔드 템플릿이 그 데이터를 의미 있는 경험으로 빚어내는 과정이었다.

그래서 무엇을 얻었는가: 마찰 없는 글쓰기, 그 이상의 가치

이 여정을 되짚어보며, 나는 단순히 ‘블로그 자동화 시스템’을 만든 것이 아님을 깨닫는다. 나는 나의 생각과 철학이 담긴, 세상에 하나뿐인 ‘콘텐츠 플랫폼’을 설계하고 구축했다.

이 시스템을 통해 내가 얻은 것은 명확하다.

  1. 완벽한 역할 분리: 창의적인 글쓰기와 아이디어 관리는 최고의 도구인 Notion에서, 기술적인 구현과 안정적인 배포는 가장 신뢰할 수 있는 시스템인 Git과 Hugo가 담당한다. 나는 두 세계의 장점만을 취할 수 있게 되었다.
  2. 마찰 제로(Zero-Friction) 환경: 글을 발행하기 위해 거쳐야 했던 모든 번거로운 수작업이 사라졌다. 이제 나의 창작 활동은 그 어떤 기술적 장애물에도 방해받지 않는다.
  3. 성장하는 플랫폼: 이 블로그는 완성된 것이 아니라, 나의 필요에 따라 계속해서 기능을 추가하고 개선해나갈 수 있는 살아있는 유기체다. 나의 고민이 깊어질수록, 이 플랫폼 또한 함께 성장할 것이다.

결국 이 모든 과정은 ‘어떻게 하면 더 편하게, 더 잘 글을 쓸 수 있을까?‘라는 지극히 개인적인 질문에 대한 나만의 답을 찾아가는 엔지니어링의 여정이었다. 그리고 그 과정에서 나는 코드로 문제를 해결하는 엔지니어로서의 희열과, 사용자의 경험을 설계하는 기획자의 즐거움을 동시에 느낄 수 있었다.

이제 나는 다시 Notion을 켠다. 이 글 역시 그렇게 시작되었고, 곧 세상에 배포될 것이다. 나의 가장 강력하고 만족스러운 도구들을 통해서!

💬 댓글

GitHub 계정으로 로그인하여 댓글을 남겨보세요. GitHub 로그인

🔧 댓글 시스템 설정이 필요합니다

GitHub Discussions 기반 댓글 시스템을 활성화하려면:

  1. Giscus 설정 페이지에서 설정 생성
  2. GISCUS_SETUP_GUIDE.md 파일의 안내를 따라 설정 완료
  3. Repository의 Discussions 기능 활성화

Repository 관리자만 설정할 수 있습니다. 설정이 완료되면 모든 방문자가 댓글을 남길 수 있습니다.