← 목록
기타 2026-06-21 16KB 읽기 16분

티옴타 개인 채널 허브 (tiomta.com /나)

개요

tiomta.com을 개인형 채널 허브로 시작. 크라우니 회원가입 = 티옴타 회원이며, 각 회원은 자신의 개인 페이지(/나)를 갖는다. 그 한 페이지에서:
  • 내 채널 링크(YouTube·Instagram·X·Threads) — 현재는 링크 모음
  • 티옴타 테스트 진입/결과 유형 표시
  • 자기개발 영상·글·링크 큐레이션을 한 눈에
  • 오늘의 목표·꿈·플랜 관리

무엇을 했는지

  1. 프로필 API (server.js)
- GET /api/profile · PUT /api/profile - 신원: 회원=Bearer 토큰(getUser→user.id) / 게스트=X-Crowny-Owner 헤더(로컬 anon id) - 저장: data/profiles/<key>.json (원자 rename, conversations 패턴 재사용) - 모델: name, tiomtaType, channels{youtube,instagram,x,threads}, goalToday, dream, plans[], links[] - 보안: mergeProfile 화이트리스트 병합(임의 필드·key 변조 차단, 길이캡), 신원 없는 PUT 401
  1. 개인 허브 페이지 public/나.html
- /나 /me /hub 별칭 라우트(퍼센트 인코딩 디코딩 후 매칭) - Anthropic 크림/골드 토큰, 다크모드, 인라인 편집 + 디바운스 자동저장 - 채널 카드(클릭=열기/설정), 오늘목표·꿈 contenteditable, 플랜 토글/추가/삭제, 링크 담기/삭제 - 미설정 시 "🔮 티옴타 테스트 시작하기" CTA → /test.html, "티옴타 미디어 →" /media - 세션 SDK(crowny-session-sdk.js) 연동: 로그인 시 Bearer, 게스트는 로컬 owner
  1. 홈 진입점 public/index.html 헤더에 🪐 내 허브 버튼(/나)
  2. 한선씨 동반 프로필.한선 — 프로필 모델(빈프로필/채널설정/플랜추가/플랜토글/남은플랜수/링크추가) 컴파일·실행 검증, 학습DB 등록

관련 파일

  • /Users/ef/crowny-tiomta/server.js (프로필 헬퍼 + 라우트 + 별칭)
  • /Users/ef/crowny-tiomta/public/나.html
  • /Users/ef/crowny-tiomta/public/index.html (헤더 버튼)
  • /Users/ef/crowny-tiomta/프로필.한선
  • 데이터: /Users/ef/crowny-tiomta/data/profiles/

검증 (E2E)

  • 게스트 GET 빈 프로필 / PUT 저장 / 재조회 영속 ✓
  • /나 /me /hub → 나.html (200, 올바른 title) / / → index 유지 ✓
  • 신원 없는 PUT 401 / key·임의필드 주입 차단 ✓
  • 한선씨 컴파일·실행 PASS ✓

잔여 이슈 / 다음

  • 플랫폼 API(/api/platforms/*)는 아직 스터브 — 향후 채널 최신영상 자동 임베드 가능
  • 게스트→회원 전환 시 프로필 마이그레이션(g_ → u_) 미구현 (현재 별개 키)
  • tiomta.com 게이트웨이 결선은 기존 :9878 그대로(추가 작업 불필요)
  • 부팅 영속: tiomta-keepalive.sh가 9878 감시(재기동 시 자동 복구)

후속 (2026-06-21 같은 날 승인 진행)

  1. 게스트→회원 프로필 자동 이관POST /api/profile/migrate (Bearer+X-Crowny-Owner).
회원 데이터 우선, 빈 필드만 게스트 값으로 비파괴 병합 후 게스트 파일 삭제. 프론트: 로그인 감지 시 tiomta_migrated 플래그로 1회 자동 호출 후 프로필 재로드. 동반: 프로필이관.한선(빈필드채움/프로필병합, 컴파일·실행 검증·학습). 검증: 토큰없는 migrate 401 ✓ (라이브 토큰 E2E는 미수행 — 로직+게이트 검증).
  1. YouTube 채널 최신영상 키리스 임베드GET /api/channel/youtube?url=.
URL→channelId 해석(/channel/ 직접 or /@handle·/c·/user 페이지 파싱)→RSS feed(videos.xml) 파싱→최신 8개. 10분 인메모리 캐시(ytCache), decodeEntities로 제목 디코드. 키/할당량 불필요. 프론트: youtube 채널 설정 시 '내 채널 최신 영상' 스트립 자동 표시(썸네일 그리드, 채널 동일하면 재요청 안 함). 검증: @handle/UC직접형식 items 8개 ✓, 캐시 적중 ✓, 잘못된 URL 400 ✓.
  • 관련 파일 추가: server.js(migrate·youtube 라우트, ytCache/decodeEntities), public/나.html(ytCard·renderYT·migrateIfNeeded), 프로필이관.한선

후속3 — 리치 링크 카드 + 품질 패스 (2026-06-21)

  1. 리치 메타데이터 oembedGET /api/oembed?url=.
- YouTube/X 키리스 oembed(www.youtube.com/oembed, publish.twitter.com/oembed) + IG/Threads/일반 OG 태그 스크랩(og:title/og:image/twitter:* 폴백). - detectProvider(youtube/x/instagram/threads/web), metaCache 1시간, decodeEntities, ogTag 파서. - 검증: YT title+thumb ✓, X author ✓(정규식 (?:\/\/|^|\.) 수정으로 //x.com/ 판별), 일반 OG(github→Anthropic+thumb) ✓, 잘못URL 400 ✓. - 동반 링크메타.한선(플랫폼판별, 컴파일·실행·학습).
  1. 링크 리치카드 + 품질 패스 (나.html)
- 링크 담기 시 oembed 자동 해석 → 썸네일·플랫폼 배지(YT/X/IG/Th/WEB) 카드, 제목 자동. - provider 미설정 기존 링크 지연 백필(중복요청 가드), URL 중복 방지(이미 담긴 링크 토스트), unshift 최신순. - 품질: YT 스트립 로딩 스켈레톤, 썸네일 onerror 제거, 링크모델 thumb/provider 영속(mergeProfile 확장). - 접근성: 목표/꿈 role=textbox+aria, 채널카드 role=button+tabindex+Enter/Space, 삭제 aria-label. - 헬스 스윕: /나·/api/profile·/api/oembed·/api/channel 모두 200, 서버 에러로그 0.
  • 관련 파일: server.js(oembed 라우트+metaCache/resolveMeta/detectProvider/ogTag), public/나.html(리치카드·backfill·스켈레톤·a11y), 링크메타.한선
  • 잔여: IG/Threads는 토큰 필요해 단건은 OG 스크랩만(피드 임베드 미지원), 라이브 토큰 migrate E2E 여전히 미수행.

후속4 — 어린이 친화 전면 개편 + 울트라 병렬 감사 (2026-06-21)

울트라 병렬 워크플로 2회(감사 7에이전트 49만토큰 → 재검증 4에이전트 35만토큰).
  • 감사 48건(critical 7/high 14) 발견 → critical/high 전수 수정. 재검증 차단급 4건(high·certain) 추가 발견 → 전부 수정.
  • 나.html 전면 재작성(우주/행성 테마, ~620줄):
  • 결함수정: P null레이스 제거(안전기본값+ready), authBtn 부트즉시동작, prompt/confirm→인라인패널, save실패 토스트, load실패시 데이터보존, flushEdits(편집중 커밋후 render), grid min-width:0+word-break, 헤더 불투명배경, 링크삭제 data-del(pv x배지 오삭제 버그), window.open noopener, esc 싱글쿼트.
  • 재미요소: 마스코트 토토(SVG, 눈동자 mouse+touch 추적, 말풍선 힌트, 키보드), 콘페티+별(IIFE 클로저), 배지 7종(잠금힌트 title), 스트릭🔥(저장시 인정), 미션진행바+달성장🌑→🪐, 이모지 스티커 플랜추가, 사운드토글(WebAudio SFX), 꿈로켓🚀(상향고정), 썸네일 호버줌▶, 첫방문 이름입력, 전체완료 배너(max-width).
  • 서버 하드닝: cleanText(태그제거)+safeUrl(javascript:차단, oembed thumb 포함), 링크 dedup, plan id sanitize, streak/badges/totalDone(누적완료=번개배지) 영속, migrate 재정제(mergeProfile), /나 전용 CSP+X-Content-Type-Options.
  • 검증: 악성 페이로드(태그/js URL/중복/id주입) 차단 ✓, CSP 헤더 ✓, 데이터 흐름 영속 ✓, 인라인 JS 문법·괄호균형 ✓, 서버 에러로그 0, 한선씨 동반 4종 컴파일 ✓.
  • 한선씨 동반 추가: 게임화.한선(완료수/스트릭갱신/배지판정). 누적=프로필/프로필이관/링크메타/게임화 4종.
  • 잔여(low, 미차단): 패널 320px 버튼wrap 최적화, nameAsk 로그인유저 순간노출(가드됨), 단일플랜 배너 미표시(의도).
  • 후속5 — 다듬기 + 실제 브라우저 시연 (2026-06-21)

  • low 3건 보완: 패널 버튼 flex-shrink:0+input flex:1 1 140px(320px wrap), nameAsk ready 가드(로그인유저 순간노출 차단), 전체완료 배너 plans>=1로 완화(단일 플랜도 축하).
  • 헤더 모바일 잘림 수정(시연 중 발견): 520px 브레이크포인트에서 미디어 텍스트 숨김(🎬만)+pill 축소+#authBtn max-width+ellipsis+로그인 텍스트 단축. 데스크탑 풀텍스트 유지.
  • Chrome 헤드리스 실제 시연: 데스크탑(900px) 풀렌더 — 마스코트/스트릭/미션바/이모지플랜/배지 모두 정상, 프레임 깨짐 0. 모바일(520px 뷰포트) 헤더 티옴타·🔔·🎬·로그인 잘림 없이 정렬, nameAsk·채널카드 정상.
  • 함정: headless --window-size=390이 ~500px 뷰포트로 렌더돼 스크린샷 폭과 뷰포트 폭 불일치 → 헤더 검증은 창 너비를 뷰포트에 맞춰(520) 촬영해야 정확.
  • 정리: 시연용 public/_demoseed.html 삭제(배포금지), 테스트 프로필 정리. /나 200, 서버 에러로그 0.
  • 후속6 — 개인페이지 고도화(드래그·태그) + 관리자 대시보드 (2026-06-21)

    사용자 확정: 개인=드래그정렬+태그, 관리자=운영 대시보드(읽기), 인증=환경변수 화이트리스트.
  • 관리자 운영 대시보드 /adminpublic/admin.html
  • 인증: ADMIN_USERS(env, 기본 kim.president.sk,admin) 화이트리스트 requireAdmin. 토큰 username/id/email 매칭. 비관리자 403 + 페이지는 🔒 게이트 UI.
  • GET /api/admin/stats — 전체 프로필 디스크 스캔 집계: 회원/게스트수, 활동(플랜/완료/링크/누적완료/최장스트릭), 인게이지먼트(목표/꿈/유형/채널), 채널종류·링크 provider 분포, 배지, 최근 20명. GET /api/admin/me.
  • 대시보드: KPI 4카드(TOAU 색보더), 채널종류·플랫폼 막대, 최근활동 테이블(회원/게스트 칩). 모든 출력 esc — 레거시(하드닝 이전) 저장분에 XSS 페이로드 존재 확인, 대시보드에서 무력화.
  • /admin 전용 CSP+nosniff. 함정: 인증우회 데모는 분류기가 차단(정상) → 순수 클라이언트 목업으로 렌더 시연.
  • 개인페이지 드래그 정렬 + 태그 (나.html)
  • enableDragSort(container, getArr, onDone) — pointer 기반(마우스+터치), 핸들 [data-drag], 거리기반 insertBefore, drop 시 DOM순서로 배열 재정렬. getter로 배열 참조(loadProfile이 P 교체하므로 필수). 플랜·링크 카드에 data-sid+핸들.
  • 링크 태그: TAGS 7종(공부/재미/영감/꿈/운동/음악), 카드 🏷️ 배지 탭=다음 태그 순환, 상단 태그 필터 칩(사용중 태그만, 개수). 서버 link.tag 영속.
  • 한선씨 동반 추가: 관리통계.한선(회원판정/완료율/집계). 누적 5종(프로필/프로필이관/링크메타/게임화/관리통계).
  • 검증: admin 게이트(비로그인 🔒)·대시보드 렌더(목업)·개인페이지 드래그핸들+최신영상 실제로드 스크린샷 ✓. stats 403·태그 영속·드래그 코드 균형 ✓. 우회코드 완전제거+재시작 확인. 서버 에러로그 0.
  • 후속7 — 디자인 통일 + 서비스 트리 + 로그인 노출 + worldview 정리 (2026-06-21)

    사용자 지적 4건 처리:
    1. /나 URL 정식화: 한글 /나는 의도적 친근 별칭(서버에 /나·/me·/hub 3별칭 모두 나.html 서빙). 공유·타이핑 안정성 위해 정식=/hub(영문). index '내 허브' 버튼·서비스카드 href를 /hub로. /나는 별칭 유지.
    2. 디자인 통일: 원인=나.html/admin.html이 crowny 골드 #C9A961 사용(메인 index는 티옴타 표준 #B58A4A). 나.html·admin.html :root를 티옴타 표준(크림#F4EFE2/초콜릿#3B2A1D/골드#B58A4A, Noto Serif KR)으로 리맵(라이트+다크). 콘페티/마스코트SVG/스트라이프 그라데이션 하드코딩 색도 표준으로. 구골드 잔존 0 확인.
    3. 메인 로그인 노출: 원인=모바일(≤600px) 헤더 처리 부재로 버튼 과밀→로그인 프레임 밖. @media(≤600): 부제·내허브·월드·앱(nav-secondary) 숨김+버튼 축소, 로그인 항상 노출. 로그인 버튼을 내허브 바로 뒤로 재배치. 480px 시연 확인.
    4. worldview 표준 재디자인(서브에이전트 위임): 다크네온(#0a0a0f/#FCD34D)→티옴타 표준. 네온/다크 잔존 0. 로고는 background-clip:text가 헤드리스서 박스로 깨져 단색 serif 골드로 교체(어디서나 안전).
    • 서비스 트리 정리: index HUB_SERVICES에 '내 허브'(href:/hub, feat) 추가(헤더에만 있던 것→발견성). hub 아이콘 SVG 추가.
    • 검증: 5라우트 200·에러 0·표준골드 반영(나6/admin3/worldview3/index8)·구골드 0. 스크린샷: 메인(데스크탑/모바일), worldview(로고 정상), admin·허브(표준색).
    • 함정: background-clip:text는 일부 헤드리스/요소서 클립 실패→골드 박스. 로고는 단색 권장. headless window-size<실제뷰포트라 헤더 잘림 오판 주의(뷰포트 폭=창폭으로 촬영).

    후속8 — 종합 마무리(정리·디자인·기능, 울트라 병렬) (2026-06-21)

    사용자: "정리, 디자인 개선, 기능 완성".
    • 정리: worldview 외부브랜드(구글/유튜브/깃허브/챗지피티/스팀/노션)→자체 서비스명(티옴타 검색/튜브/코드/타임라인/AI·월드). 고아 페이지 4종(home-full/index-light/talk/_네비데모, 참조0)+백업 6종 → archive-legacy-20260621/ 안전 아카이브(라이브 영향0).
    • 디자인 통일(울트라 6에이전트 병렬): 동선 페이지 test/bell/service-posts/planner/share/status를 티옴타 표준 토큰으로 리맵. 비표준색(crowny골드#C9A961·코랄#D97757·네온·차가운다크·Söhne/Tiempos) 전부 제거(잔존0). 제목 serif(Noto Serif KR). 추가로 canvas/tests/flow도 병렬 표준화.
    • 기능 점검: 각 페이지 onclick/fetch/폼 핸들러 동작 확인. test=12함수+API4종 연결, bell=초인종/매칭 API, service-posts=피드/등록/좋아요 API, planner=AI lite·플랜저장. 깨진 핸들러 0(대부분 정상). planner h1 background-clip:text 그라데이션→단색(깨짐 방지).
    • 잔여(기능 미완, 외부의존): service-posts 게시글 상세보기 라우트 미구현, planner Opus 버튼 결제 스터브(예정), Google Fonts CDN 사용(자체호스팅 미전환), AI lite는 Gemini 키 런타임 의존.
    • 검증: 9페이지 비표준색0·HTTP200, test.html 시연(크림+골드 정상). 한선씨 동반 변경 없음(디자인/정리 작업).
    • 추가 표준화: canvas/tests/flow 병렬 리맵(잔여0). flow는 코드주석 "제미나이"→"크라우니AI"까지 자체용어화. 최종: 라이브 14페이지 전부 비표준색 0, 13동선 라우트 200, 에러0. bell·test 시연(크림+골드 통일).

    후속9 — 미완 3기능 완성 (2026-06-21)

    1. 게시글 상세보기: server GET /api/posts/<id>(조회수+1, id 영숫자검증) + service-posts.html 카드클릭→상세 모달(작성자/시각/조회/본문 pre-wrap/좋아요), esc 싱글쿼트 보강, ESC·배경 닫기. 검증: 생성→조회 views 증가 ✓.
    2. planner Opus 심화: alert 스터브→실제 /api/ai/main(Premium 모델) 연결. 프론트 로그인 게이트(CrownySession 토큰) + 서버 게이트(ai/main은 getUser 없으면 401, 회원 전용). ai/lite는 무료 유지. planner에 세션 SDK 추가. 검증: 비로그인 ai/main 401, lite 200 ✓.
    3. 폰트 자체 호스팅: 외부 폰트 CDN(Google Fonts·jsdelivr Pretendard) 전부 제거. woff2 self-host(public/fonts/: PretendardVariable 2MB, NotoSerifKR 400/700/latin) + public/css/fonts.css(@font-face) + 6페이지(bell/app/planner/service-posts/index/status) link 교체. server MIME woff2=font/woff2 서빙. 검증: 외부CDN 0페이지, woff2 200, planner 시연(제목 serif·본문 Pretendard 정상) ✓.
    • 한선씨 동반 기능완성.한선(조회증가/회원게이트/id유효) 컴파일·실행. 누적 동반 6→7종.
    • 서버 에러로그 0. 테스트 데이터 정리.