실제 오픈카톡방 입장은 카카오가 알려주지 않아 추적할 수 없고, 집계하는 값은 신청 완료 화면의 오픈카톡 버튼을 처음 누른 시각(kakao_clicked_at)이다. '입장률'로 표시하면 실제 입장으로 오해하므로 문구를 사실대로 바꾸고 안내를 붙인다. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
아임웹 회원 유입경로 관리자 화면
아임웹 관리자 전용 페이지에 붙여 넣는 화면(imweb-members.html)과 로컬 테스트 도구.
아임웹 관리자 페이지 (코드 위젯) ← 1차 보호: 아임웹 페이지 접근 권한
│ fetch + Authorization 헤더 (토큰은 sessionStorage)
▼
백엔드 /api/admin/* ← 2차 보호: 관리자 로그인 (Authorization: Bearer)
│ 주기적 전체 동기화 (기본 60분)
▼
아임웹 Open API /member-info/members ← homePage 에 UTM 스크립트가 기록한 유입정보
유입정보는 아임웹 Header Code 의 UTM 스크립트(imweb-header-top.html)가 회원가입 폼의
'홈페이지' 항목에 https://utm.moneyguider.co.kr/?src=..&med=..&jd=.. 형식으로 남긴 값이다.
백엔드가 이 값을 풀어서 유입원·매체·캠페인별로 조회·집계한다.
파일 — 아임웹 어디에 붙이나
아임웹에 실제로 들어가 있는 코드의 원본이다. 고친 뒤에는 아임웹에 다시 붙여 넣어야 반영된다.
| 파일 | 아임웹 위치 | 하는 일 |
|---|---|---|
imweb-header-top.html |
Header Code (사이트 전체) | UTM 유입경로 기록(MG_UTM) + 업체 링크 클릭 보관(MG_PARTNER) |
imweb-header.html |
Header Code (사이트 전체) | 로그인 링크의 back_url 에 UTM 을 붙여 로그인 후에도 유지 |
imweb-free-apply.html |
/apply 페이지 HTML 위젯 |
신청 완료 화면 + 신청 기록·오픈카톡 클릭 전송 |
imweb-members.html |
관리자 전용 페이지 코드 위젯 | 관리자 대시보드 |
serve-local.js |
— | 로컬 테스트용 정적 서버 |
로컬에서 띄우기
- 백엔드 env 에 아임웹 앱 키와 관리자 계정 입력 —
backend/.env.developmentIMWEB_CLIENT_ID=... IMWEB_CLIENT_SECRET=... ADMIN_USERNAME=... # 관리자 화면 로그인 아이디 (여러 사람이 함께 쓴다) ADMIN_PASSWORD=... # 12자 이상. 바꾸고 재시작하면 모든 기기가 로그아웃된다 - 아임웹 개발자센터 앱 설정
- redirect URI:
http://localhost:3100/api/imweb/oauth/callback(= 루트BACKEND_API_URL+/imweb/oauth/callback, 정확히 일치해야 한다) - 권한:
사이트 정보 읽기∙쓰기(site-info:write),회원 정보 읽기(member-info:read),상품 읽기(product:read),주문 읽기(order:read)(백엔드가 이 네 가지를 요청한다 — 앱 설정에 하나라도 없으면 연결이 errorCode 30156 으로 거부된다. site-info:write 는 아임웹이 연동 때 필수로 요구한다)
- redirect URI:
- 백엔드 기동·마이그레이션
docker compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.development up -d docker exec moneyguide-backend npm run migration:run - 화면 띄우기 —
node imweb/serve-local.js→ http://localhost:5173/imweb-members.html - 로그인 → 아임웹 연결 → 아임웹 동의 → 돌아오면 첫 동기화가 자동으로 시작된다
과거 회원 유입경로 가져오기 (엑셀)
가입 폼 '홈페이지' 항목에 기록하기 전 가입자의 유입경로는 사용자 정의 항목 '유입경로'에만 있고, 이 항목은 Open API 로 나오지 않는다. 아임웹 관리자 > 회원 > 엑셀 다운로드로 받아 가져온다.
# Git Bash 는 MSYS_NO_PATHCONV=1 필요 (/tmp 경로가 바뀌는 것을 막는다)
docker cp "회원2026_10_05_1.xls" moneyguide-backend:/tmp/members-1.xls
docker exec moneyguide-backend npm run inflow:import -- /tmp/members-1.xls [/tmp/members-2.xls ...]
docker exec -u root moneyguide-backend rm /tmp/members-1.xls
고유키(= API memberCode)로 매칭하고유입경로열만 읽는다. 나머지 열(개인정보)은 쓰지 않는다.- 원본은
imweb_member_inflow_imports에 보관한다. 동기화가 매번 homePage 기준으로 다시 쓰므로 회원 행에 직접 넣으면 지워진다 — homePage 에 유입정보가 없을 때 이 값을 쓴다 (homePage 우선). - 같은 고유키는 덮어쓰므로 다시 실행해도 안전하다. 출력은 개수만 나온다.
- 엑셀 파일은 가져온 뒤 지운다.
- 2026-10-05
회원2026_10_05_1.xls(2026-08-20 ~ 09-07 가입자 2,489명) 가져옴. 유입경로 항목이 생긴 2026-08-20 이전 가입자(약 12,500명)는 복구할 수 없다.
강의 신청 기록 (신청 완료 화면 → 신청 내역)
아임웹 /apply 페이지의 신청 완료 위젯(imweb-free-apply.html)이 표시될 때
POST /api/applications 로 신청을 남긴다. 관리자 화면 신청 내역 메뉴에서 본다.
-
보내는 값:
MEMBER_UID(회원 아이디 또는 회원 코드), 상품번호(idx), 상품명, 최초 유입(MG_UTM.peek().value), 최종 유입(MG_UTM.peek().last를 같은 파이프 형식으로), 업체 링크 클릭 ID(MG_PARTNER.refs()) —peek()는 문자열이 아니라 객체를 돌려준다 -
가입 경로와 신청 유입은 따로 남는다 — 관리자 화면 신청 내역의 '신청 유입 집계'에서 기준을 바꿔 비교한다
기준 값 예) 3주 전 네이버로 가입 → 어제 블로그로 방문 → 오늘 페이스북 광고로 와서 신청 신청 때 (최종) 신청 직전에 마지막으로 들어온 경로 — 신청을 일으킨 유입 facebook 신청 때 (최초) 신청 시점 브라우저에 남아 있던 첫 경로 (UTM 스크립트 보관 2일) blog 가입 경로 그 회원이 가입할 때 기록된 경로 (가입 후 채워진 값은 제외) naver 유입이 한 번뿐이면 최초 = 최종이다. 다른 기기에서 신청하면 신청 유입은 direct 로 남는다.
-
백엔드는 아임웹에 실제로 있는 회원인지 확인하고(동기화 전 신규 가입자는 아임웹 API 로 직접 확인), 회원·상품당 한 건만 남긴다. 이름·연락처는 브라우저 값이 아니라 아임웹 회원 정보를 쓴다
-
오픈카톡 버튼 클릭은
POST /api/applications/{id}/kakao-clicks로 처음 한 번만 남는다 -
공개 API 라 요청 수를 제한한다 (신청 1분 10회, 클릭 1분 20회 — IP 기준)
-
운영 백엔드 CORS_ORIGIN 에 아임웹 도메인(www 유무 둘 다)이 있어야 한다
-
한계: 신청 완료 화면은 공개 페이지라 "실제로 그 회원이 보냈는지"는 확인할 수 없다 (있는 회원인지만 확인). 무료 신청 기록 용도라 이 수준으로 둔다
업체 링크 (업체별 신청·구매 추적)
상품마다 업체에 단축 링크를 발급하고, 그 링크로 들어온 회원의 신청·구매를 업체별로 센다. 관리자 화면 업체 링크 메뉴에서 업체 추가 → 링크 발급 → 실적 확인.
업체에 링크 전달 ── https://www.moneyguider.co.kr/go?c={코드} (아임웹 'go' 페이지)
│ ⓪ Header Code 가 페이지를 그리기 전에 백엔드 단축 링크 moneyguide.wandergo.net/r/{코드} 로 넘긴다
▼
백엔드 단축 링크
│ ① 클릭 기록 (카톡 미리보기·검색봇 제외) 후 상품 페이지로 이동하며 클릭 ID(mg_ref)와
│ 업체 UTM(utm_source=업체 이름, utm_medium=partner, utm_campaign=링크 코드)을 붙인다
│ (이동 주소에 관리자가 직접 넣은 UTM 이 있으면 그것을 쓴다)
▼
상품 페이지 ── imweb-header-top.html(Header Code)의 업체 링크 추적이 클릭 ID 를 30일 보관하고 주소창에서 지운다
│ (UTM 스크립트처럼 localStorage + .moneyguider.co.kr 쿠키 — www ↔ apex 이동에도 유지)
│ ② 로그인 회원이 확인되면 "이 회원이 이 클릭으로 들어왔다"를 POST /api/partner-touches 로 알린다
▼
신청: 신청 완료 위젯이 신청 기록에 클릭 ID 를 함께 보낸다 → 바로 귀속
구매: 서버가 1시간마다 최근 60일 아임웹 주문(회원 주문)을 훑어 결제완료 상품을 귀속, 환불·취소는 집계에서 뺀다
- 귀속 규칙: 신청·구매 직전 30일 안에 누른 그 상품의 업체 링크 중 마지막 것 (마지막 클릭)
- 구매 수: 결제완료 주문만 센다. 전액 환불되거나 해당 상품 품목이 모두 취소·반품되면 빠진다. 금액은 관리하지 않는다
- 링크는 지우지 않고 중지한다 — 중지해도 상품 페이지로 이동은 되지만 클릭·실적은 쌓이지 않는다. 링크가 있는 업체는 삭제할 수 없다 (실적 기록 보존)
- 업체 링크로 온 방문은 UTM 스크립트가 업체 UTM 으로 기록하므로, 신청 때 유입·가입 경로 집계에도 업체 이름이 나온다
- 이동 주소는 아임웹 사이트(www 유무 무관) 안만 허용한다 — 단축 링크가 피싱에 쓰이지 않게.
사이트 주소는
IMWEB_SITE_URL, 비우면IMWEB_ADMIN_PAGE_URL의 origin - 한계
- 링크를 누른 브라우저와 신청·구매한 브라우저가 다르면(휴대폰 → PC) 추적되지 않는다
- 비회원 주문은 회원과 이을 수 없어 세지 않는다
- 공개 페이지에서 오는 기록이라 위조를 완전히 막을 수는 없다 (실제 아임웹 회원·실제 클릭인지만 확인)
- 60일보다 오래된 구매의 환불은 반영되지 않는다
처음 켤 때
- 아임웹 개발자센터 앱 권한에
상품 읽기·주문 읽기추가 (위 '로컬에서 띄우기' 2번) - 백엔드 배포 (마이그레이션 자동 실행)
- 관리자 화면 연동 관리 → 아임웹 연결을 다시 한다 — 새 권한이 담긴 토큰을 받는다.
다시 연결하기 전에는 상품 불러오기·구매 집계가
IMWEB_006(권한 부족)으로 실패한다. 회원 동기화는 그대로 돈다 imweb-header-top.html을 아임웹 Header Code 에 다시 붙여 넣는다 (UTM 스크립트 아래에 업체 링크 추적이 추가됨).imweb-header.html(로그인 링크 UTM 보존)은 바꾸지 않았다imweb-free-apply.html을 신청 완료 위젯에 다시 붙여 넣는다 (신청 기록·클릭 ID 전송 추가됨)- 아임웹에 주소(URL)가
go인 페이지를 만든다 — 메뉴에서 숨기고 전체 공개. 내용은 비워 둬도 된다 (업체에 주는www.moneyguider.co.kr/go?c=코드의 입구. 이 페이지가 없으면 링크가 404 가 된다) - 업체 링크 메뉴에서 상품 불러오기 → 업체 추가 → 링크 발급
인증 방식 — 왜 쿠키가 아닌가
백엔드(moneyguide.wandergo.net)와 관리자 화면(www.moneyguider.co.kr)이 다른 사이트라
인증 쿠키는 제3자 쿠키가 되어 Safari·Chrome 시크릿 모드 등에서 차단된다. 그래서
- 로그인 응답으로 토큰을 받아 sessionStorage 에 두고
Authorization: Bearer로 보낸다 (탭을 닫으면 사라짐) - 관리자 계정은 env 의 단일 계정이다. 여러 사람이 같은 계정으로 각자 로그인하고, 로그아웃은 그 기기만 끊는다. env 비밀번호를 바꾸고 재시작하면 모든 기기가 끊긴다
- 액세스 1시간 / 리프레시 12시간 (
JWT_ACCESS_EXPIRES_IN/JWT_REFRESH_EXPIRES_IN) - 서버가 요청마다 토큰 세대를 확인해 로그아웃하면 발급된 토큰이 즉시 모두 무효가 된다
로컬에서 같은 조건을 재현하려면 화면을 http://127.0.0.1:5173/imweb-members.html 로 연다
(API 는 localhost:3100 — 브라우저는 둘을 다른 사이트로 본다).
연결이 안 될 때
아임웹 연결 버튼을 누른 뒤 아래 같은 JSON 화면이 뜨면 개발자센터의 redirect URI 가 다르다.
{"statusCode":400,"error":{"errorCode":"30098","message":"클라이언트 정보가 올바르지 않습니다.","innerMessage":"Invalid redirect uri"}}
개발자센터 앱 설정의 redirect URI 를 BACKEND_API_URL + /imweb/oauth/callback 과 글자 하나까지 같게 등록한다.
(로컬 http://localhost:3100/api/imweb/oauth/callback, 운영 https://moneyguide.wandergo.net/api/imweb/oauth/callback)
운영 배포 시
- 루트
.env.productionBACKEND_API_URL=https://moneyguide.wandergo.net/apiCORS_ORIGIN=https://www.moneyguider.co.kr,https://moneyguider.co.krIMWEB_ADMIN_PAGE_URL=아임웹 관리자 페이지 실제 주소
- 아임웹 개발자센터 redirect URI 를
https://moneyguide.wandergo.net/api/imweb/oauth/callback으로 등록 imweb-members.html의API_BASE운영 주소(https://moneyguide.wandergo.net/api)를 확인하고 아임웹 코드 위젯에 붙여 넣는다- 아임웹에서 그 페이지의 접근 권한을 관리자로 제한한다
- 업체 링크를 쓰려면 위 '업체 링크 → 처음 켤 때' 순서를 따른다
(
imweb-header-top.html·imweb-free-apply.html의API_BASE도 같은 운영 주소)
유입 기록 상태
| 상태 | 의미 |
|---|---|
가입 시 (at_join) |
회원가입 폼에서 기록 — 가입 경로로 집계한다 |
가입 후 (after_join) |
기록일(jd)이 가입일과 다름 — 회원정보 수정 화면에서 채워진 값이라 가입 경로가 아니다 |
없음 (none) |
스크립트 설치 전 가입, 직접 입력한 홈페이지 주소 등 |