개발 slecs

새벽 여섯 시간, 재방문 루프와 대시보드를 동시에 닫다

목차

자정 직후, 무엇부터 잡을지 결정하는 순간

자정을 넘기고 나서 슬랙을 끄고 터미널만 켜두면 이상하게 집중이 잘 된다. 오늘 새벽 시간도 그랬다. 큰 우선순위는 이미 전날 밤에 정해져 있었다. kpopdex에 "재방문하게 만드는 요소"가 너무 없다는 게 오랫동안 찜찜했던 부분이었고, 어드민 대시보드는 사이트가 49개로 늘어나면서 헬스 현황을 한눈에 보기가 점점 어려워지고 있었다. 두 개 모두 더 이상 미루면 기술 부채가 쌓이는 게 아니라 운영 감각 자체가 무뎌진다. 그래서 이 두 갈래를 동시에 치기로 했다.

kpopdex 재방문 기능 묶음 - Web Push, 기념일, 공유 버튼

이 시간대에서 가장 덩치가 컸던 작업이다. "사용자가 팔로우한 그룹의 컴백이 생기면 알려주는 구조"를 만들려면 세 레이어가 맞물려야 한다.

1. 브라우저 쪽 - 서비스워커 + 토글 UI
public/sw.js에 서비스워커를 올리고, PushToggle.astro 컴포넌트가 구독/해제를 담당한다. FollowingPage.astro 안에 토글을 심어서 팔로우 맥락에서 바로 켤 수 있게 했다. Push API는 브라우저 권한 다이얼로그가 있어서 UX가 애매한데, 토글을 팔로우 목록과 같은 화면에 붙이면 "내가 신경 쓰는 것들에 대한 알림"이라는 맥락이 자연스럽게 연결된다.

2. API 레이어 - 구독 엔드포인트
src/pages/api/push-subscribe.ts에서 구독 객체를 받아 저장한다. 여기서 주의했던 건 토큰과 브라우저 스토리지 사용 고지였다. 나중에 프라이버시 정책을 건드릴 것을 알고 있었기 때문에, 구독 저장 로직을 먼저 확정하고 정책 문구를 맞춰 쓰는 순서로 갔다.

3. 발송 레이어 - push_send.mjs
kpopdex/bot/push_send.mjs를 새로 만들었다. 컴백 일정이 확정되면 이 스크립트가 구독자들에게 웹 푸시를 보내는 구조다. 봇 레이어이면서 동시에 팬 서비스의 핵심이라, 메시지 포맷을 어떻게 할지 잠깐 고민했다. 너무 알림 스팸처럼 느껴지면 오히려 구독 해제를 유도하니까, 발송 조건은 보수적으로 잡았다.

기념일 카운트업은 조금 다른 맥락이다. Anniversary.astroFollowCountdown.astro가 데뷔 기념일, 멤버 생일을 D+N 형식으로 보여준다. "오늘이 이 그룹 데뷔 N주년입니다"라는 카드가 하나 있는 것만으로도 방문 이유가 생긴다. 공유 버튼도 같은 철학에서 나왔다. 컴백 D-day를 공유할 수 있으면 트래픽이 외부에서 들어올 수 있다. GroupPage.astroMemberPage.astro 양쪽에 모두 박았다.

이 기능 묶음을 마무리한 뒤 바로 PolicyPage.astrosrc/i18n/policy.ts를 열었다. 푸시 토큰과 localStorage 사용을 8개국어로 고지에 추가해야 했다. 이건 기능 개발이 먼저고 정책이 뒤따라오는 게 아니라, 실제로 스토리지를 쓰는 순간 정책도 동시에 업데이트해야 하는 것이다. 새벽에 이걸 빼먹으면 나중에 법무 검토할 때 다시 돌아오게 된다.

어드민 대시보드 - 49개 사이트를 어떻게 한 화면에 담나

솔직히 이게 더 지저분한 작업이었다. 사이트가 늘어날수록 대시보드가 "있긴 한데 쓸모없는" 상태가 되어가고 있었다. 문제는 두 가지였다.

첫째, 사이트 헬스 분류가 깨져 있었다. 25개 사이트가 미분류로 빠져 있었는데, dashboard-stats.ts에서 헬스 체크 로직이 특정 발행 타입을 인식 못 하고 있었다. opsvoro 발행을 추가하고, 나머지 24개는 정적 사이트로 분류했다. 이 수정으로 전체 49개가 발행정상 N/M, 정적, 기타, 총합 형태로 카드에 다 보이게 됐다.

둘째, KPI 그리드 정렬이 어색했다. 카드가 9개인데 균등하게 배치하면 마지막 줄이 1개만 덩그러니 남는다. 5+4 두 줄로 정렬했다. 사소하지만 대시보드처럼 매일 보는 화면은 레이아웃 어색함이 은근히 누적돼서 신경 쓰인다.

AdSense 부분이 이번에 좀 크게 바뀌었다. 계정이 2개로 늘었다. adsense-status-check.py가 처음에 hedvion.com 단일 도메인만 보고 있었는데, 첫 번째 계정 6개 사이트 전체로 확장하고, 두 번째 계정(dev@hedvion, pub-1135)까지 추가해서 총 11개 사이트를 추적하게 했다. 대시보드 카드도 두 계정을 같이 표시하도록 수정했다.

항목 변경 전 변경 후
사이트 헬스 분류 25개 미분류 49개 전체 분류
AdSense 추적 1계정 1도메인 2계정 11사이트
KPI 카드 레이아웃 비균등 5+4 정렬

dashboard-stats.ts에서 외부 신선도 날짜를 Date 강제 변환하는 수정도 했다. 날짜 형식이 예상과 다르게 들어오면 500이 터질 수 있는 지점이었는데, 잠재적 에러라서 실제로 발생하기 전에 잡았다. 중복 catch도 같이 정리했다. 이런 작업은 눈에 안 띄지만, 새벽에 갑자기 대시보드가 죽으면 그 원인 찾는 데 시간을 잡아먹으니까 미리 막는 게 낫다.

GSC 평균 순위 카드, 오늘 발행 요약 카드, 사이트 헬스 요약 카드를 새로 추가하면서 대시보드가 처음으로 "운영 현황을 실제로 읽을 수 있는" 화면이 됐다. 발행 카드와 사이트 헬스 카드에 클릭 링크도 달았다. 카드를 눌렀을 때 아무 일도 안 생기면 대시보드가 정적인 느낌이 되어버리는데, 연결이 있으면 자연스럽게 더 파고들게 된다.

AdSense 론칭과 SEO 국제화 - cpsrush 개시

cpsrush에 pub-1135425661100289를 올렸다. public/ads.txt, 로더 스크립트, og/twitter 메타 태그까지 한 번에 붙였다. AdSense 검토 중 상태인데, 이 단계에서 ads.txt가 정확히 있어야 검토가 진행된다. 문서도 sites-detail.md에 개시 상태를 바로 반영했다.

도구 가이드 본문을 7개 언어로 현지화한 것도 이 시간에 끼워 넣었다. ToolView.astrosrc/i18n/guides.ts인데, 기존에 영어로만 있던 가이드 본문이 es, pt, fr, de, it, ja, zh로 나간다. 검색 유입을 언어별로 분산시키는 게 목적이다. kpopdex에서 8개국어 번역을 반복하다 보니 i18n 구조가 손에 익어서 작업 자체는 빠르게 됐다.

kpopdex 번역 현황 기록

문서 커밋이 여러 개 들어와 있는데, 이건 단순 docs 정리가 아니라 상태 기록이다. 2번 그룹 요약 137개를 8개국어로 다 돌렸고 i18n 항목이 959개 생성됐다. 신규 칼럼 24개도 8개국어로 완료해서 504개가 추가됐다. 멤버 bio는 688개 중 50개 완료 상태라 아직 갈 길이 멀다.

CLAUDE.md에 정책 페이지가 legal 타입이 아니라 cms_page로 실측 정정한 것도 이 흐름에서 나왔다. 문서에 적힌 것과 실제 코드베이스가 다를 때, 실측이 단일 진실이다. 이걸 안 고쳐놓으면 나중에 다른 작업자가 엉뚱한 타입으로 쿼리 짜다가 시간을 날린다.

블로그 봇 프롬프트 튜닝 - em-dash와 카테고리 판정

scripts/bulk_seed.py에 두 가지 룰을 추가했다.

첫째는 em-dash(-)를 쓰지 말라는 규칙이다. AI가 생성한 텍스트에서 em-dash가 지나치게 자주 나오면 글에서 AI 냄새가 난다. 기술 블로그 회고체에서 em-dash는 사실 잘 쓰지 않는 표현이기도 하고, 일반 하이픈이나 쉼표로 충분히 끊을 수 있다.

둘째는 카테고리 가중 판정이다. 기존에는 dev/automation/side를 단순하게 분류했는데, dev가 흔하다 보니 automation이나 side 작업이 꽤 비중을 차지해도 dev로 묻혀버리는 경우가 생겼다. 그래서 automation이나 side 작업이 본문 한 단락 이상 차지할 만큼 비중 있으면 dev보다 그쪽을 우선 대표로 뽑도록 했다.

발행 텀도 3시간에서 6시간으로 늘렸고, 분량을 4000-6000자로 올렸다. 짧게 끝내는 글은 회고라기보다 메모에 가깝다. 배경이 뭔지, 왜 그 선택을 했는지, 어디서 막혔는지가 담겨야 나중에 돌아봤을 때 의미 있는 기록이 된다.

프롬프트에서 "시간 하드코딩 제거"도 같이 했다. 예전에는 발행 시간대를 프롬프트에 고정 문자열로 박아둔 게 있었는데, 봇이 실행되는 시각을 동적으로 넘기는 방식으로 바꿨다. 당연히 이렇게 해야 하는데 처음엔 빠르게 만들다 보니 하드코딩으로 시작했고, 이번에 청소했다.

새벽 여섯 시간이 남긴 것

돌아보면 이 시간에 상당히 많은 영역을 건드렸다. kpopdex 재방문 루프 완성, 대시보드 49개 사이트 헬스 완전 분류, AdSense 2계정 체계화, SEO 국제화, 프라이버시 정책 고지, 블로그 봇 튜닝. 이렇게 쓰면 산만해 보이지만, 실제로는 "지금 당장 사용자에게 보이는 것"과 "운영자가 매일 봐야 하는 것"을 동시에 정리한 시간이었다.

psych 테스트 자동 생성 3개가 이 시간 안에 커밋된 것도 있다. detective-solving-style, one-on-one-vs-group-friend, pcbang-type - 이건 daily 자동 파이프라인이 만들어준 것이라 별도 작업은 아니었지만, 커버 이미지까지 딸려서 올라온 걸 보면 파이프라인이 제대로 돌고 있다는 확인이기도 하다.

새벽 6시간은 짧다. 그 안에 뭘 마무리할지 초반에 결정을 잘 해야 한다. 오늘은 재방문 루프가 가장 우선이었고, 대시보드는 "이거 안 고치면 오늘 오후에 또 본다"는 부채 감각으로 끼워 넣었다. 두 가지를 다 닫았으니 아침에 커피 한 잔 하고 싶다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.