This commit is contained in:
90
examples/simple-js/JS_load.md
Normal file
90
examples/simple-js/JS_load.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# 정적 JS 파일 빌드 및 문제 해결 가이드
|
||||
|
||||
이 문서는 `aptabase.min.js`와 `aptabase.debug.js` 파일을 빌드하고, 빌드 과정에서 발생한 문제들을 해결하는 과정을 기록합니다.
|
||||
|
||||
## 초기화 및 이벤트 전송
|
||||
|
||||
### 기본 사용법
|
||||
|
||||
`trackEvent` 함수를 사용하여 이벤트를 전송할 때, 두 번째 인자로 JavaScript 객체를 전달하여 사용자 정의 속성(`props`)을 함께 보낼 수 있습니다. `props` 객체의 값으로는 문자열, 숫자, 불리언 타입이 가능합니다.
|
||||
|
||||
**예제:**
|
||||
|
||||
```html
|
||||
<script>
|
||||
// Aptabase 초기화
|
||||
window.aptabase.init("YOUR_APP_KEY");
|
||||
|
||||
function track(eventName, props) {
|
||||
// 이벤트와 함께 props 전송
|
||||
window.aptabase.trackEvent(eventName, props);
|
||||
console.log(`Event '${eventName}' sent!`, props ? { props } : '');
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- props 없이 이벤트 전송 -->
|
||||
<button onclick="track('app_started')">Track: App Started</button>
|
||||
|
||||
<!-- props와 함께 이벤트 전송 -->
|
||||
<button onclick="track('feature_a_used', { plan: 'premium', from: 'sidebar' })">Track: Used Feature A</button>
|
||||
<button onclick="track('item_purchased', { item_id: 'SKU-123', price: 9.99, on_sale: true })">Track: Item Purchased</button>
|
||||
```
|
||||
|
||||
### Self-Hosted 환경 초기화
|
||||
|
||||
자체 호스팅(Self-hosting) 환경의 Aptabase를 사용하는 경우, `init` 함수의 두 번째 인자로 `host` 옵션을 반드시 전달해야 합니다. Self-hosted용 App Key는 일반적으로 `SH` 지역 코드를 포함합니다.
|
||||
|
||||
**예제:**
|
||||
```html
|
||||
<script>
|
||||
// Self-Hosted용 App Key와 host 주소를 사용하여 초기화
|
||||
window.aptabase.init("A-SH-YOUR_UNIQUE_ID", {
|
||||
host: "https://your-aptabase-instance.com"
|
||||
});
|
||||
|
||||
function track(eventName, props) {
|
||||
window.aptabase.trackEvent(eventName, props);
|
||||
console.log(`Event '${eventName}' sent!`);
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 빌드 문제 해결 과정
|
||||
|
||||
### 1. `tsup` 빌드 설정 수정
|
||||
|
||||
- **문제**: 초기 `tsup.config.ts` 설정은 `aptabase.min.js`와 `aptabase.debug.js`를 명시적으로 생성하지 않아 minify 과정에서 문제가 발생할 가능성이 있었습니다.
|
||||
- **해결**: `tsup.config.ts` 파일을 수정하여 디버그와 minify 버전을 각각 다른 `define` 설정을 통해 생성하도록 빌드 구성을 분리했습니다.
|
||||
|
||||
### 2. `pnpm` 워크스페이스 설정
|
||||
|
||||
- **문제**: `pnpm`을 사용하는 모노레포 환경에서 `pnpm install`을 실행했음에도 `tsup` 명령어를 찾지 못하는 문제가 발생했습니다.
|
||||
- **원인**: `pnpm-workspace.yaml` 파일이 없어 `pnpm`이 워크스페이스를 올바르게 인식하지 못했습니다.
|
||||
- **해결**: 프로젝트 루트에 `pnpm-workspace.yaml` 파일을 생성하고 `packages`와 `examples` 디렉토리를 워크스페이스로 지정한 후, `pnpm install`을 다시 실행하여 의존성을 올바르게 설치했습니다.
|
||||
|
||||
### 3. 타입 정의(Typings) 문제 해결
|
||||
|
||||
- **문제**: 빌드 중 `Cannot find name 'chrome'` 및 `Cannot find name 'process'`와 같은 타입스크립트 컴파일 오류가 발생했습니다.
|
||||
- **원인**: 브라우저 및 Node.js 환경에서만 제공되는 전역 객체(`chrome`, `process`)에 대한 타입 정의가 없었습니다.
|
||||
- **해결**:
|
||||
1. `@types/chrome`과 `@types/node` 패키지를 개발 의존성으로 설치했습니다.
|
||||
2. `packages/web/tsconfig.json`의 `compilerOptions.types` 배열에 `"chrome"`과 `"node"`를 추가하여 타입스크립트가 해당 타입들을 인식하도록 설정했습니다.
|
||||
|
||||
### 4. `process` is not defined` 런타임 오류 해결
|
||||
|
||||
- **문제**: 빌드된 `aptabase.min.js` 파일을 브라우저에서 실행할 때 `ReferenceError: process is not defined` 런타임 오류가 발생했습니다.
|
||||
- **원인**: 소스코드(`shared.ts`, `index.ts`)에서 `process.env.NODE_ENV`와 `process.env.PKG_VERSION`을 참조하고 있었고, 이 코드가 브라우저 환경에서 실행<EC8BA4><ED9689>면서 `process` 객체를 찾지 못해 오류가 발생했습니다.
|
||||
- **해결**: `tsup.config.ts`의 `define` 옵션을 사용하여 빌드 시점에 해당 환경 변수들을 실제 값으로 교체하도록 설정했습니다.
|
||||
- `aptabase.min.js` (프로덕션용): `process.env.NODE_ENV`를 `'production'`으로 설정
|
||||
- `aptabase.debug.js` (디버그용): `process.env.NODE_ENV`를 `'development'`로 설정
|
||||
|
||||
## 최종 빌드 방법
|
||||
|
||||
위의 모든 문제 해결 후, 다음 명령어를 사용하여 성공적으로 `aptabase.min.js`와 `aptabase.debug.js` 파일을 빌드할 수 있습니다.
|
||||
|
||||
```bash
|
||||
# 프로젝트 루트 디렉토리에서 실행
|
||||
pnpm --filter @aptabase/web build
|
||||
```
|
||||
175
examples/simple-js/aptabase.debug.js
Normal file
175
examples/simple-js/aptabase.debug.js
Normal file
@@ -0,0 +1,175 @@
|
||||
"use strict";
|
||||
var aptabase = (() => {
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/index.ts
|
||||
var src_exports = {};
|
||||
__export(src_exports, {
|
||||
init: () => init,
|
||||
trackEvent: () => trackEvent
|
||||
});
|
||||
|
||||
// ../shared.ts
|
||||
var defaultLocale;
|
||||
var defaultIsDebug;
|
||||
var isInBrowser = typeof window !== "undefined" && typeof window.fetch !== "undefined";
|
||||
var isInBrowserExtension = typeof chrome !== "undefined" && !!chrome.runtime?.id;
|
||||
var _sessionId = newSessionId();
|
||||
var _lastTouched = /* @__PURE__ */ new Date();
|
||||
var _hosts = {
|
||||
US: "https://us.aptabase.com",
|
||||
EU: "https://eu.aptabase.com",
|
||||
DEV: "https://localhost:3000",
|
||||
SH: ""
|
||||
};
|
||||
function inMemorySessionId(timeout) {
|
||||
let now = /* @__PURE__ */ new Date();
|
||||
const diffInMs = now.getTime() - _lastTouched.getTime();
|
||||
const diffInSec = Math.floor(diffInMs / 1e3);
|
||||
if (diffInSec > timeout) {
|
||||
_sessionId = newSessionId();
|
||||
}
|
||||
_lastTouched = now;
|
||||
return _sessionId;
|
||||
}
|
||||
function newSessionId() {
|
||||
const epochInSeconds = Math.floor(Date.now() / 1e3).toString();
|
||||
const random = Math.floor(Math.random() * 1e8).toString().padStart(8, "0");
|
||||
return epochInSeconds + random;
|
||||
}
|
||||
function validateAppKey(appKey) {
|
||||
const parts = appKey.split("-");
|
||||
if (parts.length !== 3 || _hosts[parts[1]] === void 0) {
|
||||
console.warn(`The Aptabase App Key "${appKey}" is invalid. Tracking will be disabled.`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function getApiUrl(appKey, options) {
|
||||
const region = appKey.split("-")[1];
|
||||
if (region === "SH") {
|
||||
if (!options?.host) {
|
||||
console.warn(`Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled.`);
|
||||
return;
|
||||
}
|
||||
return `${options.host}/api/v0/event`;
|
||||
}
|
||||
const host = options?.host ?? _hosts[region];
|
||||
return `${host}/api/v0/event`;
|
||||
}
|
||||
async function sendEvent(opts) {
|
||||
if (!isInBrowser && !isInBrowserExtension) {
|
||||
console.warn(`Aptabase: trackEvent requires a browser environment. Event "${opts.eventName}" will be discarded.`);
|
||||
return;
|
||||
}
|
||||
if (!opts.appKey) {
|
||||
console.warn(`Aptabase: init must be called before trackEvent. Event "${opts.eventName}" will be discarded.`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(opts.apiUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"App-Key": opts.appKey
|
||||
},
|
||||
credentials: "omit",
|
||||
body: JSON.stringify({
|
||||
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
||||
sessionId: opts.sessionId,
|
||||
eventName: opts.eventName,
|
||||
systemProps: {
|
||||
locale: opts.locale ?? getBrowserLocale(),
|
||||
isDebug: opts.isDebug ?? getIsDebug(),
|
||||
appVersion: opts.appVersion ?? "",
|
||||
sdkVersion: opts.sdkVersion
|
||||
},
|
||||
props: opts.props
|
||||
})
|
||||
});
|
||||
if (response.status >= 300) {
|
||||
const responseBody = await response.text();
|
||||
console.warn(`Failed to send event "${opts.eventName}": ${response.status} ${responseBody}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Failed to send event "${opts.eventName}"`);
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
function getBrowserLocale() {
|
||||
if (defaultLocale) {
|
||||
return defaultLocale;
|
||||
}
|
||||
if (typeof navigator === "undefined") {
|
||||
return void 0;
|
||||
}
|
||||
if (navigator.languages.length > 0) {
|
||||
defaultLocale = navigator.languages[0];
|
||||
} else {
|
||||
defaultLocale = navigator.language;
|
||||
}
|
||||
return defaultLocale;
|
||||
}
|
||||
function getIsDebug() {
|
||||
if (defaultIsDebug !== void 0) {
|
||||
return defaultIsDebug;
|
||||
}
|
||||
if (true) {
|
||||
defaultIsDebug = true;
|
||||
return defaultIsDebug;
|
||||
}
|
||||
if (typeof location === "undefined") {
|
||||
defaultIsDebug = false;
|
||||
return defaultIsDebug;
|
||||
}
|
||||
defaultIsDebug = location.hostname === "localhost";
|
||||
return defaultIsDebug;
|
||||
}
|
||||
|
||||
// src/index.ts
|
||||
var SESSION_TIMEOUT = 1 * 60 * 60;
|
||||
var sdkVersion = `aptabase-web@${"0.4.3"}`;
|
||||
var _appKey = "";
|
||||
var _apiUrl;
|
||||
var _options;
|
||||
function init(appKey, options) {
|
||||
if (!validateAppKey(appKey))
|
||||
return;
|
||||
_apiUrl = options?.apiUrl ?? getApiUrl(appKey, options);
|
||||
_appKey = appKey;
|
||||
_options = options;
|
||||
}
|
||||
async function trackEvent(eventName, props) {
|
||||
if (!_apiUrl)
|
||||
return;
|
||||
const sessionId = inMemorySessionId(SESSION_TIMEOUT);
|
||||
await sendEvent({
|
||||
apiUrl: _apiUrl,
|
||||
sessionId,
|
||||
appKey: _appKey,
|
||||
isDebug: _options?.isDebug,
|
||||
appVersion: _options?.appVersion,
|
||||
sdkVersion,
|
||||
eventName,
|
||||
props
|
||||
});
|
||||
}
|
||||
return __toCommonJS(src_exports);
|
||||
})();
|
||||
//# sourceMappingURL=aptabase.debug.js.map
|
||||
1
examples/simple-js/aptabase.min.js.map
Normal file
1
examples/simple-js/aptabase.min.js.map
Normal file
File diff suppressed because one or more lines are too long
38
examples/simple-js/example.html
Normal file
38
examples/simple-js/example.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Aptabase Standalone Example</title>
|
||||
<!-- <script src="https://static.hmac.kr/aptabase.min.js"></script> -->
|
||||
<!-- <script src="./packages/web/dist/aptabase.min.js"></script> -->
|
||||
<script src="./aptabase.min.js"></script>
|
||||
<style>
|
||||
body { font-family: sans-serif; padding: 2em; }
|
||||
button { display: block; margin-bottom: 1em; padding: 0.5em 1em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Aptabase Standalone 테스트</h1>
|
||||
<p>Click the buttons below to send test events.</p>
|
||||
|
||||
<button onclick="track('app_started')">Track: App Started</button>
|
||||
<button onclick="track('feature_a_used', { plan: 'premium' })">Track: Used Feature A (with props)</button>
|
||||
<button onclick="track('feature_b_used')">Track: Used Feature B</button>
|
||||
<button onclick="track('user_signed_up')">Track: User Signed Up</button>
|
||||
<button onclick="track('item_purchased', { item_id: 'SKU-123', price: 9.99 })">Track: Item Purchased (with props)</button>
|
||||
|
||||
<script>
|
||||
// 1. Aptabase 초기화
|
||||
// App Key의 region이 'SH'인 경우 host는 필수입니다.
|
||||
window.aptabase.init("A-SH-7212023630", {
|
||||
host: "https://aptabase.hmac.kr"
|
||||
});
|
||||
|
||||
function track(eventName, props) {
|
||||
// 2. 이벤트 전송
|
||||
window.aptabase.trackEvent(eventName, props);
|
||||
console.log(`Event '${eventName}' sent!`, props ? { props } : '');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user