개인 개발한 바이크 사이트가 Google 코어 업데이트로 DAU 420 → 150으로 폭락. 4개월에 걸쳐 200으로 되돌린 모든 기록
요약
개인 개발 바이크 플랫폼 'MotoHub'는 Google Core Update 이후 DAU가 420명에서 150명으로 급락하는 심각한 위기를 겪었습니다. 작성자는 이 폭락을 경험한 후, 사이트의 근본적인 문제점(콘텐츠 깊이 부족, 저품질 페이지 대량 생성)을 분석하고, 기술적 최적화와 AI를 활용한 콘텐츠 강화 전략을 실행했습니다. 구체적으로는 품절 차량 페이지 처리 방식 변경 및 `noindex` 적용, JSON-LD 스키마 마크업 전면 도입, 그리고 Claude Sonnet 4 API를 이용해 차종별 특징과 역사 등 깊이 있는 콘텐츠를 대량 생성하여 검색 엔진의 'Helpful Content' 기준을 충족시키며 트래픽 회복에 성공했습니다.
핵심 포인트
- Google Core Update는 사이트 전체 품질(E-E-A-T, Helpful Content)을 평가하며, 얇은 페이지가 많으면 좋은 콘텐츠까지 영향을 받는다.
- 대량의 저품질/반응 없는 페이지를 처리할 때는 `200 OK` 반환과 함께 재고 여부에 따른 `noindex` 적용이 필수적이다.
- JSON-LD 스키마 마크업을 전면적으로 도입하여 검색 엔진에 사이트 구조와 콘텐츠 유형(Product, LocalBusiness 등)을 명확히 전달해야 한다.
- AI (Claude Sonnet 4)를 활용하여 차종별 특징, 역사 등 깊이 있는 전문 콘텐츠를 대량으로 생성함으로써 수동 작업의 한계를 극복하고 품질을 확보할 수 있다.
이전 기사에서 「개인 개발한 바이크 사이트를 3개월 만에 DAU 420까지 성장시킨 이야기」를 썼습니다.
그로부터 1개월 후인 2026년 3월 27일. Google March 2026 Core Update가 시작되었습니다.
4월 초순, Google Search Console을 열었을 때의 충격은 지금도 잊을 수 없습니다.
DAU 420 → 150.
노출수는 절반 이하, 클릭수는 3분의 1. 쌓아온 것들이 한순간에 무너지는 기분이었습니다.
이 기사에서는 폭락한 후 4개월 동안 무엇을 했고, DAU 200까지 되돌린 모든 기록을 작성합니다. 「회복했습니다!」라는 예쁜 이야기가 아니라, 아직 420에는 도달하지 못한 현재 진행형의 리얼한 기록입니다.
**MotoHub(motohub.jp)**는 GooBike, BDS, Webike의 중고 바이크 재고를 횡단 검색할 수 있는 플랫폼입니다. Laravel 12 + Alpine.js + Tailwind CSS로 구축하였으며, 현재 16만 대의 재고 데이터, 4,569종의 차종, 12,260개의 숍을 게재하고 있습니다.
| 지표 | 수치 |
|---|---|
| DAU | 420 |
| ... | |
| 지표 | 수치 |
| --- | --- |
| DAU | 150 |
| 월간 노출수 | 약 52,000 |
| 월간 클릭 | 약 800 |
| 평균 게재 순위 | 38위 |
특히 심각했던 것이 차종 모델 페이지(/bikes/catalog/{slug})와 숍 페이지(/shops/{slug})였습니다. 이 두 카테고리만으로 전체 트래픽의 7할을 차지하고 있었기에, 이곳이 떨어지면 전체가 붕괴합니다.
떨어진 페이지들의 공통점은 "숫자의 나열일 뿐, 읽을 가치가 없다"는 것이었습니다.
차종 페이지의 예:
- 재고 목록 테이블이 있을 뿐임
- 차종의 역사나 특징에 대한 설명이 제로(0)
- FAQ 없음, 관련 콘텐츠 없음
- 사용자가 "흐음" 하고 돌아가기만 하는 페이지
Google이 말하는 "helpful content"와 정반대였습니다.
폭락 후, 1주일에 걸쳐 원인을 분석했습니다.
4,569종의 차종 × 47개 도도부현 = 약 21만 페이지를 생성하고 있었지만, 내용은 재고 테이블과 기본 스펙뿐이었습니다. 콘텐츠의 "두께"가 제로(0)였습니다.
Google의 Helpful Content System은 사이트 전체의 품질을 평가합니다. 얇은 페이지가 대량으로 있으면 좋은 페이지까지 함께 끌려 내려갑니다.
외부로부터의 백링크(Backlink)는 약 10건. 개인 개발 사이트이기에 당연하지만, 코어 업데이트에서는 E-E-A-T(경험·전문성·권위성·신뢰성)가 중시됩니다. 도메인 파워가 약한 사이트는 영향을 받기 쉽습니다.
품절된 차량 페이지(수만 페이지)를 200 OK로 반환하면서, 내용은 "이 차량은 품절되었습니다"라는 한 문장뿐. 이것이 대량의 저품질 페이지로 평가되었을 가능성이 있습니다.
검색 결과 페이지(/bikes/search?...)에 noindex를 설정하지 않았던 시기가 있어, 파라미터가 다른 대량의 페이지가 인덱싱(Indexing)되어 있었습니다.
패닉에 빠지지 않고, 우선 기반부터 고쳤습니다.
Before: "품절되었습니다" 텍스트만 있음
After: 동일 차종의 재고 목록·유사 차종 제안·시세 정보 표시
품절 → 404가 아니라 200으로 반환
단, 재고 0대의 차종 페이지 → noindex 추가
재고가 있는 차종의 품절 개별 페이지는 200으로 반환하고, 대체 정보를 충실히 한다. 재고가 완전히 0인 차종 페이지만 noindex로 한다. 이 판단이 정답이었습니다.
// 모든 search 결과 페이지에 noindex
<meta name="robots" content="noindex, follow">
이것만으로 Google이 크롤링(Crawl)해야 할 페이지가 명확해집니다.
12종류의 JSON-LD 컴포넌트를 작성:
Website— 사이트 전체Product— 개별 차량LocalBusiness— 바이크 숍FAQPage— FAQ (후술할 AI 생성)BreadcrumbList— 브레드크럼 (6가지 패턴)BlogPosting— 블로그 기사ParkingFacility— 주차 시설
resources/views/components/jsonld/
├── website.blade.php
├── product.blade.php
...
사이트맵(Sitemap)을 생성할 때마다 IndexNow를 통해 Bing/Yandex에 알리도록 설정했습니다. Google은 지원하지 않지만, Bing으로부터 오는 트래픽도 무시할 수 없습니다.
// GenerateSitemap.php 의 마지막 부분
$this->info('IndexNow에 알리는 중...');
// 사이트맵 URL을 IndexNow API로 전송
여기서부터가 승부처였습니다. 4,569종의 차종 페이지를 수동으로 채우는 것은 물리적으로 불가능합니다. AI를 사용합니다.
두 개의 Artisan 커맨드(Command)를 생성:
# 차종의 특징·매력·고르는 법 생성
php artisan bike:generate-content --chunk=50
# 차종의 역사·계보 생성
...
메커니즘:
- 차종명·배기량·카테고리·재고 수·가격대를 프롬프트(Prompt)에 포함
- Claude Sonnet 4 (
claude-sonnet-4-20250514)로 500~800자 분량의 콘텐츠 생성 enriched_content컬럼(JSON)에 저장- 재고가 5대 이상인 차종을 우선 처리
2,000종의 차종을 처리했습니다. API 비용은 약 ¥12,000.
사람이 작성했다면 수개월이 걸렸을 작업을 3일 만에 완료했습니다.
AI가 생성한 콘텐츠에서 FAQ를 추출하여 구조화 데이터(Structured Data)로 출력:
// enriched_content가 있는 경우 AI 생성 FAQ
// 없는 경우 템플릿 FAQ
@if($model->enriched_content)
...
이를 통해 검색 결과에 FAQ 리치 스니펫(Rich Snippet)이 표시되기 시작했습니다. CTR(클릭률)이 눈에 띄게 개선되었습니다.
php artisan youtube:fetch --limit=500
YouTube Data API로 차종명을 검색하여 리뷰 영상을 DB에 캐시(7일 TTL)합니다. 차종 페이지에 영상 섹션을 추가했습니다.
체류 시간이 늘어남 → 페이지 품질(Page Quality) 시그널 개선.
Before: 스펙 표 + 재고 목록 (2개 섹션)
After: 6개 탭 구성
- 재고 목록 (기존)
- 시세 분석 — 연식별·주행 거리별 가격 차트
- 스펙 — 레이더 차트로 시각화
- 리뷰 — 사용자 리뷰
- 영상 — YouTube 임베드(Embed)
- FAQ — AI 생성 FAQ
Chart.js 차트는 지연 로딩(Lazy Loading) + 폴링(Polling) 방식으로 렌더링합니다:
// Chart.js 로딩을 기다리는 폴링 (10초 타임아웃)
var attempts = 0;
var timer = setInterval(function() {
...
Chart.js를 <script defer>로 로드하고 있는데, DOMContentLoaded 타이밍과 Chart.js 로드 완료 시점이 일치하지 않는 문제로 세 번이나 고생했습니다. IntersectionObserver + DOMContentLoaded + 콜백(Callback) 등록 조합을 모두 시도해 봤지만 실패했습니다. 결국 단순한 setInterval이 가장 확실했습니다.
SEO 기반이 갖춰졌으니, 다음은 콘텐츠의 "면"을 넓힙니다.
WordPress를 사용하지 않고, Laravel 내부에 블로그 시스템을 구축했습니다.
왜 풀 스크래치(Full Scratch)인가?
- 동일한 도메인·동일한 디자인으로 통일감을 주고 싶음
- 내부 링크(Internal Link)를 자유롭게 걸고 싶음
- BlogPost → BikeModel의 관계(Relation)를 활용하고 싶음
- Markdown 기반으로 GitHub에서 관리하고 싶음
구현 규모:
| 카테고리 | 파일 수 |
|---|---|
| Model (BlogPost, BlogSeries, BlogTag) | 5 |
| ... | 합계 |
| 약 30개 파일 |
주요 기능:
- Markdown → HTML 렌더링
- 시리즈·태그 관리
- OGP 이미지 동적 생성 (Intervention Image)
- RSS/Atom 피드
- 완독 시간 자동 계산 (일본어 기준: 500자/분)
[riders-map]쇼트코드(Shortcode)로 지도 임베드- 초안/공개/예약 발행
현재까지 18개의 기사를 공개했습니다. 모두 실제 판매 데이터에 기반한 데이터 드리븐(Data-driven) 기사입니다.
touring_spots: 279건 (전국의 투어링 스폿)
roadside_stations: 124건 (미치노에키)
47개 도도부현별 인덱스 페이지 + 각 스폿의 상세 페이지. 사이트맵에도 추가하고, 내부 링크(Internal Link)로 메인 콘텐츠와 연결.
주차장 3,569건, 역 9,032건, 미치노에키 124건, 기타 POI를 포함하여 약 4.9만 건의 위치 정보를 맵에 표시. Google Maps API 기반의 인터랙티브 맵(Interactive Map)입니다.
이것만으로는 SEO 효과가 제한적이지만, **사이트 체류 시간과 페이지 회전율(Page Circulation Rate)**에 효과가 있습니다.
프로그래매틱 SEO (Programmatic SEO):
배기량별: 50cc / 125cc / 250cc / 400cc / 대형 / 리터
타입별: 네이키드 / 스포츠 / 아메리칸 / 오프로드 /
스쿠터 / 어드벤처 / 클래식 / 투어러 /
...
각 페이지에 KPI 6개 항목(재고 수, 평균 가격, 최저가, 인기 차종 등), 가격 분포 차트, 인기 차종 랭킹을 자동 생성.
바이크 업계 뉴스를 자동 수집·표시하는 페이지:
php artisan news:fetch # 뉴스 자동 취득
php artisan news:thumbnails # 썸네일 취득
php artisan news:ranking # 랭킹 뉴스 생성
현재 518건의 뉴스를 게재. 모델별 뉴스 필터링, 댓글 기능, '좋아요' 기능도 구현. 뉴스 페이지 자체는 직접적인 SEO 효과가 미미하지만, 사이트의 '신선함(Freshness)'을 Google에 전달하는 시그널이 됩니다.
기존의 /bikes/area/{pref}/{slug}
페이지(도도부현 × 차종)를 대폭 개선:
- 타이틀 개선 ("{현}의 {차종} 중고차" → "{현}의 {차종} 재고 {N}대 | 평균 ¥XX만~")
- KPI 6개 항목 추가
- 가격 분포·연식 분포 차트
- 관련 지역으로의 내부 링크
- JSON-LD 강화
Google에만 의존하는 것은 위험하다. 그것을 통감했기에 다른 채널도 개척.
바이크 관련 질문에 매일 2~3건 답변. 프로필에 MotoHub 링크를 설치.
포인트:
- "중고 바이크 시세", "250cc 추천" 계열의 질문을 공략
- 답변 마지막에 "자세한 시세 데이터는 이쪽으로"라며 링크 삽입
- 베스트 답변을 다수 획득 → 도메인 파워(Domain Power)의 미세한 증가
바이크 구매를 검토 중인 사람의 포스트에 매일 10건 답글(Reply). 봇이 아닌 수동으로, 정말 도움이 되는 정보를 곁들여서.
Twitter/X 봇(자동 게시)도 가동 중:
# 7개의 Artisan Command
php artisan tweet:new-stock # 신착 재고
php artisan tweet:price-drop # 가격 하락
...
블로그 기사를 디스커버(Discover)에 노출시키기 위해:
<!-- layout.blade.php -->
<meta name="robots" content="max-image-preview:large, max-snippet:-1, max-video-preview:-1">
- 1200×630px의 OGP 이미지를 모든 기사에 설정
- Article JSON-LD의
image필드를 반드시 출력 - 감성을 자극하는 타이틀 ("22만 대의 데이터로 밝혀진~")
즐겨찾는 차종의 가격 인하를 LINE Messaging API로 알림:
// Flex Message로 리치 카드 전송
$service = new LineNotificationService();
$service->sendPriceDropAlert($user, $listing, $priceDiff);
페이지 표시 속도가 SEO에 영향을 미치기 때문에 메모리를 두 배로 증설. 특히 Meilisearch가 메모리를 많이 사용하기 때문에.
캐시가 날아가면 랭킹 페이지 등이 일시적으로 에러가 나는 문제가 있었기에, AOF 영속화(Persistence)를 설정.
전체 동기 방식에서 needs_reindex 플래그 방식으로 변경. 16만 건의 전체 동기화는 너무 무거웠다.
숫자로 되돌아보면:
| 기간 | 커밋(Commit) 수 | 주요 내용 |
|---|---|---|
| 4월 전반 | 약 80 | SEO 기반 수정, noindex, JSON-LD |
| ... | 합계 | 약 300 |
760개의 커밋 중 300개의 커밋이 이 4개월에 집중됨. 하루 평균 6개 커밋. 업무를 병행하며 진행하는 개인 개발로서는 상당한 페이스.
최종적인 결과는 다음과 같습니다:
Backend: Laravel 12 (PHP 8.3)
Frontend: Blade + Alpine.js + Tailwind CSS v3
React (Quiz/Game용 Island Architecture)
...
| 항목 | 수치 |
|---|---|
| PHP 파일 (app/) | 302 |
| ... | |
| 지표 | 폭락 전 |
| --- | --- |
| DAU | 420 |
| ... | |
| 평균 순위는 26위 → 10.7위로 대폭 개선. 하지만 DAU는 아직 420의 절반 이하입니다. |
이것은 무엇을 의미할까요?
노출 횟수가 돌아오지 않았습니다. 즉, Google에 인덱싱(Indexing)된 페이지의 '평가'는 올라갔지만, '게재되는 쿼리(Query)의 폭'이 돌아오지 않았습니다. 코어 업데이트(Core Update)의 영향은 아직 남아 있습니다.
| 데이터 | 건수 |
|---|---|
| 차종 마스터 | 4,569 |
| ... | |
| DAU 420일 때 "순조롭다!"라고 생각했던 제 자신이 안일했습니다. Google 검색이 유일한 트래픽 소스(Traffic Source)였기에, 코어 업데이트 한 번에 거의 전멸했습니다. |
교훈: 트래픽 소스를 분산할 것. SNS, 지식인(Yahoo Chiebukuro), 푸시 알림(Push Notification), 뉴스레터. 하나의 채널에 70% 이상 의존해서는 안 됩니다.
프로그래매틱 SEO (Programmatic SEO)로 대량의 페이지를 만드는 것은 쉽습니다. 하지만 내용이 없다면 Google에 평가받지 못하는 것은 물론, 사이트 전체의 평가를 떨어뜨립니다.
교훈: 페이지를 만들기 전에 "이 페이지에 온 사람이 만족할 것인가?"를 물을 것. No라면 noindex 처리하거나 Not Found 처리할 것.
16만 대의 재고 데이터는 대형 미디어에는 없는 무기입니다. "22만 대의 데이터 분석"이라는 관점은 사용자에게도 Google에게도 먹힙니다.
중고 바이크의 평균 판매 일수가 41일이라거나, 1~2만 km가 가장 가성비가 좋다거나 하는 이러한 데이터는 실제로 플랫폼을 운영하고 있지 않으면 내놓을 수 없습니다.
4개월 동안 300 커밋이라는 비정상적인 페이스는, Claude Code 없이는 불가능했습니다.
구체적인 예:
- 블로그 기사 데이터 수집 (DB 쿼리 6개) → 기사 생성 (Anthropic API) → 차트 이미지 생성 (QuickChart) → BlogPost 저장. 이 일련의 작업을 기사 1개당 15분 만에 완료
- JSON-LD 컴포넌트 12종 구현에 반나절
- 카테고리 LP 16페이지 생성 로직에 2시간
- 차종 페이지 UI 쇄신 (6개 탭화)에 1일
"AI에게 일자리를 빼앗기는" 것이 아니라, "AI로 1명이 10명분의 구현을 할 수 있다"가 정확한 표현입니다.
4월 중순 ~ 5월 초순은 가장 괴로운 시기였습니다. 매일 코드를 쓰고 있는데도 DAU가 150에서 움직이지 않았습니다.
GSC(Google Search Console) 데이터는 2~3일 늦게 반영되고, Google의 재평가에는 수 주일이 걸립니다. 이 "효과가 보이지 않는 기간"을 어떻게 보내느냐가 분수령입니다.
저의 경우에는 커밋 수를 지표로 삼았습니다. DAU가 아니라 "오늘은 무엇을 개선했는가"에 초점을 맞췄습니다. 하루 6커밋을 계속하면 언젠가 Google도 인정해 줄 것이라고 믿으며.
-
블로그 기사를 30개로 늘리기 (현재 18개)
-
Google Discover를 통한 트래픽 획득
-
지식인(Chiebukuro)・X를 통한 백링크(Backlink) 증가
-
사용자 투고 콘텐츠 (리뷰・투어링 리포트)
-
뉴스레터
-
LINE 공식 계정 팔로워 확보
-
ASP 제휴 (바이크 매입・보험)를 통한 수익화
-
AdSense + 제휴 마케팅(Affiliate)으로 월 5만 엔
-
트래픽 소스: 검색 40% / SNS 30% / Direct 20% / 기타 10%
코어 업데이트로 폭락하는 것은 솔직히 엄청나게 낙담하게 됩니다. 3개월 동안 쌓아 올린 것이 1주일 만에 무너지니까요.
하지만 되돌아보면, 폭락이 없었다면 이 정도로 진심을 다해 품질 개선에 임하지 않았을 것입니다. "숫자의 나열뿐인 페이지를 양산하고 있었다"라는 문제를 깨달을 수 있었던 것은 업데이트 덕분입니다.
DAU 200은 아직 지나가는 과정일 뿐입니다. 420을 넘어 500, 1,000으로 늘려가는 과정도 다시 글로 쓰겠습니다.
개인 개발로 바이크 사이트를 운영하는 사람은 아마 저뿐일지도 모르지만, "Google 코어 업데이트에 직격탄을 맞은 개인 개발자"는 산더미처럼 많을 것입니다. 이 글이 누군가에게 참고가 된다면 기쁘겠습니다.
MotoHub: https://motohub.jp
GitHub: ausssxi/MotoHub
X: @motohub_jp
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기