오늘은 기능보다 "서비스를 세상에 드러내는" 작업들을 했다. 광고, 검색 노출, 보안 취약점 — 모두 사용자를 만나기 전에 해결해야 할 것들이다.
AdSense 심사를 기다리면서 대안을 찾다가 카카오 애드핏으로 방향을 바꿨다. 국내 트래픽에서 체류 시간이 짧더라도 클릭률이 낫고, 심사 기간도 짧다.
728x90 가로형 배너를 랜딩 페이지와 매매일지, 교육, 종목 검색 등 주요 페이지에 배치했다. 모바일은 300x250 직사각형으로 다르게 설정했다. 페이지마다 광고 코드를 하드코딩하는 게 아니라 KakaoAdBanner 컴포넌트를 만들어 한 곳에서 관리한다.
광고가 페이지 이동 후 다시 로드되지 않는 SPA 이슈도 있었다. useEffect에서 kakaoAdList.push({})를 호출하는 방식으로 해결했다.
구글만 잡으면 안 된다. 국내 검색 트래픽의 상당 부분은 네이버다.
네이버 웹마스터도구 인증 파일을 public/ 에 추가하고, 검증까지 완료했다. 동시에 sitemap.xml을 Next.js MetadataRoute로 동적 생성하도록 바꿨다. 개발일지와 교육 콘텐츠가 늘어나도 자동으로 포함된다. RSS 피드(/feed.xml)도 추가했다. 피드 구독자가 생기면 SEO 외 채널이 하나 더 생기는 셈이다.
AdSense, 애드핏 모두 "실제로 운영 중인 서비스"임을 확인한다. /about 페이지에 서비스 소개, 미션, 금융 면책 조항을 담았다. 문의하기 폼도 넣었다. Supabase contact_requests 테이블에 저장되고 어드민 페이지에서 확인할 수 있다.
검색창에 두 글자 이상 입력하면 드롭다운이 뜨도록 개선했다. 이전엔 검색 버튼을 눌러야만 결과가 나왔다. 자동완성 드롭다운이 다른 요소 뒤에 가려지는 z-index 문제가 있었고, 모바일에서 검색바가 sticky 상단에 붙을 때 top 오프셋이 맞지 않는 문제도 함께 수정했다.
기존 교육 페이지는 카드만 나열됐다. 레벨별(입문/초급/중급/고급) 일러스트와 섹션 헤더를 추가하고, 강의를 6개에서 20개로 늘렸다. 학습 현황 진행 바도 붙였다.
Lucide 아이콘으로 강의 주제를 직관적으로 표현했다. TrendingUp, Brain, Shield, Globe — 아이콘 하나로 강의 성격을 30% 이상 전달할 수 있다.
어드민 계정 생성 시 upsert를 쓰면서 ignoreDuplicates: false가 기본값이라 일반 유저 role을 admin으로 덮어쓸 수 있는 취약점이 있었다. ignoreDuplicates: true로 수정했다. 작은 설정값 하나가 권한 체계 전체를 흔들 수 있다는 걸 다시 확인했다.
---
오늘 커밋이 많았다. 하나의 큰 기능이 아니라 여러 개의 "서비스를 완성하는 마지막 1%"들이었다.
이 글의 소감을 Threads에서 확인하세요.
스레드에서 보기