Cloudflare Pages로 블로그 이전하기

예전부터 뭔가 공부했던 내용을 글로 정리해서 쓰다보면 좀더 체계적으로 정리할수도 있고, 대강 알고 있었던 부분들을 좀더 정확하게 찾아보게 되는 좋은 효과들이 있다고 생각해서 블로그에 공부했던 내용들, 특히 한참 Competitive programming을 하던 때에는 대회 / 문제풀이 관련해서 정리하는 글을 많이 작성해 왔습니다.

기존에는 Github Pages 에 개인 블로그 (웹사이트?) 를 호스팅해서 사용하고 있었는데, 이번에 Cloudflare Pages로 넘어오면서 기존의 글들을 모두 정리하고, 새롭게 시작해 보기로 마음먹게 되었습니다. 새로운 블로그의 첫 포스팅으로는 이 “이사” 작업을 하기로 생각하게 된 계기와, Github pages라는 흔한(?) 방법 대신 Cloudflare pages를 선택하게 된 이유에 대해서 공유해 보려고 합니다.

Jekyll

Jekyll은 Ruby로 작성된 툴로, markdown을 이용해 작성한 포스트를 HTML 형태의 웹페이지로 바꾸어 주는 역할을 합니다. 원하는 만큼 직접 HTML을 손댈 수 있으면서도, 글 쓰는 것 자체는 markdown으로 편하게 할 수 있습니다.

무엇보다 큰 강점은 Github Pages라는 강력한 호스팅 솔루션이 native하게 지원하는 방법이라는 점입니다. 누구나 무료로 github.io 형태의 웹페이지를 만들 수 있고, 비교적 간편해서 기술적인 내용을 다루는 블로그에 널리 사용되고 있습니다.

이런 일을 하려고 할 때 우리가 생각할 수 있는 자연스러운 솔루션(?) 은 대략 아래 네 가지가 있습니다.

  1. 네이버 블로그
  2. 티스토리
  3. Jekyll + Github Pages
  4. 아예 처음부터 웹사이트 구축

이 네 가지는 1 < 2 < 3 < 4 순으로 세팅할 것이 많은 대신, 더 많은 기능과 customizability를 제공합니다 (사실 이 두가지가 서로 상반되는 것은 당연한 일입니다)

Jekyll을 선택한 배경에는 몇가지 이유가 있습니다. 아래 기준들은 모두 제 개인적인 것이기는 합니다만, 비슷한 취향이신 분들께는 도움이 될것 같습니다.

  • 수식 작성: 수식은 반드시 LaTeX 문법으로 작성 가능해야 하며, 이미지 형태로 들어가서는 안 됩니다. 즉, MathJax나 KaTeX를 사용 가능해야 합니다.
    • 네이버 블로그는 이 부분에서 약간 불편함이 있습니다. 네이버 자체 수식 편집기를 사용하거나 외부에서 렌더링해서 이미지로 가져와야 하는데, 이미지로 가져오는 방법은 당연히 아름답지 못하고, 자체 수식 편집기가 렌더링하는 결과물의 폰트도 LaTeX의 아름다운 수식 폰트와 틀립니다.1 결정적으로 인라인 수식을 사용할 수 없어서, 작은 수식도 모두 display 형태로 들어가야 합니다.
    • 티스토리나 Jekyll 모두 적절히 설정하면 이부분은 충분히 가능합니다.
  • 코드 작성: 코드 하이라이트가 가능해야 하며, 최대한 configurable해야 합니다.
    • 네이버 블로그를 제외한 나머지 세 솔루션은 이부분을 적당히 잘 지원합니다.
  • 포스트 정리: 태그 (tag) 등을 이용하여 포스트를 체계적으로 정리할 수 있어야 하며, 가능하다면 트리 형태로 정리할 수 있는 것이 바람직하다고 생각합니다. 정확히는 Collection: dict[str, Collection] | list[Collection] | post 정도로 정의되어 있으면 행복한것 같습니다.
    • 네이버 블로그는 이게 굉장히 잘 되어 있습니다. 이외에는 적절한 설정을 잡아주면 잘 되는것으로 알고 있습니다.
  • 검색: 구글, 네이버 등에서 검색 가능했으면 좋겠고, 특히 구글 검색엔진이 잘 작동했으면 좋겠습니다.
    • 네이버/티스토리로는 이것이 비교적 쉽게 가능하고, Jekyll이나 웹사이트 구축 시에는 약간의 작업을 해야 합니다.
  • 확장성: 차후에 웹 상에서 제가 뭔가를 하고싶을때, 기술적으로 불가능 한 것을 최소화하고 싶었습니다. 가령 /[some-sub-website] 에 제가 새로운 웹사이트같은걸 추가로 구축한다거나…
    • 이런 것을 하려면 4번이 ideal하고, 최소한 3번을 써야 합니다. 외부 플랫폼에 의존적이라면 그 플랫폼이 허락한것만 사용할 수 있습니다.

이런 요소들을 고려해 볼 때, Jekyll은 상당히 좋은 솔루션입니다. 물론 모든 부분이 만족스럽지는 않지만, 큰 불만 없이 사용할만 한것 같습니다.

Al-folio Theme

제가 컴퓨터공학 학부를 졸업하면서 가장 힘들었던 과목은 웹 서비스 개발 을 실제로 해보는 과목이었습니다. 체감상 저는 이 과목이 바로 그 전 학기에 들었던 (위상수학 + 복소해석 + 현대대수 + 알고리즘 + 양자컴퓨팅 + 데이터베이스) 여섯 과목의 합보다 더 힘들었다고 기억합니다.

전체적으로 개발할 양이 많았던것도 사실이지만 결정적으로 프론트엔드 가 제게 너무 고통스러웠습니다. Web browser에 올라가있는 웹사이트는 어떤 알수 없는 이유로 생각대로 동작하지 않고, 분명 표준적인 프레임워크를 사용하고 있는데도 어딘가 반쯤 나사가 풀려있는 컴포넌트들과, 분명 종이와 펜으로 쓸때는 명확했던 동작이 올려보면 미묘하게 어긋나 있고, 결정적으로 다 만들고 나면 제가 만든건 뭐가되었든 쓰기 불편하고 기분이 나쁩니다. 돌이켜서 왜 그런지를 생각해보니, 근본적으로는 UI/UX와 디자인 같은 human한 요소들에 제가 별로 재능이 없다는 생각이 듭니다.

그래서 Jekyll을 쓰면서도 최대한 모든것이 잘 갖추어진 포맷을 가져오고 싶었고, 최대한 디자인에 제 개입을 줄이기로 했습니다. 그래서 찾은 것이 Al-folio 입니다. Al-folio는 적당히 아카데믹한 느낌의 웹페이지를 잘 만들어 주고, 제가 보기에는 디자인이 괜찮아 보였습니다2.

Al-folio는 제가 꼭 쓰고 싶었던 포스트 내 인용 (Jekyll-scholar 기반), 프로젝트 등에 기반한 적당히 깔끔한 포스트 정리, (제가 보이게) 적당히 예쁜 시작 페이지 등은 물론이고, 한번도 써보지 않을것같은 수많은 소셜 네트워크들과의 연동, disqus / giscus 기반의 댓글, related posts, 여러 외부 API 지원 등이 굉장히 잘 되어 있습니다. 비슷한 느낌의 블로그를 계획하신다면 적극 추천드립니다.

한가지 단점은 너무 많은 기능들이 들어있어서 무겁고, 무엇이 어디에 있는지 알기 어렵다는 점입니다. 후자는 쓰다보면 익숙해지고, 전자는 어차피 블로그는 한번 빌드해두면 며칠이상은 그대로 돌아가며, 빌드는 내 머신이 아니라 호스팅 쪽에서 해줄것이니까 별로 상관이 없습니다. 지금은 10여 초 정도가 걸리는데, 나중에 블로그가 엄청나게 커지거나 추가 기능을 쓰게된다면 그때 다시 고민해 보겠습니다.

Cloudflare Pages

Jekyll에 기반한 웹사이트는 Github Pages를 이용, .github.io 에 무료로 호스팅할 수 있습니다.

일단 무료라는 점에서 엄청난 어드밴티지가 있고, github 에서 운영하다보니 다른 세팅을 아무것도 안 해도 git push 하면 바로 빌드가 된다는 편리함이 있지만, 그럼에도 불구하고 제가 다른 솔루션을 찾아 떠난 이유는 크게 두 가지입니다.

  • 확장성: Github Pages는 static website만을 지원하기 때문에, 백엔드 API가 필요한 일을 하려면 외부 솔루션을 사용해야 합니다. Cloudflare Pages도 이것은 마찬가지지만, Cloudflare에서는 KV storage, cloudflare worker 등의 솔루션도 같이 제공하기 때문에, 확장 시에 약간 더 편하게 작업할 수 있습니다.

  • Jekyll 사용과정의 불편함: Github pages는 github action을 이용하기 떄문에, 빌드 프로세스가 아무래도 덜 customizable합니다. 이것때문에 Jekyll에서도 어떤 패키지를 사용하게 되면 대신에 github의 자동 빌드를 쓰지 못하게 되는 것들이 있는데, 대표적으로 Jekyll-Scholar (citation) 이 그렇습니다.

  • Analytics: Github Pages도 외부에서 analytics를 달아줄수는 있는데 (구글 등), 뭔가 매우 불편했습니다. 특히 Google analytics가 저는 매우 불편하던데, 이부분은 아마 취향 차이일것 같습니다.

이외에도 속도와 보안 등의 요소들이 있다고 하는데, 제가 작업할 내용의 특성상 그런게 별로 필요하지는 않습니다. 크게 위 내용들 때문에 Cloudflare Pages를 선택하게 되었다고 보면 될 것 같습니다. 특히 google analytics가 잘 작동하지 않는다는 점이 크리티컬했고, 도대체 왜인지 모르겠지만 google search console에서 아무리 다시 등록을 해봐도 sitemap을 잘 읽지 못한다거나… 하는 부분들이 가장 컸던 것 같습니다.

그래서 마치 이사를 하면서 과감히 물건을 버리듯이, 포스트들도 정리하고, 새롭게 시작하기로 결심했습니다.

사실 기술적으로는 기존 블로그를 그대로 유지하면서 Cloudflare Pages에 올리는 것도 가능했겠지만, 그렇게 하면 아마 영원히 정리도 하지 않았을 거고, 제가 이것저것 건드려놓은 (그러면서 망가진) HTML/CSS도 영원히 수정할 수 없었을것 같아서이기도 합니다.

타조 디버깅법

여전히 해결하지 못하고 대충 덮어놓은 기술적인 문제들이 몇가지 있습니다. 당연히 처음에는 심플하게 al-folio로 시작하고 빌드 커맨드로 bundle install && jekyll build 해 보았는데,

Liquid Exception: invalid byte sequence in US-ASCII

이 에러와 몇시간정도 싸웠던것 같습니다. 당연히 US-ASCII가 아닌 바이트시퀀스가 있겠죠. 한국어를 썼으니까요. 저는 분명히 UTF-8 로 인코딩되어있기를 기대하고 있었습니다.

Liquid에 대해 잘 모르기때문에, 제가 컴퓨터공학을 전공했더라도 이런 상황에서 할수있는건 구글과 GPT-4o, GPT-4.5, GPT-o3-mini-high를 오가며 분노-타협-애원하는 수밖에 없습니다.

export LANG=en_US.UTF-8 && export LC_ALL=en_US.UTF-8 && bundle install && jekyll build

이렇게 하라고 했는데, 전혀 안 되더군요. iconv를 이용해서 글 전체를 UTF-8로 바꾸라느니 (이미 UTF-8인데요),

결국 반복적으로 GPT 모델들을 바꿔가며 분노한 끝에 (ㅋㅋ) LANGLC_ALLC.UTF-8 로 바꾸고 나니 빌드가 되었습니다. export 로 하면 안 되고, cloudflare 빌드 세팅에서 직접 variable로 설정해 줬을 때는 되었는데, 이 두개 사이에 어떤 차이가 있는지 잘 모르겠습니다.

bundle install && jekyll build

그 외에도 분명히 안 쓰는 것 같은데 지우면 뭔가가 작동을 안하는 .js 파일이라던가, 저는 도저히 어떤 원리로 작동하는건지 알수도 없고 그닥 알고싶지도 않은 al-folio 테마 구석구석 숨어있는 liquid 흑마법 등… 사실 언젠가 이 문제들이 반드시 발목을 잡을것만 같지만, 일단 웹사이트가 웹에 올라간다면 만족하기로 했습니다.

마치 머리를 풀숲에 숨기고 사냥당하지 않기를 기대하는 타조 같은 자세가 아닐 수 없습니다.


앞으로도 블로그 운영하면서 기존에 하던대로 공부했던 내용 관련 정리, 기술적인 글들 (여러 디버깅 이슈라던가…), 개인적인 이야기들 여러가지를 기록해보려고 합니다.

특히 기술적인 글들에 대해서는, 혹시 더 좋은 방법을 아시는분은 댓글 달아주시면 감사하겠습니다 ㅎㅎ;;


  1. “다름”과 “틀림”의 국어적 의미에 대해서는 알고 있으며, 제 개인적 호오를 담은 의도적인 사용입니다. 

  2. 지금까지 살아오면서, 제 디자인적 심미안이 전체 평균과 어긋나있음이 여러 차례 드러났는데, 만약 그렇다면 이게 좋은게 맞는지 잘 모르겠습니다. 

$$ \newcommand{\floor}[1]{\left\lfloor #1 \right\rfloor} \newcommand{\ceil}[1]{\left\lceil #1 \right\rceil} \newcommand{\N}{\mathbb{N}} \newcommand{\R}{\mathbb{R}} \newcommand{\Z}{\mathbb{Z}} \newcommand{\Q}{\mathbb{Q}} \newcommand{\C}{\mathbb{C}} \renewcommand{\L}{\mathcal{L}} \newcommand{\x}{\times} \newcommand{\contra}{\scalebox{1.5}{$\lightning$}} \newcommand{\inner}[2]{\left\langle #1 , #2 \right\rangle} \newcommand{\st}{\text{ such that }} \newcommand{\for}{\text{ for }} \newcommand{\Setcond}[2]{ \left\{\, #1 \mid #2 \, \right\}} \newcommand{\setcond}[2]{\Setcond{#1}{#2}} \newcommand{\seq}[1]{ \left\langle #1 \right\rangle} \newcommand{\Set}[1]{ \left\{ #1 \right\}} \newcommand{\set}[1]{ \Set{#1} } \newcommand{\sgn}{\text{sign}} \newcommand{\halfline}{\vspace{0.5em}} \newcommand{\diag}{\text{diag}} \newcommand{\legn}[2]{\left(\frac{#1}{#2}\right)} \newcommand{\ord}{\text{ord}} \newcommand{\di}{\mathrel{|}} \newcommand{\gen}[1] \newcommand{\irr}{\mathrm{irr }} \renewcommand{\deg}{\mathrm{deg }} \newcommand{\nsgeq}{\trianglelefteq} \newcommand{\nsg}{\triangleleft} \newcommand{\argmin}{\mathrm{argmin}} \newcommand{\argmax}{\mathrm{argmax}} \newcommand{\minimize}{\mathrm{minimize}} \newcommand{\maximize}{\mathrm{maximize}} \newcommand{\subto}{\mathrm{subject\ to}} \newcommand{\DKL}[2]{D_{\mathrm{KL}}\left(#1 \di\di #2\right)} \newcommand{\ReLU}{\mathrm{ReLU}} \newcommand{\E}{\mathsf{E}} \newcommand{\V}{\mathsf{Var}} \newcommand{\Corr}{\mathsf{Corr}} \newcommand{\Cov}{\mathsf{Cov}} \newcommand{\covariance}[1]{\Cov\left(#1\right)} \newcommand{\variance}[1]{\V\left[#1\right]} \newcommand{\variancewith}[1]{\V\left[#1\right]} \newcommand{\expect}[1]{\E\left[#1\right]} \newcommand{\expectwith}[2]{\E_{#1}\left[#2\right]} \renewcommand{\P}{\mathsf{P}} \newcommand{\uniform}[2]{\mathrm{Uniform}\left(#1 \dots #2\right)} \newcommand{\gdist}[2]{\mathcal{N}\left(#1, #2\right)} \DeclarePairedDelimiter{\norm}{\lVert}{\rVert} $$ \everymath{\displaystyle}