new-term-radar
Daily radar for new AI terms: names of new models, products and repos, caught on X, Hugging Face, GitHub and sitemaps before keyword tools list them.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
크리에이터 핸들 · 금액 · 제품만 입력하면 브랜드 HTML 메일이 완성돼 네이버웍스로 즉시 발송된다. TikTok Shop 크리에이터 섭외용. email-template.js 는 브라우저 미리보기와 서버 발송이 같은 코드를 쓴다. 템플릿을 두 곳에서 만들면 "미리보기와 실제 메일이 다른" 사고가 나므로 여기 한 곳에서만 만든다. 1. Vercel → Add New Project → 이 레포 임포트 2. 프레임워크 프리셋 Other, 빌드 설정 변경 없음 (정적 파일 + 서버리스 함수) 3. 아래 환경변수 등록 → Deploy查看完整 README ↗
크리에이터 핸들 · 금액 · 제품만 입력하면 브랜드 HTML 메일이 완성돼 네이버웍스로 즉시 발송된다. TikTok Shop 크리에이터 섭외용.
dalba-outreach/
├─ index.html ← 발송 화면 (로그인 + 입력 폼 + 실시간 미리보기)
├─ admin.html ← 관리자 화면 (팀 전체 현황 · 중복 시도 · 메일함)
├─ email-template.js ← 메일 템플릿 엔진 (브라우저·서버 공용)
├─ auth.js ← 직원 계정 목록 · 로그인 세션
├─ history.js ← 발송 이력 (팀 공용) · 중복 발송 차단
├─ mail.js ← 네이버웍스 IMAP 읽기
├─ sync.js ← 메일함 → 이력 변환 (버튼·자동 실행 공용)
├─ reminders.js ← 회신 없을 때 자동 팔로업(리마인드) 발송
├─ sheets.js ← 구글시트 읽기 (제품별 매출 상위 영상)
├─ i18n.js ← 화면 번역 (한국어 · English · Tiếng Việt)
├─ api/
│ ├─ login.js ← 로그인 (아이디/비번 → 세션 토큰)
│ ├─ outreach-send.js ← 네이버웍스 SMTP 발송
│ ├─ history.js ← 겹치는 수신자 미리 확인 · 최근 발송 조회
│ ├─ admin.js ← 팀 전체 집계 (관리자 전용)
│ ├─ replies.js ← 받은편지함을 훑어 크리에이터 회신 기록
│ ├─ backfill.js ← 보낸편지함을 이력으로 가져오기
│ ├─ cron.js ← 자동 동기화 (Vercel Cron 이 부른다)
│ ├─ products.js ← TikTok Shop 상품 목록 (썸네일·판매상태)
│ └─ videos.js ← 제품별 매출 상위 영상 (구글시트)
├─ package.json
└─ vercel.json
email-template.js는 브라우저 미리보기와 서버 발송이 같은 코드를 쓴다. 템플릿을 두 곳에서 만들면 "미리보기와 실제 메일이 다른" 사고가 나므로 여기 한 곳에서만 만든다.
| 이름 | 필수 | 설명 |
|---|---|---|
NW_ACCOUNTS | 권장 | 직원 목록. 넣으면 담당자별 로그인 방식으로 동작한다 (아래 참고) |
SESSION_SECRET | 선택 | 세션 토큰 서명 키. 없으면 NW_ACCOUNTS 에서 파생 — 이 경우 직원 목록을 고칠 때마다 전원 재로그인 |
SESSION_DAYS | 선택 | 로그인 유지 기간, 기본 7 일 |
DASHBOARD_PASSWORD | 대체 | 공용 비밀번호. NW_ACCOUNTS 를 넣으면 쓰이지 않는다 (로그인이 대신함) |
NW_DOMAIN | 선택 | 발신 허용 도메인, 콤마 구분. 기본 dalbausa.com,dalba.com |
NW_SMTP_HOST | 선택 | 기본 smtp.worksmobile.com |
NW_SMTP_PORT | 선택 | 기본 465 (SSL). 587 이면 STARTTLS 로 자동 전환 |
NW_BCC | 선택 | 발송 사본을 받을 주소 — 팀 공용 발송 이력 보관용 |
LOGO_URL | 선택 | 메일 헤더 로고 이미지의 공개 절대주소. 없으면 텍스트 워드마크로 그린다 |
KV_REST_API_URLKV_REST_API_TOKEN | 권장 | 발송 이력 저장소. 중복 발송 차단이 여기에 달려 있다 (아래 참고). Vercel 마켓플레이스가 자동 주입 |
HISTORY_DAYS | 선택 | 재발송 차단 기간, 기본 90 일 |
HISTORY_SINCE | 선택 | 메일함에서 이력을 채울 시작일, 기본 2026-05-01 |
HISTORY_LOG_MAX | 선택 | 발송 이력 보관 상한, 기본 100000 건. 담당자가 많아 더 쌓이면 키운다 |
CRON_SECRET | 권장 | 자동 동기화(/api/cron)를 아무나 못 부르게 하는 비밀값. Vercel 이 자동으로 헤더에 실어 보낸다 |
ADMIN_EMAILS | 선택 | 관리자 화면을 볼 수 있는 이메일 여러 명. 콤마·세미콜론·공백·줄바꿈 아무거로 구분해도 된다. 비우면 아무도 관리자가 아니다 |
NW_IMAP_HOST | 선택 | 기본 imap.worksmobile.com |
NW_IMAP_PORT | 선택 | 기본 993 (SSL) |
TikTok Shop 상품 선택기를 쓰려면 TTS_* 네 개가,
매출 상위 영상 드롭다운을 쓰려면 GOOGLE_SERVICE_ACCOUNT 가 추가로 필요하다 (아래 참고).
NW_ACCOUNTS)담당자마다 아이디·비밀번호를 주고, 네이버웍스 앱 비밀번호는 서버에만 둔다. 담당자는 앱 비밀번호를 알 필요가 없고 브라우저에도 남지 않는다.
Vercel 환경변수 NW_ACCOUNTS 에 한 줄에 한 명씩 넣는다:
아이디|로그인비번|표시이름|네이버웍스이메일|앱비밀번호|직함(선택)
hannie|Hann2026!|Hannie|[email protected]|abcd efgh ijkl mnop|Creator Partnerships
quinn|Qu1nnX7!|Quinn|[email protected]|wxyz 1234 5678 9012|Creator Partnerships
# 로 시작하는 줄과 빈 줄은 무시 → 주석·구분선으로 써도 된다| 는 못 쓴다 (로그인 비번에 | 가 필요하면 JSON 형식을 쓸 것 — auth.js 주석 참고)설정 > 보안 > 외부 앱 비밀번호 값이다수정 후 Redeploy 해야 반영된다. 퇴사·교체는 그 줄만 지우면 되고, 줄을 지우는 즉시 그 사람이 갖고 있던 세션 토큰도 무효가 된다.
From 으로 구분되고, 답장도 그 사람에게 간다NW_ACCOUNTS 를 비워두면 예전 방식으로 동작한다 — 담당자가 자기 네이버웍스 주소와
앱 비밀번호를 자기 브라우저에 등록하고, 그 자격증명이 요청마다 함께 온다.
이때는 DASHBOARD_PASSWORD 로 화면을 막고, 발신 주소는 NW_DOMAIN 회사 도메인만 허용한다.
자격증명은 서버에 저장하지 않는다.
현재 기본값: 담당자끼리 중복 차단은 꺼져 있다. 다른 담당자가 이미 보낸 크리에이터여도 그대로 발송된다 (발송 화면엔 "ℹ️ 다른 담당자 발송 있음" 참고 표시만). 계속 막히는 것은 🤝 협업 중(관리자·본인 발송 이력 제외), ⛔ 반송 주소 · 🚫 수신 거부, 같은 담당자가 30분 안에 같은 크리에이터에게 다시 보내기(시간 초과 뒤 재시도 중복 방지). 아래의 담당자끼리 차단을 다시 켜려면 Vercel 환경변수
DEDUP_ACROSS_STAFF=1.
담당자가 10명이면 명단이 겹치는 건 시간 문제다. 같은 크리에이터가 d'Alba 메일을 두세 번 받으면 브랜드 인상에 바로 흠이 나므로, 발송 직전에 팀 공용 이력을 확인하고 이미 보낸 사람은 그 건만 보류한다.
HISTORY_DAYS 로 변경)[email protected] 은 [email protected] 과 같은 편지함으로 본다force 를 무시한다.두 명이 같은 사람에게 동시에 누르는 경합은 조회로는 못 막는다(둘 다 "없음"을 본다). 그래서 조회가 아니라
SET NX(없을 때만 쓰기)로 자리를 먼저 잡고 보낸다.
KV_REST_API_URL / KV_REST_API_TOKEN 이 자동으로 들어간다무료 티어로 충분하다. 발송 1건당 명령 23개, 미리 확인은 명단 1명당 12개다.
이 도구를 쓰기 전에 나간 메일도 중복 판정에 들어와야 한다. 안 그러면 툴을 켠 날 이전에 접촉한 크리에이터에게 그대로 다시 나간다. 관리자 화면의 [📥 지난 발송 가져오기] 탭에서 담당자별로 보낸편지함을 읽어 넣는다.
Collab)messageId 로 판별). 여러 번 돌려도 안전하다⚠️ 저장소를 아예 설정하지 않으면 이 기능은 조용히 꺼지고 발송은 예전처럼 된다. 반대로 설정은 했는데 저장소가 죽으면 중복인지 알 수 없으므로 보내지 않는다. 이때는 강제 발송이 유일한 탈출구다 — 켜 놓고 조용히 중복이 나가는 게 가장 나쁜 결과라서. 관리자가 아닌 담당자는 이 경우 보내지 못하고 보류로 남는다.
/admin.html)ADMIN_EMAILS 에 등록된 사람만 볼 수 있다 — 여러 명을 콤마·세미콜론·공백·줄바꿈
아무거로 구분해 넣으면 된다 (예: [email protected], [email protected]).
발송 화면과 같은 로그인을 쓰므로 두 번 로그인하지 않아도 되고,
관리자에게만 발송 화면 헤더에 바로가기가 뜬다.
관리자 본인은 집계에서 빠진다. 관리자는 감시하는 쪽이라 담당자 실적 대상이 아니다.
ADMIN_EMAILS에 있는 사람은 담당자별·일별·발송 이력·대화·명단 어디에도 나오지 않는다 (관리자이자 담당자로 등록된 사람도 마찬가지). 실적을 봐야 하는 담당자는NW_ACCOUNTS에는 있고ADMIN_EMAILS에는 없어야 한다.
| 탭 | 보이는 것 |
|---|---|
| 담당자별 | 누가 몇 건 보냈는지 · 회신·회신율 · 중복으로 막힌 횟수 · 마지막 발송 |
| 📈 일별 | 날짜별 발송·회신 건수 (막대) + 담당자별 내역. 담당자·기간으로 거르기 |
| 🗂 내 파이프라인 | 개인용 칸반. 내가 접촉한 크리에이터를 단계별로: 첫 발송 → 1차 리마인드 → 2차+ 리마인드 → 1차 회신 → 2차+ 회신. 카드를 누르면 대화가 열린다 |
| 💬 대화 | 담당자별로 회신한 크리에이터 목록 → 누르면 협상 스레드를 본문까지 (아래 참고) |
| 발송 이력 | 시각·수신자·핸들·캠페인·담당자. 담당자/기간/검색어로 거르기 |
| ⏸ 중복 시도 | 보내려다 막힌 시도. 누가 누구에게 시도했고 원래 누가 보냈는지 |
내가 접촉한 크리에이터를 단계별로 칸반으로 보여준다: 첫 발송(회신 대기) → 1차 리마인드 보냄 → 2차+ 리마인드 보냄 → 1차 회신 → 2차+ 회신(협상). 카드를 누르면 그 사람과의 대화가 열린다.
관리자가 담당자 ↔ 크리에이터 대화를 본문까지 열어 본다. 목록은 회신한 크리에이터만 뜨고(협상은 회신이 있어야 성립), 한 명을 누르면 그 담당자 메일함에서 주고받은 메일을 시간순 채팅으로 보여준다 — 담당자가 보낸 것(오른쪽)과 크리에이터 회신(왼쪽) 양쪽 다.
지켜지는 선:
ADMIN_EMAILS 밖의 담당자는 이 경로로도 남의 메일함을 못 본다. 접근 통제는 이 하나다.mailparser 를 쓴다.GET /api/thread?staff=<담당자>&peer=<크리에이터> (관리자 전용).서버리스에는 상주 프로세스가 없어 회신이 자동으로 세어지지 않는다. 담당자별 화면의 [📥 회신 수집] 을 누르면 전원의 받은편지함을 훑어 우리가 보낸 적 있는 주소에서 온 메일만 기록한다. 화면을 열 때마다 IMAP 을 뒤지면 담당자가 10명일 때 못 견디므로, 세어서 남겨 두고 집계는 그 기록만 읽는다.
messageId 로 판별) — 여러 번 눌러도 안전하다POST /api/replies 를 부르면 된다기록의 시각은 UTC 다. 한국에서 아침 8시에 보낸 건 UTC 로는 전날 23시라,
그대로 날짜를 자르면 하루씩 밀린다. 그래서 브라우저가 tz(분 단위 시차)를 보내고
서버가 그만큼 옮겨서 자른다. 발송이 없던 날도 0 으로 채운다 — 빈 날을 빼면
막대가 다닥다닥 붙어 "매일 꾸준히 보낸 것" 처럼 보이기 때문이다.
중복 시도 탭이 이 화면의 핵심이다. 같은 크리에이터가 자주 걸린다면 명단 배분을 고쳐야 한다는 신호이고, 그 신호는 다른 곳에서는 보이지 않는다.
발송 이력은 최대 10만 건(HISTORY_LOG_MAX)까지 보관한다. 담당자가 10명이면
몇 달치가 쉽게 만 단위를 넘는데, 집계(담당자별·일별·요약)는 저장된 전부를 읽어 센다.
저장소에서 값을 내려받을 때는 큰 리스트가 REST 응답을 터뜨리지 않도록 청크로 나눠
받고, 실제 쌓인 만큼만(LLEN) 읽어 멈춘다. 발송 이력·중복·회신 목록 탭은 화면 표시만
개수로 끊고(limit, 기본 1,000), 상한에 걸리면 화면에 "잘렸음" 을 알린다 —
집계 숫자는 목록 표시 개수와 무관하게 항상 전체 기준이다.
ADMIN_EMAILS 가 비어 있으면 아무도 관리자가 아니다(화면이 열리지 않는다).
관리자 판정을 코드에 숨겨 두면 나중에 이 배포를 보는 사람이 권한 범위를 알 수 없기 때문에,
일부러 "설정하지 않으면 닫힘" 으로 만들었다.
담당자별 화면의 [🔄 메일함에서 동기화] 하나로 두 가지를 채운다.
2026-05-01 이후 보낸 메일을 이력에 넣는다발송에 쓰는 그 외부 앱 비밀번호가 IMAP 에도 그대로 통한다. 새로 등록할 것은 없다.
envelope 만 fetch)messageId 로 판별). 여러 번 눌러도 안전하다Sent / 보낸메일함 …) IMAP 의 용도 플래그(\Sent)
로 찾고, 없을 때만 이름으로 추측한다 — 이름 목록에 의존하면 어느 계정에서 조용히 빈 목록이 된다그대로 밀어 넣으면 동료·거래처 주소가 크리에이터로 잡히고, 그 주소로는 앞으로 발송이 막힌다.
그래서 사내 도메인(NW_DOMAIN) 수신자와 수신자 6명 이상(공지·회람)은 자동으로 거른다.
그래도 섞이는 게 있으면 POST /api/backfill 에 subject 를 주어 좁힐 수 있다.
⚠️ 관리자센터에서 IMAP/POP3 사용 허용이 켜져 있어야 한다 (SMTP 와 같은 설정 화면).
vercel.json 의 crons 가 15분마다 /api/cron 을 부른다 (Pro 플랜 기준 — Hobby 는
하루 1회로 제한되므로 그때는 0 22 * * * 같은 하루 1회 스케줄로 바꿔야 한다).
매 실행은 ① 예약 발송(기한이 된 것 — 시각 약속이라 가장 먼저) ② 메일함 동기화
③ 자동 리마인드 순서로 처리한다.
CRON_SECRET 을 설정하면 그 값을 가진 요청만 부를 수 있다 (Vercel 이 자동으로 실어 보낸다).
로그인한 관리자도 /api/cron 을 직접 열어 상태를 확인할 수 있다발송 화면 상단 [🔎 중복 검사] 탭에서 크리에이터 핸들 또는 이메일을 넣으면, 전체 담당자 (관리자 포함) 의 발송 이력에서 그 사람에게 보낸 적이 있는지 · 누가 · 언제 보냈는지를 찾아 준다.
api/lookup.js 가 공용 발송 로그(+회신 로그)를 훑어 그 크리에이터에게 간 모든 발송을 모은다
(예약 키와 달리 "누가누가 보냈나" 를 전부 보여준다). 관리자 발송도 빼지 않는다.발송 화면에서 [나중에 보내기] 를 켜면 시간대·날짜·시각을 골라 예약한다. 미국 크리에이터에게는 미국이 활발한 시간, 영국은 영국 시간에 맞춰 보내기 위한 것 — 고른 시간대의 현지 시각 기준이다.
api/schedule.js — Intl 시간대 규칙 사용)send-core.js)라 중복 차단·자동 리마인드·CC·첨부가 똑같이 적용된다발송 화면에서 [회신 없으면 자동 리마인드] 를 켜면, 그 발송에 대해 며칠마다·최대 몇 번 팔로업을 보낼지 담당자가 직접 정한다(기본 3일마다·최대 2번). 회신이 오면 즉시 멈춘다.
outreach:reminders HASH — 담당자+크리에이터 한 쌍당 하나)./api/cron)가 메일함을 훑어 회신을 갱신한 뒤 기한이 된 예약을 처리한다.
· 회신했으면 → 멈춤 · 정한 횟수를 다 채웠으면 → 멈춤 · 담당자가 명단에서 빠졌으면 → 멈춤Re:). 긴 제안을 다시 보내지
않는다 — 짧은 넛지가 회신율이 더 높고 스팸처럼 보이지 않는다. 헤더 로고·서명·"no thanks 면 그만"
안내는 그대로 붙는다.크론이 15분마다 돌므로 "3일마다" 는 예약 시각 이후 첫 크론(최대 15분 안)에 나간다. Hobby 플랜으로 내리면 크론이 하루 1회라 리마인드도 하루 한 번만 처리된다.
POP3/IMAP은 메일을 받는 프로토콜이고, 프로그램이 보내려면 SMTP 인증이 필요하다. Gmail 연동·Gmail API는 불필요 — 웍스 SMTP 위에 한 겹 더 얹는 것이라 단계만 늘어난다.
| 항목 | 값 |
|---|---|
| SMTP 서버 | smtp.worksmobile.com |
| 포트 | 465 (SSL) — 587(STARTTLS)도 지원 |
| 아이디 | 네이버웍스 이메일 전체 (예: [email protected]) |
| 비밀번호 | 로그인 비번 ❌ → 외부 앱 비밀번호 ⭕ |
설정 > 보안 > 외부 앱 비밀번호 에서 발급NW_ACCOUNTS 에 등록 → 담당자는 아이디/비번으로 로그인만 하면 끝
(로그인을 안 쓰는 배포라면 화면 오른쪽 위 [내 발신 계정] 에 직접 입력)계정당 24시간 20,000 수신자, 분당 60회. To/Cc/Bcc 모두 카운트되고 PC웹·모바일·SMTP·API 가 합산된다. 초과 시 각각 최대 1시간 / 1분 차단.
사람마다 환경변수를 따로 두면 입사할 때마다 Vercel 설정 + 재배포가 필요해 확장이 안 된다.
그래서 NW_ACCOUNTS 한 개에 줄만 추가하는 방식을 쓴다 (위 담당자별 로그인).
Reply-To 동일)오픈 릴레이 방지 3겹 — ① SMTP 호스트/포트는 서버 고정(요청으로 변경 불가)
② 발신자는 로그인 세션에서 확정 (BYO 배포에서는 NW_DOMAIN 회사 도메인만 허용)
③ 로그인 없이는 발송 API 자체가 401
크리에이터는 대부분 폰으로 메일을 연다. 그래서 폭을 두 겹으로 맞춘다.
width:100% + max-width:600px 다.
예전에는 width="600" 이 박혀 있어서 좁은 화면에서 잘렸다.
표의 width 속성은 max-width 를 모르는 클라이언트에서 그대로 이겨 버린다.Outlook 데스크톱(Word 엔진)은 max-width 를 무시해 본문이 창 너비만큼 늘어난다.
그래서 MSO 전용 주석으로 600px 고정 표를 하나 더 감쌌다(고스트 테이블).
검증: 320 / 360 / 393 / 768 / 1280px 에서 가로 넘침 0, <style> 를 통째로 지운 상태에서도 320px 통과.
화면 오른쪽 미리보기 위의 [📱 모바일] 을 누르면 390px 폭으로 좁혀 보인다. 메일 안의 미디어 쿼리가 iframe 폭에 반응하므로 실제 폰에서 보이는 그대로다.
헤더(달바 로고·캠페인 배지·제목) → 인사 → 브랜드 소개 → 개인화 한 줄 → 💰금액 / 📈커미션 → 📦협업 상세 → 🧴제품(이미지·설명·링크) → 🔥바이럴 영상 최대 4개 → 📋Next steps → CTA 버튼 → 서명
브랜드 컬러 — 헤더는 배너와 같은 노랑→크림 그라데이션에 검정 로고다. 그라데이션을 못 읽는 클라이언트(Outlook 등)는 단색 노랑으로 떨어져도 브랜드에 맞다. 본문은 흰 카드 + 따뜻한 크림 박스, 강조는 앰버, CTA 는 검정 버튼.
로고 — 기본은 실제 로고를 본뜬 세리프 d'Alba + piedmont **워드마크(텍스트)**라
어떤 클라이언트에서도 반드시 보인다.
실제 이미지 로고는 레포의 logo-black.png(검정 워드마크·투명배경)를 쓴다.
메일 안에 인라인(cid)으로 박아 넣는다 — 외부 URL 로 불러오면 배포 보호·리다이렉트·
전파 지연 등으로 그 요청이 실패해 로고가 깨질 때가 있는데, 파일을 메일에 담으면
외부 요청이 아예 없어 깨지지 않는다(제품 이미지 첨부와 같은 방식). 미리보기도 같은
파일을 data URL 로 그려 외부 요청이 없다. 이미지를 아예 꺼둔 수신자에게는 alt 로
브랜드명이 뜬다. 다른 도메인·CDN 을 쓰려면 LOGO_URL 에 공개 절대주소를 넣으면
인라인 대신 그 URL 을 쓴다.
협업 유형 3종: 유상 / 어필리에이트 / 유상+어필리에이트(합산 문구 자동 추가). 후불제 — "선불(up front)" 이 아니라 "콘텐츠 게시·승인 후 지급" 으로 문구를 맞췄다
제품 이미지 — URL 직링크를 넣거나, 상품이 TikTok Shop 목록에 없으면 파일을 첨부한다. 첨부한 이미지는 브라우저에서 최대 1000px 로 줄여 JPEG 로 다시 인코딩(요청 본문·발송 속도 보호)한 뒤 메일에 인라인(cid) 첨부로 담긴다 — 외부 링크가 아니라 메일 안에 들어가므로 링크 없이도 보인다. URL 과 첨부가 둘 다 있으면 첨부가 우선
Next steps — 크리에이터가 답장에 무엇을 담아야 하는지 번호로 제시. 4줄까지 화면에서 편집하고 빈 칸은 자동으로 빠진다. 전부 비우면 섹션째 사라짐. 기본값은 ①관심 회신 ②WhatsApp/선호 연락처
개인화 변수 — 제목·본문 어디서나
{{name}} {{handle}} {{brand}} {{campaign}} {{product}} {{amount}} {{commission}}
<table> + 인라인 CSS — Gmail·네이버웍스 웹에서 레이아웃이 깨지지 않음.
플레인 텍스트 대체본을 함께 만들어 스팸 점수를 낮춘다
통화는 기본 USD. KRW·GBP·EUR·JPY 선택 가능
(통화 기호 대신 USD 500 처럼 코드로 표기 — ₩ 의 가로 이중선이 숫자에 취소선처럼 겹쳐 보임)
제품 칸 위의 [TikTok Shop에서 선택] 버튼을 누르면 썸네일 그리드가 뜨고, 상품을 클릭하면 제품명 · 이미지 URL · 제품 링크가 한 번에 채워진다.
ACTIVATE)만 기본 표시. 체크박스로 전체 보기 전환.webp → .jpeg 로 바꿔 내보낸다 (Outlook 등이 webp 를 렌더링하지 못함)| 이름 | 어디서 |
|---|---|
TTS_APP_KEY | Partner Center → Manage apps → App key |
TTS_APP_SECRET | 같은 화면 → App secret |
TTS_SHOP_CIPHER | 샵별 식별자 |
TTS_REFRESH_TOKEN | 샵 Authorize 후 /api/v2/token/get 으로 발급 |
액세스 토큰은 저장하지 않는다. 서버리스라 디스크에 쓸 수 없으므로 refresh token 으로
필요할 때 발급받아 메모리에만 두고, 만료 5분 전 선제 갱신 + 105002 응답 시 1회 재시도한다.
⚠️ refresh token 을
dalba-check와 공유하면, TikTok 이 갱신 때마다 새 refresh token 을 발급하는 정책일 경우 한쪽이 갱신했을 때 다른 쪽이 만료될 수 있다. 인증 오류가 반복되면 앱을 하나 더 만들어 토큰을 분리하는 것이 정석이다.
GET /api/products?debug=1 — 원본 응답의 필드 구조를 함께 반환GET /api/products?all=1 — 판매중이 아닌 상품까지 포함GET /api/products?id=<상품ID> — 그 상품 하나만 (목록을 거치지 않는다)GET /api/products?fresh=1 — 10분 캐시 무시⚠️ 페이지네이션 주의 —
products/search의page_token은 쿼리 파라미터다. 본문에 넣으면 API 가 조용히 무시하고 매번 1페이지를 돌려주므로, 같은 상품이 두 번 나오고 100번째 뒤 상품은 영영 안 보인다. 그래서 응답의total이 실제 상품 수와 맞는지가 이 엔드포인트의 첫 번째 점검 항목이다 (id기준 중복 제거도 함께 걸어 뒀다).
왜 CRUVA 가 아니라 TikTok Shop 원본 API 인가: CRUVA 응답에는 상품 이미지 필드가 없고, 상태도 문서화되지 않은 숫자 코드(1/3/4/6)로만 온다. 상품 페이지에서 og:image 를 긁는 우회로는 연속 요청 시 CAPTCHA 가 떠서 쓸 수 없다. 원본 API 는
main_images와 문자열status를 함께 주므로 두 문제가 한 번에 풀린다.
상품을 고르면 그 제품ID(pid)로 실제 매출이 난 영상이 매출순 드롭다운으로 뜬다.
누르면 바이럴 영상 칸에 URL 과 @핸들 · GMV $12,345 라벨이 함께 채워진다.
출처는 TikTok Shop API 가 아니라 구글시트다 — 그 집계는 이미
영상성과_신API테스트 탭에 매일 쌓이고 있어서, 같은 API 를 두 번 부를 이유가 없다.
읽는 열은 F(핸들) · H(GMV) · M(영상ID) · O(제품ID) 네 개뿐이다.
F:O 범위만 받아온다 (통째로 받으면 함수 시간·메모리를 넘긴다)| 이름 | 기본값 / 설명 |
|---|---|
GOOGLE_SERVICE_ACCOUNT | 서비스 계정 JSON 통째로. 시트를 그 계정 이메일에 뷰어로 공유해야 한다 |
SHEET_ID | 1_qkd6LZ1wFoihhJSuYdabQ4iRbx-jsFYVxeGIoEb-_g |
SHEET_TAB | 영상성과_신API테스트 |
SHEET_CSV_URL | (대안) 웹에 게시한 CSV 주소 |
⚠️
SHEET_CSV_URL은 설정이 제일 쉽지만 주소를 아는 사람은 누구나 볼 수 있다. 크리에이터 핸들과 매출이 담긴 표라서, 시트를 비공개로 두는 서비스 계정 쪽을 권장한다. 서비스 계정 JSON 에는 개인키가 들어 있다 — Vercel 환경변수에만 넣고 레포에 커밋하지 말 것.
GET /api/videos?probe=1 로 설정 여부만 먼저 확인할 수 있다.
헤더 오른쪽 KO / EN / VI 버튼으로 바꾼다. 선택은 브라우저에 저장되고, 처음 여는 사람은 브라우저 언어를 따라 자동으로 맞춰진다.
번역은 i18n.js 가 그려진 뒤의 DOM 을 훑어 바꾸는 방식이다. 화면을 그리는 코드는
하나도 건드리지 않았고, MutationObserver 가 붙어 있어 나중에 추가되는 문구도 자동으로 잡힌다.
사전의 키는 한국어 원문 그대로다 — 번역이 없으면 한국어가 그대로 남지, 화면이 비지는 않는다.
주의할 점 세 가지 (전부 실제로 밟았던 함정):
{n} {e} 로 가려 두고 번역 후 되돌린다. 안 그러면 사전 키가 값마다 달라진다setAttribute 는 값이 같아도 변경 기록을 남긴다 → 옵저버가 다시 돌아 무한 루프.
모든 쓰기 앞에 같은 값인지 비교를 건다.langs). 안 그러면 EN 화면에서 "한국어" 버튼이 사라진다메일 본문은 번역되지 않는다. 화면 UI 만이다. 크리에이터에게 나가는 메일 문구는 담당자가 직접 쓰는 값이라 자동 번역 대상이 아니다.
폼에 입력한 캠페인·제품·영상·Next steps 는 그 사람 브라우저에 자동 저장되어 다음에 열면 그대로 남아 있다. 한 번만 세팅하면 이후엔 크리에이터 정보만 넣으면 된다.
반면 수신자별 값(이름·핸들·이메일·개인화 한 줄)은 일부러 저장하지 않는다 — 앞사람 내용이 다음 사람에게 잘못 나가는 것이 이 툴에서 가장 위험한 사고이기 때문.
구글시트에서 헤더 포함 복사 → 붙여넣기. 탭(시트)·콤마(CSV) 모두 인식.
| 인식 열 | 의미 |
|---|---|
email (필수) | 수신 주소 |
name · handle | 이름 / 틱톡 핸들 |
amount · commission | 있으면 공통 조건보다 우선 (사람마다 다른 단가) |
whyYou | 개인화 한 줄 |
E-Mail, Email Address, Creator Name, TikTok Handle, 한글 헤더 등)| 요청 | 동작 |
|---|---|
GET /api/login | 이 배포의 인증 방식(accounts / byo)·세션 기간·등록 인원 수. 아이디 목록은 알려주지 않음 |
POST /api/login | { id, pw } → { token, user }. 실패 메시지는 아이디·비번을 구분하지 않음 |
GET /api/outreach-send | 설정 상태 조회 (SMTP 호스트·허용 도메인·배치 상한·로그인한 사람). 비밀번호류는 반환하지 않음 |
POST /api/outreach-send | 발송. { campaign, recipients[] } — 로그인 방식에서는 auth 불필요 |
POST + force: true | 이미 보낸 사람에게도 발송 (중복 차단 무시). 관리자만 유효 — 아니면 서버가 무시 |
POST + dryRun: true | 발송 없이 렌더 결과(제목·HTML)만 반환 |
GET /api/history | 중복 차단이 켜져 있는지 · 차단 기간 |
GET /api/history?recent=200 | 최근 발송 기록 (누가·언제·누구에게) |
POST /api/history | { recipients[] } 중 겹치는 사람 미리 확인 |
GET /api/admin | 담당자별 집계 (관리자 전용) |
POST /api/replies | 받은편지함을 훑어 회신 기록. all:true 는 관리자만(전원) |
GET /api/admin?view=replies | 회신 기록 (담당자·검색어로 거르기) |
GET /api/admin?view=daily&tz=-540 | 일별 발송 건수. tz 는 브라우저 시차(분) — 없으면 UTC 기준이라 날짜가 밀린다 |
GET /api/admin?view=people|sent|blocked | 크리에이터별 / 발송 이력 / 중복 시도. by q days limit 로 거르기 |
POST /api/backfill | 보낸편지함 → 발송 이력. since 기본 2026-05-01, dryRun:true(기본)면 미리보기만 |
GET /api/products | 판매중 상품 목록 (썸네일·가격·재고) |
GET /api/products?probe=1 | 자격증명 설정 여부만 확인 (상품 조회 안 함) |
GET /api/videos?pid=<상품ID> | 그 제품의 매출 상위 영상 (핸들·GMV·영상ID·URL). limit 기본 20 |
GET /api/videos?probe=1 |
/api/login 을 뺀 나머지는 인증이 필요하다 —
로그인 방식은 Authorization: Bearer <token>, BYO 방식은 x-dashboard-password: <비번>.
발송 화면의 ✉️ 발송은 d'Alba(노랑), 🌿 Veganery 발송은 비거너리 by d'Alba(연두~초록) 메일이다.
logo-black.png ↔ logo-veganery.png)와 색상이 바뀐다 — 미리보기·실제 발송·자동 리마인드·예약 발송 모두 같은 테마email-template.js 의 THEMES, 로고는 logos.js 한 곳에서 관리한다 (새 브랜드도 같은 방식으로 추가)실제 네이버웍스·Upstash 에 붙지 않고, 가짜 저장소(메모리)와 가짜 메일 서버로 돈다. 비밀값도 필요 없다.
npm test # 서버 테스트 (test/*.test.js) — 설치 없이 바로 돈다
npm run test:ui # 화면 테스트 (test/ui/*.test.mjs) — playwright + chromium 필요
npm install --no-save playwright && npx playwright install chromium 뒤에 돌린다.
Chromium 을 따로 깔아 뒀으면 CHROMIUM_PATH=<경로> 로 지정할 수 있다..github/workflows/test.yml). 실패하면 GitHub 의 Actions 탭에 빨간 X 가 뜬다..vercelignore).매출·광고·회신 트래커 대시보드는 별도 레포(dalba-email)와 별도 Vercel 프로젝트에 있다.
헤더에 바로가기를 띄우려면 index.html 상단의 DASHBOARD_SITE 상수에 그 사이트 주소를 넣으면 된다.
GET /api/mailbox?ping=1 로 연결부터 확인할 것.| 시트 접근이 설정돼 있는지만 확인 |