generated from ttipo/project-base
feat: 업체 링크 추적을 실제 아임웹 Header Code 로 합치고 신청 유입정보 누락 수정
- imweb-header-top.html·imweb-header.html: 아임웹에 실제로 들어가 있는 Header Code 원본을 저장소에 둔다 - 업체 링크 추적을 imweb-header-top.html 의 UTM 스크립트 아래로 옮기고 partner-ref.html 은 삭제 (UTM 스크립트처럼 localStorage + .moneyguider.co.kr 쿠키에 저장 — www ↔ apex 이동·인앱브라우저 대비) - 신청 완료 위젯: MG_UTM.peek() 는 객체라 문자열 검사에 걸려 유입정보가 빠지고 있었다 — peek().value 를 보낸다 - 단축 링크 리다이렉트에 Referrer-Policy strict-origin-when-cross-origin (helmet 기본 no-referrer 때문에 업체가 링크를 올린 곳이 지워져 유입원이 direct 로 잡혔다) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -47,7 +47,7 @@ export class MemberApplication {
|
||||
@Column({ name: 'kakao_clicked_at', type: 'timestamptz', nullable: true })
|
||||
kakaoClickedAt!: Date | null;
|
||||
|
||||
/** 신청 순간의 유입정보 원본 (header-top.html 이 저장해 둔 값, 파이프 형식) */
|
||||
/** 신청 순간의 유입정보 원본 (imweb-header-top.html 이 저장해 둔 값, 파이프 형식) */
|
||||
@Column({ name: 'inflow_raw', type: 'text', nullable: true })
|
||||
inflowRaw!: string | null;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { CLICK_REF_PATTERN } from '@/shared/utils/partner-link';
|
||||
|
||||
const MEMBER_ID_MAX_LENGTH = 255;
|
||||
const PRODUCT_NAME_MAX_LENGTH = 200;
|
||||
/** header-top.html 이 만드는 값은 200자 남짓 — 넉넉히 두되 무한정 받지 않는다 */
|
||||
/** imweb-header-top.html 이 만드는 값은 200자 남짓 — 넉넉히 두되 무한정 받지 않는다 */
|
||||
const INFLOW_RAW_MAX_LENGTH = 1000;
|
||||
|
||||
/** 신청 완료 화면이 보내는 신청 기록 */
|
||||
@@ -44,7 +44,7 @@ export class CreateApplicationDto {
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
'신청 순간의 유입정보 (header-top.html 형식: src=..|med=..|cmp=..)',
|
||||
'신청 순간의 유입정보 (imweb-header-top.html 형식: src=..|med=..|cmp=..)',
|
||||
example: 'src=facebook|med=paid|cmp=fall|jd=2026-10-05',
|
||||
})
|
||||
@IsOptional()
|
||||
@@ -54,7 +54,7 @@ export class CreateApplicationDto {
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
'브라우저가 보관한 업체 링크 클릭 ID 목록 (imweb/partner-ref.html 의 MG_PARTNER.refs())',
|
||||
'브라우저가 보관한 업체 링크 클릭 ID 목록 (imweb/imweb-header-top.html 의 MG_PARTNER.refs())',
|
||||
example: ['AbCd1234EfGh5678'],
|
||||
type: [String],
|
||||
})
|
||||
|
||||
@@ -39,7 +39,7 @@ export const IMWEB_CALLBACK_PATH = '/imweb/oauth/callback';
|
||||
|
||||
const DEFAULT_SYNC_INTERVAL_MINUTES = 60;
|
||||
const DEFAULT_PAGE_SIZE = 100;
|
||||
/** header-top.html 이 홈페이지 항목에 쓰는 가짜 호스트 ('https://utm.' + OWN_HOST) */
|
||||
/** imweb-header-top.html 이 홈페이지 항목에 쓰는 가짜 호스트 ('https://utm.' + OWN_HOST) */
|
||||
export const DEFAULT_TRACKING_HOST = 'utm.moneyguider.co.kr';
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,10 @@ import { CreatePartnerTouchDto } from './dto/partner-touch.dto';
|
||||
import { PartnerAttributionService } from './partner-attribution.service';
|
||||
import { PartnerLinksService, SHORT_LINK_PATH } from './partner-links.service';
|
||||
|
||||
const REFERRER_POLICY_HEADER = 'Referrer-Policy';
|
||||
/** 브라우저 기본값 — 다른 사이트로는 출처(도메인)만 보낸다 */
|
||||
const REDIRECT_REFERRER_POLICY = 'strict-origin-when-cross-origin';
|
||||
|
||||
/**
|
||||
* 업체 단축 링크 — moneyguide.wandergo.net/r/{코드} (main.ts 에서 /api prefix 제외).
|
||||
*
|
||||
@@ -46,11 +50,14 @@ export class PartnerRedirectController {
|
||||
);
|
||||
// 매 클릭을 서버가 받아야 클릭 ID 가 새로 발급된다 — 브라우저·프록시가 저장하지 않게 한다
|
||||
res.setHeader('Cache-Control', 'no-store');
|
||||
// helmet 기본값(no-referrer)이 리다이렉트에 붙으면 업체가 링크를 올린 곳(블로그 등)이 지워져
|
||||
// 상품 페이지의 UTM 스크립트가 유입원을 direct 로 기록한다 — 출처(도메인)만은 넘긴다
|
||||
res.setHeader(REFERRER_POLICY_HEADER, REDIRECT_REFERRER_POLICY);
|
||||
res.redirect(HttpStatus.FOUND, target);
|
||||
}
|
||||
}
|
||||
|
||||
/** 아임웹 Header Code 스크립트(imweb/partner-ref.html)가 회원 ↔ 링크 클릭 연결을 보낸다 */
|
||||
/** 아임웹 Header Code(imweb/imweb-header-top.html 의 업체 링크 추적)가 회원 ↔ 링크 클릭 연결을 보낸다 */
|
||||
@ApiTags('Partner Touches')
|
||||
@Controller('partner-touches')
|
||||
export class PartnerTouchesController {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { InflowStatus, parseInflow } from './inflow';
|
||||
|
||||
const HOST = 'utm.moneyguider.co.kr';
|
||||
|
||||
/** header-top.html 의 serialize(asUrl) 와 같은 방식으로 홈페이지 값을 만든다 */
|
||||
/** imweb-header-top.html 의 serialize(asUrl) 와 같은 방식으로 홈페이지 값을 만든다 */
|
||||
function homePage(params: Record<string, string>): string {
|
||||
const query = Object.entries(params)
|
||||
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 회원 유입경로 파싱 유틸 (순수 함수).
|
||||
*
|
||||
* 아임웹 Header Code 의 UTM 스크립트(header-top.html)가 회원가입 폼의 '홈페이지' 항목에
|
||||
* 아임웹 Header Code 의 UTM 스크립트(imweb/imweb-header-top.html)가 회원가입 폼의 '홈페이지' 항목에
|
||||
* 아래 형식으로 유입정보를 기록한다. 아임웹 Open API 회원 조회에는 사용자 정의 가입 항목이
|
||||
* 나오지 않고 homePage 만 나오기 때문에 이 값을 풀어서 쓴다.
|
||||
*
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
* 업체 링크 유틸 (순수 함수) — 이동 주소 검증, 클릭 ID 부착, 봇 판별.
|
||||
*
|
||||
* 업체에 주는 단축 링크(/r/{코드})를 누르면 서버가 클릭마다 고유 ID 를 발급해
|
||||
* 상품 페이지 주소에 `mg_ref` 로 붙여 보낸다. 아임웹 Header Code 스크립트(imweb/partner-ref.html)가
|
||||
* 상품 페이지 주소에 `mg_ref` 로 붙여 보낸다. 아임웹 Header Code 스크립트(imweb/imweb-header-top.html 의 업체 링크 추적)가
|
||||
* 이 값을 브라우저에 보관했다가, 로그인한 회원이 확인되면 서버에 알려 회원과 클릭을 잇는다.
|
||||
*/
|
||||
|
||||
/** 상품 페이지 주소에 붙이는 클릭 ID 파라미터 — imweb/partner-ref.html 과 같아야 한다 */
|
||||
/** 상품 페이지 주소에 붙이는 클릭 ID 파라미터 — imweb/imweb-header-top.html 과 같아야 한다 */
|
||||
export const PARTNER_REF_PARAM = 'mg_ref';
|
||||
|
||||
/** 업체 링크 코드 — /r/{코드} */
|
||||
|
||||
Reference in New Issue
Block a user