[Part 9] 프롬피드 회고: 잘한 것과 아쉬운 것
TL;DR
- "무료로 계속 운영한다"는 제약 하나가 캐시 구조, API 한도, 이미지 전략을 한 방향으로 정리했다.
- 아쉬운 건 UX를 위해 넣은 것이 SEO·성능을 해친 일, 그리고 공유한 마이그레이션 파일을 고친 사고다.
- 기능보다 콘텐츠가 병목이라는 걸 늦게 알았다. 다음 목표는 공개 글 30개다.
숫자로 보는 프롬피드
| 항목 | 값 |
|---|---|
| 운영비 | 0원 (Supabase 무료, Vercel Hobby, 네이버·유튜브 무료 한도, GitHub Actions) |
| API 한도 | 유튜브 하루 4,800유닛 (무료 10,000), 네이버 월 20,000회 (무료 775,000) |
| 수집 | 키워드 18개, 하루 1회 자동 |
| Lighthouse 모바일 | 홈 63 → 98 |
나의 선택과 이유
| 결정 | 버린 선택지 | 이유 |
|---|---|---|
| 무료 플랜으로 끝까지 | 유료 DB·서버 | 초기 서비스는 트래픽이 불확실하다. 방문자가 늘어도 비용·DB 부하가 늘지 않는 구조를 먼저 만들었다 |
| 수집은 AI 없이 규칙 기반 | LLM으로 추출·요약 | 비용 0, 결과를 예측할 수 있다. 대신 추출 규칙을 테스트로 고정했다 |
| 자동 발행 X, 사람이 승인 | 수집 즉시 공개 | 광고·저품질 글이 섞이면 신뢰가 무너진다. 대신 "바로 발행"으로 승인 시간을 10초로 줄였다 |
| 유튜브·네이버만 자동 수집 | 인스타·스레드·틱톡 API | 메타 앱 검수·권한 문제로 현실적으로 불가능하다. 링크 직접 추가와 방문자 제보로 보완했다 |
| 한국어·한국 채널만 | 전 세계 | 타깃이 한국 SNS 사용자다 |
| 로그인 없는 저장함 | 회원가입 | 가입 장벽을 없앴다. 대신 "이 브라우저에만 저장" 안내를 붙였다 |
한 글 여러 프롬프트를 본문 표시 줄 [[프롬프트: 제목]]로 | 별도 테이블·JSON 칸 | DB 변경 없이 검색·빈칸·캐시가 그대로 동작한다. 관리자는 버튼으로 넣으니 문법을 몰라도 된다 |
| 패치노트 탭 대신 공지사항의 '업데이트' 분류 | 헤더에 탭 추가 | 메뉴가 늘면 복잡해진다. 같은 역할은 하나로 |
| 이미지 품질 한 종류 (WebP 80) | 화면마다 다른 품질 | Vercel 무료 이미지 변환 횟수(월 약 5,000) 안에서 화질을 유지한다 |
| 화면 테스트는 예시 데이터로 | 운영 DB로 테스트 | 운영 데이터 오염을 막는다 |
보안과 운영 원칙
- DB는 RLS로 공개 글만 읽히게 했다. 관리자 확인은 DB 함수(
is_admin)로 한다. - service_role 키는 어디에도 쓰지 않는다. 수집과 알림처럼 서버 권한이 필요한 일은, 비밀값을 검증하는 DB 함수(RPC)로 한다. DB 함수는 비밀값 원문이 아니라 sha256 해시로 비교한다.
- 비밀값은 Vercel 환경변수에만 둔다. 코드와 문서에는 이름만 있고 값은 없다.
- 방문자 입력(검색어 기록, 구독, 제보)은 DB 함수에서 길이, 형식, 폭주(10분에 N건)를 제한한다.
- 오류는 서버와 브라우저 모두 기록해서 대시보드에서 본다 (
instrumentation.ts의onRequestError). - 이미지 저장소는 관리자만 쓰고, 누구나 읽는다.
트러블슈팅 모음
연재에서 자세히 다룬 것 말고도 부딪힌 문제가 많았다. 짧게 모아 둔다.
| 문제 | 원인 → 해결 |
|---|---|
| 이미지 크게 보기의 닫기 버튼이 모바일 헤더에 가려짐 | 등장 애니메이션(transform)이 걸린 부모 안에서는 position: fixed의 기준이 화면이 아니라 부모가 된다 → body로 portal |
| 네이버 검색 API가 개발자센터에 없음 | NAVER API HUB로 옮겨 갔다 → 엔드포인트와 인증 헤더(X-NCP-APIGW-API-KEY-ID) 교체 |
| 자동 수집 Cron이 "unauthorized" | Vercel의 CRON_SECRET과 DB에 저장한 비밀값 해시가 달랐다 → 같은 값으로 다시 등록 |
| 마이그레이션이 반만 적용됨 | 0013 파일에 기능을 덧붙이기 전 버전을 실행했다 → 새 칸이 없어서 이미지 여러 장까지 안 보이는 연쇄 문제. 덧붙인 부분은 0014로 분리하고, 없는 칸을 빼고 다시 읽는 폴백을 넣었다 |
| '사진 첨부' 자동 판별 오탐 | "배우자 이미지를 상상해", "셀카 느낌" 같은 결과 설명까지 사진 첨부로 봤다 → "첨부한 사진", "이 사진을"처럼 사진을 넣는 동작만 보도록 좁히고 테스트로 고정 |
| 카드 말풍선에 넷째 줄이 반쯤 비침 | line-clamp와 안쪽 여백을 같은 요소에 줬다 → 줄 제한은 안쪽 span으로 |
| 공유 이미지(Satori)에서 줄바꿈이 안 됨 | 발췌문을 " · "로 이어 붙였다 |
| SVG 가로선에 그라데이션이 안 보임 | 높이 0인 요소에 objectBoundingBox 기준 그라데이션 → gradientUnits="userSpaceOnUse" |
| 클라이언트 파일에서 export한 상수가 서버 컴포넌트에서 비어 있음 | 공용 모듈로 분리 |
| 카카오 공유 오류 4019 | 카카오 콘솔 [제품 링크 관리]에 웹 도메인과 JavaScript SDK 도메인을 등록하지 않았다 |
잘한 것
- "무료로 계속 운영"을 처음부터 제약으로 뒀다. 캐시 구조, API 한도, 이미지 전략이 한 방향으로 정리됐다.
- 환경변수가 없으면 예시 데이터로 도는 구조. 덕분에 운영 DB를 건드리지 않고 화면 테스트까지 자동화했다.
- 테스트가 실제 버그를 잡았다. 가짜 404가 대표적이다. 테스트는 나중이 아니라 기능과 같이 넣는 게 결국 빠르다.
아쉬운 것
- 로딩 화면, 등장 애니메이션처럼 UX를 위해 넣은 것이 SEO와 성능을 해쳤다. 넣을 때 측정부터 했어야 했다.
- 공유한 마이그레이션 파일을 고쳐서 반만 적용되는 사고를 냈다. 이미 공유한 파일은 고치지 말고 새 번호로 만든다.
- 콘텐츠(공개 글)가 아직 적다. 기능보다 콘텐츠가 병목이라는 걸 늦게 체감했다.
다음
- 공개 글 30개를 채우고 애드센스를 신청한다.
- 방문자가 결과를 공유하는 "나도 해봤어요", 결과 예시를 채우는 도우미를 만든다.
- 서치 콘솔로 검색 유입을 확인하고, 인기 주제 위주로 수집 키워드를 조정한다.
프롬피드 개발기
- 개요. SNS에서 유행하는 AI 프롬프트, 빈칸만 채워 바로 쓰게 만들었다
- 1편. 방문자가 늘어도 DB 조회는 그대로다
- 2편. AI 없이 유행 프롬프트 자동 수집기 만들었다
- 3편. 유튜브 API 무료 한도, 10,000번이 아니었다
- 4편. 복사해도 숫자가 안 올라가던 이유
- 5편. 이미지가 깨져 보였던 이유 3가지
- 6편. Lighthouse 모바일 63점을 98점으로 올렸다
- 7편. 개인정보 없이 매주 알림 보내기 (웹 푸시)
- 8편. 테스트가 깨지면 배포가 안 되게 막았다
- 9편. 프롬피드 회고: 잘한 것과 아쉬운 것 (지금 읽는 글)
프롬피드: promfeed.solfany.com