@teamsparta/cross-platform-logger
Version:
```typescript import * as CPL from "@teamsparta/cross-platform-logger";
99 lines (93 loc) • 4.74 kB
TypeScript
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
declare const UTM_KEYS: readonly ["utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"];
type UtmKey = (typeof UTM_KEYS)[number];
type UtmRecord = Partial<Record<UtmKey, string>>;
type ConsumerMiddleware = (req: NextRequest, event?: NextFetchEvent) => NextResponse | void | undefined | null | Promise<NextResponse | void | undefined | null>;
/**
* utm-collector 백엔드로 보내는 페이로드.
*
* Lambda(`utm-collector`)의 입력 스키마와 정확히 매칭된다.
* 스키마가 바뀌면 양쪽을 함께 갱신해야 한다.
*/
type UtmCapturePayload = {
userId?: string;
anonId: string;
utm: UtmRecord;
url: string;
referrer?: string;
capturedAt: string;
};
type WithUtmCaptureOptions = {
/**
* 백엔드 인증 토큰 (필수).
*
* 컨슈머가 자신의 시크릿 관리 방식대로 주입한다 — `process.env.XXX`,
* secret manager 등. SDK는 토큰을 어디서 가져오는지 알지 못한다.
*
* 클라이언트 번들에 절대 노출되어선 안 된다 — server-only env 또는 server-only
* 시크릿으로 관리할 것 (`NEXT_PUBLIC_` 접두사 금지).
*/
token: string;
/**
* 인증된 user_id 추출. 미인증 요청은 anonId만 사용.
* 동기 또는 비동기 모두 지원.
*/
getUserId?: (req: NextRequest) => string | undefined | Promise<string | undefined>;
/** 쿠키 도메인. default '.spartaclub.kr' */
cookieDomain?: string;
/** anon id 쿠키 이름. default 'sparta_anon_id' */
anonCookieName?: string;
/** anon id 쿠키 만료 (초). default 180일 */
anonCookieMaxAge?: number;
};
/**
* 미들웨어를 감싸서 UTM 캡처를 추가하는 HOF.
*
* 컨슈머의 미들웨어 결과(NextResponse / redirect / rewrite)를 그대로 보존하면서
* anon 쿠키 set과 백엔드 영속화만 얹는다.
*
* - 컨슈머 미들웨어가 throw하면 그대로 throw (의도된 흐름 보장).
* - UTM 캡처 단계 에러는 절대 throw하지 않음 — stderr 로그만 남기고 응답은 정상 반환.
*
* 컨슈머가 NextResponse를 반환하지 않으면 (void/null) `NextResponse.next()`로 fallback.
* 기존 미들웨어가 없는 경우 `() => NextResponse.next()`를 넘기면 된다.
*
* 백엔드 호출은 `event.waitUntil`로 백그라운드에서 실행되므로 응답 TTFB에 영향이 없다.
* `event`가 주입되지 않은 환경(테스트 등)에서는 await fallback으로 동작한다.
* anon 쿠키 set은 첫 await 이전에 동기로 res에 박히므로, 백그라운드 실행이어도
* 응답에 함께 실려나간다.
*
* 백엔드 endpoint는 SDK 내부에 hardcode되어 있다 — 컨슈머는 Lambda의 존재를 모른다.
* 인증 토큰은 `opts.token`으로 주입한다 — 컨슈머가 자신의 시크릿 관리 방식대로
* (`process.env.XXX`, secret manager 등) 결정한다.
*
* 토큰은 server-only로 관리할 것 — 클라이언트 번들에 노출되어선 안 된다.
* Edge/Node 어느 런타임에서도 동작한다 — fetch + Web Crypto만 쓰므로 Node 전용 API 의존 없음.
*/
declare function withUtmCapture(middleware: ConsumerMiddleware, opts: WithUtmCaptureOptions): (req: NextRequest, event?: NextFetchEvent) => Promise<NextResponse>;
type WithUtmCookieOptions = {
/** 쿠키 도메인. default '.spartaclub.kr' */
cookieDomain?: string;
/**
* utm 쿠키 이름.
* default 'utm_last' (브라우저 SDK의 setUTMCookies와 호환).
*/
utmCookieName?: string;
/** utm 쿠키 만료 (초). default 7일 */
utmCookieMaxAge?: number;
};
/**
* 미들웨어를 감싸 UTM 쿠키 SSR 발급을 추가하는 HOF.
*
* query string에 utm이 있을 때 1st-party 쿠키로 발급한다.
* 백엔드 영속화나 식별자 발급은 하지 않는다.
*
* 기존 미들웨어가 없는 경우 `() => NextResponse.next()`를 넘기면 된다.
*
* 브라우저 측 `setUTMCookies`도 동일한 `utm_last` 쿠키를 발급한다.
* SSR 응답에서 1차 발급(이 함수) → hydration 후 클라 SDK가 동일 키로 재발급.
* 두 발급 경로 모두 raw JSON value를 사용해 정합성을 유지한다 — 백엔드가
* 어느 쪽 발급의 쿠키를 받든 cookie-parser → JSON.parse로 동일하게 처리된다.
*/
declare function withUtmCookie(middleware: ConsumerMiddleware, opts?: WithUtmCookieOptions): (req: NextRequest, event?: NextFetchEvent) => Promise<NextResponse>;
export { type UtmCapturePayload, type UtmRecord, type WithUtmCaptureOptions, type WithUtmCookieOptions, withUtmCapture, withUtmCookie };