Build-Time Data와 Instant Search로 내 정적 블로그 재구축

발행: (2026년 2월 5일 오전 02:51 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

Cover image for Rebuilding My Static Blog with Build-Time Data and Instant Search

정적 사이트는 빠르고, 간단하며, 신뢰할 수 있어야 합니다. 시간이 지나면서 내 개인 블로그는 동적 앱처럼 동작하기 시작했어요—런타임 API 호출, 어디에나 있는 페이지네이션 로직, 그리고 여러 플랫폼에 흩어져 있는 조회수 집계 등.

지난 주에 ravgeet.in(Nuxt.js)의 블로그 섹션을 완전히 다시 만들면서 이 문제를 제대로 해결했습니다. 최종 결과물은 여전히 정적 사이트이지만, 이제는 생동감을 느낍니다: 집계된 조회수, 즉시 검색 및 정렬 기능, 그리고 외부 API에 대한 런타임 의존성이 전혀 없습니다.

이 글에서는 그 재구축 과정에서의 사고 흐름, 아키텍처, 그리고 트레이드‑오프를 자세히 살펴봅니다.

내 기존 설정의 문제점

  • 블로그 콘텐츠는 Hashnode에 존재했으며 (정식 출처)
  • 일부 게시물은 Dev.to에도 교차 게시되었습니다
  • 페이지는 Hashnode의 GraphQL API를 사용하여 런타임에 블로그 데이터를 가져왔습니다
  • 페이지네이션 로직(hasNextPage, 커서)은 UI 내부에 존재했습니다

단점

  • 실시간 API에 의존하는 정적 사이트는 어색하게 느껴졌습니다
  • 로컬 개발 및 빌드가 느리고 불안정했습니다
  • 검색이나 정렬 같은 기능을 추가하려면 더 많은 API가 필요했습니다

블로그는 정적으로 유지하되, 더 똑똑해지길 원했습니다.

빌드 시점 데이터 계약으로

외부 데이터 가져오기를 모두 빌드 시점으로 옮기고, 결과를 불변(static) 데이터로 취급합니다.

런타임에 블로그를 가져오는 대신, 다음과 같은 빌드 단계를 도입했습니다:

  • Hashnode에서 블로그를 가져옵니다
  • Dev.to에서 글을 가져옵니다
  • 플랫폼 간 동일한 글을 매칭합니다
  • 조회수를 집계합니다
  • 모든 데이터를 하나의 JSON 파일에 기록합니다

런타임에서는 사이트가 해당 JSON만 읽습니다.

Hashnode + Dev.to

Build‑time fetch & normalize

static/blogs.json

Nuxt UI (search, sort, views)

블로그 데이터 가져오기 및 집계

Hashnode: 정규 콘텐츠

Hashnode는 다음에 대한 진실된 출처입니다:

  • 제목, 슬러그, 내용, 태그
  • 게시 날짜
  • 표지 이미지
  • 기본 조회 수

모든 게시물은 Hashnode의 GraphQL API를 사용해 가져오며, 페이지네이션은 Node.js 스크립트 내부에서 처리됩니다.

Dev.to: 배포 및 추가 도달

Dev.to는 추가 독자를 제공하므로 해당 조회수를 무시하는 것이 잘못된 느낌이었습니다. 개인 액세스 토큰을 사용한 Dev.to API를 이용해 모든 글을 가져와 다음을 추출합니다:

  • slug
  • canonical_url
  • page_views_count

플랫폼 간 기사 매칭

기사 매칭은 단계적 전략을 사용합니다:

  1. 슬러그 매치
  2. 정규 URL 매치
  3. 제목 매치

매칭이 완료되면 최종 조회 수는 다음과 같이 됩니다:

combinedViews = hashnodeViews + devtoViews;

각 블로그에 대한 출력에는 다음이 포함됩니다:

  • 합산 조회수
  • 플랫폼별 조회수 (디버깅용)
  • Dev.to URL (매칭된 경우)

정적 데이터 계약 작성

모든 처리된 데이터가 static/blogs.json에 기록됩니다. 이 파일은:

  • 빌드 시 생성됨
  • Git에서 무시됨
  • 앱에서 읽기 전용으로 취급됨

또한 마지막 업데이트 시간 및 전체 블로그 수와 같은 메타데이터를 포함하여, 사실상 전체 블로그 API를 대체합니다.

런타임 API를 정적 서비스로 교체하기

이전에는 services/blogs.js가 실시간 GraphQL 호출을 수행했습니다. 리팩터링 후:

  • 서비스가 blogs.json을 동적으로 import합니다
  • find, findOne, search가 로컬에서 동작합니다
  • Axios 없음, 페이지네이션 상태 없음, 네트워크 실패 없음

UI 관점에서는 아무 변화가 없었지만, 내부적으로는 모든 것이 예측 가능해졌습니다.

즉시 검색 및 정렬

With all blog data local, search becomes trivial. I added:

  • Client‑side text search (title, brief, tags)

Sorting options

  • Published date (recent / oldest)
  • View count (most / least)

Because the dataset is small and static:

  • Search results are instant
  • No debouncing hacks
  • No loading states
  • Sorting is deterministic

This dramatically improves discoverability without introducing a search service.

트레이드오프와 배운 교훈

  • 빌드 시간이 약간 증가합니다
  • JSON 파일이 시간이 지남에 따라 커집니다
  • 실시간 분석에는 적합하지 않습니다

하지만 개인 블로그에선 이러한 트레이드오프가 충분히 가치가 있습니다. 핵심 요점

  • 정적이라고 해서 생명력이 없는 것은 아니다
  • 빌드 시점 데이터 파이프라인은 과소평가되고 있다
  • 하나의 깔끔한 데이터 계약이 UI, UX, 그리고 성능을 단순화한다

궁금하시다면 전체 구현은 ravgeet.in 리포지토리에 있습니다.

Back to Blog

관련 글

더 보기 »