①문서가 왜 세 곳으로 나뉘는가
이 저장소의 문서는 성격이 다른 세 그룹으로 나뉜다. 이 페이지는 그중 guides/(공식 문서)를 위한 진입점이고, 나머지 둘은 필요할 때만 찾아가면 된다.
docs/features/ 200개 문서를 전수 조사해 이 세 그룹으로 나눴다. 물리적으로 폴더를 옮긴 것은 guides/로 이동한 것(공식화)과 archive/로 이동한 것(폐기) 뿐이고, 나머지는 docs/features/에 그대로 있다.②소셜 레코딩 파이프라인 구조
token.flow 이벤트 하나가 들어와서 소셜 데이터가 DB에 저장되기까지, 실은 시간순으로 쌓인 4개 계층과 3개 서브시스템을 거친다. 이름이 비슷해 헷갈리기 쉬워 관계를 다이어그램으로 남긴다.
flowchart TD
SP["social-sync-pipeline
전송 계층
token.flow 큐 구독 · DTO 파싱 · 저장 없음"]
SCHEMA["social-schema-definition
스키마 계층
contents·accounts·venues·token_links 정의"]
RP["social-recording-process
오케스트레이션 계층
dedupe → lock → Generator 라우팅 → 저장"]
RC["reconcile-token
배치·복구 계층
/reconcile · /retry(1시간 크론)"]
AUDIT["social-url-structure-audit
8소셜 URL 구조 실측 감사"]
GEN["social-generator
8소셜별 fetch → 스키마 객체 변환"]
E1["social-url-entry
URL 유입 창구 일원화"]
E2["social-url-dedupe
재사용 캐시(TTL 14일)"]
E3["lock-before-execute-recording
토큰 단위 동시성 락"]
REFACTOR["social-recording-refactor
전체 관통 사후 수선"]
SP --> RP
SCHEMA --> RP
RP --> RC
AUDIT --> GEN
GEN --> RP
E1 --> E2 --> E3
E3 --> RP
REFACTOR -. 검증·수정 .-> SCHEMA
REFACTOR -. 검증·수정 .-> RP
classDef layer fill:#3b6cff22,stroke:#3b6cff,stroke-width:1.5px;
classDef sub fill:#1a9c5322,stroke:#1a9c53,stroke-width:1.5px;
classDef refactor fill:#b8720a22,stroke:#b8720a,stroke-width:1.5px,stroke-dasharray:4 3;
class SP,SCHEMA,RP,RC layer;
class AUDIT,GEN,E1,E2,E3 sub;
class REFACTOR refactor;
app.module.ts가 sync-pipeline과 recording-process를 같은 큐(token.flow)에 동시에 물린다. 각 계층의 정본 문서는 §3~④의 표에서 링크된다.Generator 실행 결과 → URL 상태(FetchStatus) 전이
③의 8개 Generator 중 하나가 URL 하나를 처리한 결과가 이 상태들 중 하나로 떨어진다. 재시도·크론이 그중 어떤 상태를 다시 여는지도 여기서 정해진다 — 정본은 verification-runbook.md §7.
stateDiagram-v2
[*] --> pending : 신규 토큰의 URL 목록 선생성
pending --> ok : 객체를 확정했다
pending --> not_found : 불렀는데 대상이 없다
pending --> blocked : 대상 쪽 사정(비공개·초대전용·IP차단)
pending --> invalid : 200을 받았는데 응답이 계약을 깼다
pending --> error : 외부 호출 실패 · DB 일시 오류
pending --> unsupported : 담당 Generator가 없다(호출 0회)
pending --> skipped_paid : 유료라 안 불렀다(호출 0회)
not_found --> ok : 재시도로 결과가 바뀐다
blocked --> ok : 차단이 풀린다
error --> ok : 일시 오류가 지나간다
error --> exhausted : attempts가 MAX_ATTEMPTS(5)에 닿았다
ok --> ok : TTL(14일) 만료 후 재수집 — points가 쌓인다
unsupported --> ok : 담당 Generator가 생기면
skipped_paid --> ok : 유료 게이트를 켜면
invalid --> [*] : 100% 같은 실패다 — 배치가 명시적으로 열어야 한다
exhausted --> [*] : 상한에 닿았다 — 배치가 명시적으로 열어야 한다
unsupported·skipped_paid는 attempted_at이 없어 재시도 백오프를 원리적으로 통과하고, 다시 처리해도 같은 값이 나온다 — 이 둘이 열리는 조건은 거래가 아니라 배포다(Generator를 새로 만들거나 유료 게이트를 켜는 것).| 계층/서브시스템 | 정본 문서 | 한 줄 요약 |
|---|---|---|
| 전송 | docs/features/social-sync-pipeline/be-system-design.md | token.flow 구독 + DTO 파싱. 저장 없음(아직 guides로 옮기지 않음 — §7 참고) |
| 스키마 | schema.html | contents·accounts·venues·token_links·metric_series 필드 명세(v5, 2026-08-14 정정 포함) |
| 오케스트레이션 | service-overview.md | as-built 요약. 설계 시점 문서(HTML 4종)는 docs/features/social-recording-process/에 이력으로 남음 |
| 배치/복구 | reconcile-token.md | /reconcile(과거 토큰 채움) · /retry(실패 URL 재시도, 크론) |
| Generator 8종 | ③ 표 | 소셜별 fetch → Content/Account/Venue 변환 |
| URL 처리 정책 | url-pipeline.html | 정규화 → 분류 → fetch 단계별 계약. URL 문자열을 누가 언제 고칠 수 있는지의 정본 |
| URL 3부작 | url-entry · url-dedupe · lock | 유입 창구 일원화 → 재사용 캐시(TTL 14일) → 토큰 단위 동시성 락 |
| URL 기록 상태(FetchStatus) state machine | verification-runbook.md §7 | pending→ok/not_found/blocked/invalid/error/unsupported/skipped_paid→exhausted 전이 규칙. tokens.social_urls[].status 어휘의 정본 |
| 사후 수선 | docs/features/social-recording-refactor/ | 런타임 결함 대장(H-*) · 구조 개편(R-*) · 스키마 재확정 |
| 횡단 결정 + 인덱스 | decisions.md · index-audit.html | 8소셜 공통 규칙(G-1~21) · 인덱스 26개 감사 |
| 통합 검증(살아있는 도구) | verification-runbook.md · verification-rubric.md | 실제 소셜 API 응답으로 6개 컬렉션 저장을 검증하는 반복 실행 도구. 스크립트·결과물은 docs/features/recording-integration-verification/(경로 의존이라 그대로 둠) |
③소셜별 Generator 스펙 8종
어느 소셜이 어떤 벤더로 뭘 만드는지. 단가는 세션 내 실측 비용분석 근거(apify-cost-model.html) 기준.
| 소셜 | 문서 | 소스 벤더 | 상태 | 핵심 특징 |
|---|---|---|---|---|
| X | x.md | 유료 twitterapi.io ($0.15~0.20/1000) | 완료 | Creator+Content+Venue 전부 갖는 유일한 소셜. 인용/리트윗 사슬 fan-out(깊이 2) |
| YouTube | youtube.md | 무료 Data API v3 | 완료 | 실호출 15 unit 검증. 구독자 큰 채널은 3자리 유효숫자로 반올림돼 옴 |
| Telegram | telegram.md | 무료 HTML 파싱 | 완료 | Venue 전용, Content 없음. 가드 포털 재사용 실측(근거) |
| GitHub | github.md | 무료 GitHub API | 완료 | repo/owner. fork·archived 플래그로 스캠 위장 탐지 |
| TikTok | tiktok.md | 유료 Apify ($0.0047/건) | 완료 | 프로필/게시물 응답이 같은 평면 구조라 추가 호출 0회 |
| instagram.md | 유료 Apify ($0.0027~0.0054/건) | 완료 | 게시물 조회 시 작성자 프로필 보충콜 1회 추가 | |
| Website | website.md | 무료 HTTP GET | 완료 | sourceType(호출 전)과 패턴(응답 후) 이중 축. 실호출 검증 완료(23토큰) |
| reddit.md | 유료 Apify ($0.022/건, 전체 최고가) | 완료 | Content/Account를 의도적으로 안 묶음(V-2) — 작성자가 대개 공용 밈 서브레딧의 일반 유저 |
④근거자료 — 코드가 직접 인용하는 문서
아래는 social-url-structure-audit(8소셜 URL 구조 실측 감사)에서 나온 산출물 중, 지금도 fetcher/generator 코드 주석이 직접 인용하는 것만 추렸다. 코드를 고치기 전에 먼저 봐야 하는 문서다.
| 문서 | 플랫폼 | 코드가 인용하는 곳 | 성격 |
|---|---|---|---|
| x.html | X | x/x.types.ts | sourceType·필드 확정 통합뷰 |
| youtube.html | YouTube | youtube/youtube.types.ts | 동일 |
| github.html | GitHub | github/github.types.ts | 동일 |
| instagram.html | instagram/instagram.types.ts | 동일 | |
| tiktok.html | TikTok | tiktok/tiktok.types.ts | 동일 |
| reddit.html | reddit/reddit.types.ts (직접 인용 — scaffold JSON 없는 유일한 소셜) | 동일 | |
| website-kinds.html | Website | website.consts.ts | 도메인 5종 분류(툴·소셜·디렉터리·참조·사이트) |
| website-content-mapping.html | Website | website.generator.ts | ContentInput 추출 규격(패턴 7개) |
| website-pipeline.html | Website | 워킹트리 diff와 실시간 대조됨 | 파이프라인 15단계 현황(구현/미구현/제안) |
| website-qna.md | Website | 위 세 문서의 결정 기록 | 지속 갱신되는 Q&A |
| apify-cost-model.html | TikTok·Instagram·Reddit | Apify 실측 usageTotalUsd | 단가 모델 — Reddit이 비용의 66% |
| telegram-guard-reuse.html | Telegram | telegram.fetcher.ts | 가드 포털 재사용 3종 실측(69채널) |
docs/features/social-url-structure-audit/에 이력으로 남아 있다. 위 12개는 그중 지금도 코드가 참조하는 것만 추린 것이다.⑤일반 엔지니어링 가이드
소셜 레코딩 파이프라인에 국한되지 않는, 프로젝트 전반의 규칙.
⑥개발 이력 — docs/features/
17개 폴더, 154개 문서. 전부 완료됐거나 진행 중이던 작업의 기록이고 이제 더 갱신되지 않는다. "왜 지금 이 구조가 됐나"가 궁금할 때만 연다. 폐기된 23개는 docs/archive/에 따로 있다.
token.flow 큐 구독)⑦유지관리 — 새 작업이 끝나면
이 페이지는 자동으로 갱신되지 않는다. 수동 관리다.
- 새 feature 작업이 "공식 문서" 성격(스키마·운영정책·코드가 인용하는 근거자료)을 만들었다면 — 완료 시점에 그 문서를
guides/(적절한 하위폴더)로 옮기고, 이동 전 상대링크를 원 폴더 기준에서 새 위치 기준으로 고친다. 이 페이지 §2~④ 표에 행을 추가한다. - 기존 공식 문서를 대체하는 새 버전이 나왔다면 — 이전 버전을
docs/archive/로 옮기고docs/archive/README.md에 대체 사유를 한 줄 추가한다. 이 페이지에서 옛 링크를 새 문서로 바꾼다. - 순수 작업 로그·착수 문서라면 —
docs/features/<feature>/에 그대로 둔다. 이 페이지를 고칠 필요는 없다(§⑥은 폴더 단위 요약이라 파일이 늘어도 대개 그대로 맞는다. 폴더 성격 자체가 바뀌면 그때 문구만 수정한다). - 주기적으로 — §④(근거자료) 표의 문서가 실제로 코드에서 아직 인용되는지 가끔 grep으로 확인한다. 인용이 끊겼다면 §⑥ 이력으로 강등하거나 archive로 옮긴다.
guides/로 옮기지 않은 후보: social-sync-pipeline/be-system-design.md(전송 계층 정본), social-recording-refactor/schema-refactor-plan.md(스키마의 "왜"). 스크립트 경로 의존성 때문에 이번 라운드에서는 docs/features/에 남겨뒀다 — 다음 정리 라운드 후보다.