Gatsby v3가 기존 v2에 비해 변경된 점이 많아서 마이그레이션을 계속 미루고 있었다. 기존 사이트는 모노리포 아래에 메인 웹사이트와 한국어 웹사이트를 별도 패키지로 구성하고 있었는데 공유하는 컴포넌트를 양쪽에서 사용할 수 있는건 좋긴 했지만 매번 글을 작성할 때마다 지나치게 많은 시간과 리소스를 사용했었다. 여름학기가 끝난 기념으로 재빨리 웹사이트를 정리했다.

계획

일단 마이그레이션 문서를 확인했다. 변경된 부분이 참 많았다. 하하하. 그냥 새 스타터를 받아서 하나씩 옮기는 쪽으로 전략을 바꿨다. 마이그레이션 과정에서 해야 할 일을 정리했다.

  • static 폴더 정리하기: 기존 워드프레스에서 옮겨올 때 대부분 리소스를 wp-content 폴더 안에 저장한 그대로 옮겨온 탓에 워드프레스도 아닌데 wp-content가 계속 존재했다. 각 리소스를 사용하는 포스트를 폴더 형태로 리소스까지 함께 보관하도록 변경한다.
  • mdx 적용
  • 분리된 사이트를 통합: 템플릿을 쉽게 적용할 수 있는 구조를 만든다.
  • 플러그인 마이그레이션: 직접 만든 플러그인, 설치해서 사용하는 플러그인 확인하기.
  • sitemap, rss를 영문과 국문 별도 설정
  • noindex 설정 추가
  • 답답하지 않은 디자인

과정

파일 정리

먼저 static 폴더를 정리하는 일을 가장 하고 싶었다. 그동안 사이트가 분리되어 있던 탓에 늘 어떤 방식으로든 경로 처리 문제가 있었다. 웹사이트 프리픽스를 지정할 수 있도록 gatsby에서 지원하고 있긴 하지만 플러그인마다 동작이 보장되어 있질 않았다. 그런 이유로 gatsby-remark-sharp 같은 플러그인은 제대로 동작하지 않았다. 이참에 wp-content 폴더도 정리하고 모든 정적 리소스도 함께 관리하도록 구조를 변경했다. sharp에서 지원 안하는 파일용으로 gatsby-remark-copy-linked-files도 설치했다.

스크립트를 작성해서 해당 리소스를 사용하는 포스트를 검색해서 리소스와 포스트를 폴더로 묶었다. 처음엔 쉘스크립트로 작성하다가 답답해서 js로 작성했고 생각보다 금방 할 수 있었다. (문자열 찾는 작업은 ripgrep-js를 활용했다.) 불필요한 크롭 이미지와 어디에도 연결되지 않은 리소스 모두 제거할 수 있었다.

Gatsby 관련

mdx는 가장 기대하고 얼른 설치해보고 싶은 것 중 하나였는데 생각보다 손봐야 할 부분이 많아서 일단은 remarkMarkdown을 유지하기로 했다. 다음에 기회가 된다면 0순위 작업이 될 것 같다.

Gatsby는 폴더 구조대로 url을 생성하는 것이 기본이지만 frontmatter 에서 주소를 직접 지정하는 플러그인을 쓰고 있다. 파일 구조는 언제든 바꿔도 url이 유지되었으면 해서 만든 플러그인이었는데 v3에서도 큰 문제 없이 동작했다.

lang과 type을 기준으로 템플릿을 찾아서 페이지를 만들도록 작성했다. 카테고리와 태그 페이지 생성도 기존 코드에서 아주 조금 바꾸는 것으로도 충분했다.

sitemap, rss는 동일한 플러그인을 여러 번 config에 넣는 것으로 여러 페이지를 만들 수 있었다. 다만 gatsby-plugin-sitemap은 생각과 다르게 filterPages가 동작하는 문제로 excludes만 적용했는데 해당 플러그인에서 사용하는 minimatch가 생각처럼 동작하지 않았다. 차라리 정규표현식으로 되어 있으면 편할 텐데. 일단은 sitemap이 크게 급한 상황은 아니니까 대략 숨겨야 하는 페이지만 숨기도록 수정했다. 이 과정에서 noindex 페이지도 몇 설정했다.

전 디자인도 크게 답답하지는 않았는데 너무 삭막한 것 같아서 조금 손봤다. styled-components로 작성했던 컴포넌트를 쉽게 옮겨 올 수 있었다. 다크 테마 설정은 추후에 하기로 하고 일단 뒀다.

대부분 플러그인은 문제 없이 동작했다.

  • gatsby-remark-attrremarkjs의 판올림으로 더이상 동작하지 않는다. 자주 쓰는 기능 중 하나였는데 아쉽다. common markdown의 directives를 지원하는 remark-directive를 설정했는데 attr처럼 동작하지는 않지만 그럭저럭 문제 없이 동작하긴 한다.
  • 그 사이에 netlify도 성능이 많이 좋아졌고 기능도 많아졌다. 빌드 이미지도 변경하고 캐시도 설치했다. 예전엔 gatsby-plugin-netlify-cache가 있었는데 deprecated 되었고 netlify에서 만든 플러그인, netlify-plugin-gatsby를 설치했다. 기존 플러그인과 동시에 설정하면 충돌이 있어서 둘 중 하나를 골라야 한다.

Netlify 관련

Netlify에서 사용중인 빌드 이미지가 오래되어서 해당 설정도 변경했다. 다만 빌드 중에 설치된 라이브러리를 제대로 인식하지 못하는 문제가 있었다. 내 경우에는 설치된 ruby 버전을 인식 못해서 새로 받는 일이 반복되었다. Site setting > build & deploy > build setting 에서 Edit setting 후 동일한 리포지터리를 다시 설정하고 저장하면 해결 된다.

Netlify Forms를 사용하지 않는다면 Site setting > build & deploy > form detection 설정을 끈다. 배포 속도가 향상된다.

결과

전체 배포 시간이 평균 4분~6분에서 1분 30초 선으로 줄어들었다.


워드프레스에서 Gatsby로 변경한 이후로 버전 업데이트는 좀 번거롭지만 보안과 서버 관리에 대한 걱정은 확실히 없어졌다. 다만 서드파티 플러그인이 버전을 따라가지 못하거나 하는 문제는 어디나 비슷한 것 같다. 그런 면에서 주요 플러그인을 모노리포에서 같이 관리하는 gatsby의 전략이 참 멋지다.

블로그를 처음 옮길 때와 달리 정적 호스팅 선택지가 많아졌다. 지금은 netlify를 계속 쓰고 있긴 하지만 Gatsby Cloud라든지 Azure 에서 제공하는 서비스도 확인해보고 싶다.

Remark에 흥미가 많이 생겼다. Rehype이나 mdx를 어떻게 지원하는지, 조만간 더 살펴보고 기능과 구조를 정리해보고 싶다.

늘 그렇듯 업데이트도 한 김에 좀 더 부지런히 글도 쓰고 생각도 정리하는 기회가 되었으면 좋겠다.

wordpress를 사용해서 운영하고 있던 블로그를 gatsby 기반으로 전환했다. 전환하는 과정에서 고려한 부분과 생각한 점을 정리하려고 한다.

전환 이전의 상황

그동안 wordpress를 직접 호스팅해서 사용하고 있었는데 매월 digitalocean에 비용을 내고 있는게 조금 아까웠다. 예전에는 블로그 외에도 이것 저것 올렸던 사이트가 있어서 크게 고민이 없었지만 이제는 다 정리되고 블로그만 남아 있었다. 게다가 거의 방치되다시피 하다보니 보안 문제도 계속 신경이 쓰일 수 밖에 없었다. wordpress를 실무에서 더이상 사용하고 있지 않아서 무슨 문제가 생기더라도 예전만큼 빠르게 알고서 대응할 수 없을 거란 생각도 들었다.

이런 이유에서 정적 사이트 생성기로 전환할 생각은 오래 전부터 했었다. 비용도 거의 무료인 데다 보안에도 크게 걱정할 필요가 없었다. 하지만 실천으로 옮기기 쉽지 않았다. 전적으로 마음에 드는 도구도 찾기 힘들었다. 블로그에 촛점을 맞춘 도구가 대부분이라 유연함이 상대적으로 부족했다. 또한 데이터 마이그레이션에 대한 두려움이 있었다. 모든 자료를 문제 없이 옮기더라도 문제가 있는지 없는지 검증할 필요가 있었다. 자동으로 추출하는 도구를 사용하더라도 분명 손이 가는 부분이 있을 것 같았다.

그리고 wordpress는 최고다. 그저 클릭 몇 번으로 새로운 기능을 쉽게 설치할 수 있고 테마도 손쉽게 바꿀 수 있는 데다 웹엔드에 앱도 제공한다. wordpress에서 벗어나고 싶어도 게으름이 내 발목을 꼭 잡고 놓아주질 않았다! 그래서 매번 "바꿀까?" 생각이 들 때마다 얼마 후에 "굳이 그래야 할까?"로 자연스럽게 의식의 변화가 생겼다.

I regret everything
모든 걸 후회합니다ㅏㅏ

쉬고 있는 동안 글은 안쓰는데 비용 나가는 것이 눈에 보이니 지금이라도 서둘러 옮겨야겠구나 하고 도구부터 찾았다.

도구 찾기

맨 처음엔 nextjs를 사용하려고 했었는데 생각보다 구현이 단순해서 직접 만져야 하는 부분이 많았다. 특히 파일 구조가 아닌 내가 원하는 url 구조를 넣으려니 생각보다 시간이 걸렸다. 한 3일 정도 만졌는데 뭔가 알 수 없는 404 오류가 계속 콘솔에 찍혀서 시간을 허비하고 있었다. 그러던 중 gatsby가 생각나서 튜토리얼만 봐야지 했는데 직접 만들려고 했던 기능들이 이미 다 훨씬 멋지게 제공되고 있었다.

특히 graphql로 쉽게 filesystem도 불러오고 사용할 수 있도록 구현되어 있고 url 구조도 내가 원하는 대로 생성할 수 있다는 점에 바로 마음을 돌렸다.

개발 과정

데이터 추출하기

Gatsby는 마크다운으로 작성되기만 하면 활용에 큰 문제가 없도록 구현되어 있어서 특히 편리했다. 그래서 hugo용이더라도 상관이 없어서 wordpress-to-hugo-exporter 플러그인을 사용해 추출했다.

데이터를 추출하기 전에 haruair.com에서 어떤 주소였는지 frontmatter에 history로 저장하도록 플러그인을 수정했다. 그리고 기존에 사용하던 headline도 저장했다.

글 몇 개를 제외하고 변환되긴 했는데 마크다운이 아닌 html로 저장된 문서도 몇 발견할 수 있었다. 지금도 다 확인하지 못해서 소셜 embed가 깨지는 페이지가 좀 있다. 변환이 안된 글도 있는데 플러그인에서 yaml 변환 과정에 문제가 있으면 깨진다. 누락되지 않는 페이지가 있는지 뒤늦게 확인했고 몇 개 안되어 손으로 변환했다.

영어와 한국어를 분리하고 싶어서 lang도 추가했다.

구현 사항과 사용한 플러그인

  • URL 처리: createPage()에서 path만 값만 넣으면 손쉽게 커스텀 경로를 사용할 수 있었다. frontmatter에 추가한 langtype, slug를 조합해서 사용하도록 했다. 또한 절대 경로도 사용할 수 있도록 url도 추가했다.
  • RSS 피드: gatsby-plugin-feed을 사용하면 쉽게 만들 수 있다. 다만 정적 사이트라서 xml 확장자를 써야만 하는 점이 아쉽다. /feed.xml/ko/feed.xml을 분리해서 생성하도록 config에 추가했다. 각각 어느 피드인지 title도 넣었다.
  • sitemap: 구글 웹마스터 도구에 제출하기 위해서 gatsby-plugin-sitemap을 추가했다.
  • PWA용 기능: gatsby-plugin-offlinegatsby-plugin-manifest를 추가했다. PWA는 잘 몰라도 속도가 빨라진 것은 바로 체감할 수 있었다.
  • 문법 강조: prismjs를 설치했다. 항상 hightlightjs를 사용해와서 조금 어색하긴 하지만 마음에 든다.
  • 소셜 공유: react-helmet으로 og meta와 twitter meta를 추가했다. 플러그인이 있을 것 같은데 일단 helmet으로 해결했다. 테스트 해보니 큰 문제 없었다.
  • 404 페이지 링크 문제: 내 코드에 문제인가 한참 씨름했는데 gatsby의 버그였고 현재는 고쳐졌다.
  • 배포: 도메인은 cloudflare에 있었고 github pages에 배포하려고 생각했는데 netlify가 정말 멋지게 서비스를 만들어둬서 netlify를 사용하게 되었다.
  • Google Analytics 설정: gatsby-plugin-google-analytics로 간편히 설정했다.

redirect과 canonical

앞서 자료를 추출하며 추가한 history로 redirect.json을 생성했다. 이 파일로 haruair.com에서 yoast SEO의 canonical 란에 새 주소를 저장했다.

<?php
if (php_sapi_name() !== 'cli') exit;
require_once(__DIR__ .'/wp-load.php');

$filename = './redirect.json';
$key = '_yoast_wpseo_canonical';

$data = json_decode(file_get_contents($filename));

foreach($data as $r) {
  $u = explode('/', $r->from);
  $id = array_pop($u);
  if (!is_numeric($id)) continue;

  $canonical = get_post_meta($id, $key);
  if ($canonical !== null) continue;

  update_post_meta($id, $key, $r->to);
  echo "SET {$id} {$r->to}". PHP_EOL;
}

사용하고 있는 amp 플러그인에서는 yoast의 canonical을 불러오고 있지 않아서 다음처럼 추가했다.

<?php
function haruair_amp_canonical($data, $post) {
  $data['canonical_url'] = get_post_meta($post->ID, '_yoast_wpseo_canonical', true);
  return $data;
}

add_filter('amp_post_template_data', 'haruair_amp_canonical', 100, 2);

여기까지 하고 Google Webmasters에 sitemap을 제출했다.

amp 페이지는 캐싱 되는 주기가 길어서 빨리 반영이 되지 않는 데다 사용하고 있던 cloudflare의 캐시를 매번 지워줘야 해서 확인이 어려웠다.

게다가 canonical을 추가하니 기존 사이트가 검색에서 사라졌는데 새 페이지가 아직 구글에 크롤링 되지 않아 아예 검색 결과에 나오지 않는 문제가 있었다. 그래서 모든 페이지에서 301 Moved Permanently로 새 주소에 이동하도록 코드를 추가했다.

<?php
function haruair_redirect_canonical() {
  if (!is_single()) return;
  $canonical = get_post_meta(get_the_ID(), '_yoast_wpseo_canonical', true);
  if ($canonical) {
    wp_redirect($canonical, 301);
    exit;
  }
}

add_action('template_redirect', 'haruair_redirect_canonical');

이제 색인이 어느 정도 정리되면 haruair.com 도메인도 netlify에서 제공하는 _redirect로 변경할 예정이다.

edykim.com

그동안 haruair.com을 오래 사용했는데 하나로 다 통합하기로 결정했다. 새 도메인에서 다시 시작하는 기분으로 깔끔하게 정리했다. 예전에는 블로그 구조에서 벗어나기 어려워서 노트처럼 사용하려는 취지와 조금 멀어졌는데 여기서는 좀 더 차분하게 내 자료를 쌓아갈 수 있으면 좋겠다.

처음 사용해본 Gatsby도 마음에 들었다. 주변에서도 많이 언급해서 궁금했었는데 이 정도로 만족스러울 줄은 몰랐다. Gatsby를 사용하면서 source와 transformer, plugin의 구분으로 확장의 성격을 명확하게 분리하는 점과, GraphQL로 마치 데이터를 가져와 페이지를 생성하면서도 그 과정 어느 순간이든 사용자가 개입할 수 있도록 열린 구조가 너무 좋았다. 작업 하면서 사소하지만 작은 코드를 기여하기도 했는데 기여 방법도 어떤 과정으로 참여할 수 있는지 잘 정리되어 있어서 손쉽게 제출할 수 있었다. 아직 코드가 지저분한데 정리도 하면서 gatsby도 좀 더 깊이 살펴보는 기회로 삼고 싶다.

HUZZAH!!!
어예! 공부다 공부 ✌

아직도 개인 신상이 정리가 되질 않아서 제출한 서류 결과만 기다리고 있는 상황이라 조금 답답했었다. 그래도 오랜만에 코드 만지면서 예전 생각도 들고, 얼른 다시 코드 짜는 삶으로 돌아가고 싶어졌다. 연말까지는 큰 일 없이 이렇게 지내게 될 것 같은데 조금 더 멀리 보고 부지런히 준비해야겠다.

색상을 바꿔요

눈에 편한 색상을 골라보세요 :)

Darkreader 플러그인으로 선택한 색상이 제대로 표시되지 않을 수 있습니다.