Stan 기술블로그

블로그를 이틀 동안 두 번 옮겼다

네이버 블로그에 아홉 편을 썼다. 코드와 표가 많은 글인데 제대로 보이지 않아서 GitHub Pages로 옮겼고, 하루 뒤 다시 Cloudflare와 내 도메인으로 옮겼다. 그 사이에 빌드는 “성공”인데 글이 조용히 사라졌고, 가릴 것을 검사하던 도구가 오히려 가릴 목록을 공개하고 있었고, 주소에 계정 이름이 박혔다. 다음에 또 걸릴 함정들을 남겨 둔다.


왜 옮겼나

두 가지였다. 하나는 보이는 모양이다. 설정 파일, 로그, 표가 많은 글인데 네이버 편집기에서는 코드 블록과 표가 원하는 대로 나오지 않았다. 다른 하나는 날짜다. 일한 날과 글이 올라간 날을 맞추고 싶었는데, 발행 시각을 내 마음대로 정할 수 없었다.

Jekyll로 사이트를 만들어 GitHub Pages에 올리고, 네이버에 있던 아홉 편을 옮겼다. 글은 마크다운 파일 하나에 맨 위 front matter(제목·날짜·카테고리)를 적는 식이다.

빌드는 성공인데 글이 없다

GitHub Pages에서 가장 당황스러웠던 건, 빌드가 성공으로 끝나는데 글이 사이트에 없는 경우였다. 세 번 겪었다.

미래 날짜. 새 글 세 편을 올렸는데 404가 났다. 근거 없이 날짜를 하루 이틀 뒤로 넣었던 게 원인이었다. Jekyll은 날짜가 빌드 시각보다 미래인 글을 에러 없이 건너뛴다. 날짜를 원고를 쓴 시각으로 고치자 바로 나타났다.

깨진 front matter. 다음 날 아침, 멀쩡하던 글 하나가 사이트에서 사라졌다. 맨 위 설정에 큰따옴표 안에 큰따옴표를 또 쓴 줄이 있어서 YAML이 깨졌는데, 빌드는 역시 “성공”이었다. 그 글만 조용히 빠졌다.

사라지는 중괄호. 본문에 적은 docker ps --format '{{.Names}}' 같은 명령이 화면에서는 docker ps --format ''로 나왔다. Jekyll이 이중 중괄호를 자기 템플릿 문법(Liquid)으로 읽고 빈 값으로 바꾼 것이다. 그런 줄은 raw 태그로 감싸야 그대로 나온다.

세 가지 모두 빌드 로그에는 아무 문제가 없었다. 그래서 커밋하는 순간에 막기로 했다. 커밋 훅이 front matter를 Jekyll과 같은 파서로 읽어 보고, 날짜가 미래인지, 본문에 감싸지 않은 이중 중괄호가 있는지 검사한다. 배포 뒤에는 모든 글 주소가 200인지 확인하는 스크립트를 돌린다.

화면은 여러 번 갈아엎었다

디자인은 여러 번 갈아엎었다. 카드형 첫 화면은 AI가 만든 티가 났고, 이력서처럼 꾸민 2단 구성은 더 어색했고, 참고한 블로그를 따라 한 건 너무 똑같았다. 결국 흰 바탕에 왼쪽 메뉴, 오른쪽에 제목 ··· 날짜 한 줄 목록만 남은 단순한 기록용 블로그가 됐다.

몇 가지 규칙은 그 과정에서 생겼다.

  • 목차 번호는 레이아웃이 매긴다. 글마다 소제목에 번호를 직접 달다 보니 0부터·1부터·없음이 섞여 목차가 들쭉날쭉했다. 소제목 번호를 전부 걷어내고, 목차와 본문에 번호를 자동으로 붙였다.
  • 본문에서 다른 절을 번호로 가리키지 않는다. 번호를 자동으로 바꾸자, 본문의 “3장에서”라는 문장들이 한 칸씩 엉뚱한 절을 가리키게 됐다. 절 이름으로 고쳤다.
  • CSS 주소에 빌드 시각을 붙인다. 배포 직후 목차가 단어마다 줄이 바뀌어 보인 적이 있다. 새 HTML과 10분 동안 캐시된 옛 CSS가 섞인 것이다. CSS 링크 뒤에 ?v=빌드시각을 붙여 둘이 항상 같이 바뀌게 했다.

검사기가 가릴 목록을 공개하고 있었다

사내 일을 적는 블로그라 도메인·IP·고객사명 같은 걸 가려야 한다. 그래서 커밋할 때 금지 패턴을 찾는 검사기를 만들었다. 그런데 나중에 보니, 그 검사기 코드 안에 가릴 대상 목록이 그대로 들어 있었다. 그리고 그 레포는 공개 상태였다. 가리려던 걸 검사기가 공개하고 있던 셈이다.

목록은 개인 비공개 저장소로 옮기고, 검사기는 그 파일을 읽게 바꿨다. 파일이 없으면 검사 없이 넘어가지 않고 커밋을 막는다. 하지만 이미 올라간 예전 커밋에는 목록이 남아 있었다. 레포 자체를 비공개로 돌려야 했다.

여기서 막혔다. GitHub 무료 계정은 비공개 레포로 Pages를 쓸 수 없다. 레포를 비공개로 바꾸는 순간 사이트가 내려간다.

Cloudflare로

비공개 레포를 그대로 연결해서 무료로 올릴 수 있는 곳으로 Cloudflare를 골랐다. 생각보다 화면이 달라서 몇 번 헤맸다.

  • 대시보드는 Workers 흐름으로 들어간다. 정적 사이트용 Pages 입구는 거의 보이지 않았다. Workers로 정적 사이트를 올리려면 레포에 wrangler.jsonc를 두고 “빌드 결과 폴더(_site)를 올려라”라고 적어 줘야 한다. 그리고 대시보드에서 정한 Worker 이름과 이 파일의 name이 같아야 빌드가 된다.
  • 빌드 서버의 기본 Ruby가 3.4였다. 이 블로그의 Jekyll 묶음은 3.3에서만 빌드해 봤기 때문에 .ruby-version으로 3.3.6을 고정했다. 그 대가로 빌드마다 Ruby를 설치하느라 3~4분, 길면 9분까지 걸린다.
  • 빈 커밋으로는 빌드가 돌지 않는다. 자동 배포가 되는지 빈 커밋으로 시험했다가 6분을 기다렸다. 바뀐 파일이 있어야 빌드한다. 빌드 진행 상황은 GitHub 커밋에 Cloudflare가 남기는 체크로 터미널에서 볼 수 있다.

레포를 비공개로 돌린 뒤에도 Cloudflare는 연결된 앱 권한으로 이 레포 하나만 읽어서 빌드를 계속했다.

내 도메인

Workers 기본 주소는 프로젝트이름.계정이름.workers.dev다. 계정 이름 자리에 메일 아이디가 그대로 들어갔다. 바꿀 수는 있지만, 하루 만에 주소가 두 번 바뀐 걸 생각하면 호스팅을 또 옮겨도 그대로 남는 주소가 필요했다. 도메인을 하나 샀다.

  • 가비아에서 바꿀 건 “네임서버”였다. DNS 관리 화면의 “레코드 추가”를 누를 뻔했다. Workers에 도메인을 붙이려면 DNS 관리 자체를 Cloudflare로 넘겨야 하고, 그러려면 네임서버를 Cloudflare가 알려 준 두 개로 바꿔야 한다. 넘기고 나면 가비아 쪽 레코드는 쓰이지 않는다.
  • 반영은 생각보다 빨랐다. 네임서버를 바꾸고 수십 분 안에 상위 서버가 Cloudflare를 가리켰다. Worker에 도메인을 연결하자 HTTPS 인증서도 알아서 붙었다.
  • http는 https로, www는 본 주소로. “항상 HTTPS 사용”을 켜고, www를 본 주소로 넘기는 규칙을 만들었다. 넘기기만 할 www에도 DNS 레코드는 있어야 해서, 빈 주소(100::)를 가리키되 Cloudflare를 거치는(Proxied) 레코드를 하나 뒀다.
  • www가 안 열린다고 생각했는데, 내 맥이 기억하고 있었다. 레코드를 만들기 전에 www로 한 번 접속해 본 탓에, 맥의 DNS 캐시가 “그런 주소 없음”을 들고 있었다. 휴대폰 LTE로 열어 보니 잘 됐다. 길면 30분쯤 그렇게 기억한다.
  • 마지막으로 메일 아이디가 들어간 workers.dev 주소는 꺼 버렸다.

지금 구성

무엇 어디
소스 GitHub 비공개 레포
빌드·호스팅 Cloudflare Workers(정적 자산), 푸시하면 자동 빌드
도메인 가비아에서 등록, DNS는 Cloudflare
커밋 때 검사 가림 패턴, front matter(날짜·Liquid·소제목 번호), 프로젝트 데이터
배포 뒤 확인 모든 글·페이지가 200인지

마치며

진짜로 배운 것

  1. “성공”한 빌드를 믿지 않는다. 정적 사이트 생성기는 조용히 건너뛰고 조용히 지운다. 빌드 결과가 아니라 실제 주소가 열리는지로 확인하고, 조용히 빠지는 원인은 커밋 때 막는다.
  2. 가리는 도구가 가릴 대상을 품고 있지 않은지 본다. 금지 목록을 공개 저장소의 코드에 쓰면 그 자체가 유출이다. 목록은 비공개인 곳에 둔다.
  3. 주소는 호스팅과 떼어 둔다. 이틀 만에 주소가 두 번 바뀌었다. 내 도메인이 있으면 다음에 또 옮겨도 주소는 그대로다.
  4. “안 된다”고 결론 내기 전에 내 쪽 캐시를 의심한다. CSS 캐시도, DNS 캐시도 실제로는 다 됐는데 내 화면에서만 안 된 것이었다.

인프라 운영 카테고리의 글