Skip to content

블로그 설계 & 운영 가이드 ​

이 블로그를 어떻게 만들었고, 글이 늘어나면 어떻게 관리·확장할지를 한곳에 정리한 문서입니다. 새 글을 쓰기 전, 구조를 바꾸기 전에 여기부터 봅니다.

0. 대전략 ​

README의 원칙을 그대로 따릅니다.

  • "표현"과 "내용"의 완전한 분리 (= 독립적 진화)
    • 표현: 어떻게 보여줄지 — 폰트, 테마, 레이아웃, 카테고리/사이드바 구성
    • 내용: 글 자체 — docs/contents/의 마크다운 파일
  • 글을 쓸 때는 표현을 신경 쓰지 않고, 표현을 바꿀 때는 글을 건드리지 않는다.
  • 그래서 글은 순수 마크다운 + frontmatter로만 두고, 노출 방식(홈·사이드바·검색)은 스크립트가 frontmatter를 읽어 자동으로 조립합니다.

1. 현재 구조 ​

기술 스택 ​

  • VitePress 2.x — 마크다운 → 정적 사이트 생성기
  • GitHub Pages — 호스팅 (/blog/ 하위 경로로 배포)
  • GitHub Actions — main 푸시 시 자동 빌드·배포
  • Makefile + Node 스크립트 — frontmatter 기반으로 메인/사이드바/태그통계 자동 생성

디렉토리 ​

text
blog/
├─ Makefile                      # ⭐ 운영 명령어 (make landing / category / tag-stats)
├─ scripts/                      # 자동화 스크립트
│  ├─ lib/posts.mjs              # frontmatter 파싱 공용 모듈
│  ├─ landing.mjs                # 최근 글 21개 → recent.json
│  ├─ category.mjs               # category 기준 → sidebar.json
│  └─ tag-stats.mjs              # 태그 통계 → 콘솔 + tag-stats.md
├─ docs/
│  ├─ index.md                  # 홈(hero + <RecentPosts/>)
│  ├─ contents/                 # ⭐ 모든 글이 여기에
│  ├─ public/fonts/             # 정적 자원(폰트)
│  └─ .vitepress/
│     ├─ config.mts             # 표현 설정(네비·검색). 사이드바는 generated에서 읽음
│     ├─ generated/             # 스크립트 산출물(커밋 대상): recent.json, sidebar.json
│     └─ theme/                 # 커스텀 테마(style.css, RecentPosts.vue)
└─ .github/workflows/deploy.yml # 자동 배포

배포 흐름 ​

text
글 작성/수정 → make gen(=landing+category) → git push (main) → GitHub Actions
  → npm ci → npm run docs:build → dist 업로드 → Pages 반영
  • 로컬 dev/preview는 루트(/), Actions 빌드만 base = /blog/. (config.mts가 GITHUB_ACTIONS 환경변수로 분기)
  • generated/(recent.json, sidebar.json)는 빌드에 필요하므로 커밋합니다. 푸시 전에 make gen을 돌려 최신화하세요.

2. 새 글 추가하는 법 ​

① 파일 생성 — 네이밍 컨벤션 ​

docs/contents/ 아래에 만듭니다. 파일명은 YYMMDD-slug.md 규칙을 씁니다.

text
docs/contents/260702-vitepress-search.md
docs/contents/260715-kafka-consumer-lag.md
  • 날짜 프리픽스 → 목록이 자연스럽게 최신순 정렬됨 (landing/category가 이 날짜로 정렬)
  • slug는 영문·하이픈으로 (URL이 깔끔: /contents/260702-vitepress-search)
  • 한글 파일명은 URL 인코딩되어 지저분해지므로 피합니다. 제목은 frontmatter로 한글을 씁니다.

② frontmatter 작성 — 모든 자동화의 근거 ​

모든 글 맨 위에 붙입니다. 이 값들로 홈 최근글·사이드바·태그통계가 자동 생성됩니다.

yaml
---
title: VitePress 로컬 검색 붙이기
description: 한 줄 요약(홈 카드/검색 결과에 노출)
date: 2026-07-02
category: 서비스개발        # 대분류 1개 (MECE, 5~7개로 고정)
tag: #서비스운영 #아키텍처   # 세부 태그 여러 개 (M:N)
editLink: true
---
  • category: 글당 정확히 하나. 없으면 미분류로 모입니다.
  • tag: #해시태그를 공백으로 나열합니다. (tags: [a, b] 리스트 형식도 지원)
  • date: 생략하면 파일명 YYMMDD 프리픽스에서 자동 추론합니다.

⚠️ YAML에서 #는 원래 주석 기호입니다. tag: #...는 우리 스크립트가 원문을 직접 파싱하므로 정상 동작하지만, VitePress의 useData()로 frontmatter.tag를 읽으면 null이 됩니다. 태그는 스크립트(사이드바/통계)용으로만 쓰고, 페이지 안에서 직접 읽어야 하면 tags: [a, b] 형식을 쓰세요.

③ 반영 → 배포 ​

글 파일만 만들면 끝이 아니라, 메타데이터를 홈/사이드바에 반영해야 합니다. make가 대신 해줍니다.

bash
make gen              # landing + category 재생성 (홈·사이드바 갱신)
make dev              # 로컬 확인 (http://localhost:5173)  ※ gen 자동 실행
git add . && git commit -m "post: vitepress 검색" && git push

푸시하면 Actions가 알아서 배포합니다.

3. 컨텐츠가 늘어나면 — 관리·분리 전략 ​

지금은 docs/contents/에 파일을 평평하게(flat) 쌓고 있습니다. 사이드바는 이미 category 기준으로 자동 그룹화되므로(§5), 물리적 폴더 분리는 필수가 아닙니다. 다만 글이 수십 개를 넘어가면 다음을 검토합니다.

주제별 폴더로 분리 (선택) ​

파일이 아주 많아지면 카테고리 = 폴더로 나눠 파일 탐색을 쉽게 합니다. posts.mjs가 하위 폴더까지 재귀 스캔하므로 자동화는 그대로 동작합니다.

text
docs/contents/
├─ service-dev/260702-vitepress-search.md
├─ backend/260715-kafka-consumer-lag.md
└─ til/260702.md
  • 이동 시 URL이 바뀌므로(/contents/x → /contents/service-dev/x) 옮기려면 초기에 하는 게 비용이 쌉니다.

인덱스 페이지 (선택) ​

카테고리별 진입점이 필요하면 docs/contents/<cat>/index.md를 두거나, VitePress 데이터 로더로 태그 페이지를 자동 생성할 수 있습니다.

4. 카테고리 & 태그 설계 철학 ​

이 블로그의 분류는 2축입니다. 하나는 뼈대, 하나는 혈관입니다.

카테고리(MECE) = 블로그의 '뼈대' (책의 목차) ​

  • 독자가 처음 들어왔을 때 "이 블로그는 주로 무슨 주제를 다루는구나"를 직관적으로 보여주는 대문 역할.
  • 하나의 글은 무조건 하나의 카테고리에만 속하므로, URL을 깔끔하게 뽑거나(예: /category/service-dev/post-1) 대분류별 통계를 내기 좋습니다.

태그(M:N) = 콘텐츠의 '혈관' (그물망) ​

  • 카테고리라는 딱딱한 벽을 깨고 글과 글 사이를 유연하게 연결합니다.
  • 예: 서비스개발이 아니라 비즈니스 카테고리에 #서비스운영 태그를 가진 글이 올라오면, 독자는 카테고리를 넘나들며 '운영'에 대한 글만 모아 볼 수 있습니다.

이렇게 설계했을 때의 이점 ​

  1. 탐색 경험(UX) 극대화
    • 목차형 독자: "이 블로그의 '서비스개발' 글을 정주행하겠어!" → 카테고리
    • 파도타기형 독자: "'아키텍처' 관련 다른 글도 재밌겠는데?" → 태그 클릭
  2. SEO에 유리 — 태그의 M:N 연결이 자연스러운 내부 링크(Internal Link) 네트워크를 형성해 검색엔진 점수를 높입니다.
  3. 글 쓸 때 결정장애 해결 — 큰 틀(카테고리) 하나만 툭 정하고, 세부 속성은 태그로 다 때려 넣으면 되니 글쓰기 부담이 줍니다.

운영 가이드라인 (이것만 지키면 완벽) ​

  • 카테고리는 굳이 늘리지 않기 — MECE 유지를 위해 5~7개 이내로 고정. 웬만한 새 주제는 카테고리 신설 대신 기존 카테고리 + 새 태그로 해결합니다.
  • 태그 파편화 방지 — M:N의 유일한 단점은 관리 안 하면 태그가 난장판이 된다는 것. #서비스운영 / #서비스-운영 / #운영처럼 제각각이면 연결이 깨집니다. 나만의 '태그 사전'을 두고 일관되게 입력하세요. → make tag-stats로 주기적으로 점검합니다.

결론: "카테고리로 내비게이션(길 찾기)을 제공하고, 태그로 콘텍스트(맥락 탐색)를 제공한다." 이 방향 그대로 밀고 나갑니다.

구현 연결 ​

  • 카테고리 → make category가 category frontmatter를 읽어 사이드바를 자동 그룹핑 (§5).
  • 태그 → make tag-stats가 태그 사용 빈도를 집계해 파편화를 점검 (§5).

5. 자동화 명령어 (Makefile) ​

frontmatter만 성실히 채우면 나머지는 명령어가 처리합니다. make help로 목록을 볼 수 있습니다.

make landing — 메인페이지 최근 글 ​

  • 최신 글 21개(3열 × 7행)를 골라 docs/.vitepress/generated/recent.json으로 저장.
  • 홈(docs/index.md)의 <RecentPosts /> 컴포넌트가 이 JSON을 읽어 카드 그리드로 렌더링(제목·카테고리·날짜·태그).
  • 정렬 기준: date frontmatter → 없으면 파일명 YYMMDD.

make category — 사이드바 자동 생성 ​

  • 모든 글의 category를 읽어 카테고리별로 묶은 generated/sidebar.json 생성.
  • config.mts가 이 파일을 읽어 themeConfig.sidebar로 사용 → 글에 category만 적으면 사이드바가 자동 갱신.
  • 미분류 카테고리는 항상 맨 뒤로 정렬됩니다. (사이드바를 손으로 고치던 방식은 폐기)

make tag-stats — 태그 통계 (파편화 점검) ​

  • 태그별 사용 횟수, 카테고리 분포를 콘솔에 막대그래프로 출력.
  • 리포트를 tag-stats.md로도 저장. 이 파일은 로컬 점검용이라 .gitignore에 등록되어 커밋되지 않습니다.
  • 뜻이 같은데 표기가 다른 태그를 찾아 '태그 사전'대로 통일하는 데 씁니다.

묶음 명령어 ​

  • make gen = landing + category (푸시 전에 실행)
  • make dev / make build = gen 후 개발 서버 / 빌드
  • make preview = 빌드 결과 미리보기

6. 검색 ​

현재 — VitePress 로컬 검색 (활성화됨) ​

config.mts에 아래가 설정되어 상단 검색창이 동작합니다. 빌드 시 클라이언트 인덱스(MiniSearch)를 만드는 내장 기능으로 외부 서비스·비용이 없고 GitHub Pages 정적 배포와 완벽히 호환됩니다.

ts
// docs/.vitepress/config.mts → themeConfig
search: { provider: 'local' }

한글 검색 유의점 ​

  • 로컬 검색의 기본 토크나이저는 공백 기준이라 한국어 형태소 분리가 약합니다(부분 단어 매칭이 아쉬울 수 있음).
  • 개선책: search.options.miniSearch로 커스텀 토크나이저 지정, 또는 글에 영문 키워드/태그를 병기해 적중률을 높입니다.
ts
search: {
  provider: 'local',
  options: {
    miniSearch: { /* tokenize, searchOptions ... 한글 부분매칭 튜닝 지점 */ }
  }
}

장기 개선 — Algolia DocSearch ​

글이 수백 개 규모가 되거나 더 정교한 랭킹·오타 보정이 필요하면 검토합니다.

ts
search: {
  provider: 'algolia',
  options: { appId: '...', apiKey: '...', indexName: '...' }
}
  • 장점: 강력한 검색 품질, 오타 보정, 분석 / 단점: 외부 가입·크롤러 설정 필요, 한국어도 별도 튜닝 필요.
  • 결론: 로컬 검색으로 시작 → 규모가 커지면 Algolia로 승격.

7. 로드맵 / 체크리스트 ​

완료

  • [x] 로컬 검색 켜기 (search: { provider: 'local' })
  • [x] config.mts 정리 — 222222·중복 링크·placeholder 제거, 사이드바 자동화로 전환
  • [x] 홈 메인에 최근 글 21개 그리드(make landing + <RecentPosts/>)
  • [x] category 기반 사이드바 자동 생성(make category)
  • [x] 태그 통계/파편화 점검(make tag-stats)

진행/예정

  • [ ] 네이밍 컨벤션 확정 및 통일(YYMMDD-slug, 하이픈)
  • [ ] 글이 늘면 contents/ 주제별 폴더 분리
  • [ ] frontmatter tag 기반 태그 페이지(데이터 로더)
  • [ ] 한글 검색 토크나이저 튜닝 / 필요 시 Algolia 전환