네이버 검색어 트래킹 · 로그인 보안 강화 · 댓글 관리 고도화
네이버 검색어 트래킹을 붙이고, 로그인 보안을 강화하고, 댓글 관리 기능을 한 단계 더 다듬은 기간입니다.
네이버 추정 검색어 트래킹
구글은 서치콘솔으로 실제 검색어를 볼 수 있는데 네이버는 그런 공식 도구가 없습니다. 대신 네이버가 아직 검색결과 리퍼러 URL에서 검색어를 안 가린다는 점(구글은 프라이버시 때문에 이미 가림)을 이용해서, document.referrer가 search.naver.com이면 query 파라미터를 뽑아 커스텀 GA4 이벤트(naver_search_referral)로 기록하는 방식을 붙였습니다.
이 값을 GA4 Data API로 조회하려면 GA4 관리자 콘솔에서 이벤트 파라미터를 맞춤 측정기준으로 미리 등록해둬야 합니다. 등록 전엔 API가 에러를 내서, 이 조회 하나만 별도 함수로 분리하고 실패하면 조용히 빈 배열을 반환하도록 격리했습니다 — 등록이 안 돼 있어도 나머지 대시보드가 같이 죽지 않게 하기 위해서입니다.
애널리틱스 대시보드 쪽엔 구글/네이버 검색어 섹션에 브랜드 컬러로 재해석한 작은 배지(구글은 4색 도트, 네이버는 초록 사각형)를 붙이고, 순서를 구글 유입 페이지 → 구글 검색어 → 네이버 추정 검색어로 정리했습니다. 집계기간 표시도 캘린더 아이콘이 붙은 칩 형태로 다듬었고, 서치콘솔은 처리 지연 때문에 최근 3일을 제외하고 집계하는데 이 이유를 숨기지 않고 칩에 그대로 표기해뒀습니다 — 네이버 쪽 기간과 달라 보이는 게 버그가 아니라 실제 데이터 파이프라인 차이라는 걸 감추기보다 설명하는 쪽을 택했습니다. 클릭수·노출수·평균 검색 순위 같은 지표도 처음 보면 뭘 뜻하는지 모호해서, 물음표 아이콘을 누르면 뜨는 커스텀 툴팁으로 각각 설명을 붙였습니다.
사이트 전역 깨진 링크 수정
브라우저 콘솔에 /video 404가 찍힌다는 제보로 찾아보니, 홈페이지 "전문 서비스 영역" 카드 중 컨텐츠·영상·광고 디자인 세 개가 /content, /video, /ad로 링크돼 있었습니다. 이 셋은 최상위 라우트가 아니라 디자인 섹션의 하위 카테고리라 /design/content처럼 /design 접두사가 필요한데 빠져 있었던 거였습니다. 같은 패턴의 버그가 게임 퍼블리싱 페이지 브레드크럼(/game-ad → /design/game-ad)에도 있어서 같이 고쳤습니다.
로그인 보안 강화 · 로그아웃 버튼
로그인 세션이 30일 동안 유지되는데 로그아웃 기능 자체가 없다는 걸 발견했습니다. 코드를 더 살펴보니 두 가지가 더 걸렸습니다 — 세션을 서명하는 NEXTAUTH_SECRET이 "change-in-production"이라고 적힌 플레이스홀더 값 그대로였고, 관리자 비밀번호도 평문으로 저장돼 있었습니다. 전자는 유출되면 비밀번호 없이도 세션을 위조할 수 있는 문제라 가장 심각했습니다.
새 무작위 시크릿으로 교체하고, 비밀번호는 이미 코드에 있던 bcrypt 폴백 로직($2로 시작하면 해시 비교, 아니면 평문 비교)을 그대로 활용해 해시로 전환했습니다. 로그인 화면·입력값은 그대로라 사용자 입장에서 체감되는 변화는 없고, 시크릿을 바꾸는 순간 기존 세션이 풀리는 것뿐입니다. 로그아웃은 우측 하단 "관리자 모드" 배지 위에 전원 아이콘 버튼을 추가해서 해결했습니다.
로그인 후 화면 전환이 안 되던 버그
로그인은 성공했는데 /admin으로 안 넘어가고 스피너만 계속 도는 문제가 있었습니다. 원인은 두 겹이었습니다. 먼저 router.push('/admin')과 router.refresh()를 동시에 호출하던 게 서로 경쟁해서 간헐적으로 이동 자체가 씹혔고, 이걸 없앤 뒤에도 클라이언트 라우터 캐시에 남아있던 예전(로그인 전) /admin 응답이 재사용되는 경우가 있었습니다. 결국 캐시를 아예 안 타는 window.location.href로 바꿔서 확실하게 해결했습니다.
댓글 관리 고도화
댓글 기능에 몇 가지를 더 얹었습니다.
- IP 차단/동일인 확인: 관리자 화면에 댓글 작성 IP를 표시하고, 클릭하면 같은 IP가 쓴 댓글만 필터링해서 볼 수 있게 했습니다.
BlockedIp테이블을 새로 만들어서 IP 차단 버튼을 누르면 이후 그 IP에서는 댓글 작성 자체가 서버에서 거부되도록 했고, 차단 목록도 펼쳐서 관리할 수 있게 했습니다. - 댓글 섹션 위치: 기존엔 관련 게시글 목록보다도 아래에 있어서 스크롤 안 해보면 있는지도 모를 뻔했습니다. CTA 섹션 바로 아래로 옮겼습니다.
- 더보기 페이지네이션: 댓글이 10개 넘으면 최근 10개만 보이고 "더보기"로 이전 댓글을 펼치도록 했습니다.
- 글자수 300자로 축소: 원래 1000자였는데, 짧고 가벼운 댓글 위주로 유도하기 위해 줄였습니다.
관리자 신규 댓글 알림
댓글이 달려도 관리자가 직접 들어가 보기 전엔 알 방법이 없어서, 우측 하단 플로팅 UI에 종 모양 알림 버튼을 추가했습니다. 처음엔 화면 중앙에 뜨는 모달로 만들었는데, 화면을 다 가리는 게 과하다는 피드백을 받아 종 버튼 근처에서 여닫히는 팝오버로 바꿨습니다.
최근 댓글 10개를 보여주고, 클릭하면 그 댓글이 달린 게시글로 이동하면서(댓글마다 id="comment-{id}" 앵커를 심어둬서 해당 위치로 스크롤됨) 읽음 처리가 됩니다. 읽음 여부와 개별 알림 제거(X 버튼) 상태는 localStorage에 저장해서 새로고침해도 유지됩니다.
이 과정에서 댓글 시간 표시가 실제보다 9시간 느리게 나오는 버그도 같이 발견했습니다. UTC 시각을 변환 없이 그대로 보여주고 있었던 건데, 한국은 서머타임이 없어 고정 오프셋이라 +9시간 후 UTC 게터로 값을 뽑는 공용 함수(src/lib/kst.ts)로 통일해서 고쳤습니다. 물음표 툴팁도 텍스트 글리프 대신 검증된 아이콘(Feather Icons)의 SVG 패스로 바꿔서 미세하게 어긋나던 중심 정렬 문제를 없앴습니다.
댓글
관련 게시글