본문으로 건너뛰기

인기 정책 문서

(2026년 4월 20일 updated)

1. 목적

사용자의 콘텐츠 소비 데이터를 기반으로 가장 인기 있는 도서를 스티커 리워드 기반으로 보여주는 '인기' 탭의 구성과 동작 방식을 정의한다. 인기 콘텐츠, 개인화된 미보유 스티커, 향후 공개될 스티커 정보를 한 화면에서 제공하여 탐색 경험과 리워드 동기를 강화한다.


2. 진입 및 기본 구성

  • 진입 위치: 하단 GNB의 [인기] 탭 클릭 시

  • 기본 구조: 3개의 스티커 기반 서브탭으로 구성된 세로 스크롤 화면

    • 인기 스티커
    • 나만 없는 스티커
    • 공개 예정 스티커
  • 탭 전환 방식: 사용자가 스크롤을 내리면 순차적으로 자동 전환되는 앵커 기반 구조, 자동 전환시 햅틱 피드백 제공

  • 탭 이동 기준: 각 영역 진입 시점 (스크롤 위치 기준 자동 전환)


3. 스티커 기본 모델

3.1 Sticker 모델

필드타입설명
nameTranslatedField다국어 스티커 이름
episodeOneToOneField연결된 에피소드 (1:1)
image_urlURLField스티커 이미지 URL
base_scoreFloatField사전 계산된 기본 점수 (크론 업데이트)
score_updated_atDateTimeField점수 최종 업데이트 시각

3.2 스티커 기본 점수 산정 (base_score)

스티커의 기본 점수는 다음 요소를 합산하여 크론 작업(매 5분, precalculate_sticker_scores)으로 갱신된다.

요소가중치 / 공식설명
신규 스티커max(0, 3.0 × (1 − days_since_created/30))출시일 기준 30일 선형 감쇠
최근 7일 수집+0.5 × count_7d즉각 트렌딩 신호 (raw count)
최근 30일 인기도log10(1 + collectors_30d) × 3트렌딩 윈도우 인기 (log 압축)
캠페인 연계+2 (StickerCampaign.priority_boost)활성 캠페인에 포함 시 (선택, 모델 도입 시)
어제 노출 패널티−2 (StickerExposure 7일 내)다양성 확보용 (선택, 모델 도입 시)

산정 공식:

base_score = (
new_decay_bonus(created_at, 30d, max=3.0) # 신규 부스트 (선형 감쇠)
+ recent_7d_collections * 0.5 # 즉각 트렌딩
+ log10(1 + collectors_30d) * 3.0 # 트렌딩 인기도 (log 압축)
)

가중치 (StickerScoreConfig):

상수비고
NEW_STICKER_MAX_BONUS3.0day 0 → +3
NEW_STICKER_DECAY_DAYS30day 30 → 0
RECENT_COLLECTION_WEIGHT0.57일 raw count
RECENT_COLLECTION_DAYS7
TRENDING_LOG_WEIGHT3.0log 가중치
TRENDING_WINDOW_DAYS30트렌딩 윈도우

4. 탭별 정책

4.1 인기 스티커

4.1.1 노출 데이터 소스

Sticker.base_score 컬럼(매 5분 cron 갱신)을 직접 정렬 기준으로 사용한다.

출처 필드타입설명
Sticker.base_scoreFloatField (db_index)사전 계산된 점수 (3.2 산정 공식)
Sticker.score_updated_atDateTimeField점수 최종 갱신 시각
UserSticker.earned_atDateTimeField정렬 tie-breaker (30일 윈도우 collectors)

period(daily/weekly/monthly) 또는 country별 분리 노출이 필요해지면 별도 StickersPopular 사전 집계 테이블 도입.

4.1.2 정렬 및 노출

  • 정의: base_score(3.2 공식)가 가장 높은 스티커 상위 10개 도서 노출
  • 정렬 기준 (결정적):
    ORDER BY base_score DESC, collectors_30d DESC, created_at DESC, id ASC
    • 1차: base_score
    • 2차: 최근 30일 collectors
    • 3차: 최신 콘텐츠 우선
    • 4차: id
  • 갱신 주기: 매 5분 cron (precalculate_sticker_scores)
  • 노출 개수: 최대 10개 (Top 3 도서는 가로 와이드 카드 강조, 이후 4~10번은 카드 그리드형)

4.1.3 API

항목내용
목록 조회GET /api/stickers/popular/
디버그 조회GET /api/stickers/popular/debug/<id>/
설명기간별/국가별 인기 스티커 목록 및 개별 점수 상세 확인

4.1.4 UI 구성

  • 카드 전체 클릭 시 도서 상세 이동
  • CTA 버튼은 없이, 배지로 상태 표현
    • 스티커 받기 가능
    • 획득 완료

4.2 나만 없는 스티커

4.2.1 모델 (StickersMissing)

필드타입설명
userForeignKey대상 사용자
stickerForeignKey미보유 스티커
positionIntegerField노출 순서

4.2.2 노출 패널티 (StickerExposure) — 미구현

사용자에게 이미 노출된 스티커에 대해 점수를 감산하여 다양한 스티커가 노출되도록 한다.

필드타입설명
userForeignKey대상 사용자
stickerForeignKey노출된 스티커
exposure_typeCharField노출 영역: popular / missing
exposed_atDateTimeField노출 시각
  • 패널티 규칙: 최근 7일 내 Popular/Missing 영역에서 노출된 스티커에 -2점 적용
  • 유니크 조건: 동일 유저-스티커-타입 조합은 하나만 유지 (exposed_at만 갱신)

4.2.3 정렬 및 노출

  • 정의: 사용자가 아직 수집하지 못한 스티커 중 인기 콘텐츠를 선별해 제공
  • 정렬 기준:
    • 인기 스티커 Top 10 제외
    • 사용자 미보유 스티커 중 최근 열람/찜 기록 기반 테마 우선 정렬
  • 노출 개수: 최대 6개 (2열 카드형)
  • 카드 구조 및 배지 정책: 인기 스티커와 동일
  • 중복 콘텐츠 회피: 인기 탭에서 이미 본 제외되며, 그래도 동일 도서가 나올 경우 우선순위 후순위 배치

4.2.4 테마 정렬 우선순위: 테마 기반 개인화 정렬 정책

아래 항목을 조합하여 **개인화 테마 점수(score)**를 계산하고, 이를 기준으로 우선 정렬함.

우선 항목기준점수 예시
최근 열람 도서의 테마 태그최근 7일 내 열람 도서가 포함한 theme_tag+3점
최근 북마크 도서의 테마 태그최근 30일 내 찜한 도서가 포함한 theme_tag+5점
최근 완독 도서의 테마 태그최근 30일 내 완독한 도서의 theme_tag+3점
전체 열람 비율이 높은 테마전체 열람 도서 중 반복적으로 등장하는 테마+2점 (누적 가능)
  • 점수 총합이 높은 theme_tag 우선 정렬
  • 동일 점수일 경우, 최근 출간일 기준 오름차순

4.2.5 API

항목내용
엔드포인트GET /api/stickers/missing/
설명사용자 미보유 스티커 중 추천 목록 조회

4.3 공개 예정 스티커

4.3.1 모델 (StickersUpcoming)

필드타입설명
stickerForeignKey대상 스티커
positionIntegerField노출 순서
release_dateDateTimeField공개 예정일
is_featuredBooleanField피처드(강조) 여부

4.3.2 위시리스트 (StickerWishlist)

사용자가 관심 있는 공개 예정 스티커를 저장할 수 있다.

필드타입설명
userForeignKey사용자
stickerForeignKey관심 스티커
created_atDateTimeField등록 시각

4.3.3 노출 정책

  • 갱신 주기: 매주 월요일 오전 6시 (UTC+9) 기준으로 자동 갱신
  • 노출 기준: 사용자의 접속 국가 또는 앱 설정 언어에 따라 해당 국가의 다음 주 공개 예정 도서 목록 노출
  • 갱신 내용: 다음 주 동안 공개 예정인 도서에 연결된 스티커 미리보기 (영업일 기준 매일 1권, 총 5권/ 토-일 제외)
  • 운영 기준: 각 국가별 콘텐츠 일정은 운영 CMS에 사전 등록되어야 하며, 미등록 시 기본 콘텐츠를 노출

예시:

  • 한국 사용자는 KST 기준 월요일 오전 6시에 한국어 콘텐츠 목록 갱신
  • 일본 사용자는 JST 기준, 스페인 사용자는 CET 기준으로 갱신됨

4.3.4 API

항목내용
목록 조회GET /api/stickers/upcoming/
위시리스트 등록POST /api/stickers/wishlist/
위시리스트 삭제DELETE /api/stickers/wishlist/<sticker_id>/

4.3.5 UI 구성

  • 공개 예정일 표기
  • 대표 도서 썸네일 또는 시리즈 아이콘 노출
  • 클릭 시 작품 상세로 이동

5. 스티커 캠페인 (선택 — 미구현)

5.1 모델 (StickerCampaign)

이벤트/캠페인과 스티커를 연결하여 특정 기간 동안 해당 스티커의 노출 우선순위를 높인다.

필드타입설명
stickerForeignKey대상 스티커
eventForeignKey (nullable)연계 이벤트
campaign_nameCharField캠페인 명칭
is_activeBooleanField활성화 여부
priority_boostIntegerField가산 점수 (기본 +2)
start_dateDateTimeField캠페인 시작일
end_dateDateTimeField (nullable)캠페인 종료일

5.2 캠페인 진행 판정

캠페인은 다음 조건을 모두 만족할 때 진행 중으로 판정된다:

  • is_active = True
  • 현재 시각이 start_date 이후
  • end_date가 null이거나, 현재 시각이 end_date 이전

5.3 점수 반영

  • 진행 중인 캠페인에 포함된 스티커에 priority_boost(기본 +2점) 가산
  • 기본 점수 산정 시 캠페인 점수가 자동 합산됨

6. 스티커 통계

6.1 모델 (StickerStats)

각 스티커의 수집자 통계를 사전 집계하여 관리한다.

필드타입설명
stickerOneToOneField대상 스티커 (1:1)
total_collectorsIntegerField전체 누적 수집자 수
daily_collectorsIntegerField일간 수집자 수
weekly_collectorsIntegerField주간 수집자 수
monthly_collectorsIntegerField월간 수집자 수
last_updatedDateTimeField통계 최종 갱신 시각

6.2 API

항목내용
엔드포인트GET /api/stickers/stats/
설명스티커별 수집자 통계 조회

7. 공통 UI 정책

  • 스크롤 전환 방식: 스크롤에 따라 자연스럽게 다음 탭으로 이동 (자동 전환 및 햅틱 피드백)

  • 배지 표현:

    • 스티커 받기 가능 → 아직 읽지 않은 도서
    • 획득 완료 → 이미 완독하여 보상 수령 완료
  • 찜 아이콘: 각 카드 우상단에 항상 노출

  • 디자인 계층화: 인기 Top 3 강조 카드 / 나머지 기본 카드형 구분


8. 통계 및 로그 수집 항목

  • 탭 진입 로그 (인기/나만 없는/공개 예정)
  • 개별 카드 클릭 로그
  • 카드별 스티커 상태(획득/미획득) 비율 집계
  • 탭별 체류 시간
  • 자동 스크롤 전환 발생 위치

9. 예외 및 공백 처리

  • 스티커 조건에 맞는 도서가 하나도 없을 경우 해당 탭은 미노출
  • 네트워크 오류 시 기본 안내 문구 및 재시도 버튼 노출

10. API 요약

API메서드설명
/api/stickers/popular/GET인기 스티커 목록 (기간별/국가별)
/api/stickers/popular/debug/<id>/GET인기 스티커 점수 디버그 상세
/api/stickers/missing/GET나만 없는 스티커 목록
/api/stickers/upcoming/GET공개 예정 스티커 목록
/api/stickers/wishlist/POST위시리스트 등록
/api/stickers/wishlist/<sticker_id>/DELETE위시리스트 삭제
/api/stickers/stats/GET스티커 통계 조회
/api/stickers/GET사용자 수집 스티커 목록
/api/stickers/earn/<episode_id>/POST스티커 획득