TheFan
개발 계획서 · v0.5 · 2026-08-13 KST

개발 계획서 v0.5 / 결정 7건 반영

크리에이터가 준 숫자만
순위가 된다

한국·중국·일본 OnlyFans 크리에이터 랭킹 서비스. 데이터를 크리에이터 본인 제출로만 받기로 결정하면서, 이 프로젝트의 최대 난제는 크롤링 기술이 아니라 초기 참여자 확보가 됐다. 계획의 순서도 그에 맞춰 뒤집힌다 — 수집 파이프라인보다 온보딩이 먼저다.

입력 PRD-1.md · PRD-2.md 벤치마크 ranking.bio/en/asians 개정 v0.5 — 중국 본토 포함 확정 · 아웃리치 267건 적재
Q1 데이터 출처크리에이터 직접 제출공급자 계약 없음 · 크롤링 없음
Q2 대상 국가KR · CN · JP 동시3개 코호트 병행
Q3 서비스 언어한국어기본 /ko · 다국어는 후순위
Q4 중국 세그먼트4분류 전체 포함본토 거주까지 · v0.5 확정
Q5 미클레임 아바타참여자만 표시시드는 비공개 명단 — v0.3 개정
Q6 도메인thefan.oig.kr 유지개발 단계 현행

공개 활동 랭킹 — 점수 구성

국가 코호트 내 백분위 · log(1+x) 변환 후 가중합 · 일 1회 확정

구독자 수·매출은 이 산식에 없다 — 공개 프로필에서 대조 검증이 불가능하기 때문. 별도 인증 랭킹으로 분리한다.

01

결정 로그

PRD-1은 “빠르게 인덱싱”, PRD-2는 “동의 없으면 쓰지 않는다”. Q1 답변으로 이 축은 PRD-2 완승으로 닫혔다. 나머지 결정은 그 전제 위에서 재정렬된다.

쟁점PRD-1PRD-2확정실행 규칙
1차 데이터 출처 비공식 API·상용 스크래핑으로 5,000계정 인덱싱 크롤링 금지, 크리에이터 옵트인·직접 제출 PRD-2 공급자 계약·자체 크롤러 모두 미도입. 지표는 크리에이터 제출값(B등급)만 랭킹에 사용하고, 운영자가 공개 프로필에서 대조 검증한 뒤 확정한다.
미클레임 계정 노출 전체 인덱싱 후 노출 (옵트인 전제) v0.3 개정 공개 시드 목록을 만들지 않는다. 시드는 /admin/outreach 비공개 아웃리치 명단으로만 존재. 팬 화면에는 참여 크리에이터만 노출된다. 근거는 2장 B절.
국가 분류 bio 언어감지·location 가중합으로 자동 추정 외모·이름·언어로 국적/민족 추정 금지 PRD-2 제출 시 크리에이터가 국적·거주국·주력시장을 각각 선택. 자동 추정 코드는 작성하지 않는다.
랭킹 보드 수 단일 종합 산식 1개 공개 / 트렌딩 / 인증 3분리 PRD-2 MVP는 공개·트렌딩 2보드. 인증 보드는 참여자 확보 후.
구독자 수·매출 공개 설정 시 보조 지표 공개 랭킹 제외, 0으로 채우지 않음 PRD-2 제출받되 공개 프로필 대조가 불가하므로 공개 산식 미사용. 인증 보드 전용.
인증 배지 가산 없음, 동점 시 우선 공개/인증 랭킹 분리 PRD-1 모든 참여자가 클레임을 거치므로 배지는 OnlyFans 자체 인증 여부 표시로 용도 변경.
유료 홍보 상단 고정 슬롯 광고와 자연 순위 완전 분리 절충 랭킹 표 바깥 별도 섹션 + Promoted 라벨 고정. 순위 번호 미부여.
복수 계정 (미언급) 대표 계정 1개만 종합 랭킹 PRD-2 creators ↔ of_accounts 1:N + is_primary. 합산 금지.
갱신 주기 전 계정 24시간 1회 고정 온리팬스 공개 지표 일 1회 변경 제출 기반이라 일 1회 자동 수집이 불가능하다. 랭킹 확정만 일 1회, 제출은 크리에이터 주기(권장 주 1회) + 14일 미갱신 시 stale.
02

옵트인 전환의 대가

Q1 결정은 법적으로 가장 안전한 선택이고, 그 대가는 명확하다. 개발 착수 전에 이것들을 인지하고 시작해야 한다.

A. 잃는 것

잃는 것영향이 계획의 대응
초기 규모 PRD-1의 “6개월 5,000계정”은 달성 불가. 옵트인으로는 3개국 합쳐 수백 명 단위가 현실적 상한. KPI 재설정 — 6개월 목표를 코호트별 50명 · 합계 150명으로 잡는다. 랭킹은 규모가 아니라 정확성으로 판다.
자동 갱신 크리에이터가 직접 넣지 않으면 지표가 멈춘다. 미갱신 계정이 순위를 점유하는 문제. 제출 지속성을 산식에 15% 편입. 14일 미갱신 stale, 30일 초과 시 랭킹 제외(삭제 아님). 갱신 리마인더 자동 발송.
자기신고 신뢰도 제출값 부풀리기가 곧 순위 조작이 된다. 제출 항목을 공개 프로필에서 대조 가능한 것으로 한정(4장 B절). 등록·갱신 시 프로필 URL을 함께 받아 운영자 검토 큐로 표본 검증. 전회 대비 ±20% 초과는 자동 보류.
최대 리스크 — 콜드스타트

코호트에 사람이 없으면 백분위 자체가 성립하지 않는다. 랭킹 공개 시점은 개발 완료가 아니라 코호트별 50명 확보 시점이며, 이건 개발 일정이 아니라 크리에이터 모집 일정이 결정한다. 10장의 G-COHORT 게이트가 여기에 대응한다.

따라서 P1의 최우선 산출물은 랭킹 화면이 아니라 크리에이터가 등록하고 싶어지는 온보딩이다.

B. 시드 목록을 공개하지 않는 이유 v0.3 개정

v0.2까지는 운영자가 등록한 미참여 계정을 아바타·핸들까지 공개 노출하는 안이었다. 얻는 것과 드는 것을 붙여보면 균형이 맞지 않아 공개하지 않는 쪽으로 뒤집었다.

공개했을 때 드는 것

  • 아바타 캐시의 저작권 처리
  • DMCA · 초상권 대응 (G5 부담 상승)
  • “왜 내 계정이 여기 있나” 항의 처리
  • 삭제요청 72시간 SLA 상시 운영
  • 등록 인력 — 건당 3분

공개해서 얻는 것

  • 지표도 순위도 없는 아바타 목록 — 팬 입장에서 볼 가치 낮음
  • 아웃링크가 눌려도 레퍼럴 수익 없음 (레퍼럴은 크리에이터 가입 기준)
  • 모집 효과도 제한적 — 본인이 목록에 있는 걸 발견하려면 어차피 우리가 알려야 한다

알릴 거면 공개 목록이 필요 없다. DM 한 통이면 끝나는 일에 법적 노출을 얹는 구조이므로, 시드는 /admin/outreach 비공개 명단으로만 유지한다. 필요한 필드도 핸들 · 출처 URL · 접촉 상태 3개로 줄어든다.

따라서 Q5는 “참여 크리에이터 아바타 표시”로 축소되고, 시드의 국가 배치 문제(자기신고가 없는 계정을 어느 코호트에 넣을 것인가)도 함께 사라진다.

C. 모집 채널 — 개인 DM으로는 숫자가 안 나온다

아웃리치 200건 × 응답 15% × 등록 50%  ≈  15명
목표 150명                            →  아웃리치 2,000건 필요

성인 크리에이터는 DM을 하루 수십 통 받는다. 개인 접촉은 전환율이 낮고 인력만 소모한다. 주 채널은 에이전시로 잡는다 — OF 크리에이터 상당수가 매니지먼트 소속이고, 한 곳을 확보하면 수십 명이 한 번에 들어오며 의사결정자가 1명이라 설득 비용도 낮다.

채널개발이 지원할 것단계
에이전시 주력 대리 등록 — 매니저가 소속 크리에이터를 일괄 등록. 단 동의 3항목(4장 C절)은 크리에이터 본인이 직접 눌러야 한다 — 대리 동의는 타협 불가이므로 본인 확인 링크를 메일/SMS로 발송한다. 에이전시 대시보드에서 소속 크리에이터 순위·유입 클릭을 묶어본다. P1
초대 코드 참여 크리에이터가 동료를 초대. 초대자 대시보드에 유입 수 표시. 옵트인 서비스의 자생 성장 채널. P1
아웃리치 명단 /admin/outreach — 핸들·출처 URL·접촉 상태 관리. 공개 노출 없음. 에이전시 발굴의 입구로도 쓴다. P2
크리에이터 랜딩 /ko/join — 참여 시 얻는 것(아시아 팬 노출·유입 클릭 통계·인증 배지)을 한 화면에. 랭킹 미개설 상태에서도 단독 동작. P1
순위 미리보기 제출 직후 “현재 참여자 기준 예상 위치”를 즉시 표시. 등록 완료율을 올리는 유일한 즉시 보상. P2
유입 클릭 대시보드 우리가 보낸 트래픽을 숫자로 증명. 재갱신 동기이자 이탈 방지 장치. P2
03

데이터 현실

제출 기반으로 바뀌면서 “관측 가능”의 기준도 바뀐다. 이제 기준은 크리에이터가 넣을 수 있고, 우리가 공개 프로필에서 확인할 수 있는가이다.

제출 + 대조 검증 가능 → 공개 랭킹

  • 누적 좋아요 of_likes_total
  • 게시물 / 사진 / 영상 / 오디오 수
  • 구독 정가 · 현재 할인가 · 무료 여부
  • 가입일 · OnlyFans 자체 인증 배지
  • 표시명 · 핸들 · bio · 아바타
  • 공식 SNS 공개 지표 (OAuth 연결 시 A등급)
  • 자체 서비스 조회 · 아웃링크 클릭

대조 불가 → 인증 보드 격리 또는 제외

  • 유료 활성 구독자 수 인증 보드
  • 매출 성장률 · 갱신률 인증 보드
  • 게시물 조회수 · 팬 목록 제외
  • 국적·민족 자동 추정 금지
  • 실명 · SNS 역추적 정보 미수집
  • 예상 수익 추정치 미구현

출처 신뢰도 등급 — 이 서비스에서 실제로 쓰는 것

등급출처사용 범위화면 표기
A공식 API · 크리에이터 OAuth 연결 (SNS)공개 랭킹 · 인증 랭킹표기 없음(기본)
B크리에이터 제출 + 공개 프로필 대조공개 랭킹의 주 출처최근 갱신일 표시
C운영자 공개 프로필 수동 확인제출값 대조 검증용 · 아웃리치 명단(비공개)화면 노출 없음
D외부 데이터 공급자미사용 — Q1 결정—
E추정 모델미사용—
04

1차 임무 — 최소 수집 필드

“랭킹에 필요한 최소 정보를 수집한다”가 첫 임무다. 제출 기반이므로 기준이 하나 더 붙는다 — 크리에이터가 30초 안에 입력할 수 있는 양. 입력 항목이 많아질수록 등록 완료율이 떨어진다.

A. 계정 식별 · 표시 클레임 시 1회

필드타입입력 방식비고
of_usernametext크리에이터 입력아웃링크 URL · 소유권 검증 대상
of_user_idbigint검증 시 확보핸들 변경에도 유지되는 불변 키
display_nametext자동(프로필) + 편집 가능이모지 원본 보존
bio_snippettext크리에이터 작성60자. 연락처·외부링크 입력 차단
avatar_urltext업로드 또는 프로필 확인썸네일 리사이즈본만 저장
of_is_verifiedbool운영자 확인OnlyFans 자체 인증 배지
join_datedate프로필 확인활동 기간
is_primarybool크리에이터 선택복수 계정 중 대표 1개

B. 랭킹 지표 P0 주 1회 갱신 권장

전부 OnlyFans 공개 프로필 상단에 그대로 표시되는 값이다. 그래서 크리에이터가 옮겨 적기 쉽고, 우리가 대조 검증할 수 있다. 이 대조 가능성이 공개 랭킹의 신뢰 기반이다.

필드타입산식 사용검증
of_likes_totalbigint30% + 성장 25%프로필 공개 표시값과 대조
of_posts_totalint지속성 보조
of_photos_totalint미디어 생성 20%
of_videos_totalint
of_audios_totalint
of_subscription_list_pricenumeric필터정가
of_subscription_current_pricenumeric필터프로모션 반영가 — 별도 컬럼
of_is_freebool필터 · 코호트가격 0 여부
of_fans_publicint NULL미사용NULL 허용. 0으로 채우지 않는다
submitted_attimestamptz지속성 15% · 성장률 분모제출 간격이 성장률 계산의 기준

C. 국가 · 동의 P0

Q2·Q4 결정으로 KR·CN·JP 3개 코호트를 동시에 연다. 중국은 단일 코호트로 묶지 않고 4분류로 나눠 받는다 — 국적·혈통·거주지·주력시장이 서로 다른 개념이기 때문이다.

필드값비고
nationality_self_declaredISO 3166-1 / NULL공개 랭킹 코호트의 유일한 기준
heritage_self_declaredISO / NULL본인이 공개를 원할 때만
residence_countryISO 3166-1 / NULL현재 활동 국가
primary_marketkr · jp · zh · global중국권 4분류의 축
country_verificationself_declared · social_confirmed · document_verified기본 self_declared
consent_rankingbool동의 항목 분리 저장. 하나의 체크박스로 묶지 않는다
consent_sensitivebool
consent_cross_borderbool
is_adult_verifiedbool인증업체 결과값만 저장. 신분증 원본 미보관
opt_out_attimestamptz NULL설정 시 72시간 내 인덱스·캐시 전면 제거

D. 관측 메타데이터 — 모든 제출 행에 필수

-- 제출 1건 = 지표별 1행. 원본 제출은 감사용으로 통째로 보존한다.
CREATE TABLE submissions (
  id            bigserial PRIMARY KEY,
  account_id    bigint    NOT NULL REFERENCES of_accounts,
  submitted_by  bigint    NOT NULL REFERENCES users,
  submitted_at  timestamptz NOT NULL,  -- UTC 저장 / 표시·집계는 KST 변환
  payload       jsonb     NOT NULL,    -- 제출 원본 전체
  proof_url     text,                   -- 공개 프로필 URL
  review_state  text      NOT NULL,    -- pending | approved | held | rejected
  reviewed_by   bigint, reviewed_at timestamptz
);

CREATE TABLE metric_snapshots (
  id            bigserial PRIMARY KEY,
  account_id    bigint    NOT NULL REFERENCES of_accounts,
  submission_id bigint             REFERENCES submissions,
  platform      text      NOT NULL,    -- onlyfans | x | youtube | tiktok | instagram | internal
  metric_name   text      NOT NULL,
  value         numeric,                -- NULL = 미관측. 0 대입 금지
  scope         text,                   -- public | active | paid | lifetime
  observed_at   timestamptz NOT NULL,
  source_type   text      NOT NULL,    -- official_api | creator_verified | public_manual
  consent_id    bigint,
  confidence    smallint,               -- 0–100
  UNIQUE (account_id, metric_name, observed_at)
);
CREATE INDEX ON metric_snapshots (account_id, metric_name, observed_at DESC);
시간대

observed_at·submitted_at은 UTC로 저장하고, 랭킹 회차 경계·일자 집계·화면 표시는 전부 KST 변환 후 처리한다 (observed_at AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Seoul'). UTC 네이티브 ::date 비교는 9시간 오귀속을 만든다.

05

제출 · 검증 루프

크롤러가 없어졌으므로 파이프라인의 심장은 배치가 아니라 검토 큐다. 운영자 1명이 하루에 처리할 수 있는 양으로 설계해야 한다 — 자동 통과 조건을 넓히고, 사람은 예외만 본다.

[온보딩]  가입 → OnlyFans 핸들 입력 → 일회용 코드 발급
    │      → 크리에이터가 bio에 코드 삽입 → 운영자/자동 확인 → 소유권 확정
    │      → 국가·동의 항목 각각 선택 → 성인 인증(결과값만 저장)
    ▼
[제출]   지표 폼 입력 (4장 B절, 10개 필드) + 프로필 URL
    │     ※ 최초 제출 즉시 "현재 참여자 기준 예상 위치" 표시
    ▼
[자동 게이트]  ┌ 첫 제출               → held (운영자 검토 필수)
               ├ 전회 대비 ±20% 초과   → held
               ├ 지표 감소             → held (콘텐츠 삭제일 수 있으므로 반려 아님)
               ├ 제출 간격 < 7일       → 저장하되 성장률 계산 제외
               └ 그 외                 → approved (자동 통과)
    ▼
[운영자 검토 큐]  프로필 URL 열어 표시값 대조 → approve / hold / reject
    │             표본 검증: approved 건 중 무작위 10%도 사후 대조
    ▼
[metric_snapshots]  append-only 시계열
    ▼
[랭킹 배치]  KST 04:00 · 일 1회 · 코호트 분리 → log(1+x) → 백분위
    │        → 상하위 1% 윈저라이징 → 가중합 → 0–100점
    ▼
[rankings]  round_date · board · cohort · rank · score · delta · coverage
    ▼
[웹]  ISR 일 1회 재생성 · 아웃링크는 /go 라우트에서 카운트 후 302

운영 규칙

  • 신선도 — 최근 제출 14일 초과 시 stale 배지, 30일 초과 시 해당 회차 랭킹 제외(계정 삭제 아님). 7·13·29일차에 갱신 리마인더 발송.
  • 성장률 — 제출 간격이 불규칙하므로 일평균 증가량으로 정규화한다: (현재값 − 직전값) / 실제 경과일. 최소 7일 간격 2회 제출이 있어야 성장 지표가 생긴다.
  • 신규 — 제출 2회 미만 또는 참여 7일 미만은 종합 랭킹 제외, /ko/new 목록에만 노출.
  • 감소 — 좋아요·미디어 감소는 정상 이벤트로 기록하고 성장률만 0으로 클리핑. 부정행위로 판정하지 않는다.
  • 봇 제거 — IP+UA+세션 해시로 24시간 내 중복 클릭 1회 산입. 데이터센터 IP 대역·헤드리스 UA·본인/운영팀 접속 제외. 원본 IP는 저장하지 않는다.
  • 옵트아웃 — 랭킹·검색·썸네일·캐시 전면 제거 72시간 SLA. 처리 이력만 감사 로그로 남긴다.
운영 부하 추정

참여자 150명 × 주 1회 제출 = 주 150건. 자동 통과율을 80%로 잡으면 검토 큐는 주 30건 · 하루 5건 안팎이다. 운영자 1명으로 감당 가능한 수준이며, 이 균형이 깨지지 않게 자동 게이트 임계값은 운영 콘솔에서 조정 가능하게 만든다.

06

랭킹 3보드 · 코호트

관측 조건이 다른 지표는 다른 표에 둔다. 그리고 옵트인 서비스에서는 코호트에 몇 명이 있는가가 산식만큼 중요하다.

코호트 정의 Q2 · Q4 반영

코호트기준개설 조건비고
한국nationality_self_declared = KR참여자 50명 이상1차 주력
일본= JP동시 오픈
중국= CN (아래 4분류로 세분)G4 통과 필요
전체3개국 통합50명기본 진입 화면

중국 4분류 — 하나로 합치지 않는다

분류조건1차 노출
중국 국적 · 해외 활동nationality=CN · residence≠CN노출
중국계 · 타 국적heritage=CN · nationality≠CN노출 — 본인이 공개 선택한 경우만
중국어권 시장 대상primary_market=zh노출
중국 본토 거주residence=CN노출 — v0.5에서 포함 확정. 플래그는 유지하되 기본값이 노출이다
v0.5 개정 — 중국 본토 거주 포함 확정

v0.3까지는 본토 거주만 격리해 G4 자문 결과로 노출을 결정하는 안이었다. 4분류 전체를 포함하는 것으로 확정한다.

residence=CN 플래그는 삭제하지 않고 유지한다. 기본값이 “노출”로 바뀔 뿐이며, 자문 결과나 규제 변화가 있으면 코호트 단위로 되돌릴 수 있게 남겨둔다. 플래그를 빼면 나중에 되돌릴 방법이 없어진다.

대신 PIPL 국외이전 요건은 절차로 처리한다 — 등록 시 동의 3항목 중 consent_cross_border를 본토 거주자에게 필수로 걸고, 국외 이전 사실·수신 국가·보관 기간을 고지한 뒤 별도 동의를 받는다. 이건 4장 C의 동의 분리 구조로 이미 커버된다.

운영 유의

아웃리치 명단의 본토 거주 15건은 접촉 대상에 포함한다. 다만 접촉 자체가 당사자에게 위험이 될 수 있는 지역이므로, 공개 채널(공개 멘션·공개 댓글) 접촉은 하지 않고 DM·메일 등 비공개 경로만 사용한다. 이건 법무 요건이 아니라 상대를 보호하는 운영 규칙이다.

보드 1 — 공개 활동 MVP

제출 지속성 15%는 옵트인 모델 때문에 새로 넣은 항목이다 — 90일 내 제출 횟수와 최근성으로 계산하며, 데이터를 꾸준히 갱신하는 계정이 상위에 오도록 한다. SNS 도달 지표는 OAuth 연결 계정만 보유하므로 미연결 계정에 0을 넣지 않기 위해 MVP 산식에서 제외하고 P4에 편입한다.

보드 2 — 트렌딩 MVP

전부 일평균 환산 기준. 소규모 계정의 변동성이 크므로 누적 좋아요 1,000 이상 · 제출 2회 이상을 최소 조건으로 둔다.

보드 3 — 인증 성과 P4

구독자·매출처럼 공개 프로필로 대조할 수 없는 지표만 모은 보드. 별도 동의 + 대시보드 증빙 제출이 전제이며, 참여자 30명 미만이면 개설하지 않는다.

가중치지표출처
25%활성 유료 팬 백분위B등급 — 크리에이터 제출 + 증빙 검증
25%30일 유료 팬 순증가율
20%구독 갱신률
15%팬당 참여도
15%30일 매출 성장률 (절대 매출 아님)

정규화 절차 (전 보드 공통)

  1. 코호트 분리 — (국가, 보드, 무료/유료)
  2. log(1 + x) 변환
  3. 코호트 내 백분위(0–100) — percent_rank()로 SQL 내 처리
  4. 상·하위 1% 윈저라이징
  5. 가중합 → 0–100점
  6. 커버리지 표시 — 사용 가능한 지표 비율. 재가중이 발생하면 상세 페이지에 명시
투명성 의무

산식·가중치·버전 변경 이력을 /ko/methodology에 전부 공개한다. 한국 개인정보보호법의 자동화된 결정 조항상 기준·절차 공개와 인적 검토 요청 창구가 요구될 수 있어 선택이 아닌 요구사항으로 취급한다. 서비스 1차 언어가 한국어(Q3)이므로 이 페이지도 한국어가 원본이다.

07

화면 설계

벤치마크는 ranking.bio — 필터 바 + 밀도 높은 순위 행 + 즉시 아웃링크. 한 행에서 “누구고, 얼마고, 얼마나 크고, 지금 갈 수 있는가”가 전부 읽혀야 한다. 기본 언어는 한국어(Q3).

라우트

경로내용렌더링
/ko/all3개국 통합 랭킹 — 기본 진입ISR · 일 1회
/ko/kr /ko/jp /ko/cn국가 코호트별 공개 활동 랭킹
/ko/trending트렌딩 보드ISR
/ko/new신규 크리에이터 (제출 2회 미만)ISR
/ko/c/[username]상세 — 지표 시계열 · 커버리지 · 최근 갱신일ISR
/go/[username]아웃링크 — 클릭 기록 후 302동적 · no-store
/ko/join크리에이터 랜딩 — 참여 혜택 · 등록 시작정적 P1
/ko/dashboard크리에이터 — 지표 제출 · 유입 클릭 · 순위 추이 · 초대 코드동적 P1
/ko/agency에이전시 — 소속 크리에이터 대리 등록 · 묶어보기동적 P1
/ko/consent/[token]대리 등록된 크리에이터 본인 동의 확인동적 P1
/ko/methodology산식 · 가중치 · 변경 이력 공개정적
/ko/opt-out랭킹 제외 · 정정 · 인적 검토 요청동적
/admin/review운영자 검토 큐 · 자동 게이트 임계값 조정동적 P1
/admin/outreach비공개 아웃리치 명단 — 핸들 · 출처 URL · 접촉 상태동적 P2

랭킹 행 — 목표 형태

/ko/kr — 한국 · 공개 활동 랭킹 · 2026-08-13 04:00 KST 확정
전체 62무료 11<$10 18$10–20 24$20+ 9영상 보유OF 인증인기순 ▾
1 표시명 · 인증배지@handle · bio 스니펫 60자 · 갱신 2일 전 1.24M좋아요 ▲ 2 OnlyFans ↗
2 표시명@handle · $9.99/월 · 갱신 5일 전 980K좋아요 ▼ 1 OnlyFans ↗
3 표시명@handle · 무료 · 영상 412 · 갱신 16일 전 ⚠ stale 771K좋아요 — OnlyFans ↗

아웃링크 규칙 P0

  • 모든 크리에이터 링크는 /go/[username]을 향한다. 서버에서 클릭을 기록하고 OnlyFans로 302.
  • 새 탭 고정: target="_blank" rel="noopener noreferrer nofollow sponsored". 성인 사이트 이동이므로 현재 탭을 대체하지 않는다.
  • 레퍼럴 파라미터는 /go 서버 쪽에서 부착 — 클라이언트 HTML에 노출하지 않는다.
  • 행 전체가 아니라 버튼만 아웃링크. 행 클릭은 상세 페이지로 (오클릭 방지 · 클릭 통계 오염 방지).
  • 클릭 로그: creator_id, clicked_at, ip_hash, ua_hash, session_hash, referrer — 원본 IP 미저장.

팬 화면에 나오는 계정 v0.3 개정

참여 크리에이터만 나온다. 시드(미참여) 계정은 공개 화면 어디에도 표시하지 않고 /admin/outreach 비공개 명단에만 둔다 — 근거는 2장 B절. 따라서 아바타 캐시는 동의를 받은 참여자에 한해 생성되며, 옵트아웃 시 디렉토리 단위로 퍼지한다.

미참여 계정을 노출하지 않으므로 G-COHORT 통과 전에는 팬 화면 자체를 열지 않는다. 그 기간에 공개되는 건 /ko/join 크리에이터 랜딩뿐이다.

성인 인증 게이트 P0

최초 진입 시 18+ 확인 인터스티셜. 확인 전에는 아바타·bio를 서버에서 렌더하지 않는다. 쿠키 저장, 접속 국가별 정책 분기가 가능하도록 미들웨어에 배치.

08

디자인 시스템

이 카테고리의 기본값은 검정 배경 + 네온 핑크다. 그 방향은 “또 하나의 성인 디렉토리”로 읽힌다. TheFan은 밝은 파스텔로 간다 — 사진이 주인공인 갤러리에서 배경 채도를 낮추면 크리에이터 사진이 튀고, 성인 카테고리의 어두운 관성과도 거리를 둘 수 있다.

v0.4 개정 — 제품 팔레트 분리

이 문서를 포함한 내부 문서는 잉크·그래파이트 기록지 톤을 유지하고, 서비스 화면은 아래 파스텔 토큰을 쓴다. 문서는 읽는 것이고 제품은 보는 것이라 목적이 다르다. 시안은 /design/a(균일)와 /design/b(매거진)에서 확인한다.

토큰값용도
--ink#1A1E29본문 (순검정 대신 푸른 기운의 소프트 다크)
--surface#FFFFFF카드 · 내비 · 칩
배경블루 #DCE8FF · 라벤더 #EDE6FC · 핑크 #FBE2EE상단에 광원 3개를 얹고 아래로 흰색으로 수렴
--violet#6D5AE6주 강조 — 링크 · 활성 필터 · CTA · 2·3위 배지
--pink#FF7A9C보조 — 로고 그라데이션 · 즐겨찾기
--gold#F0A63C1위 전용 — 다른 곳에 쓰지 않는다
--mint#22A98D무료 배지 · 성장 수치 · 인증 체크
모서리14–16px · 칩/배지 999px파스텔과 각진 모서리는 충돌한다

타이포그래피

  • Bricolage Grotesque — 순위 숫자 · 섹션 제목. 라틴 숫자에 성격을 주고 한글은 Pretendard로 폴백.
  • Pretendard Variable — 한글 본문. 서비스 1차 언어가 한국어(Q3)이므로 Inter/Roboto 조합은 쓰지 않는다.
  • JetBrains Mono — 지표 수치 · 필드명 · 타임스탬프. 자릿수 정렬이 순위표 가독성을 결정한다.

컴포넌트 원칙

  • 랭킹은 카드 갤러리다 (ranking.bio 형태). 1:1 사진 + 하단 정보 블록, 순위는 사진 좌상단 알약 배지. 프로모션은 그리드 바깥 별도 스트립에 둔다.
  • 수치는 우측 정렬 + 3자리 콤마. 가격은 $9.99처럼 통화 기호 고정. 전화·금액 입력 필드는 자동 포맷.
  • 모든 지표 옆에 최근 갱신일. 제출 기반이므로 이게 신뢰의 핵심 신호다. stale은 배지로 명시.
  • 색만으로 상승/하락을 표현하지 않고 ▲ ▼ — 기호 병기 (색각 접근성).
  • 터치 타깃 44×44 이상, 아웃링크 버튼 간 8px 이상 간격.
  • 썸네일 width/height 고정 → CLS 0.1 미만. WebP/AVIF, 첫 화면 밖은 지연 로드.
09

기술 스택 · 배포

랭킹은 하루 1회만 바뀐다. 실시간 렌더링이 필요 없다는 점이 스택 선택을 단순하게 만든다.

영역선택이유
웹Next.js 15 App Router + TypeScriptISR로 랭킹 페이지를 일 1회만 생성. 페이지당 DB 조회 0회
스타일Tailwind + CSS 변수 토큰8장 토큰을 그대로 @theme에 매핑
DBPostgreSQL 16 (서버 기설치 · 가동 중)백분위를 percent_rank()로 SQL 내 처리
인증이메일 + 매직링크크리에이터 대상. 비밀번호 저장 회피
메일다우오피스 SMTP (기존 공용 계정)매직링크 · 갱신 리마인더 발송
배치Node 스크립트 + systemd timerKST 04:00 랭킹 확정. Airflow류는 과함
이미지Next Image + 로컬 디스크 캐시썸네일만. 옵트아웃 시 디렉토리 단위 퍼지
런타임/opt/node24 (Node 24)시스템 Node 18과 분리
배포/opt/thefan + nginx 프록시 + Let's Encrypt기존 tag.oig.kr 패턴 재사용
현재 배포 상태

이 문서는 141.164.41.106:/opt/thefan/prd/에 정적 파일로 배포되어 https://thefan.oig.kr/prd로 서비스 중이다. DNS는 이미 해당 서버를 가리키며 인증서도 발급 완료. 애플리케이션은 같은 도메인 루트(/)에 Next.js를 프록시하는 구조로 얹는다 (Q6 — 도메인 현행 유지).

핵심 테이블

users             id, email, role, created_at        -- role: creator | agency | admin
agencies          id, name, contact_user_id, contract_state, created_at
creators          id, user_id, agency_id, slug, nationality_self_declared,
                  heritage_self_declared, residence_country, primary_market,
                  country_verification, consent_ranking, consent_sensitive,
                  consent_cross_border, consent_confirmed_at, -- 본인 확인 시각
                  is_adult_verified, invited_by, opt_out_at
outreach          id, of_username, source_url, contact_state, added_by, added_at
                  -- 비공개. 팬 화면에 노출되지 않는다
of_accounts       id, creator_id, of_user_id, of_username, is_primary, claim_state,
                  claim_code, verified_at, join_date, of_is_verified, last_submitted_at
submissions       4장 D절 참조 — 제출 원본 + 검토 상태
metric_snapshots  4장 D절 참조 — append-only 시계열
rankings          round_date, board, cohort, creator_id, rank, score,
                  delta, coverage_pct, formula_version
outbound_clicks   creator_id, clicked_at, ip_hash, ua_hash, session_hash, referrer
optout_requests   subject_ref, requested_at, verified_at, purged_at, note
10

마일스톤

v0.1에서 순서가 뒤집혔다. 옵트인 모델에서는 수집 파이프라인을 먼저 만들어도 넣을 데이터가 없다. 온보딩 → 제출·검토 → 랭킹 공개 순서로 간다.

P1온보딩

크리에이터가 등록할 수 있게 만든다

  • 4장 A·C 스키마 확정 · Postgres 마이그레이션 · 매직링크 인증
  • 클레임: 코드 발급 → bio 삽입 → 소유권 확정 (자동 확인 + 운영자 폴백)
  • 국가 4필드 · 동의 3항목 분리 입력 · 성인 인증 결과 저장
  • /ko/join 크리에이터 랜딩 · 초대 코드 발급
  • 에이전시 대리 등록 + /ko/consent/[token] 본인 동의 확인 링크
  • 옵트아웃 셀프서비스 · 삭제요청 처리 경로
4주산출: 등록 가능
P2제출·검토

데이터가 들어오고 검증된다

  • 4장 B절 지표 제출 폼 (10필드) + 프로필 URL
  • 자동 게이트 (첫 제출 · ±20% · 감소 · 간격) → 검토 큐
  • /admin/review 운영자 콘솔 · 임계값 조정 · 표본 검증
  • /admin/outreach 비공개 아웃리치 명단 (핸들 · 출처 URL · 접촉 상태)
  • metric_snapshots 적재 · stale 판정 · 갱신 리마인더 메일
  • 크리에이터 대시보드 — 제출 이력 · 예상 위치 미리보기 · 유입 클릭
  • 에이전시 대시보드 — 소속 크리에이터 묶어보기
3주산출: 검증된 시계열
G게이트

G-COHORT — 코호트별 50명 확보

  • 이 게이트는 개발이 아니라 모집이 통과시킨다. P3 개발은 병행하되 공개 오픈은 여기서 막힌다
  • 시드 공개 목록이 없으므로 이 시점까지 팬 화면은 열지 않는다. 공개되는 건 /ko/join뿐
  • 주 채널은 에이전시(2장 C절). 한 곳당 수십 명 단위라 게이트 통과 여부가 여기서 갈린다
  • 미달 시 대안: 전체(3개국 통합) 보드만 먼저 개설, 국가별 보드는 순차 오픈
  • 11장의 G2 · G3 · G5도 이 시점까지 병렬 진행
미정모집 일정 종속
P3랭킹·공개

순위가 계산되고 팬이 본다

  • 랭킹 배치 (코호트 → log → 백분위 → 윈저라이징 → 가중합) · KST 04:00
  • 공개·트렌딩 보드 · 국가별 페이지 · 필터 · 상세 · 시계열 그래프
  • 중국 4분류 적용 (본토 거주는 토글로 격리)
  • /go/[username] 아웃링크 + 봇 제거 클릭 집계
  • 성인 인증 게이트 · /ko/methodology
  • 8장 디자인 시스템 구현
4주산출: 공개 베타
P4확장·수익

정확도를 올리고 수익을 붙인다

  • SNS 공식 API OAuth 연결 (X / YouTube / TikTok / IG) → A등급 지표 확보
  • SNS 도달·성장을 공개 산식에 편입 (커버리지 재가중 공개)
  • 인증 성과 보드 (참여자 30명 이상 확보 시)
  • 유료 홍보 섹션 — 자연 순위와 물리적 분리 · Promoted 라벨
  • OnlyFans 레퍼럴 연동 · 정산 리포트
  • 다국어 (en / ja / zh) — 한국어가 원본
5주산출: 수익화

개발 누적 16주 (P1이 에이전시 대리 등록으로 1주 증가). 단, 공개 오픈일은 G-COHORT 통과 시점이며 개발 완료일과 별개다. P1 착수와 동시에 에이전시 접촉을 시작해야 두 일정이 만난다.

11

리스크 · 법무 게이트

Q1 결정으로 가장 큰 법적 리스크(무단 크롤링)는 제거됐다. 남은 것은 개인정보와 국가별 규제, 그리고 콜드스타트다.

게이트내용차단 대상
G1 해소데이터 공급자 계약 — Q1 결정으로 불필요. 공급자·크롤러 미도입—
G2법인 소재지 · 서버 위치 · 성인업종 PG 확정 + 한·중·일 현지 자문공개 오픈
G3개인정보 처리방침 — 민감정보(성생활 관련) 별도 동의 구조, 자동화된 결정 설명 · 인적 검토 절차공개 오픈
G4 전환중국 — 노출 여부는 포함으로 확정(v0.5). 남은 것은 PIPL 국외이전 절차 요건: 이전 사실·수신 국가·보관 기간 고지 + consent_cross_border 별도 동의. 차단 게이트가 아니라 등록 화면 요건이다등록 화면 구현
G5 축소DMCA · 초상권 · 삭제요청 창구 및 SLA(72h) — 시드 비공개 전환(v0.3)으로 대상이 참여 크리에이터로 한정돼 부담이 줄었다공개 오픈
G6 신규에이전시 계약 — 대리 등록 권한 범위, 크리에이터 본인 동의 확보 책임, 이탈 시 데이터 처리에이전시 온보딩
G-COHORT 신규코호트별 참여자 50명 확보랭킹 공개
리스크영향대응
콜드스타트 최상위참여자 없으면 서비스 자체가 성립 불가. 개인 DM 전환율로는 2,000건 접촉이 필요해 인력으로 감당 불가에이전시를 주 채널로(2장 C절) — 한 곳당 수십 명. 대리 등록 + 본인 동의 링크를 P1에 넣는다. 보조로 초대 코드 · 예상 위치 미리보기 · 유입 클릭 증명. 미달 시 통합 보드부터 순차 오픈
에이전시 의존한 곳 이탈 시 코호트가 통째로 비는 집중 리스크단일 에이전시 비중 상한을 코호트의 40%로 관리. 이탈 시에도 동의 주체는 크리에이터 본인이므로 계정은 남는다 — 대리 동의를 허용하지 않는 이유가 여기에도 있다
제출값 부풀리기순위 신뢰도 붕괴제출 항목을 공개 프로필 대조 가능 범위로 한정. 첫 제출 · ±20% 초과 · 감소는 자동 보류 후 운영자 검토. approved 건 10% 사후 표본 검증
갱신 중단멈춘 데이터가 상위 점유제출 지속성 15% 산식 편입 · 14일 stale 배지 · 30일 랭킹 제외 · 리마인더 3회
미참여 계정 노출 항의 해소—v0.3에서 공개 시드 목록을 폐기해 노출 자체가 없어졌다. 아웃리치 명단은 비공개이며 접촉 시 옵트아웃 경로를 함께 안내한다
중국 본토 거주 세그먼트현지 위법 · PIPL 국외이전포함하되 residence=CN 플래그는 유지해 되돌릴 수 있게 남긴다. 국외이전은 별도 동의로 처리. 접촉은 비공개 경로만 — 공개 멘션은 당사자를 노출시킨다
클릭 어뷰징트렌딩·아웃링크 지표 오염24시간 고유 클릭 · 데이터센터 IP 제외 · 본인/운영팀 제외 · z>4 이상치 검토 큐
한국 내 접속 차단트래픽 손실주 타깃 시장 · 서버 위치 결정을 G2에 포함. 국가별 노출 정책을 미들웨어에서 분기 가능하게 설계
다음 액션

결정 대기 항목은 모두 닫혔다. P1 착수 조건은 G4 자문 착수(중국 세그먼트, 개발과 병렬 가능)와 에이전시 1곳 접촉 개시다. 후자는 개발 산출물이 아니지만 G-COHORT를 통과시키는 유일한 현실적 경로이므로, P1 착수와 같은 주에 시작해야 일정이 맞는다.

다음 산출물: P1 상세 태스크 분해 · /ko/join 랜딩 카피 · 에이전시 제안서(참여 시 얻는 것 + 대리 등록 절차 + 동의 처리 방식).