블로그 기능 12종 추가와 Lighthouse 100점 달성기
목차
Astro 정적 블로그에 기능 12종을 하루 만에 추가하고, Lighthouse Performance를 76점에서 100점으로 끌어올린 기록
블로그의 기본 뼈대가 잡히면 자연스럽게 “더 나은 경험”에 대한 욕심이 생긴다. 태그를 클릭하면 관련 글만 모아 보고 싶고, 글을 다 읽으면 자연스럽게 다음 글로 이어지면 좋겠고, 모바일에서도 목차를 쉽게 펼칠 수 있으면 한다. 이런 기능을 하나씩 쌓다 보니 어느새 12종이 되었다.
기능을 다 붙인 뒤 Lighthouse를 돌려보니 Performance가 76점이었다. 원인은 1.4MB짜리 PNG 히어로 이미지. WebP 변환 한 번으로 40KB까지 줄이자 100점이 나왔다. 이 글은 어떤 기능을 왜 넣었는지, 그리고 Lighthouse 감점 요인을 어떻게 해결했는지를 정리한 기록이다.
탐색과 검색
블로그에 글이 쌓이면 탐색 수단이 필요하다. 태그 시스템과 전문 검색, 두 가지를 추가했다.
태그 필터
기존에는 태그가 단순한 텍스트였다. <span>을 <a>로 바꾸고, getStaticPaths로 태그별 동적 라우트를 만들었다. /blog/tag/kubernetes처럼 태그를 클릭하면 해당 태그의 글만 필터링되어 보인다. 루트 페이지 하단에는 글 수에 비례하는 크기의 태그 클라우드를 배치해서 어떤 주제가 많은지 한눈에 파악할 수 있게 했다.
Pagefind 검색
정적 사이트에서 검색을 구현하려면 빌드 타임에 인덱스를 만들어야 한다. Pagefind가 딱 이 용도다. astro build && npx pagefind --site dist로 빌드 파이프라인에 끼워 넣으면, 빌드 결과물에서 자동으로 검색 인덱스를 생성한다. 프론트엔드에서는 Cmd+K로 모달을 열고 Pagefind UI를 지연 로드하는 방식으로 구현했다. 초기 번들에 검색 UI 코드가 포함되지 않으므로 성능 영향이 없다.
네비게이션과 UI
글을 읽는 경험을 개선하는 UI 요소들을 추가했다.
이전/다음글 네비게이션
블로그 글 하단에 이전/다음글 링크를 2열 그리드로 배치했다. getStaticPaths에서 날짜순 정렬된 글 목록의 인접 항목을 props로 전달하는 방식이다. 추가로 좌/우 화살표 키보드 단축키도 넣어서 키보드만으로 글 사이를 이동할 수 있게 했다.
모바일 최적화
데스크탑에서는 사이드바에 목차(TOC)가 보이지만, 모바일에서는 공간이 부족하다. <details>/<summary> 태그로 접기/펼치기가 가능한 모바일 목차를 만들었다. 네비게이션 메뉴도 md 브레이크포인트 미만에서는 햄버거 메뉴로 전환되도록 했다. Menu/X 아이콘이 토글되고, 링크 클릭 시 자동으로 닫힌다.
읽기 진행률과 Back to Top
상단에 3px 높이의 accent 색상 프로그레스 바를 추가했다. 스크롤 위치에 따라 읽기 진행률이 실시간으로 표시된다. 스크롤이 400px 이상 내려가면 우하단에 원형 Back to Top 버튼이 나타나서 빠르게 상단으로 돌아갈 수 있다.
페이지 전환 애니메이션
Astro의 ClientRouter를 적용해서 페이지 간 이동 시 SPA 스타일의 부드러운 전환 효과를 넣었다. 전체 새로고침 없이 콘텐츠만 교체되므로 체감 속도가 빨라진다. 다만 astro:after-swap 이벤트를 통해 클라이언트 스크립트를 재초기화해야 하는 점은 주의가 필요하다.
콘텐츠 표현
글 자체의 가독성과 공유 편의를 높이는 기능들이다.
소셜 공유
글 하단에 X, Facebook, LinkedIn 공유 버튼과 링크 복사 버튼을 추가했다. 좋아요 버튼과 같은 라인에 좌우로 배치해서 공간을 절약했다.
코드 블록 개선
코드 블록 상단에 언어별 색상 배지를 추가했다. JavaScript는 노란색, TypeScript는 파란색, Bash는 터미널 스타일의 어두운 배경 등 14종의 언어를 구분한다. 15줄 이상의 긴 코드 블록은 자동으로 접히고, 하단 그라데이션과 함께 “펼치기/접기” 버튼이 나타난다.
SEO 구조화 데이터
검색 엔진이 사이트 구조를 더 잘 이해할 수 있도록 JSON-LD 구조화 데이터를 추가했다. 전체 사이트에는 WebSite 스키마를, 블로그 글에는 BreadcrumbList와 BlogPosting 스키마를 적용했다. 읽기 시간, 이미지, canonical URL 등 메타데이터를 포함시켰다.
Lighthouse 최적화
기능을 모두 추가한 뒤 Lighthouse를 돌려보았다.
최적화 전 점수
| 카테고리 | Home | Blog Post |
|---|---|---|
| Performance | 76 | 100 |
| Accessibility | 95 | 96 |
| Best Practices | 77 | 77 |
| SEO | 100 | 100 |
블로그 글 페이지는 이미 Performance 100점이었지만, 홈 페이지는 76점이었다. 원인을 분석하니 크게 세 가지였다.
감점 원인과 해결
LCP 8.1초 — 히어로 이미지 최적화
홈 페이지의 LCP(Largest Contentful Paint)가 8.1초였다. 범인은 1.4MB짜리 PNG 히어로 이미지. cwebp -q 80 -resize 896 0으로 WebP 변환하니 40KB로 줄었다. <picture> 태그로 WebP를 우선 로드하되 PNG 폴백을 유지하고, fetchpriority="high"를 추가했다. 결과적으로 LCP가 1.6초로 단축되었다.
HTTPS 미사용 — GoatCounter URL 수정
GoatCounter 스크립트가 프로토콜 상대 경로로 로드되고 있었다. 명시적으로 https://로 변경해서 Best Practices의 HTTPS 항목을 통과시켰다.
색상 대비 부족 — Footer 투명도 제거
Footer 크레딧 텍스트에 60% 투명도가 적용되어 WCAG AA 색상 대비 기준을 충족하지 못했다. 투명도를 제거하고 기본 secondary 색상만 사용하도록 수정했다.
최적화 후 점수
| 카테고리 | Home | Blog Post |
|---|---|---|
| Performance | 100 | 100 |
| Accessibility | 100 | 96 |
| Best Practices | 96 | 77 |
| SEO | 100 | 100 |
Best Practices의 나머지 4점은 로컬 환경에서 API 호출이 실패하면서 발생하는 콘솔 에러로, 실제 배포 환경에서는 발생하지 않는다.
테마 전환 이슈
다크/라이트 테마 전환에 startViewTransition API로 애니메이션을 넣으려 했다. 하지만 Shiki 코드 하이라이팅의 듀얼 테마 방식과 충돌이 발생했다. Shiki는 인라인 CSS 변수(--shiki-dark)로 다크 모드 색상을 관리하는데, View Transition이 DOM 스냅샷을 찍는 시점에 이 변수가 아직 반영되지 않아 코드 블록 색상이 깨졌다. 결국 startViewTransition을 제거하고 즉시 전환 방식으로 돌아갔다. 페이지 간 전환 애니메이션은 ClientRouter가 처리하므로 체감상 부족함은 없다.
마무리
하루 동안 12종의 기능을 추가하고 Lighthouse 최적화까지 마쳤다. 돌이켜보면 가장 효과가 큰 작업은 히어로 이미지의 WebP 변환이었다. 1.4MB PNG를 40KB WebP로 바꾸는 것만으로 Performance가 24점 올랐다. 이미지 최적화는 가장 쉬우면서도 가장 극적인 성능 개선 수단이다.
기능 추가 측면에서는 Pagefind 검색과 태그 필터가 핵심이었다. 정적 사이트라는 제약 안에서도 빌드 타임 인덱싱과 동적 라우팅으로 충분한 탐색 경험을 제공할 수 있다.