카카오페이 기술 블로그는 어떻게 만들었을까요?

카카오페이 기술 블로그는 어떻게 만들었을까요?

시작하며

안녕하세요. 카카오페이 PFM-L 파티에서 자산관리 서비스의 프론트엔드 개발자로 근무하고 있는 나나라고 합니다. 이번 글을 통해 지금 읽고 계시는 카카오페이 기술 블로그를 개발한 경험을 회고해 보고자 합니다. Gatsby를 기반으로 기술 블로그를 개발하고, Astro 마이그레이션하며 느낀 장단점 등을 공유하고자 합니다. 기술 블로그 개발을 준비하는 분들, 어떤 플랫폼을 선택할지 고민하는 분들에게 도움이 되기를 바랍니다.

플랫폼 및 프레임워크 선택과 개발 과정

개발자들이 기술 블로그를 만들 때 가장 고민하는 부분이 어디에 작성할 것인가 인 것 같습니다. 개인 블로그를 구축할 때에도 이를 고민하다가 결국 글을 쓰지 않고 접을 때가 많을 듯합니다. Tistory, Medium, Velog 등의 전문 블로그 플랫폼을 쓰는 방법도 있고 깃허브 페이지를 사용하거나 Vercel 등에 직접 웹페이지를 구축하는 방법이 존재합니다.

당시 기술 블로그 기획자와 개발자 모두 플랫폼 이용보다는 웹페이지를 직접 구축하길 희망했고, 짧은 기간 안에 개발 완료를 위해서 스태틱 페이지로 배포할 수 있는 깃허브를 활용하기로 결정하였습니다. 서버 없이 SSG 렌더링 방식으로 개발할 경우 개발 리소스 단축이 가능하고 운영 비용 절감도 가능하기 때문입니다. 또한 깃허브로 기술 블로그를 운영하는 회사들도 다수 존재했기에 큰 이슈는 없을 것이라고 판단했습니다.

Gatsby를 선택한 이유

깃허브 페이지에 SSG 렌더링 방식으로 스태틱 페이지를 제공하는 데에는 Gatsby, NextJS, Jekyll 등의 선택지가 있었는데, 그중에 제가 선택한 것은 Gatsby였습니다. 개인 블로그를 Gatsby로 개발했기 때문에 가장 자신 있는 프레임워크이기도 했고, 아래와 같은 이점들이 있어 Gatsby를 골랐습니다.

  • Gatsby는 GraphQL로 마크다운과 Static Assets들을 조회 및 가공할 수 있도록 제공함
  • NextJS는 개발 모드에서 마크다운을 수정해도 HMR이 적용되지 않음
  • NextJS가 SSG 모드에서 이미지 최적화를 지원하지 않음
  • Jekyll은 커뮤니티가 크기 때문에 기존 테마를 그대로 쓰거나 약간 수정해서 사용하기에는 편리하지만, 루비 기반이어서 커스텀 하는데 어려움이 있을 것으로 생각함

Markdown 컨트롤

기술 블로그는 화면이 많지 않고, 화려한 JS 구현도 필요하지 않습니다. 따라서 마크다운 기반의 기술 블로그를 개발할 때에는 마크다운 콘텐츠 처리에 가장 공을 들이게 되는 것 같습니다.

Unified와 마크다운

unified
unified

마크다운을 처리할 때에는 보통 unified라는 라이브러리를 사용합니다. unified는 콘텐츠를 구조화된 데이터인 syntax tree로 변경하여 처리할 수 있도록 코어 프로세스와 플러그인을 제공합니다. unified가 동작하는 과정을 살펴보면, Input 값을 parser를 통해 syntax tree로 파싱하고, 이 tree를 다양한 transformer들을 통해서 가공한 뒤 compiler로 output을 얻을 수 있습니다. 마크다운의 경우 remark-parse라는 parser를 통해서 mdast (markdown syntax tree)로 파싱 되고, 이 상태에서 remark 플러그인들을 적용해서 자동 링크를 붙이거나 각주 등을 생성할 수 있으며, remark-rehype를 통해 mdast에서 hast (html syntax tree)로 변경이 가능하고, 마지막으로 브라우저에서 볼 수 있는 html string으로 가공이 됩니다.

마크다운 플러그인 작성하기

Gatsby의 경우 기본적인 마크다운 처리를 지원하기 때문에 parser와 compiler 부분은 크게 신경 쓰지 않아도 되며, syntax tree 단계에 어떠한 플러그인을 적용하느냐에 따라서 같은 마크다운이더라도 다른 output을 얻을 수 있습니다. 적용할 수 있는 플러그인에는 크게 remarkPlugins, rehypePlugins, 그리고 gatsbyRemarkPlugins의 3가지가 있습니다. remarkPlugins나 rehypePlugins는 unified 플러그인들을 그대로 사용하면 되고 차이점은 mdast에 적용되느냐 또는 hast에 적용되느냐에 있습니다.

unified의 플러그인뿐만이 아니라 gatsby에 특화된 플러그인들도 존재하며 직접 작성도 가능한데, 이는 gatsbyRemarkPlugins에 적용하면 됩니다. 기술 블로그를 작성하시는 분들이 영상을 넣을 때도 마크다운 이미지 문법으로 넣으면 간편할 것 같아서 이를 별도 플러그인으로 구현했고, 아래와 같은 형태가 됩니다.

const fs = require('fs');
const path = require('path');
const visit = require('unist-util-visit');
const isRelativeUrl = require('is-relative-url');

const allowedExtension = ['.webm', '.mp4'];

module.exports = ({ files, markdownAST, markdownNode, getNode }) => {
  const { dir } = getNode(markdownNode.parent);

  const getHTML = (node) => {
    // 중략...
  };

  visit(markdownAST, 'image', (node) => {
    const ext = path.extname(node.url);
    if (isRelativeUrl(node.url) && allowedExtension.includes(ext)) {
      const rawHTML = getHTML(node);
      if (rawHTML) {
        node.type = `html`;
        node.value = rawHTML;
      }
    }
  });

  return markdownAST;
};

이후 문단에서 기술하겠지만 Astro로 마이그레이션 했을 때에도 unified 기반으로 마크다운을 처리하는 것은 동일했기 때문에 큰 차이점은 없었습니다.

다만 Gatsby v4 사용 시 주의할 점은 현재 unified 관련 패키지들은 최신 버전이 전부 ESM only인데 gatsby의 설정 파일들은 노드 환경에서 돌아가기 때문에 최신 버전을 설치하면 오류가 발생하게 됩니다. 따라서 CJS를 지원하는 낮은 버전으로 다운그레이드 해서 설치해야 했습니다.

SEO 챙기고 구글 검색 잘 걸리게 하기

SEO (Search Engine Optimization, 검색 엔진 최적화)1란 웹사이트가 검색 결과에 더 잘 보이도록 최적화하는 과정입니다. 기술 블로그, 마케팅 블로그, 브랜드 사이트 등 홍보 목적의 웹을 론칭할 때 가장 중요한 부분은 SEO라고 할 수 있습니다. 아무리 웹 사이트를 잘 만들고 양질의 콘텐츠를 업로드하더라도 검색에 걸리지 않는다면 무용지물이기 때문입니다. 검색 엔진은 SEO 가이드라인을 제공하긴 하지만, 실제로 어떻게 랭킹이 매겨지는가는 공개되어 있지 않습니다. 동료들이 열심히 쓴 글들을 구글 검색 최상단에 올리겠다는 포부와 야망을 가지고 나름의 노하우(?)를 쏟아부었습니다.

<title>글제목 | 카카오페이 기술 블로그</title>
<meta name="description" content="요약글" />
<meta name="keywords" content="frontend,FE짱짱맨,킹짱벤장님,1등자산관리" />
<link rel="canonical" href="https://tech.kakaopay.com/post/slug/" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://tech.kakaopay.com/post/slug/" />
<meta property="og:title" content="글제목 | 카카오페이 기술 블로그" />
<meta property="og:description" content="요약글" />
<meta property="og:image" content="이미지" />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://tech.kakaopay.com/post/slug/" />
<meta property="twitter:title" content="글제목 | 카카오페이 기술 블로그" />
<meta property="twitter:description" content="요약글" />
<meta property="twitter:image" content="이미지" />
  • Meta 태그 잘 쓰기
    • title, description, keywords, og tag 등을 잘 기입합니다
    • 블로그의 경우 태그 -> keywords, 요약 글 -> descriptions으로 매칭하면 보다 효과적인 것 같습니다
    • og:type의 경우 일반적으로는 website를 쓰는데, 블로그 글은 article을 쓰면 좋다고 합니다
    • canonical URL (표준 URL)을 메타 태그로 표기하면 좋습니다
  • sitemap.xml 생성 및 구글 서치 콘솔 등록
    • 구글봇이 크롤링 할 사이트맵을 제출
    • 이때 불필요한 페이지는 제외해도 됩니다(페이징 화면)
  • 구글신에게 기도합니다. 구-멘🙏

검색 최상단에 걸린 카카오페이 기술 블로그 아티클
검색 최상단에 걸린 카카오페이 기술 블로그 아티클

카카오페이 기술 블로그 Lighthouse
카카오페이 기술 블로그 Lighthouse

Astro로의 마이그레이션, 그리고 장단점

비교적 짧은 기간 동안 개발을 완료하고 운영을 하다 보니 개인 블로그와는 달리 생각지도 못한 이슈가 많았습니다. 대표적인 이슈는 바로 발행되는 블로그 글 개수 증가에 따른 빌드 시간 지연이었습니다. 가장 큰 이유는 생각보다 배포가 잦았던 점인데요, 회사 블로그는 다양한 사람들이 글을 투고하고 업데이트 주기도 짧습니다. 2022년 6월 카카오페이 기술 블로그 오픈 당시에는 단 6개의 콘텐츠를 발행했습니다. 오픈 이후 지금까지 카카오페이 기술 블로그에서 누적 50개의 콘텐츠가 발행되었으며, 앞으로도 그 숫자는 지속적으로 증가할 예정입니다. 이처럼 발행되는 콘텐츠 개수가 증가하면서 자연스레 빌드 속도도 빠르게 느려졌습니다.

실제 서비스 개발의 경우 안정성을 위해 그렇게 자주 배포를 하지는 않고 배포에 어느 정도 시간이 걸리는 부분은 감안하고 있지만, 블로그는 다릅니다. 블로그 특성상 오타 수정이나 간단한 내용 수정을 할 때도 있기 때문에, 점점 늘어나는 빌드 시간이 염려되었습니다. 그래서 스냅샷 플러그인을 적용하는 등 다양한 시도를 해봤지만 Webpack 기반인 Gatsby의 빌드 시간이 드라마틱 하게 줄어들지는 않았습니다.

이때 Vite 기반의 Astro라는 프레임워크를 접하고, 레이아웃을 제외한 콘텐츠 부분만 마이그레이션 시도를 해본 뒤 동일 환경에서 3배 정도의 빌드 속도 향상을 체감한 후 마이그레이션을 결정했습니다. 물론 카카오페이의 프론트엔드 서비스 구축과 유지 보수의 경우 여러 개발자들이 꾸준히 개발하고 관리할 필요가 있기 때문에 이렇게 단기간에 프레임워크를 변경하지 않지만, 기술 블로그의 경우 개발 완료 이후에는 콘텐츠 위주로 운영이 되기 때문에 마이그레이션이 가능했다는 부분은 고려할 필요가 있습니다. GraphQL에서 Astro content collection으로 변경하는 작업이 간단하진 않았지만, 데이터가 많은 서비스가 아니어서 비교적 짧은 시간 동안 마이그레이션을 완료하였습니다.

Astro 공식 문서에서 언급하는 바와 같이 Astro는 대시보드나 SNS 등의 복잡한 웹 애플리케이션보다는 콘텐츠 중심의 정적 웹 사이트를 만드는 데에 적합하며, 마이그레이션 이후 Lighthouse 점수도 소폭 상승되었고 Vite 기반이기 때문에 서버 스타트 속도가 빨라서 개발자 경험도 향상되었습니다. Gatsby와는 달리 ESM Only 패키지를 다운그레이드 없이 사용할 수 있다는 점도 장점인 것 같습니다. Astro는 공식적으로 다양한 Integrations를 지원하기 때문에 react, image, mdx, partytown, prefetch, sitemap 등으로 손쉽게 기존 기술 블로그와 동일한 사이트를 구축할 수 있었습니다.

단점으로는 Gatsby에 비해 커뮤니티가 작아 여러 제약이 있다는 점입니다. 가령, 다양한 플러그인이 존재하는 Gatsby와 달리, Astro는 필수 기능을 넘어선 추가 기능은 대부분 직접 구현해야 합니다. 특히 이미지 처리 지원이 Gatsby에 비해 상대적으로 부족합니다. Gatsby를 사용할 때에는 placeholder blur 이미지를 이미지 최적화 플러그인에서 지원했는데, Astro에는 해당 기능이 없어서 아쉬운 부분이 있습니다. 게다가 Astro의 이미지 최적화 플러그인인 @astrojs/image는 마크다운 이미지는 처리해 주지 않아서 아래와 같이 플러그인을 별도로 구현했습니다.

import type { Root } from 'mdast';
import crypto from 'node:crypto';
import remarkMdx from 'remark-mdx';
import remarkParse from 'remark-parse';
import { type Transformer, unified } from 'unified';
import type { Parent } from 'unist';
import { visit } from 'unist-util-visit';

const parser = unified().use(remarkParse).use(remarkMdx);

export function remarkMdxImages(): Transformer<Root> {
  return function transformer(tree, file, done) {
    visit(tree, 'image', (node, index, parent) => {
      if (isImageWithCaption(parent)) {
        return;
      }

      if (!/\.(png|jpg|jpeg|webp)$/.test(node.url) || /^http/.test(node.url)) {
        return;
      }

      const alt = node.alt || '';
      const uuid = crypto.randomBytes(4).toString('hex');
      const Picture = 'Picture' + uuid;

      const newTree = parser.parse(
        [
          `import { Picture as ${Picture} } from '@astrojs/image/components';\n\n`,
          `<figure>`,
          `<${Picture}`,
          ` src={import('${node.url}')}`,
          ` alt="${alt}"`,
          ` widths={[480, 790]}`,
          ` sizes="(max-width: 790px) 100vw, 790px"`,
          ` formats={['png', 'webp', 'avif']}`,
          `/>`,
          `<figcaption>${alt}</figcaption>`,
          `</figure>`,
        ].join(''),
      );

      parent?.children.splice(index || 0, 1, ...newTree.children);
    });

    done();
  };
}

const isImageWithCaption = (parent: Parent | null) => {
  return parent?.type === 'picture';
};

다행히 Astro 최근 버전에서 astro:assets이라는 실험적 기능을 출시했고, 해당 플러그인에서는 마크다운 이미지 처리도 지원한다고 합니다.

또한 Astro는 다양한 프론트엔드 언어를 지원하기 때문에 react, vue, svelte 등을 한 번에 같이 사용할 수 있어서 프론트엔드 개발자가 다루기 어려운 프레임워크는 아니지만, Astro만이 지원하는 특수한 문법이 존재하고(e.g. template directives) 자체 UI 언어와 규칙이 있기 때문에 이에 대해 학습할 필요가 있습니다. Astro는 서버 측에서 렌더링 되므로 클라이언트에서 렌더링 되는 컴포넌트와 구분해서 설계해야 하며, 이는 최근 떠오르고 있는 서버 컴포넌트 개념과 유사한 부분이 있습니다.

처음부터 Gatsby로 개발을 했기 때문에 빠른 마이그레이션을 위해 Astro를 선택했지만, 11ty도 대안이 될 수 있을 것 같습니다. eslint.org, web.dev 등 유명한 사이트들이 Eleventy로 개발되었고, JS 기반 사이트 제네레이터 중에서는 빌드 성능이 가장 우수2하다고 합니다.

SSG 기반 기술 블로그를 운영하면서 느끼는 아쉬움

개발자 입장에서는 서버와 DB 없이도 SSG 기반으로 기술 블로그를 개발했기 때문에 개발 기간을 단축할 수 있어 부담이 덜했습니다. 그러나, 비 개발자인 블로그 관리자에게는 git과 마크다운에 대한 지식을 학습해야 한다는 큰 단점이 존재합니다. 블로그 관리자가 git과 마크다운 경험이 없는 경우 학습 시간이 소요되며, 이 점이 부담되어 결국 다른 플랫폼을 선택하는 경우도 있습니다. 설령 오픈했다 하더라도, 마크다운에 오류가 있거나 이미지 경로, 작성자 지정에 이슈가 있으면 배포에 실패하는데 이때는 분명 개발자의 도움이 필요합니다. 업무가 많다 보니 즉각적으로 도움을 드리기 어려울 때가 많은데, 기술적으로 어떻게 보완이 가능할지 고민하고 있습니다.

기술 블로그의 가치와 효과

회사 기술 블로그는 크게 2가지 가치가 있습니다. 우선 회사 입장에서는 외부에서 알기 어려운 사내 서비스 개발 경험이나 다양한 개발자들이 업무에 딥 다이브 하면서 얻은 지식들을 담고 있습니다. 이를 통해 회사의 기술 레벨을 어필할 수 있으며, 개발 문화에 대해서도 공유할 수 있습니다. 기술적인 인사이트를 널리 공유하고 싶지만 마땅히 글을 작성할 공간이 없던 사내 개발자들에게도 회사 기술 블로그는 자기 PR의 공간이 될 수 있을 것 같습니다.

2022년 6월 오픈 이후 지난 1년간 무려 12만 5천 명이나 되는 분들이 카카오페이 기술 블로그를 방문해 주셨습니다. 입사 지원 동기에 카카오페이 기술 블로그에 대한 언급이 눈에 띄게 늘어났을 때도 큰 보람을 느꼈습니다.

마치며

저는 껍데기만 만들었을 뿐이고 기술 블로그의 가장 중요한 부분은 역시 콘텐츠인데, 항상 양질의 글을 작성해 주시는 크루 분들에게 우선 감사의 말씀을 드리고 싶습니다. 저는 PO공대생WER이라 글을 정말 못 쓰는데, 다른 분들이 작성하신 글들을 보면 술술 읽혀서 부럽기도 하고 이런 글을 담아낼 수 있어서 영광이기도 합니다. 그리고 무엇보다도 긴 시간 동안 운영에 힘써주시는 기술전략팀 분들이 있어서 기술 블로그가 잘 운영되고 있는 것 같습니다. 메트릭 수집은 물론이고 좋은 콘텐츠를 담기 위해 다양한 방향으로 노력해 주시는 기술전략팀 크루 분들과 함께 앞으로도 카카오페이 기술 블로그에서 사내외로 유익한 콘텐츠를 전달하기 위해 노력하겠습니다.

Footnotes

  1. 출처: https://developers.google.com/search/docs/fundamentals/seo-starter-guide?hl=ko#glossary

  2. 출처: https://www.11ty.dev/docs/performance/#build-performance

nana.na
nana.na

카카오페이 PFM 클랜에서 프론트엔드 개발을 하고 있는 나나입니다. 퍼포먼스 최적화에 관심이 많고 좋은 코드를 작성하기 위해 노력합니다.

태그