하루 동안 세 가지를 했다. 구글/네이버 검색 노출 개선, 공지·문의 게시판 + 관리자 계정 추가,
그 와중에 터진 Cloudflare 다중 계정 자격증명 문제 해결.
사이트맵 URL(/sitemap.xml)을 직접 요청해 보니 HTTP 200 + 정상 XML, Googlebot User-Agent로도 정상.
즉 파일 자체는 멀쩡했다. GSC의 "가져올 수 없음"은 처음 제출(약 한 달 전) 시점에 일시 오류가 났고,
그 뒤 구글의 재시도 주기가 느려서 방치된 상태였다. *.pages.dev 도메인에서 흔히 보고되는 증상.
검색이 안 되는 진짜 원인은 따로 있었다:
1. SPA라서 서버가 주는 HTML의 <div id="root">가 비어 있음 — 자바스크립트를 실행해야만 본문이 생김.
신규·저권위 사이트에서는 구글이 JS 렌더링을 뒤로 미루기 때문에 불리하다.
2. "텅장"이라는 붙여 쓴 문자열이 title·description·본문 어디에도 없었다. 전부 "텅~ 장"으로만 표기.
3. 구조화 데이터(JSON-LD)가 전혀 없어서 브랜드명을 인식할 근거가 부족.
index.html)<title> / <meta name="description"> 에 "텅장" 표기를 자연스럽게 포함.keywords, og:site_name, og:locale, twitter:card 등 메타 보강.WebSite + SoftwareApplication, name: "텅장",alternateName: ["텅~ 장", "텅장 가계부"], applicationCategory: FinanceApplication.<div id="root"> 안에 정적 히어로 마크업(로고 + h1 + 소개문 + 이용약관/개인정보 링크)을 넣음.createRoot().render()가 첫 렌더에 자식을 비우므로 사용자 화면엔 영향이 없고,<noscript> 블록에도 같은 취지의 소개 텍스트.<meta name="naver-site-verification" ...> 추가..pages.dev 조합은 SEO에 3중으로 불리하다. 최소한 HTML에 브랜드명과 핵심 문구가기존 앱에는 권한(role) 개념이 아예 없었다. 최소 변경으로:
DB (마이그레이션 1개)
users 에 is_admin INTEGER NOT NULL DEFAULT 0 컬럼 추가.email 컬럼(UNIQUE, 형식 제약 없음)을board_posts 테이블 하나로 공지/문의를 같이 관리:type('notice'|'qna'), user_id, author_name, title, content,is_private, is_pinned, answer, answered_at, 타임스탬프.answer 컬럼). 별도 댓글 테이블 없이 단순하게.백엔드 (Cloudflare Pages Functions)
functions/lib/admin.ts — isAdmin(env, userId) 헬퍼.POST /api/auth/admin-login — 일반 로그인과 분리. 아이디+비번을 받고 is_admin=1 계정만 통과.me / login / register 응답의 user 객체에 is_admin 포함.GET/POST /api/board, GET/PATCH/DELETE /api/board/[id]:is_private면 작성자+관리자만 열람.프론트 (React + TS)
AuthPage 에 "관리자 로그인" 모드 추가(하단 링크로 진입, 이메일 대신 아이디, 탭 숨김).BoardView 신규 — 공지/문의 서브탭 + 단일 글쓰기 폼(유형은 서브탭을 따라감,App.tsx 좌측 메뉴에 'board' 탭(Megaphone 아이콘).tsc -b, lint, build 통과. 로컬 D1에 마이그레이션 적용 후 wrangler pages dev 로 실제 요청 테스트:
type 400.코드는 git push → GitHub Actions가 Pages를 자동 배포. 문제는 D1 스키마는 자동 반영이 안 된다는 것.
wrangler d1 execute budget-db --remote --file=... 를 실행했더니:
✘ [ERROR] A request to the Cloudflare API (/accounts/558c8a68…/d1/database/…/import) failed.
Authentication error [code: 10000]
👋 You are logged in with an User API Token, associated with the email asdf1378kk@gmail.com.
budget-db 와 Pages 프로젝트는 계정 A (2@gmail.com, 558c8a68…799) 소속.~/.bashrc 의 전역 CLOUDFLARE_API_TOKEN 이 계정 B (kk@gmail.com, 6209ea4d…985a)대시보드 → Workers & Pages → D1 → budget-db → Console 탭에 마이그레이션 SQL을 직접 붙여넣어 실행.
토큰/계정 설정을 아예 안 건드려도 되는 가장 확실한 방법. IF NOT EXISTS / INSERT OR IGNORE 로
작성해 둬서 재실행해도 안전.
~/.bashrc 의 토큰 값을 계정 A 것으로 교체.wrangler whoami → Db848552@gmail.com 확인. d1 execute --remote 정상 동작 확인.조사해 보니 이 PC의 Cloudflare 프로젝트가 두 계정에 흩어져 있었다:
| 계정 | 프로젝트 |
|---|---|
A: Db848552@gmail.com (558c8a68…799) | budge, wedding-admin, wedd |
B: asdf1378kk@gmail.com (6209ea4d…985a) | ppnation, easyprompt, wedd-e9smwzxxan |
전역 토큰은 하나뿐이라, 한 계정을 맞추면 다른 계정 프로젝트가 깨진다. 해결책:
CLOUDFLARE_API_TOKEN=… CLOUDFLARE_ACCOUNT_ID=… npx wrangler deploydirenv 로 폴더별 .envrc 자동 적용.sudo apt-get install -y direnv + ~/.bashrc 에 eval "$(direnv hook bash)".envrc 에 토큰 + Account ID, direnv allow.envrc 는 반드시 .gitignore 에 추가 (토큰이 커밋되면 안 됨)DEPLOY.md 를 두어 "이 프로젝트는 어느 계정, 어떻게 실행"을 기록.CLOUDFLARE_API_TOKEN 은 머신 전체에 적용된다. 프로젝트별 설정이 아니다.direnv 또는 명령 프리픽스)하고,wrangler.toml 에 account_id 를 박아 계정을 명시하는 게 안전하다. (Account ID는 비밀이 아님)--remote 실행하거나 대시보드feat: 구글 검색 노출 개선 — "텅장" 브랜드 SEO 메타/구조화 데이터 추가chore: 네이버 서치어드바이저 사이트 소유확인 메타태그 추가feat: 공지/문의(Q&A) 게시판 + 관리자 계정 로그인chore: 게시판/관리자 기능 배포 및 원격 D1 마이그레이션 적용 완료 기록chore: .envrc gitignore 추가 + 다중 계정 자격증명 정리 기록그 외 ppnation / easyprompt 저장소에 DEPLOY.md 추가.