애널리틱스 대시보드 마무리 · 게시글 댓글 기능 구현
애널리틱스 대시보드를 마무리하고, 게시글 댓글 기능을 새로 만든 날입니다.
키워드 검색량 탭, 서치콘솔 연동 마무리
지난번까지 만들어둔 GA4 커스텀 대시보드에 남아있던 두 가지를 마저 붙였습니다. 서치콘솔은 같은 서비스 계정을 재사용해서 Search Console API만 추가로 활성화하고 권한을 부여하는 걸로 끝났고, google-auth-library의 JWT 클라이언트로 REST 엔드포인트를 직접 호출하는 방식으로 붙였습니다 — googleapis 패키지를 통째로 넣기엔 무거워서, GA4 클라이언트가 이미 의존하고 있던 라이브러리를 그대로 꺼내 썼습니다.
네이버 키워드 검색량은 검색광고 API를 HMAC-SHA256 서명으로 붙였는데, 실제로 호출해보니 요청한 키워드 말고 연관 키워드까지 같이 내려주는 걸 확인했습니다. 사용자가 입력한 키워드만 정확히 보여주는 게 맞다고 판단해서 응답을 요청 키워드 기준으로 필터링했습니다.
이건 자사 사이트 지표가 아니라서(우리 검색량이 아니라 키워드 자체의 시장 검색량) "월별 보기" 옆에 별도 탭으로 분리해서 시각적으로 구분했습니다. 그리고 이 숫자만 던져주면 어떻게 활용해야 할지 감이 안 올 것 같아서, 탭 안에 접었다 펼 수 있는 사용법 안내(우리 사이트 지표 vs 키워드 검색량 비교, 활용 시나리오 4가지)를 같이 넣었습니다.
대시보드 다듬기
몇 가지 UI 피드백을 반영했습니다.
- 클래식/NFP 테마 토글을 없애고 NFP 테마 하나로 통일 — 안 쓰게 된 CSS 변수(
--ga4-series-1/2)까지 정리 - "개선이 필요한 콘텐츠" 서브타이틀에 참여율(engaged session 비율) 기준을 명시적으로 풀어써서, 뭘 보는 지표인지 바로 이해되게 함
- 인사이트 요약 문장을 스탯 타일 바로 아래로 옮기고, 안에 있는 게시글 제목·유입경로 이름을 단순 글자색이 아니라 배지 형태로 강조
- 순위 리스트 블럭이 7개라 마지막 줄 정렬이 깨졌던 걸, 세부 카테고리(디자인/게임즈/크리에이티브) 3블럭과 합쳐 6+3=9개로 맞춰 3×3 레이아웃이 되게 정리
- 세부 카테고리 블럭에 서브브랜드 키컬러(디자인
#01FF75, 게임즈#FFF200, 크리에이티브#00f0ff) 적용 — CLAUDE.md에 정의된 브랜드 스펙과 동일하게 맞춤
2차 카테고리 미선택 발행 시 404 나던 문제
새 글을 발행했는데 404가 떴습니다. 원인을 찾아보니, 라우트가 /[section]/[category]/[subcategory]/[id] 3단 구조뿐인데 getPostUrl은 subcategory가 비어있으면 2단 URL(/[section]/[category]/[id])을 만들어서, 그 2단 URL을 받아주는 페이지 자체가 없었습니다. 글쓰기 폼에서 2차 카테고리는 "선택 안함"이 가능한 옵션이라, 카테고리에 서브카테고리가 있어도 비워둔 채 발행하면 무조건 깨지는 구조였습니다.
같은 일이 또 생기지 않게 두 군데에 검증을 넣었습니다. 글쓰기 페이지(handleSave)에서 발행 시점에 막고, API(/api/posts, /api/posts/[id])에도 동일한 체크를 넣었습니다 — 어드민 목록의 게시/비공개 토글은 글쓰기 페이지를 거치지 않고 바로 API를 호출하는 구조라서, 거기서도 걸리게 하려면 서버 쪽 검증이 필요했습니다.
댓글 기능
이름·비밀번호·내용만 입력하는 익명 댓글 기능을 새로 만들었습니다. 관리자 제어를 먼저 만들고, 그 위에 공개 작성/삭제 UI를 얹는 순서로 진행했습니다.
스키마: Comment 모델을 새로 추가하고 Post에 onDelete: Cascade로 연결했습니다. 비밀번호는 bcryptjs로 해시해서 저장하고, ip 컬럼은 어드민 화면에는 노출하지 않고 스팸 방지용 rate-limit 체크에만 씁니다.
관리자 제어(/admin/comments): 언제(분 단위) 어떤 게시글에 무슨 내용으로 달렸는지 보여주는 로그 테이블, 내용/작성자/게시글 제목 검색, 페이지네이션(20개 단위), 그리고 댓글 기능 자체를 켜고 끄는 토글을 만들었습니다. 토글을 끄면 getCommentsEnabled()가 false를 반환해서 게시글 페이지에서 댓글 섹션 자체가 서버 컴포넌트 단계에서 안 그려집니다 — 숨김 처리가 아니라 진짜로 렌더링을 안 하는 방식이라, 기능을 만들기 전 상태와 동일하게 돌아갑니다.
공개 작성/삭제: 디자인·게임즈·채널 세 섹션의 게시글 상세 페이지가 전부 _PostView.tsx 하나를 공유하고 있어서, 거기 한 곳에만 붙이면 사이트 전체에 적용됩니다. 스팸 대비로 허니팟 필드(봇이 채우면 저장 없이 성공 응답만 돌려줌)와 같은 IP 30초 rate limit을 넣었습니다. 삭제는 본인이 입력한 비밀번호로만 가능하고, 관리자로 로그인된 상태라면 비밀번호 없이 바로 지울 수 있게 별도 경로(/api/admin/comments/[id])를 붙였습니다.
삭제 버튼을 눌렀을 때 레이아웃이 갑자기 밀리는 문제가 있어서, 인라인으로 버튼이 늘어나는 대신 position: absolute 팝오버로 바꿨습니다. 문서 흐름에 영향을 안 주기도 하고, "정말 삭제하시겠어요?" 확인 문구를 자연스럽게 넣을 자리도 생겼습니다. 여기에 페이드 트랜지션과 버튼 호버 효과까지 더해서 마무리했습니다.
관리자 표시 배지
로그인한 상태로 공개 사이트를 둘러볼 때 지금 관리자로 보고 있다는 걸 알 수 있게 우측 하단에 고정 배지를 추가했습니다. 상단 바 형태도 같이 만들어서 비교해봤는데, 스크롤이나 메인 허브 페이지에서 콘텐츠를 가리는 문제가 있어 하단 배지 하나로 정리했습니다. 모바일 크리에이티브/툴 페이지에 있는 MobileFloatingNav와 위치(bottom:24px / right:16px)가 겹치길래, 배지를 그 위로 살짝 띄워서 겹치지 않게 했습니다.
댓글
관련 게시글