diff --git a/docs/src/content/en/_meta.ts b/docs/src/content/en/_meta.ts index 29d0799..1c36a1e 100644 --- a/docs/src/content/en/_meta.ts +++ b/docs/src/content/en/_meta.ts @@ -1,6 +1,6 @@ import { MetaRecord } from "nextra"; -const meta: MetaRecord = { +export default { "getting-started-separator": { type: "separator", title: "Getting Started", @@ -12,21 +12,21 @@ const meta: MetaRecord = { layout: "default", }, }, - installation: { - title: "Installation", + "why-event-tracker": { + title: "Why Event Tracker?", theme: { toc: true, layout: "default", }, }, - "quick-start": { - title: "Quick Start", + installation: { + title: "Installation", theme: { toc: true, layout: "default", }, }, - "api-separator": { + "api-reference-separator": { title: "API Reference", type: "separator", }, @@ -38,21 +38,21 @@ const meta: MetaRecord = { }, }, components: { - title: "components", + title: "Components", theme: { toc: true, layout: "default", }, }, - hook: { - title: "hook", + "use-tracker": { + title: "useTracker", theme: { toc: true, layout: "default", }, }, "advanced-separator": { - title: "Advanced", + title: "Guides", type: "separator", }, batching: { @@ -62,6 +62,22 @@ const meta: MetaRecord = { layout: "default", }, }, -}; - -export default meta; + "data-type-validation": { + title: "Data Type Validation", + theme: { + toc: true, + layout: "default", + }, + }, + "example-separator": { + title: "Examples", + type: "separator", + }, + "basic-example": { + title: "Basic", + theme: { + toc: true, + layout: "default", + }, + }, +} satisfies MetaRecord; diff --git a/docs/src/content/en/quick-start.mdx b/docs/src/content/en/basic-example.mdx similarity index 83% rename from docs/src/content/en/quick-start.mdx rename to docs/src/content/en/basic-example.mdx index 0a5728b..6c06e3e 100644 --- a/docs/src/content/en/quick-start.mdx +++ b/docs/src/content/en/basic-example.mdx @@ -1,6 +1,6 @@ -# Quick Start +# Guide -Here's a simple example of how to use event-tracker: +Here's a simple example demonstrating how to use `event-tracker`: ```tsx import { createTracker } from "@offlegacy/event-tracker"; @@ -14,7 +14,7 @@ const [Track, useTracker] = createTracker({ }, }); -// Use in your app +// Usage within the app function App() { return ( diff --git a/docs/src/content/en/components.mdx b/docs/src/content/en/components.mdx deleted file mode 100644 index 64113ec..0000000 --- a/docs/src/content/en/components.mdx +++ /dev/null @@ -1,189 +0,0 @@ -# Components - -event-tracker provides several components for tracking different types of events. Each component is designed to be easy to use while maintaining type safety and performance. - -## Provider - -The `Provider` component connects and provides initial context to your application. - -```tsx -import { createTracker } from '@offlegacy/event-tracker' - -const [Track] = createTracker({...}) - -function App() { - return ( - - {/* Your app content */} - - ) -} -``` - -### Props - -- `initialContext: unknown` - Initial context value - -## DOMEvent - -Used for tracking DOM events. Wraps a child component and fires the specified event handler. - -```tsx -import { createTracker } from "@offlegacy/event-tracker"; - -const [Track] = createTracker({ - DOMEvents: { - onFocus: (params, context) => { - // Handle focus event - }, - }, -}); - -function App() { - return ( - - - - - - ); -} -``` - -### Props - -- `type: DOMEventNames` - Event name (e.g., onClick, onFocus) -- (With schema) - - `params: SchemaParams | (context: Context) => SchemaParams` - Event parameters - - `schema?: string` - A name of schema that will be used to validate the event parameters -- (Without schema) - - `params: EventParams | (context: Context) => EventParams` - Event parameters - -## Click - -A specialized version of `DOMEvent` for click events (`type="onClick"`). - -```tsx -import { createTracker } from "@offlegacy/event-tracker"; - -const [Track] = createTracker({ - DOMEvents: { - onClick: (params, context) => { - // Handle click event - }, - }, -}); - -function App() { - return ( - - - - - - ); -} -``` - -### Props - -- With schema - - `params: SchemaParams | (context: Context) => SchemaParams` - Click event parameters - - `schema?: string` - A name of schema that will be used to validate the event parameters -- Without schema - - `params: EventParams | (context: Context) => EventParams` - Click event parameters - -## Impression - -Tracks impression events using the Intersection Observer API. - -```tsx -import { createTracker } from "@offlegacy/event-tracker"; - -const [Track] = createTracker({ - impression: { - onImpression: (params, context) => { - // Handle impression event - }, - options: { - threshold: 0.5, - }, - }, -}); - -function App() { - return ( - - -
Tracked content
-
-
- ); -} -``` - -### Props - -- Without schema - - `params: EventParams | (context: Context) => EventParams` - Impression event parameters - - `options?: ImpressionOptions` - Optional configuration (overrides global options) -- With schema - - `params: SchemaParams | (context: Context) => SchemaParams` - Impression event parameters - - `schema?: string` - A name of schema that will be used to validate the event parameters - - `options?: ImpressionOptions` - Optional configuration (overrides global options) - -## PageView - -Tracks page view events on component mount. - -```tsx -import { createTracker } from "@offlegacy/event-tracker"; - -const [Track] = createTracker({ - pageView: { - onPageView: (params, context) => { - // Handle page view event - }, - }, -}); - -function App() { - return ( - - - - ); -} -``` - -### Props - -- With schema - - `params: SchemaParams | (context: Context) => SchemaParams` - Page view event parameters - - `schema?: string` - A name of schema that will be used to validate the event parameters -- Without schema - - `params: EventParams | (context: Context) => EventParams` - Page view event parameters - -## SetContext - -Sets or updates the tracking context. - -```tsx -import { createTracker } from '@offlegacy/event-tracker' - -const [Track] = createTracker({...}) - -function App() { - return ( - - - - ) -} -``` - -### Props - -- `context: unknown | ((prevContext: unknown) => unknown)` - New context value or update function diff --git a/docs/src/content/en/components/index.mdx b/docs/src/content/en/components/index.mdx index 814a375..13da1ae 100644 --- a/docs/src/content/en/components/index.mdx +++ b/docs/src/content/en/components/index.mdx @@ -1,6 +1,6 @@ # Provider -The `Provider` component connects and provides initial context to your application. +The `Provider` component attaches and provides the initial context to your application. It is one of the event components included as the first element of the tuple returned by `createTracker`. ```tsx import { createTracker } from '@offlegacy/event-tracker' @@ -10,12 +10,23 @@ const [Track] = createTracker({...}) function App() { return ( - {/* Your app content */} + {/* Application */} ) } ``` +## Reference + ### Props -- `initialContext?: Context` - Initial context value +| Props | Type | Description | Example | +| ---------------- | --------- | --------------------- | ------------------- | +| `initialContext` | `Context` | Initial context value | `{ userId: '123' }` | + +--- + +#### `initialContext` (required) + +- Type: `Context` +- Description: The initial context value provided to the application. diff --git a/docs/src/content/en/create-tracker.mdx b/docs/src/content/en/create-tracker.mdx index 229279b..6d143a0 100644 --- a/docs/src/content/en/create-tracker.mdx +++ b/docs/src/content/en/create-tracker.mdx @@ -1,6 +1,6 @@ -# createTracker(config) +# createTracker -The main function to create a tracker instance with your desired configuration. +`createTracker` is a function used to create tracker instances with custom configurations. It allows you to define event tracking behaviors through parameters, returning a tuple containing the tracker instance and a custom hook for usage. ```tsx const [{ Provider, DOMEvent, Click, Impression, PageView, SetContext }, useTracker] = createTracker({ @@ -19,95 +19,109 @@ const [{ Provider, DOMEvent, Click, Impression, PageView, SetContext }, useTrack }); ``` -### Configuration Options +## Reference -#### init +### Parameters + +`createTracker` accepts a single configuration object as its parameter. + +| Option | Type | Description | +| ------------ | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | +| `init` | `(initialContext: Context, setContext: SetContext) => void \| Promise` | Initialization function executed before any event occurs. Supports async operations. | +| `send` | `(params: EventParams \| (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` | Function for sending events externally. Supports async operations. | +| `DOMEvents` | [`DOMEvents`](https://developer.mozilla.org/docs/Web/API/Event) | Collection of React DOM event handlers (`onClick`, `onMouseEnter`, etc.). | +| `impression` | `ImpressionOptions` | Configuration for impression event tracking. | +| `pageView` | `PageViewOptions` | Configuration for page view tracking. | +| `batch` | `BatchConfig` | Configuration for event batching. | +| `schemas` | `SchemaConfig` | Configuration for event schema validation. | + +--- + +#### `init` - Type: `(initialContext: Context, setContext: SetContext) => void | Promise` -- Optional -- Function executed before any events happen -- If it returns a promise, events will be delayed until the promise resolves +- Description: Function to set the initial context before any event occurs. Supports asynchronous functions. -#### send +#### `send` - Type: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` -- Optional -- Standard function to send events -- If it returns a promise, events will be delayed until the promise resolves +- Description: Function for sending events externally. Supports asynchronous operations. -#### DOMEvents +#### `DOMEvents` - Type: `DOMEvents` -- Optional -- Collection of standard React DOM events (`onClick`, `onMouseEnter`, etc.) -- Each handler is executed when that event occurs on a `` -- If a handler returns a promise, subsequent event callbacks will be delayed until it resolves - -#### impression +- Description: Standard React DOM event handlers. Used by `` components. -Configuration for impression tracking: +#### `impression` -- onImpression +- `impression.onImpression` - Type: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` - - Optional - - Executed when impression occurs on a `` child - - Promise return value will delay subsequent callbacks + - Description: Callback executed when `` event occurs. -- options - - Type: `ImpressionOptions` - - Optional - - Configuration options for impression tracking: - - `threshold`: Percentage of visibility needed (default: 0.2) - - `freezeOnceVisible`: Freeze intersection state after visibility (default: true) - - `initialIsIntersecting`: Initial intersection state (default: false) +- `impression.options` -#### pageView + - Type: `ImpressionOptions` + - Properties: -Configuration for page view tracking: + - `threshold`: Visibility threshold ratio (default: `0.2`) + - `freezeOnceVisible`: Fix visibility state after impression (default: `true`) + - `initialIsIntersecting`: Initial intersection state (default: `false`) -##### onPageView +#### `pageView` -- Type: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` -- Optional -- Executed when `` is mounted -- Promise return value will delay subsequent events +- `pageView.onPageView` -#### batch + - Type: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` + - Description: Executed when `` component mounts. -Configuration for event batching: +#### `batch` - Type: `BatchConfig` -- Optional - Properties: - - `enable`: Enable batching (default: false) - - `interval`: Flush interval in ms (default: 3000) - - `thresholdSize`: Max batch size (default: 25) - - `onFlush`: Function to handle batch flush (required if enabled) - - `onError`: Error handler (optional) -#### schemas + - `batch.enable`: Enable batching (default: `false`) + - `batch.interval`: Sending interval in milliseconds (default: `3000`) + - `batch.thresholdSize`: Maximum batch size (default: `25`) + - `batch.onFlush`: Function to handle batched event sending + - `batch.onError`: Optional error handling function -Configuration for data type validation: +#### `schemas` - Type: `SchemaConfig` -- Optional - Properties: - - `schemas`: A record of [Zod](https://zod.dev/) schemas - - `onSchemaError`?: Function to handle schema validation errors - - `abortOnError`?: Whether to abort event tracking if a schema validation error occurs (default: false) -### Return Value + - `schemas.schema`: Schema definitions based on [Zod](https://zod.dev/) + - `schemas.onSchemaError`: Function executed when schema validation errors occur + - `schemas.abortOnError`: Abort events on validation error (default: `false`) -The `createTracker` function returns a tuple containing: +### Returns -1. An object with tracking components: +`createTracker` returns a tuple containing the following two elements: - - [`Provider`](/docs/components) - - [`DOMEvent`](/docs/components/dom-event) - - [`Click`](/docs/components/click) - - [`Impression`](/docs/components/impression) - - [`PageView`](/docs/components/page-view) - - [`SetContext`](/docs/components/set-context) +```tsx +const [{ Provider, DOMEvent, Click, Impression, PageView, SetContext }, useTracker] = createTracker({...}) +``` + +#### [Components](./components) + +The first element of the returned tuple contains various event components: + +```tsx +const [{ Provider, DOMEvent, Click, Impression, PageView, SetContext }] = createTracker(config); +``` + +- `Provider` +- `DOMEvent` +- `Click` +- `Impression` +- `PageView` +- `SetContext` + +#### [useTracker](./use-tracker) + +```tsx +const [, useTracker] = createTracker(config); +``` -2. The [custom hook](/docs/hook) +The second element of the returned tuple is a custom React hook. It provides access to event tracking functionalities and context management within components. diff --git a/docs/src/content/en/hook.mdx b/docs/src/content/en/hook.mdx deleted file mode 100644 index 0c8c4e2..0000000 --- a/docs/src/content/en/hook.mdx +++ /dev/null @@ -1,223 +0,0 @@ -# hook - -A custom React hook returned as the second array item from [`createTracker`](/docs/create-tracker). -It provides access to tracking functionality and context management within your components. - -```tsx -import { createTracker } from "@offlegacy/event-tracker"; - -const [Track, useTracker] = createTracker({...}) - -function MyComponent() { - const { setContext, getContext, track, trackWithSchema } = useTracker(); - - return ( - // Your component content - ); -} -``` - -### Return Value - -The hook returns an object with the following properties: - -#### setContext - -- Type: `(context: Context) => void` -- Sets or updates the current tracking context -- Can be used to update user information, session data, etc. - -```tsx -const { setContext } = useTracker(); - -// Set new context -setContext({ userId: "user-123" }); - -// Update context based on previous value -setContext((prev) => ({ - ...prev, - lastActive: new Date(), -})); -``` - -#### getContext - -- Type: `() => Context` -- Returns the current tracking context -- Useful for accessing current tracking state - -```tsx -const { getContext } = useTracker(); - -const currentContext = getContext(); -console.log("Current user:", currentContext.userId); -``` - -#### track - -- Type: `Record void>` -- Object containing all configured event tracking functions -- Keys match the event names defined in your tracker configuration -- Accepts optional `TrackingOptions` for advanced control - -```tsx -const { track } = useTracker(); - -// Track a simple click event -track.onClick({ buttonId: "submit" }); - -// Track with conditional logic -track.onClick( - { buttonId: "premium" }, - { - enabled: (context) => context.user?.isPremium, - }, -); - -// Track with debouncing -track.onClick( - { buttonId: "search" }, - { - debounce: { delay: 300, leading: false, trailing: true }, - }, -); - -// Track with throttling -track.onClick( - { buttonId: "rapid-action" }, - { - throttle: { delay: 1000, leading: true, trailing: false }, - }, -); - -// Track an impression -track.onImpression({ elementId: "hero" }); -``` - -#### trackWithSchema - -- Type: `Record void>` -- Object containing all configured event tracking functions with schema validation -- Keys match the event names defined in your tracker configuration -- Accepts optional `TrackingOptions` for advanced control - -```tsx -const { trackWithSchema } = useTracker(); - -// Track a click event with schema -trackWithSchema.onClick({ schema: "click", params: { buttonId: "submit" } }); - -// Track with conditional logic and schema -trackWithSchema.onClick( - { - schema: "premium_click", - params: { buttonId: "premium", userId: "123" }, - }, - { - enabled: (context, params) => context.user?.id === params.userId, - }, -); - -// Track with throttling and schema -trackWithSchema.onImpression( - { - schema: "impression", - params: { elementId: "hero", userId: "123" }, - }, - { - throttle: { delay: 2000, leading: true, trailing: false }, - }, -); -``` - -### TrackingOptions - -Both `track` and `trackWithSchema` methods accept an optional second parameter with the following options: - -- `enabled?: boolean | ((context: Context, params: EventParams) => boolean)` - Conditionally enable/disable event tracking -- `debounce?: DebounceConfig` - Debounce configuration to prevent rapid successive events -- `throttle?: ThrottleConfig` - Throttle configuration to limit event frequency - -**Note:** `debounce` and `throttle` are mutually exclusive and cannot be used together. - -#### DebounceConfig - -```tsx -interface DebounceConfig { - delay: number; // Delay in milliseconds - leading?: boolean; // Execute on leading edge (default: false) - trailing?: boolean; // Execute on trailing edge (default: true) -} -``` - -#### ThrottleConfig - -```tsx -interface ThrottleConfig { - delay: number; // Delay in milliseconds - leading?: boolean; // Execute on leading edge (default: true) - trailing?: boolean; // Execute on trailing edge (default: false) -} -``` - -### Example Usage - -Here's a complete example showing how to use the custom hook: - -```tsx -import { createTracker } from "@offlegacy/event-tracker"; - -const [Track, useTracker] = createTracker({ - onClick: (params) => { - // Send event to analytics service - analytics.track(params); - }, - pageView: { - onPageView: (params) => { - // Send event to analytics service - analytics.pageView(params); - }, - }, -}); - -function UserProfile({ userId }) { - const { setContext, track, trackWithSchema } = useTracker(); - - useEffect(() => { - // Update context when user ID changes - setContext({ userId }); - - // Track page view - track.onPageView({ page: "profile" }); - }, [userId]); - - const handleSettingsClick = () => { - // Track custom event - trackWithSchema.onClick({ schema: "settings", params: { userId } }); - }; - - return ( -
-

User Profile

- -
- ); -} -``` - -### Best Practices - -1. **Context Updates** - - - Use `setContext` for global state that affects multiple events - - Consider using the function form of `setContext` for updates based on previous state - -2. **Event Tracking** - - - Use the specific event functions from `track` or `trackWithSchema` when possible - -3. **Performance** - - Avoid calling tracking functions in render - - Use callbacks or effects for tracking - - Consider using [batching](/docs/batching) for better performance - - Consider using [data type validation](/docs/data-type-validation) for data type safety diff --git a/docs/src/content/en/index.mdx b/docs/src/content/en/index.mdx index 2a87bb1..c2585a0 100644 --- a/docs/src/content/en/index.mdx +++ b/docs/src/content/en/index.mdx @@ -1,202 +1,89 @@ -# Introduction - -## Motivation - -Event tracking is a complex task that requires a lot of boilerplate code and complexity. -Take a look at this example. - -```tsx {14,15, 23-30} -// Traditional event tracking - -const Page = () => { - const { user, userId } = useUser(); - - return ( -
-

User: {user.name}

- -
- ); -}; - -const Counter = ({ userId }: { userId: string }) => { - // Receives 'userId' as a prop just for event tracking purposes. - - const [count, setCount] = useState(0); - const { track } = useTrackEvent(); - - const handleIncrement = () => { - setCount(count + 1); - - track({ - event: "click", - params: { - type: "count", - value: count + 1, - userId, - }, - }); - }; +import { Steps } from "nextra/components"; - return ( -
-

Count: {count}

- -
- ); -}; -``` - -The two main inconveniences caused by event tracking in the code are: +# Introduction -1. **Prop Drilling**: The `userId` is passed as a prop to the `` component from the `` component. If the `` component is nested deeper within the component tree, prop drilling can become more severe, reducing the readability and maintainability of the code. -2. **Coupling of Event Tracking Logic and Business Logic**: The `handleIncrement` function contains both the logic for incrementing the count and tracking the event. Separating the event tracking logic can make the code cleaner and easier to maintain. +Welcome to the Event Tracker documentation. -## The New Paradigm +## What is Event Tracker? -`event-tracker` introduces a new paradigm for event tracking. -The declarative nature of this paradigm simplifies -the complexity traditionally associated with event tracking, making it accessible to developers of all skill levels. +Event Tracker is a declarative React library designed to simplify the implementation of complex event tracking, allowing developers to focus more on their business logic. It is designed to efficiently manage event tracking in applications of any scale. -### Declarative event tracking +```tsx +import { createTracker } from "@offlegacy/event-tracker"; -```tsx {5, 10, 24, 26} -const Page = () => { - const { user, userId } = useUser(); +// Creating a tracker instance +const [Track, useTracker] = createTracker({ + DOMEvents: { + onClick: (params, context) => { + log("Click event:", params, context); + }, + }, +}); +// Usage within an app +function App() { return ( - -
-

User: {user.name}

- -
+ + + + ); -}; +} +``` -const Counter = () => { - const [count, setCount] = useState(0); +### Key Features - const handleIncrement = () => { - setCount(count + 1); - }; +Event Tracker provides various features prioritizing both developer experience and application performance. - return ( -
-

Count: {count}

- - - -
- ); -}; -``` +| Feature | Description | +| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Declarative API with Type Safety | Fully supports [TypeScript](https://www.typescriptlang.org/), reducing errors during development and increasing productivity through auto-completion. | +| Powerful Data Type Validation | Ensures data reliability through schema-based validation using [Zod](https://zod.dev/). | +| Optimized Performance | Minimizes network requests through batching, debouncing, or throttling, reducing the impact on application performance. | +| Guaranteed Execution Order | Ensures asynchronous events are processed in the intended sequence. | +| Analytics Tool Independence | Flexible integration with any analytics tool (e.g., [Google Analytics](https://analytics.google.com/), [Amplitude](https://amplitude.com/)), without being tied to any specific provider. | +| Clear Separation of Concerns | Effectively separates tracking logic from business logic, maximizing code maintainability and scalability. | -Using `event-tracker` allows for declarative event tracking, which improves code readability and reduces complexity. This helps developers understand and use event tracking more easily. -Your `handleIncrement` function is now only responsible for incrementing the count, and the event tracking is handled by the `` component. -**This declarative approach makes the developer focus on _'what to track'_ and not _'how to track'._** -How to track should be defined outside of the React app. +## Core Concepts -### Improve code cohesion outside your application. +There are several key concepts you need to understand to use Event Tracker effectively. -```tsx -const [Track] = createTracker({ - DOMEvents: { - onClick: (params, context) => { - log({ - event: "click", - params: { - ...params, - userId: context.userId, - }, - }); - }, - }, - onImpression: (params, context) => { - log({ - event: "impression", - params: { - ...params, - userId: context.userId, - }, - }); - }, -}); -``` + -Your instructions for **'how to track'** is now separated from your business logic. -It's located outside your application, so you can change your event tracking provider without having to change your application code. +### Instance (`createTracker`) -### Data Type Validation +The foundational starting point of the library. Use the `createTracker` function to generate a tracker instance (a collection of `Track` components and the `useTracker` hook). Define event tracking by configuring DOM event handlers, impression event handlers, schemas, and more. -`event-tracker` provides built-in schema validation using [Zod](https://zod.dev/), a TypeScript-first schema validation library. +Multiple tracker instances can be created according to specific purposes (e.g., separate instances for sending events to Google Analytics and Amplitude). -```tsx -import { z } from "zod"; -import { createTracker } from "@offlegacy/event-tracker"; +### Provider (`Track.Provider`) -interface Context { - // ... -} +Implemented using React's Context API. Wrap your application or component tree at the top with `Track.Provider` to provide common tracking data (context) to child components. For example, providing information such as `userId` and `pageName` through context allows utilizing this information during each event tracking. -interface Params { - // ... -} +### Event Components (`Track.Click`, `Track.PageView`, etc.) -// Define Schemas -const schemas = { - page_view: z.object({ - title: z.string(), - }), - click_button: z.object({ - target: z.string(), - }), -}; - -// Configure Tracker -const [Track] = createTracker({ - // other configurations... - - schema: { - schemas: { - page_view, - click_button, - }, - onSchemaError: (error) => { - console.error("Schema validation error:", error); - }, - abortOnError: true, - }, -}); +Special components provided to track events declaratively. These are available from the first element of the array returned by `createTracker`. -// Use the schemas -; -; -``` +- `Track.Click`: Tracks click events occurring on child elements. +- `Track.Impression`: Tracks when child elements become visible on the screen. +- `Track.PageView`: Tracks page view events upon component mount. -## Key Features +In addition, you can customize or utilize various provided components that respond to different user interactions and lifecycle events. Each component can receive and utilize specific event-related data through the `context` and `params` props. -- ๐ŸŽฏ **Type-safe APIs**: Declarative event tracking with complete type safety -- ๐Ÿ›ก๏ธ **Data Type Validation**: Ensures data type safety and consistency using schemas -- โšก๏ธ **Optimized Performance**: Enhanced performance through event batching -- ๐Ÿ”„ **Guaranteed Order**: Guaranteed execution order for async operations -- ๐Ÿ”Œ **Analytics Agnostic**: Works with any analytics provider of your choice -- ๐Ÿงฉ **Clean Separation**: Keeps tracking logic separate from business logic -- ๐Ÿ“ฆ **Lightweight**: Minimal bundle size impact on your application +### Custom Hook (`useTracker`) -## Basic Concepts +Used for more complex or conditional event tracking unrelated directly to component lifecycles or DOM events. The `useTracker` hook lets you access context information from `Track.Provider` and execute defined tracking logic imperatively. -`event-tracker` is built around a few core concepts: + -1. **Tracker Creation**: Use `createTracker` to create a tracker instance with your desired event tracking instructions. -2. **Provider**: Wrap your app with `Track.Provider` to provide context. -3. **Event Components**: Use components like `Track.Click` or `Track.Impression` to track events. -4. **Hooks**: Use the `useTracker` hook to access tracking functionality in your components imperatively. +## Next Steps -Check out the other sections for detailed documentation on each feature: +These core concepts work synergistically to provide Event Tracker's powerful and flexible event-tracking environment. For detailed usage and in-depth information about each feature, refer to the documents below: -- [createTracker](/docs/create-tracker) - Detailed API documentation on `createTracker` -- [components](/docs/components) - Available tracking components -- [hook](/docs/hook) - Track using the hook -- [Batching](/docs/batching) - Optimizing performance with event batching -- [Data Type Validation](/docs/data-type-validation) - Ensuring data type safety and consistency using schemas +- [Why Event Tracker?](/docs/why-event-tracker): Introduction to the necessity and core features of Event Tracker. +- [`createTracker`](/docs/create-tracker): Guide to creating tracker instances and detailed configurations. +- [Components](/docs/components): Examples and details of all available tracking components. +- [`useTracker`](/docs/hook): Custom tracking techniques using the custom hook. +- [Batching](/docs/batching): Strategies for optimizing performance through event batching. +- [Data Type Validation](/docs/data-type-validation): Guide to data validation using Zod schemas. diff --git a/docs/src/content/en/installation.mdx b/docs/src/content/en/installation.mdx index d2c9a98..2cba716 100644 --- a/docs/src/content/en/installation.mdx +++ b/docs/src/content/en/installation.mdx @@ -1,19 +1,9 @@ # Installation -Using npm: +Event Tracker is a library designed to simplify event tracking in React applications. It supports versions `React 18.0.0` and higher. -```bash -npm install @offlegacy/event-tracker -``` +To install the latest stable version, run the following command: -Using yarn: - -```bash -yarn add @offlegacy/event-tracker -``` - -Using pnpm: - -```bash -pnpm add @offlegacy/event-tracker +```shell npm2yarn +npm install @offlegacy/event-tracker ``` diff --git a/docs/src/content/ko/hook.mdx b/docs/src/content/en/use-tracker.mdx similarity index 94% rename from docs/src/content/ko/hook.mdx rename to docs/src/content/en/use-tracker.mdx index c775e8f..81385cb 100644 --- a/docs/src/content/ko/hook.mdx +++ b/docs/src/content/en/use-tracker.mdx @@ -1,7 +1,8 @@ -# hook +# useTracker -[`createTracker`](/docs/create-tracker)์—์„œ ๋‘ ๋ฒˆ์งธ ๋ฐฐ์—ด ํ•ญ๋ชฉ์œผ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ์ปค์Šคํ…€ React hook์ž…๋‹ˆ๋‹ค. -์ด ํ›…์€ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๊ธฐ๋Šฅ๊ณผ ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. +[`createTracker`](/docs/create-tracker)์—์„œ ๋‘ ๋ฒˆ์งธ ๋ฐฐ์—ด ํ•ญ๋ชฉ์œผ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ์ปค์Šคํ…€ React hook์ž…๋‹ˆ๋‹ค. ์ด ํ›…์€ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๊ธฐ๋Šฅ๊ณผ ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ๋น„๋™๊ธฐ ์ž‘์—…์ด ์™„๋ฃŒ๋œ ํ›„ ๋˜๋Š” ์‚ฌ์šฉ์ž์˜ ํŠน์ • ์ž…๋ ฅ ๊ฐ’์— ๋”ฐ๋ผ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์•ผ ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ```tsx import { createTracker } from "@offlegacy/event-tracker"; diff --git a/docs/src/content/en/why-event-tracker.mdx b/docs/src/content/en/why-event-tracker.mdx new file mode 100644 index 0000000..96f9050 --- /dev/null +++ b/docs/src/content/en/why-event-tracker.mdx @@ -0,0 +1,184 @@ +import { Steps } from "nextra/components"; + +# Why Event Tracker? + +Modern web applications need to continuously analyze user behavior to improve service quality. However, traditional event tracking approaches have various issues. + +## Why is Event Tracker Necessary? + +The following example illustrates common problems with traditional event tracking methods: + +- **Pain of Prop Drilling**: Often, tracking data must be passed through multiple layers of components, negatively impacting readability and maintainability. +- **Strong Coupling of Logic**: Mixing business logic and tracking logic increases complexity and makes it harder to independently test and modify each. +- **Increased Boilerplate Code**: Repetitive tracking code significantly hampers developer productivity. + +```tsx {8,15,24-31} +function Page() { + const { user, userId } = useUser(); // Retrieves user information and ID. + + return ( +
+

User: {user.name}

+ {/* Passes userId to Counter component solely for event tracking purposes */} + +
+ ); +} + +// Receives 'userId' as prop exclusively for event tracking. +// Prop drilling intensifies if Counter is placed even deeper in the tree. +function Counter({ userId }: { userId: string }) { + const [count, setCount] = useState(0); + const { track } = useTrackEvent(); // Hypothetical tracking hook + + const handleIncrement = () => { + const newCount = count + 1; + setCount(newCount); + + // Mixing business logic (incrementing count) with tracking logic. + track({ + event: "click_increment", + params: { + type: "count", + value: newCount, + userId, // userId received from the parent component + }, + }); + }; + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +## New Paradigm Offered by Event Tracker + +Event Tracker introduces a new paradigm for event tracking. Its declarative approach simplifies traditional complexities, making event tracking accessible for all developers. + + + +### Declarative Event Tracking + +```tsx {7,10,12,32,34} +function Page() { + const { user, userId } = useUser(); + + // Provides tracking context (userId) to child components via Track.Provider. + // Eliminates the need for prop drilling. + return ( + +
+

User: {user.name}

+ {/* No need to pass userId as prop */} +
+
+ ); +} + +function Counter() { + const [count, setCount] = useState(0); + + const handleIncrement = () => { + // handleIncrement now focuses solely on increment logic. + setCount(count + 1); + }; + + return ( +
+

Count: {count}

+ {/* + Track.Click component wraps the click event and automatically tracks the event + with the provided parameters. The userId provided in the context is automatically included. + */} + + + +
+ ); +} +``` + +With Event Tracker, declarative event tracking significantly improves code readability and reduces complexity, helping developers more easily understand and implement tracking. + +Now the `handleIncrement` function is only responsible for increasing the count, while the `` component handles tracking. +**This declarative approach lets developers focus on 'what to track' rather than 'how to track.'** +The actual tracking logic is defined externally from the React app. + +### Improved Cohesion in Event Tracking + +```tsx {4-10,14-20} +const [Track, useTracker] = createTracker({ + // Callback executed on DOM events + DOMEvents: { + onClick: (params, context) => { + // Call the actual tracking library (e.g., Google Analytics, Amplitude) + logEvent("click_event", { + ...params, // { value: ..., type: "count" } + userId: context.userId, // userId from Track.Provider + }); + }, + // Define additional DOM event handlers (onMouseOver, onFocus, etc.) as needed + }, + // Callback executed on Impression events + onImpression: (params, context) => { + logEvent("impression_event", { + ...params, + userId: context.userId, + pagePath: window.location.pathname, + }); + }, +}); +``` + +Now the code for **'how to track'** is separated from business logic. Located externally, it can be modified without impacting business logic. + +### Data Type Validation + +```tsx {13-20,24-33} +import { z } from "zod"; +import { createTracker } from "@offlegacy/event-tracker"; + +interface Context { + /* ... */ +} + +interface Params { + /* ... */ +} + +// Schema definition +const schemas = { + page_view: z.object({ + title: z.string(), + }), + click_button: z.object({ + target: z.string(), + }), +}; + +// Tracker setup +const [Track] = createTracker({ + schema: { + schemas: { + page_view, + click_button, + }, + onSchemaError: (error) => { + console.error("Schema validation error:", error); + }, + abortOnError: true, + }, +}); + +// Using schemas +; +; +``` + +Event Tracker optionally integrates with [Zod](https://zod.dev/) to offer robust schema-based data validation. This ensures data correctness from the development stage, enhancing tracking data reliability. + +
diff --git a/docs/src/content/ko/_meta.ts b/docs/src/content/ko/_meta.ts index 2a81597..7705f64 100644 --- a/docs/src/content/ko/_meta.ts +++ b/docs/src/content/ko/_meta.ts @@ -1,6 +1,6 @@ -import { MetaRecord } from "nextra"; +import type { MetaRecord } from "nextra"; -const meta: MetaRecord = { +export default { "getting-started-separator": { type: "separator", title: "์‹œ์ž‘ํ•˜๊ธฐ", @@ -12,22 +12,22 @@ const meta: MetaRecord = { layout: "default", }, }, - installation: { - title: "์„ค์น˜", + "why-event-tracker": { + title: "์™œ Event Tracker์ธ๊ฐ€์š”?", theme: { toc: true, layout: "default", }, }, - "quick-start": { - title: "๊ฐ€์ด๋“œ", + installation: { + title: "์„ค์น˜ํ•˜๊ธฐ", theme: { toc: true, layout: "default", }, }, - "api-separator": { - title: "API", + "api-reference-separator": { + title: "API ๋ ˆํผ๋Ÿฐ์Šค", type: "separator", }, "create-tracker": { @@ -38,21 +38,22 @@ const meta: MetaRecord = { }, }, components: { - title: "์ปดํฌ๋„ŒํŠธ", + title: "Components", theme: { toc: true, layout: "default", + collapsed: true, }, }, - hook: { - title: "hook", + "use-tracker": { + title: "useTracker", theme: { toc: true, layout: "default", }, }, "advanced-separator": { - title: "๊ณ ๊ธ‰", + title: "๊ฐ€์ด๋“œ", type: "separator", }, batching: { @@ -62,6 +63,22 @@ const meta: MetaRecord = { layout: "default", }, }, -}; - -export default meta; + "data-type-validation": { + title: "๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ", + theme: { + toc: true, + layout: "default", + }, + }, + "example-separator": { + title: "์˜ˆ์ œ", + type: "separator", + }, + "basic-example": { + title: "๊ธฐ๋ณธ", + theme: { + toc: true, + layout: "default", + }, + }, +} satisfies MetaRecord; diff --git a/docs/src/content/ko/quick-start.mdx b/docs/src/content/ko/basic-example.mdx similarity index 100% rename from docs/src/content/ko/quick-start.mdx rename to docs/src/content/ko/basic-example.mdx diff --git a/docs/src/content/ko/components/dom-event.mdx b/docs/src/content/ko/components/dom-event.mdx index fdc0149..1623333 100644 --- a/docs/src/content/ko/components/dom-event.mdx +++ b/docs/src/content/ko/components/dom-event.mdx @@ -1,6 +1,8 @@ +import { Callout } from "nextra/components"; + # DOMEvent -DOM ์ด๋ฒคํŠธ๋ฅผ ์ถ”์ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ๊ณ  ์ง€์ •๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. +DOM ์ด๋ฒคํŠธ๋ฅผ ์ถ”์ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ๊ณ  ์ง€์ •๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. `createTracker`์˜ ๋ฆฌํ„ด ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์จฐ ์š”์†Œ์ธ ์ด๋ฒคํŠธ ์ปดํฌ๋„ŒํŠธ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ```tsx import { createTracker } from "@offlegacy/event-tracker"; @@ -24,21 +26,64 @@ function App() { } ``` +## Reference + ### Props -- `type: DOMEventNames` - ์ด๋ฒคํŠธ ์ด๋ฆ„ (์˜ˆ: onClick, onFocus) -- ์Šคํ‚ค๋งˆ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ - - `params: SchemaParams | (context: Context) => SchemaParams` - ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜ ๋งค๊ฐœ๋ณ€์ˆ˜ - - `schema: string` - ์ด๋ฒคํŠธ ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ฒ€์ฆ์„ ์œ„ํ•œ ์Šคํ‚ค๋งˆ ์ด๋ฆ„ -- ์Šคํ‚ค๋งˆ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ - - `params: EventParams | (context: Context) => EventParams` - ์ด๋ฒคํŠธ ๋งค๊ฐœ๋ณ€์ˆ˜ -- `enabled?: boolean | ((context: Context, params: EventParams) => boolean)` - ์ด๋ฒคํŠธ ์ถ”์ ์„ ์กฐ๊ฑด๋ถ€๋กœ ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™” (๊ธฐ๋ณธ๊ฐ’: `true`) -- `debounce?: DebounceConfig` - ์—ฐ์†์ ์ธ ์ด๋ฒคํŠธ ๋ฐœ์ƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋””๋ฐ”์šด์Šค ์„ค์ • -- `throttle?: ThrottleConfig` - ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋นˆ๋„๋ฅผ ์ œํ•œํ•˜๋Š” ์Šค๋กœํ‹€ ์„ค์ • +| Props | ํƒ€์ž… | ์„ค๋ช… | ํ•„์ˆ˜ | +| ------------- | ---------------- | ----------------------------------------------------- | ---- | +| DOMEventNames | DOMEventNames | ์ด๋ฒคํŠธ ์ด๋ฆ„ (์˜ˆ: `"onClick"`, `"onFocus"`) | O | +| `enabled` | `boolean | ((context: Context, params: EventParams) => boolean)` | - | +| `debounce` | `DebounceConfig` | ์—ฐ์†์ ์ธ ์ด๋ฒคํŠธ ๋ฐœ์ƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋””๋ฐ”์šด์Šค ์„ค์ • | - | +| `throttle` | `ThrottleConfig` | ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋นˆ๋„๋ฅผ ์ œํ•œํ•˜๋Š” ์Šค๋กœํ‹€ ์„ค์ • | - | + +์ฐธ๊ณ : `debounce`์™€ `throttle`์€ ์ƒํ˜ธ ๋ฐฐํƒ€์ ์ด๋ฉฐ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. + +์Šคํ‚ค๋งˆ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์™€ ์•„๋‹ ๊ฒฝ์šฐ ํƒ€์ž…์ด ๋‹ฌ๋ผ์ง€๋Š” ์†์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. + +| Props | ์Šคํ‚ค๋งˆ ์—ฌ๋ถ€ | ํƒ€์ž… | ์„ค๋ช… | ํ•„์ˆ˜ | +| -------- | ----------- | ---------------------------------------------------- | --------------------------------------- | ---- | +| `params` | O | `SchemaParams \| (context: Context) => SchemaParams` | ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜ ๋งค๊ฐœ๋ณ€์ˆ˜ | O | +| `schema` | O | `string` | ์ด๋ฒคํŠธ ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ฒ€์ฆ์„ ์œ„ํ•œ ์Šคํ‚ค๋งˆ ์ด๋ฆ„ | O | +| `params` | - | `EventParams \| (context: Context) => EventParams` | ์ด๋ฒคํŠธ ๋งค๊ฐœ๋ณ€์ˆ˜ | O | + +--- + +#### `params` (ํ•„์ˆ˜) + +์Šคํ‚ค๋งˆ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ: + +- ํƒ€์ž…: `SchemaParams | (context: Context) => SchemaParams` +- ์„ค๋ช…: ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜ ๋งค๊ฐœ๋ณ€์ˆ˜ + +์Šคํ‚ค๋งˆ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ: + +- ํƒ€์ž…: `EventParams | (context: Context) => EventParams` +- ์„ค๋ช…: ์ด๋ฒคํŠธ ๋งค๊ฐœ๋ณ€์ˆ˜ + +#### `schema` (ํ•„์ˆ˜) + +์Šคํ‚ค๋งˆ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ: + +- ํƒ€์ž…: `string` +- ์„ค๋ช…: ์ด๋ฒคํŠธ ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ฒ€์ฆ์„ ์œ„ํ•œ ์Šคํ‚ค๋งˆ ์ด๋ฆ„ + +#### `enabled` + +- ํƒ€์ž…: `boolean | ((context: Context, params: EventParams) => boolean)` +- ์„ค๋ช…: ์ด๋ฒคํŠธ ์ถ”์ ์„ ์กฐ๊ฑด๋ถ€๋กœ ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™” (๊ธฐ๋ณธ๊ฐ’: `true`) + +#### `debounce` + +- ํƒ€์ž…: `DebounceConfig` +- ์„ค๋ช…: ์—ฐ์†์ ์ธ ์ด๋ฒคํŠธ ๋ฐœ์ƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋””๋ฐ”์šด์Šค ์„ค์ • + +#### `throttle` -**์ฐธ๊ณ :** `debounce`์™€ `throttle`์€ ์ƒํ˜ธ ๋ฐฐํƒ€์ ์ด๋ฉฐ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. +- ํƒ€์ž…: `ThrottleConfig` +- ์„ค๋ช…: ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋นˆ๋„๋ฅผ ์ œํ•œํ•˜๋Š” ์Šค๋กœํ‹€ ์„ค์ • -### ์ถ”์  ์˜ต์…˜ ์˜ˆ์ œ +### Examples #### ์กฐ๊ฑด๋ถ€ ์ถ”์  diff --git a/docs/src/content/ko/components/impression.mdx b/docs/src/content/ko/components/impression.mdx index d664b5c..7bfbb58 100644 --- a/docs/src/content/ko/components/impression.mdx +++ b/docs/src/content/ko/components/impression.mdx @@ -1,6 +1,6 @@ # Impression -Intersection Observer API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋…ธ์ถœ ์ด๋ฒคํŠธ๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. +Intersection Observer API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋…ธ์ถœ ์ด๋ฒคํŠธ๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. `createTracker`์˜ ๋ฆฌํ„ด ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์จฐ ์š”์†Œ์ธ ์ด๋ฒคํŠธ ์ปดํฌ๋„ŒํŠธ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ```tsx import { createTracker } from "@offlegacy/event-tracker"; diff --git a/docs/src/content/ko/components/index.mdx b/docs/src/content/ko/components/index.mdx index 2189f78..a565b32 100644 --- a/docs/src/content/ko/components/index.mdx +++ b/docs/src/content/ko/components/index.mdx @@ -1,6 +1,6 @@ # Provider -`Provider` ์ปดํฌ๋„ŒํŠธ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. +`Provider` ์ปดํฌ๋„ŒํŠธ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. `createTracker`์˜ ๋ฆฌํ„ด ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์จฐ ์š”์†Œ์ธ ์ด๋ฒคํŠธ ์ปดํฌ๋„ŒํŠธ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ```tsx import { createTracker } from '@offlegacy/event-tracker' @@ -10,12 +10,23 @@ const [Track] = createTracker({...}) function App() { return ( - {/* ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ธ  */} + {/* ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ */} ) } ``` +## Reference + ### Props -- `initialContext?: Context` - ์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ ๊ฐ’ +| Props | ํƒ€์ž… | ์„ค๋ช… | ์˜ˆ์ œ | +| ---------------- | --------- | ---------------- | ------------------- | +| `initialContext` | `Context` | ์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ ๊ฐ’ | `{ userId: '123' }` | + +--- + +#### `initialContext` (ํ•„์ˆ˜) + +- ํƒ€์ž…: `Context` +- ์„ค๋ช…: ์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ ๊ฐ’ diff --git a/docs/src/content/ko/create-tracker.mdx b/docs/src/content/ko/create-tracker.mdx index 5b6cbed..dea5919 100644 --- a/docs/src/content/ko/create-tracker.mdx +++ b/docs/src/content/ko/create-tracker.mdx @@ -1,6 +1,6 @@ -# createTracker(config) +# createTracker -์›ํ•˜๋Š” ๊ตฌ์„ฑ์œผ๋กœ ํŠธ๋ž˜์ปค ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์ธ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. +`createTracker`๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์„ค์ •์œผ๋กœ ํŠธ๋ž˜์ปค ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์„ ์ •์˜ํ•˜๊ณ , ๋ฐ˜ํ™˜๋œ ํŠœํ”Œ์„ ํ†ตํ•ด ํŠธ๋ž˜์ปค ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ```tsx const [{ Provider, DOMEvent, Click, Impression, PageView, SetContext }, useTracker] = createTracker({ @@ -19,95 +19,109 @@ const [{ Provider, DOMEvent, Click, Impression, PageView, SetContext }, useTrack }); ``` -### ๊ตฌ์„ฑ ์˜ต์…˜ +## Reference -#### init +### Parameters -- Type: `(initialContext: Context, setContext: SetContext) => void | Promise` -- ์˜ต์…”๋„ -- ๋ชจ๋“  ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์ „์— ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜ -- Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์ด๋ฒคํŠธ๊ฐ€ Promise๊ฐ€ ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ์ง€์—ฐ๋ฉ๋‹ˆ๋‹ค. +`createTracker`๋Š” ํ•˜๋‚˜์˜ ์„ค์ • ๊ฐ์ฒด๋ฅผ ์ธ์ž๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. -#### send +| ์„ค์ • | ํƒ€์ž… | ์„ค๋ช… | +| ------------ | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- | +| `init` | `(initialContext: Context, setContext: SetContext) => void \| Promise` | ๋ชจ๋“  ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์ „์— ์‹คํ–‰๋˜๋Š” ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜. Promise ์ง€์›. | +| `send` | `(params: EventParams \| (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` | ์ด๋ฒคํŠธ ์ „์†ก ํ•จ์ˆ˜. Promise ์ง€์›. | +| `DOMEvents` | [`DOMEvents`](https://developer.mozilla.org/docs/Web/API/Event) | React DOM ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋ชจ์Œ (`onClick`, `onMouseEnter` ๋“ฑ) | +| `impression` | `ImpressionOptions` | ๋…ธ์ถœ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ์„ค์ • | +| `pageView` | `PageViewOptions` | ํŽ˜์ด์ง€ ๋ทฐ ํŠธ๋ž˜ํ‚น ์„ค์ • | +| `batch` | `BatchConfig` | ์ด๋ฒคํŠธ ๋ฐฐ์นญ ์„ค์ • | +| `schemas` | `SchemaConfig` | ์ด๋ฒคํŠธ ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ ์„ค์ • | -- Type: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` -- ์˜ต์…”๋„ -- ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๋Š” ํ‘œ์ค€ ํ•จ์ˆ˜ -- Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์ด๋ฒคํŠธ๊ฐ€ Promise๊ฐ€ ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ์ง€์—ฐ๋ฉ๋‹ˆ๋‹ค. +--- -#### DOMEvents +#### `init` -- Type: `DOMEvents` -- ์˜ต์…”๋„ -- ํ‘œ์ค€ React DOM ์ด๋ฒคํŠธ (`onClick`, `onMouseEnter`, ๋“ฑ)์˜ ๋ชจ์Œ -- ๊ฐ ํ•ธ๋“ค๋Ÿฌ๋Š” ``์—์„œ ํ•ด๋‹น ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. -- ํ•ธ๋“ค๋Ÿฌ๊ฐ€ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํ›„์† ์ด๋ฒคํŠธ ์ฝœ๋ฐฑ์ด ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ์ง€์—ฐ๋ฉ๋‹ˆ๋‹ค. +- ํƒ€์ž…: `(initialContext: Context, setContext: SetContext) => void | Promise` +- ์„ค๋ช…: ๋ชจ๋“  ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์ „, ์ดˆ๊ธฐ ์ปจํ…์ŠคํŠธ๋ฅผ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ์ง€์›. -#### impression +#### `send` -๋…ธ์ถœ ์ด๋ฒคํŠธ๋ฅผ ํŠธ๋ž˜ํ‚นํ•˜๊ธฐ ์œ„ํ•œ ์„ค์ •์ž…๋‹ˆ๋‹ค. +- ํƒ€์ž…: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` +- ์„ค๋ช…: ์ด๋ฒคํŠธ๋ฅผ ์™ธ๋ถ€๋กœ ์ „์†กํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ. -- onImpression +#### `DOMEvents` - - Type: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` - - Optional - - Executed when impression occurs on a `` child - - Promise return value will delay subsequent callbacks +- ํƒ€์ž…: `DOMEvents` +- ์„ค๋ช…: ํ‘œ์ค€ React DOM ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. `` ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. -- options - - Type: `ImpressionOptions` - - ์˜ต์…”๋„ - - ๋…ธ์ถœ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๊ตฌ์„ฑ ์˜ต์…˜: - - `threshold`: ๋…ธ์ถœ ํ•„์š” ๋น„์œจ (๊ธฐ๋ณธ๊ฐ’: 0.2) - - `freezeOnceVisible`: ๋…ธ์ถœ ํ›„ ๊ต์ฐจ ์ƒํƒœ ๊ณ ์ • (๊ธฐ๋ณธ๊ฐ’: true) - - `initialIsIntersecting`: ์ดˆ๊ธฐ ๊ต์ฐจ ์ƒํƒœ (๊ธฐ๋ณธ๊ฐ’: false) +#### `impression` -#### pageView +- `impression.onImpression` -ํŽ˜์ด์ง€ ๋ทฐ ์ด๋ฒคํŠธ๋ฅผ ํŠธ๋ž˜ํ‚นํ•˜๊ธฐ ์œ„ํ•œ ์„ค์ •์ž…๋‹ˆ๋‹ค. + - ํƒ€์ž…: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` + - ์„ค๋ช…: `` ๋ฐœ์ƒ ์‹œ ์‹คํ–‰๋˜๋Š” ์ฝœ๋ฐฑ -##### onPageView +- `impression.options` -- Type: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` -- ์˜ต์…”๋„ -- ``๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. -- Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํ›„์† ์ด๋ฒคํŠธ๊ฐ€ ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ์ง€์—ฐ๋ฉ๋‹ˆ๋‹ค. + - ํƒ€์ž…: `ImpressionOptions` + - ์†์„ฑ: -#### batch + - `threshold`: ๋…ธ์ถœ ๋น„์œจ ๊ธฐ์ค€ (๊ธฐ๋ณธ๊ฐ’: `0.2`) + - `freezeOnceVisible`: ๋…ธ์ถœ ํ›„ ์ƒํƒœ ๊ณ ์ • ์—ฌ๋ถ€ (๊ธฐ๋ณธ๊ฐ’: `true`) + - `initialIsIntersecting`: ์ดˆ๊ธฐ ๊ต์ฐจ ์—ฌ๋ถ€ (๊ธฐ๋ณธ๊ฐ’: `false`) -์ด๋ฒคํŠธ ๋ฐฐ์นญ์„ ์œ„ํ•œ ์„ค์ •์ž…๋‹ˆ๋‹ค. +#### `pageView` -- Type: `BatchConfig` -- ์˜ต์…”๋„ -- Properties: - - `enable`: ๋ฐฐ์นญ ํ™œ์„ฑํ™” (๊ธฐ๋ณธ๊ฐ’: false) - - `interval`: ๋ฒ„ํผ๋ง ๊ฐ„๊ฒฉ (ms) (๊ธฐ๋ณธ๊ฐ’: 3000) - - `thresholdSize`: ์ตœ๋Œ€ ๋ฐฐ์นญ ํฌ๊ธฐ (๊ธฐ๋ณธ๊ฐ’: 25) - - `onFlush`: ๋ฐฐ์น˜ ๋น„์šฐ๊ธฐ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ (ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ํ•„์š”) - - `onError`: ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ (์˜ต์…”๋„) +- `pageView.onPageView` -#### schemas + - ํƒ€์ž…: `(params: EventParams | (context: Context) => EventParams, context: Context, setContext: SetContext) => TaskReturnType` + - ์„ค๋ช…: `` ๋งˆ์šดํŠธ ์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. -๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ์„ ์œ„ํ•œ ์„ค์ •์ž…๋‹ˆ๋‹ค. +#### `batch` -- Type: `SchemaConfig` -- ์˜ต์…”๋„ -- Properties: - - `schemas`: [Zod](https://zod.dev/) ์Šคํ‚ค๋งˆ์˜ ๋ ˆ์ฝ”๋“œ - - `onSchemaError`?: ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ - - `abortOnError`?: ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ ์ด๋ฒคํŠธ ์ถ”์  ์ค‘๋‹จ ์—ฌ๋ถ€ (๊ธฐ๋ณธ๊ฐ’: false) +- ํƒ€์ž…: `BatchConfig` +- ์†์„ฑ: -### Return Value + - `batch.enable`: ๋ฐฐ์น˜ ํ™œ์„ฑํ™” ์—ฌ๋ถ€ (๊ธฐ๋ณธ๊ฐ’: `false`) + - `batch.interval`: ์ „์†ก ๊ฐ„๊ฒฉ(ms, ๊ธฐ๋ณธ๊ฐ’: `3000`) + - `batch.thresholdSize`: ์ตœ๋Œ€ ๋ฐฐ์น˜ ํฌ๊ธฐ (๊ธฐ๋ณธ๊ฐ’: `25`) + - `batch.onFlush`: ์ด๋ฒคํŠธ ์ „์†ก ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ + - `batch.onError`: ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ (์˜ต์…”๋„) -`createTracker` ํ•จ์ˆ˜๋Š” ๋‹ค์Œ ํŠœํ”Œ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค: +#### `schemas` -1. ํŠธ๋ž˜ํ‚น ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด: +- ํƒ€์ž…: `SchemaConfig` +- ์†์„ฑ: - - [`Provider`](/docs/components) - - [`DOMEvent`](/docs/components/dom-event) - - [`Click`](/docs/components/click) - - [`Impression`](/docs/components/impression) - - [`PageView`](/docs/components/page-view) - - [`SetContext`](/docs/components/set-context) + - `schemas.schema`: [Zod](https://zod.dev/) ๊ธฐ๋ฐ˜์˜ ์Šคํ‚ค๋งˆ ์ •์˜ + - `schemas.onSchemaError`: ์Šคํ‚ค๋งˆ ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ + - `schemas.abortOnError`: ์˜ค๋ฅ˜ ์‹œ ์ด๋ฒคํŠธ ์ค‘๋‹จ ์—ฌ๋ถ€ (๊ธฐ๋ณธ๊ฐ’: `false`) -2. [์ปค์Šคํ…€ hook](/docs/hook) +### Returns + +`createTracker`๋Š” ๋‹ค์Œ ๋‘ ๊ฐ’์„ ํฌํ•จํ•œ ํŠœํ”Œ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. + +```tsx +const [{ Provider, DOMEvent, Click, Impression, PageView, SetContext }, useTracker] = createTracker({...}) +``` + +#### [Components](./components) + +๋ฆฌํ„ด ๋ฐฐ์—ด์—์„œ ์ฒซ ์š”์†Œ์ธ ์ด๋ฒคํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ด๋ฒคํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +```tsx +const [{ Provider, DOMEvent, Click, Impression, PageView, SetContext }] = createTracker(config); +``` + +- `Provider` +- `DOMEvent` +- `Click` +- `Impression` +- `PageView` +- `SetContext` + +#### [useTracker](./use-tracker) + +```tsx +const [, useTracker] = createTracker(config); +``` + +๋ฆฌํ„ด ๋ฐฐ์—ด์—์„œ ๋‘๋ฒˆ์งธ ์š”์†Œ์ธ ์ปค์Šคํ…€ React hook์ž…๋‹ˆ๋‹ค. ์ด ํ›…์€ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๊ธฐ๋Šฅ๊ณผ ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. diff --git a/docs/src/content/ko/index.mdx b/docs/src/content/ko/index.mdx index 97aed6d..03c0269 100644 --- a/docs/src/content/ko/index.mdx +++ b/docs/src/content/ko/index.mdx @@ -1,202 +1,89 @@ -# ์†Œ๊ฐœ - -## `event-tracker`๊ฐ€ ํ•„์š”ํ•œ ์ด์œ  - -์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์€ ๋งŽ์€ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ์™€ ๋ณต์žก์„ฑ์„ ์ˆ˜๋ฐ˜ํ•˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค. -๋‹ค์Œ ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด์„ธ์š”. - -```tsx {14,15, 23-30} -// ์ „ํ†ต์ ์ธ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๋ฐฉ์‹ +import { Steps } from "nextra/components"; -const Page = () => { - const { user, userId } = useUser(); - - return ( -
-

User: {user.name}

- -
- ); -}; - -const Counter = ({ userId }: { userId: string }) => { - // ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น๋งŒ์„ ์œ„ํ•ด์„œ 'userId'๋ฅผ prop์œผ๋กœ ์ „๋‹ฌ๋ฐ›์Œ - - const [count, setCount] = useState(0); - const { track } = useTrackEvent(); - - const handleIncrement = () => { - setCount(count + 1); - - track({ - event: "click", - params: { - type: "count", - value: count + 1, - userId, - }, - }); - }; - - return ( -
-

Count: {count}

- -
- ); -}; -``` - -์œ„ ์ฝ”๋“œ๋กœ ์•Œ์•„๋ณธ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์œผ๋กœ ์ธํ•œ ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ๋ถˆํŽธ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: +# ์†Œ๊ฐœ -1. **Prop Drilling**: `userId`๊ฐ€ `` ์ปดํฌ๋„ŒํŠธ์—์„œ `` ์ปดํฌ๋„ŒํŠธ๋กœ prop์œผ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. `` ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ ๊นŠ์ˆ™์ด ์ค‘์ฒฉ๋˜์–ด ์žˆ๋‹ค๋ฉด, prop drilling์ด ๋” ์‹ฌ๊ฐํ•ด์ ธ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -2. **์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๋กœ์ง๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ๊ฐ•๊ฒฐํ•ฉ**: `handleIncrement` ํ•จ์ˆ˜๋Š” ์นด์šดํŠธ ์ฆ๊ฐ€ ๋กœ์ง๊ณผ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๋กœ์ง์„ ๋ชจ๋‘ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๋ฉด ์ฝ”๋“œ๋ฅผ ๋” ๊น”๋”ํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +Event Tracker ๋ฌธ์„œ์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค. -## `event-tracker`๊ฐ€ ์ œ์‹œํ•˜๋Š” ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„ +## Event Tracker๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”? -`event-tracker`๋Š” ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์„ ์œ„ํ•œ ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค. -`event-tracker`๊ฐ€ ์ œ์‹œํ•˜๋Š” ์„ ์–ธ์  ๋ฐฉ์‹์€ ์ „ํ†ต์ ์œผ๋กœ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น๊ณผ ๊ด€๋ จ๋œ ๋ณต์žก์„ฑ์„ ๋‹จ์ˆœํ™”ํ•˜์—ฌ, -๋ชจ๋“  ๊ฐœ๋ฐœ์ž๋“ค์ด ์‰ฝ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. +Event Tracker๋Š” ๋ณต์žกํ•œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๊ตฌํ˜„ ๊ณผ์ •์„ ๋‹จ์ˆœํ™”ํ•˜๊ณ , ๊ฐœ๋ฐœ์ž๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๋” ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” ์„ ์–ธ์  ๋ฐฉ์‹์˜ React ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ทœ๋ชจ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์„ ์‰ฝ๊ณ  ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. -### ์„ ์–ธ์  ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น +```tsx +import { createTracker } from "@offlegacy/event-tracker"; -```tsx {5, 10, 24, 26} -const Page = () => { - const { user, userId } = useUser(); +// ํŠธ๋ž˜์ปค ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ +const [Track, useTracker] = createTracker({ + DOMEvents: { + onClick: (params, context) => { + log("Click event:", params, context); + }, + }, +}); +// ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ +function App() { return ( - -
-

User: {user.name}

- -
+ + + + ); -}; +} +``` -const Counter = () => { - const [count, setCount] = useState(0); +### ์ฃผ์š” ๊ธฐ๋Šฅ - const handleIncrement = () => { - setCount(count + 1); - }; +Event Tracker๋Š” ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜๊ณผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์„ ๋ชจ๋‘ ๊ณ ๋ คํ•œ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. - return ( -
-

Count: {count}

- - - -
- ); -}; -``` +| Feature | Description | +| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ๊ฐ–์ถ˜ ์„ ์–ธ์  API | [TypeScript](https://www.typescriptlang.org/)๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ์ง€์›ํ•˜์—ฌ ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ์˜ ์˜ค๋ฅ˜๋ฅผ ์ค„์ด๊ณ , ์ž๋™ ์™„์„ฑ์„ ํ†ตํ•ด ์ƒ์‚ฐ์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค. | +| ๊ฐ•๋ ฅํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ | [Zod](https://zod.dev/)๋ฅผ ํ™œ์šฉํ•œ ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜ ๊ฒ€์ฆ์œผ๋กœ ๋ฐ์ดํ„ฐ์˜ ์‹ ๋ขฐ์„ฑ์„ ํ™•๋ณดํ•ฉ๋‹ˆ๋‹ค. | +| ์ตœ์ ํ™”๋œ ์„ฑ๋Šฅ | ๋ฐฐ์นญ์ด๋‚˜ ๋””๋ฐ”์šด์Šค, ์Šค๋กœํ‹€๋ง ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ตœ์†Œํ™”ํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ์ค„์ž…๋‹ˆ๋‹ค. | +| ์‹คํ–‰ ์ˆœ์„œ ๋ณด์žฅ | ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋“ค์— ๋Œ€ํ•ด์„œ๋„ ์˜๋„ํ•œ ์ˆœ์„œ๋Œ€๋กœ ์ฒ˜๋ฆฌ๋˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. | +| ์• ๋„๋ฆฌํ‹ฑ์Šค ๋„๊ตฌ ๋…๋ฆฝ์„ฑ | ํŠน์ • ์• ๋„๋ฆฌํ‹ฑ์Šค ์„œ๋น„์Šค์— ์ข…์†๋˜์ง€ ์•Š๊ณ , ์›ํ•˜๋Š” ๋ชจ๋“  ๋„๊ตฌ([Google Analytics](https://analytics.google.com/), [Amplitude](https://amplitude.com/) ๋“ฑ)์™€ ์œ ์—ฐํ•˜๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. | +| ๋ช…ํ™•ํ•œ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ | ํŠธ๋ž˜ํ‚น ๋กœ์ง๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํšจ๊ณผ์ ์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์ฝ”๋“œ์˜ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•ฉ๋‹ˆ๋‹ค. | -`event-tracker`๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„ ์–ธ์  ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์ด ๊ฐ€๋Šฅํ•ด์ ธ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ํ–ฅ์ƒ๋˜๊ณ  ๋ณต์žก์„ฑ์ด ๊ฐ์†Œํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœ์ž๋“ค์ด ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์„ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•์Šต๋‹ˆ๋‹ค. -์ด์ œ `handleIncrement` ํ•จ์ˆ˜๋Š” ์นด์šดํŠธ ์ฆ๊ฐ€์—๋งŒ ์ฑ…์ž„์ด ์žˆ๊ณ , ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์€ `` ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. -**์ด๋Ÿฌํ•œ ์„ ์–ธ์  ์ ‘๊ทผ ๋ฐฉ์‹์€ ๊ฐœ๋ฐœ์ž๊ฐ€ '์–ด๋–ป๊ฒŒ ํŠธ๋ž˜ํ‚นํ• ์ง€'๊ฐ€ ์•„๋‹Œ '๋ฌด์—‡์„ ํŠธ๋ž˜ํ‚นํ• ์ง€'์— ์ง‘์ค‘ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.** -์–ด๋–ป๊ฒŒ ํŠธ๋ž˜ํ‚นํ• ์ง€๋Š” React ์•ฑ ์™ธ๋ถ€์—์„œ ์ •์˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +## ํ•ต์‹ฌ ๊ฐœ๋… -### ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์™ธ๋ถ€์—์„œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ์‘์ง‘๋„ ๊ฐœ์„  +Event Tracker๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์•Œ์•„์•ผ ํ•  ๋ช‡ ๊ฐ€์ง€ ํ•ต์‹ฌ ๊ฐœ๋…์ด ์žˆ์Šต๋‹ˆ๋‹ค. -```tsx -const [Track] = createTracker({ - DOMEvents: { - onClick: (params, context) => { - log({ - event: "click", - params: { - ...params, - userId: context.userId, - }, - }); - }, - }, - onImpression: (params, context) => { - log({ - event: "impression", - params: { - ...params, - userId: context.userId, - }, - }); - }, -}); -``` + -์ด์ œ **'์–ด๋–ป๊ฒŒ ์ถ”์ ํ• ์ง€'**์— ๋Œ€ํ•œ ์ฝ”๋“œ๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ„๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. -์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์™ธ๋ถ€์— ์œ„์น˜ํ•˜๋ฏ€๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๋กœ์ง์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +### ์ธ์Šคํ„ด์Šค (`createTracker`) -### ๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ +๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์ถœ๋ฐœ์ ์ž…๋‹ˆ๋‹ค. `createTracker` ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠธ๋ž˜์ปค ์ธ์Šคํ„ด์Šค(`Track` ์ปดํฌ๋„ŒํŠธ ์ปฌ๋ ‰์…˜๊ณผ `useTracker` ํ›…)๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ, DOM ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ, ๋…ธ์ถœ(Impression) ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ, ์Šคํ‚ค๋งˆ ๋“ฑ์„ ์„ค์ •ํ•˜์—ฌ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. -`event-tracker`๋Š” [Zod](https://zod.dev/)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. +๋ชฉ์ ์— ๋”ฐ๋ผ ๊ตฌ๋ถ„ํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ํŠธ๋ž˜์ปค ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์˜ˆ๋ฅผ ๋“ค์–ด, Google Analytics๋กœ ๋ณด๋‚ด๋Š” ์ด๋ฒคํŠธ์™€ Amplitude๋กœ ๋ณด๋‚ด๋Š” ์ด๋ฒคํŠธ๋ฅผ ๊ตฌ๋ถ„ํ•˜์—ฌ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.) -```tsx -import { z } from "zod"; -import { createTracker } from "@offlegacy/event-tracker"; +### ํ”„๋กœ๋ฐ”์ด๋” (`Track.Provider`) -interface Context { - // ... -} +React์˜ Context API๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋˜๋Š” ํŠน์ • ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์˜ ์ตœ์ƒ๋‹จ์—์„œ `Track.Provider`๋กœ ๊ฐ์‹ธ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค์— ํŠธ๋ž˜ํ‚น์— ํ•„์š”ํ•œ ๊ณตํ†ต ๋ฐ์ดํ„ฐ(์ปจํ…์ŠคํŠธ)๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, `userId`, `pageName` ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์ปจํ…์ŠคํŠธ๋กœ ์ „๋‹ฌํ•˜๋ฉด, ๊ฐ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ์‹œ ์ด ์ •๋ณด๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -interface Params { - // ... -} +### ์ด๋ฒคํŠธ ์ปดํฌ๋„ŒํŠธ (`Track.Click`, `Track.PageView` ๋“ฑ) -// ์Šคํ‚ค๋งˆ ์ •์˜ -const schemas = { - page_view: z.object({ - title: z.string(), - }), - click_button: z.object({ - target: z.string(), - }), -}; - -// ํŠธ๋ž˜์ปค ์„ค์ • -const [Track] = createTracker({ - // ๊ธฐํƒ€ ์„ค์ •... - - schema: { - schemas: { - page_view, - click_button, - }, - onSchemaError: (error) => { - console.error("Schema validation error:", error); - }, - abortOnError: true, - }, -}); +์„ ์–ธ์ ์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ํŠธ๋ž˜ํ‚นํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œ๊ณต๋˜๋Š” ํŠน์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋“ค์ž…๋‹ˆ๋‹ค. `createTracker` ๋ฆฌํ„ด ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ์ž…๋‹ˆ๋‹ค. -// ์Šคํ‚ค๋งˆ ์‚ฌ์šฉํ•˜๊ธฐ -; -; -``` +- `Track.Click`: ์ž์‹ ์š”์†Œ์—์„œ ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํŠธ๋ž˜ํ‚นํ•ฉ๋‹ˆ๋‹ค. +- `Track.Impression`: ์ž์‹ ์š”์†Œ๊ฐ€ ํ™”๋ฉด์— ๋…ธ์ถœ๋˜์—ˆ์„ ๋•Œ ํŠธ๋ž˜ํ‚นํ•ฉ๋‹ˆ๋‹ค. +- `Track.PageView`: ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ ํŽ˜์ด์ง€ ๋ทฐ ์ด๋ฒคํŠธ๋ฅผ ํŠธ๋ž˜ํ‚นํ•ฉ๋‹ˆ๋‹ค. -## ์ฃผ์š” ๊ธฐ๋Šฅ +์ด ์™ธ์—๋„ ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜ ๋ฐ ์ƒ๋ช…์ฃผ๊ธฐ ์ด๋ฒคํŠธ์— ๋Œ€์‘ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ œ๊ณตํ•˜๊ฑฐ๋‚˜ ์ปค์Šคํ…€ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์ปดํฌ๋„ŒํŠธ๋Š” `context`, `params` prop์„ ํ†ตํ•ด ํ•ด๋‹น ์ด๋ฒคํŠธ์™€ ๊ด€๋ จ๋œ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›๊ณ  ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -- ๐ŸŽฏ **ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ๊ฐ–์ถ˜ API**: ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ๊ฐ–์ถ˜ ์„ ์–ธ์  ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ์ œ๊ณต -- ๐Ÿ›ก๏ธ **๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ**: ์Šคํ‚ค๋งˆ๋ฅผ ์‚ฌ์šฉํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž… ์•ˆ์ „์„ฑ๊ณผ ์œ ํšจ์„ฑ ๋ณด์žฅ -- โšก๏ธ **์ตœ์ ํ™”๋œ ์„ฑ๋Šฅ**: ์ด๋ฒคํŠธ ๋ฐฐ์นญ์„ ํ†ตํ•œ ํ–ฅ์ƒ๋œ ์„ฑ๋Šฅ -- ๐Ÿ”„ **์ˆœ์„œ ๋ณด์žฅ**: ๋น„๋™๊ธฐ ์ž‘์—…์— ๋Œ€ํ•œ ์‹คํ–‰ ์ˆœ์„œ ๋ณด์žฅ -- ๐Ÿ”Œ **์• ๋„๋ฆฌํ‹ฑ์Šค ๋„๊ตฌ์™€์˜ ๋…๋ฆฝ์„ฑ**: ์„ ํƒํ•œ ๋ชจ๋“  ์• ๋„๋ฆฌํ‹ฑ์Šค ๋„๊ตฌ์™€ ํ•จ๊ป˜ ์ž‘๋™ -- ๐Ÿงฉ **๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ**: ํŠธ๋ž˜ํ‚น ๋กœ์ง๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ๋ถ„๋ฆฌ ์œ ์ง€ -- ๐Ÿ“ฆ **์ž‘์€ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ**: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ฏธ์น˜๋Š” ๋ฒˆ๋“ค ํฌ๊ธฐ ์˜ํ–ฅ ์ตœ์†Œํ™” +### ์ปค์Šคํ…€ ํ›… (`useTracker`) -## ํ•ต์‹ฌ ๊ฐœ๋… +์ปดํฌ๋„ŒํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋‚˜ DOM ์ด๋ฒคํŠธ์™€ ์ง์ ‘์ ์œผ๋กœ ๊ด€๋ จ๋˜์ง€ ์•Š์€, ๋ณด๋‹ค ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ์กฐ๊ฑด๋ถ€์ ์ธ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์ด ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. `useTracker` ํ›…์„ ์‚ฌ์šฉํ•˜๋ฉด `Track.Provider`๋กœ๋ถ€ํ„ฐ ์ปจํ…์ŠคํŠธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ์ •์˜๋œ ํŠธ๋ž˜ํ‚น ๋กœ์ง์„ ๋ช…๋ นํ˜•์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -`event-tracker`๋Š” ๋ช‡ ๊ฐ€์ง€ ํ•ต์‹ฌ ๊ฐœ๋…์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์ถ•๋˜์—ˆ์Šต๋‹ˆ๋‹ค: + -1. **Tracker ์ƒ์„ฑ**: `createTracker`๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ์ง€์นจ์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠธ๋ž˜์ปค ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. -2. **Provider**: `Track.Provider`๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ์„ ๋ž˜ํ•‘ํ•˜์—ฌ ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. -3. **์ด๋ฒคํŠธ ์ปดํฌ๋„ŒํŠธ**: `Track.Click` ๋˜๋Š” `Track.Impression`๊ณผ ๊ฐ™์€ ์ด๋ฒคํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฒคํŠธ๋ฅผ ํŠธ๋ž˜ํ‚นํ•ฉ๋‹ˆ๋‹ค. -4. **์ปค์Šคํ…€ Hook**: `useTracker` ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ช…๋ น์ ์œผ๋กœ ํŠธ๋ž˜ํ‚น ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +## ๋‹ค์Œ ๋‹จ๊ณ„ -๋‹ค๋ฅธ ์„น์…˜์—์„œ ๊ฐ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: +์ด๋Ÿฌํ•œ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค์€ ์„œ๋กœ ์œ ๊ธฐ์ ์œผ๋กœ ์ž‘๋™ํ•˜์—ฌ Event Tracker์˜ ๊ฐ•๋ ฅํ•˜๊ณ  ์œ ์—ฐํ•œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋” ์ž์„ธํ•œ ์‚ฌ์šฉ๋ฒ•๊ณผ ๊ฐ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์‹ฌ์ธต์ ์ธ ๋‚ด์šฉ์€ ์•„๋ž˜ ๋ฌธ์„œ๋“ค์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -- [createTracker](/docs/create-tracker) - `createTracker`์— ๋Œ€ํ•œ ์ž์„ธํ•œ API ๋ฌธ์„œ -- [components](/docs/components) - ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํŠธ๋ž˜ํ‚น ์ปดํฌ๋„ŒํŠธ -- [hook](/docs/hook) - ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠธ๋ž˜ํ‚น -- [Batching](/docs/batching) - ์ด๋ฒคํŠธ ๋ฐฐ์นญ์„ ํ†ตํ•œ ์„ฑ๋Šฅ ์ตœ์ ํ™” -- [Data Type Validation](/docs/data-type-validation) - ์Šคํ‚ค๋งˆ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ํƒ€์ž… ์•ˆ์ „์„ฑ๊ณผ ์œ ํšจ์„ฑ ๋ณด์žฅ +- [์™œ Event Tracker์ธ๊ฐ€์š”?](/docs/why-event-tracker): Event Tracker์˜ ํ•„์š”์„ฑ๊ณผ ์ฃผ์š” ๊ธฐ๋Šฅ ์†Œ๊ฐœ +- [`createTracker`](/docs/create-tracker): ํŠธ๋ž˜์ปค ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ๋ฐ ์ƒ์„ธ ์„ค์ • ๊ฐ€์ด๋“œ +- [Components](/docs/components): ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ํŠธ๋ž˜ํ‚น ์ปดํฌ๋„ŒํŠธ์™€ ์‚ฌ์šฉ ์˜ˆ์‹œ +- [`useTracker`](/docs/hook): ์ปค์Šคํ…€ ํ›…์„ ํ™œ์šฉํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ํŠธ๋ž˜ํ‚น ๊ธฐ๋ฒ• +- [Batching](/docs/batching): ์ด๋ฒคํŠธ ๋ฐฐ์นญ์„ ํ†ตํ•œ ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ „๋žต +- [Data Type Validation](/docs/data-type-validation): Zod ์Šคํ‚ค๋งˆ๋ฅผ ํ™œ์šฉํ•œ ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๊ฐ€์ด๋“œ diff --git a/docs/src/content/ko/installation.mdx b/docs/src/content/ko/installation.mdx index 87fba1a..9a3705c 100644 --- a/docs/src/content/ko/installation.mdx +++ b/docs/src/content/ko/installation.mdx @@ -1,19 +1,9 @@ # ์„ค์น˜ -npm์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜ํ•˜๊ธฐ: +Event Tracker๋Š” React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. `React 18.0.0` ์ด์ƒ์˜ ๋ฒ„์ „์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -```bash -npm install @offlegacy/event-tracker -``` +์ตœ์‹  ์•ˆ์ • ๋ฒ„์ „์„ ์„ค์น˜ํ•˜๋ ค๋ฉด ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”. -yarn์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜ํ•˜๊ธฐ: - -```bash -yarn add @offlegacy/event-tracker -``` - -pnpm์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜ํ•˜๊ธฐ: - -```bash -pnpm add @offlegacy/event-tracker +```shell npm2yarn +npm install @offlegacy/event-tracker ``` diff --git a/docs/src/content/ko/use-tracker.mdx b/docs/src/content/ko/use-tracker.mdx new file mode 100644 index 0000000..81385cb --- /dev/null +++ b/docs/src/content/ko/use-tracker.mdx @@ -0,0 +1,225 @@ +# useTracker + +[`createTracker`](/docs/create-tracker)์—์„œ ๋‘ ๋ฒˆ์งธ ๋ฐฐ์—ด ํ•ญ๋ชฉ์œผ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ์ปค์Šคํ…€ React hook์ž…๋‹ˆ๋‹ค. ์ด ํ›…์€ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๊ธฐ๋Šฅ๊ณผ ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ๋น„๋™๊ธฐ ์ž‘์—…์ด ์™„๋ฃŒ๋œ ํ›„ ๋˜๋Š” ์‚ฌ์šฉ์ž์˜ ํŠน์ • ์ž…๋ ฅ ๊ฐ’์— ๋”ฐ๋ผ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์•ผ ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. + +```tsx +import { createTracker } from "@offlegacy/event-tracker"; + +const [Track, useTracker] = createTracker({...}) + +function MyComponent() { + const { setContext, getContext, track, trackWithSchema } = useTracker(); + + return ( + // ์ปดํฌ๋„ŒํŠธ ๋‚ด์šฉ + ); +} +``` + +### ๋ฐ˜ํ™˜ ๊ฐ’ + +์ด hook์€ ๋‹ค์Œ ์†์„ฑ์„ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค: + +#### setContext + +- Type: `(context: Context) => void` +- ํ˜„์žฌ ํŠธ๋ž˜ํ‚น ์ปจํ…์ŠคํŠธ๋ฅผ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. +- ์‚ฌ์šฉ์ž ์ •๋ณด, ์„ธ์…˜ ๋ฐ์ดํ„ฐ ๋“ฑ์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```tsx +const { setContext } = useTracker(); + +// ์ƒˆ๋กœ์šด ์ปจํ…์ŠคํŠธ ์„ค์ • +setContext({ userId: "user-123" }); + +// ์ด์ „ ๊ฐ’์— ๊ธฐ๋ฐ˜ํ•œ ์ปจํ…์ŠคํŠธ ์—…๋ฐ์ดํŠธ +setContext((prev) => ({ + ...prev, + lastActive: new Date(), +})); +``` + +#### getContext + +- Type: `() => Context` +- ํ˜„์žฌ ํŠธ๋ž˜ํ‚น ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. +- ํ˜„์žฌ ํŠธ๋ž˜ํ‚น ์ƒํƒœ์— ์ ‘๊ทผํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. + +```tsx +const { getContext } = useTracker(); + +const currentContext = getContext(); +console.log("Current user:", currentContext.userId); +``` + +#### track + +- Type: `Record void>` +- ๋ชจ๋“  ๊ตฌ์„ฑ๋œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ํ•จ์ˆ˜๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด +- key๋Š” ํŠธ๋ž˜์ปค ๊ตฌ์„ฑ์— ์ •์˜๋œ ์ด๋ฒคํŠธ ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. +- ๊ณ ๊ธ‰ ์ œ์–ด๋ฅผ ์œ„ํ•œ ์„ ํƒ์  `TrackingOptions`๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. + +```tsx +const { track } = useTracker(); + +// ๊ฐ„๋‹จํ•œ ํด๋ฆญ ์ด๋ฒคํŠธ ์ถ”์  +track.onClick({ buttonId: "submit" }); + +// ์กฐ๊ฑด๋ถ€ ๋กœ์ง๊ณผ ํ•จ๊ป˜ ์ถ”์  +track.onClick( + { buttonId: "premium" }, + { + enabled: (context) => context.user?.isPremium, + }, +); + +// ๋””๋ฐ”์šด์‹ฑ๊ณผ ํ•จ๊ป˜ ์ถ”์  +track.onClick( + { buttonId: "search" }, + { + debounce: { delay: 300, leading: false, trailing: true }, + }, +); + +// ์Šค๋กœํ‹€๋ง๊ณผ ํ•จ๊ป˜ ์ถ”์  +track.onClick( + { buttonId: "rapid-action" }, + { + throttle: { delay: 1000, leading: true, trailing: false }, + }, +); + +// ๋…ธ์ถœ ์ด๋ฒคํŠธ ์ถ”์  +track.onImpression({ elementId: "hero" }); +``` + +#### trackWithSchema + +- Type: `Record void>` +- ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ์ด ํฌํ•จ๋œ ๋ชจ๋“  ๊ตฌ์„ฑ๋œ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ํ•จ์ˆ˜๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด +- key๋Š” ํŠธ๋ž˜์ปค ๊ตฌ์„ฑ์— ์ •์˜๋œ ์ด๋ฒคํŠธ ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. +- ๊ณ ๊ธ‰ ์ œ์–ด๋ฅผ ์œ„ํ•œ ์„ ํƒ์  `TrackingOptions`๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. + +```tsx +const { trackWithSchema } = useTracker(); + +// ์Šคํ‚ค๋งˆ์™€ ํ•จ๊ป˜ ํด๋ฆญ ์ด๋ฒคํŠธ ์ถ”์  +trackWithSchema.onClick({ schema: "click", params: { buttonId: "submit" } }); + +// ์กฐ๊ฑด๋ถ€ ๋กœ์ง๊ณผ ์Šคํ‚ค๋งˆ์™€ ํ•จ๊ป˜ ์ถ”์  +trackWithSchema.onClick( + { + schema: "premium_click", + params: { buttonId: "premium", userId: "123" }, + }, + { + enabled: (context, params) => context.user?.id === params.userId, + }, +); + +// ์Šค๋กœํ‹€๋ง๊ณผ ์Šคํ‚ค๋งˆ์™€ ํ•จ๊ป˜ ์ถ”์  +trackWithSchema.onImpression( + { + schema: "impression", + params: { elementId: "hero", userId: "123" }, + }, + { + throttle: { delay: 2000, leading: true, trailing: false }, + }, +); +``` + +### TrackingOptions + +`track`๊ณผ `trackWithSchema` ๋ฉ”์„œ๋“œ ๋ชจ๋‘ ๋‹ค์Œ ์˜ต์…˜์„ ํฌํ•จํ•˜๋Š” ์„ ํƒ์  ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค: + +- `enabled?: boolean | ((context: Context, params: EventParams) => boolean)` - ์ด๋ฒคํŠธ ์ถ”์ ์„ ์กฐ๊ฑด๋ถ€๋กœ ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™” +- `debounce?: DebounceConfig` - ์—ฐ์†์ ์ธ ์ด๋ฒคํŠธ ๋ฐœ์ƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋””๋ฐ”์šด์Šค ์„ค์ • +- `throttle?: ThrottleConfig` - ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋นˆ๋„๋ฅผ ์ œํ•œํ•˜๋Š” ์Šค๋กœํ‹€ ์„ค์ • + +**์ฐธ๊ณ :** `debounce`์™€ `throttle`์€ ์ƒํ˜ธ ๋ฐฐํƒ€์ ์ด๋ฉฐ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. + +#### DebounceConfig + +```tsx +interface DebounceConfig { + delay: number; // ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„์˜ ์ง€์—ฐ ์‹œ๊ฐ„ + leading?: boolean; // ์„ ํ–‰ ์—์ง€์—์„œ ์‹คํ–‰ (๊ธฐ๋ณธ๊ฐ’: false) + trailing?: boolean; // ํ›„ํ–‰ ์—์ง€์—์„œ ์‹คํ–‰ (๊ธฐ๋ณธ๊ฐ’: true) +} +``` + +#### ThrottleConfig + +```tsx +interface ThrottleConfig { + delay: number; // ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„์˜ ์ง€์—ฐ ์‹œ๊ฐ„ + leading?: boolean; // ์„ ํ–‰ ์—์ง€์—์„œ ์‹คํ–‰ (๊ธฐ๋ณธ๊ฐ’: true) + trailing?: boolean; // ํ›„ํ–‰ ์—์ง€์—์„œ ์‹คํ–‰ (๊ธฐ๋ณธ๊ฐ’: false) +} +``` + +### ์‚ฌ์šฉ ์˜ˆ์ œ + +๋‹ค์Œ์€ ์ด hook์„ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค: + +```tsx +import { createTracker } from "@offlegacy/event-tracker"; + +const [Track, useTracker] = createTracker({ + onClick: (params) => { + // ์ด๋ฒคํŠธ๋ฅผ ์• ๋„๋ฆฌํ‹ฑ์Šค ์„œ๋น„์Šค๋กœ ์ „์†ก + analytics.track(params); + }, + pageView: { + onPageView: (params) => { + // Send event to analytics service + analytics.pageView(params); + }, + }, +}); + +function UserProfile({ userId }) { + const { setContext, track, trackWithSchema } = useTracker(); + + useEffect(() => { + // ์‚ฌ์šฉ์ž ID๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์ปจํ…์ŠคํŠธ ์—…๋ฐ์ดํŠธ + setContext({ userId }); + + // ํŽ˜์ด์ง€ ๋ทฐ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น + track.onPageView({ page: "profile" }); + }, [userId]); + + const handleSettingsClick = () => { + // ์‚ฌ์šฉ์ž ์„ค์ • ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น + trackWithSchema.onClick({ schema: "settings", params: { userId } }); + }; + + return ( +
+

User Profile

+ +
+ ); +} +``` + +### Best Practices + +1. **์ปจํ…์ŠคํŠธ ์—…๋ฐ์ดํŠธ** + + - ์—ฌ๋Ÿฌ ์ด๋ฒคํŠธ์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ์ „์—ญ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•ด `setContext`๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. + - ์ด์ „ ์ƒํƒœ์— ๊ธฐ๋ฐ˜ํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•ด `setContext`์˜ ํ•จ์ˆ˜ ํ˜•ํƒœ๋ฅผ ๊ณ ๋ คํ•˜์„ธ์š”. + +2. **์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น** + + - ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ `track` ๋˜๋Š” `trackWithSchema`์—์„œ ์ด๋ฒคํŠธ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. + +3. **์„ฑ๋Šฅ** + + - ๋ Œ๋”๋ง ์ค‘์— ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ๋งˆ์„ธ์š”. + - ์ฝœ๋ฐฑ ๋˜๋Š” ํšจ๊ณผ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. + - [๋ฐฐ์นญ](/docs/batching)์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค์„ธ์š”. + - [๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ](/docs/data-type-validation)์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ํ™•์ธํ•˜์„ธ์š”. diff --git a/docs/src/content/ko/why-event-tracker.mdx b/docs/src/content/ko/why-event-tracker.mdx new file mode 100644 index 0000000..83331c5 --- /dev/null +++ b/docs/src/content/ko/why-event-tracker.mdx @@ -0,0 +1,186 @@ +import { Steps } from "nextra/components"; + +# ์™œ Event Tracker์ธ๊ฐ€์š”? + +ํ˜„๋Œ€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์‚ฌ์šฉ์ž์˜ ํ–‰๋™์„ ๋ถ„์„ํ•˜์—ฌ ์„œ๋น„์Šค ํ’ˆ์งˆ์„ ์ง€์†์ ์œผ๋กœ ๊ฐœ์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ธฐ์กด์˜ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๋ฐฉ์‹์€ ์—ฌ๋Ÿฌ ๋ฌธ์ œ์ ์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. + +## Event Tracker๊ฐ€ ํ•„์š”ํ•œ ์ด์œ  + +๋‹ค์Œ์€ ์ „ํ†ต์ ์ธ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ ์„ ๋ณด์—ฌ์ฃผ๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. + +- **Prop Drilling์˜ ๊ณ ํ†ต**: ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๊นŒ์ง€ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด ์ˆ˜๋งŽ์€ ๊ณ„์ธต์„ ๊ฑฐ์ณ prop์„ ๋‚ด๋ ค๋ณด๋‚ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์„ ํ•ด์น˜๊ณ  ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. +- **๋กœ์ง์˜ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ**: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๋กœ์ง์ด ํ•œ๋ฐ ์„ž์—ฌ ์ฝ”๋“œ์˜ ๋ณต์žก๋„๋ฅผ ๋†’์ด๊ณ , ๊ฐ ๋กœ์ง์˜ ๋…๋ฆฝ์ ์ธ ํ…Œ์ŠคํŠธ์™€ ์ˆ˜์ •์„ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. +- **๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ ์ฆ๊ฐ€**: ๋ฐ˜๋ณต์ ์ธ ํŠธ๋ž˜ํ‚น ์ฝ”๋“œ ์ž‘์„ฑ์€ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์„ ์ €ํ•ดํ•˜๋Š” ์š”์ธ์ด ๋ฉ๋‹ˆ๋‹ค. + +```tsx {8,15, 24-31} +function Page() { + const { user, userId } = useUser(); // ์‚ฌ์šฉ์ž ์ •๋ณด์™€ ID๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. + + return ( +
+

User: {user.name}

+ {/* Counter ์ปดํฌ๋„ŒํŠธ์— ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์„ ์œ„ํ•ด userId๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. */} + +
+ ); +} + +// ์˜ค์ง ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น๋งŒ์„ ์œ„ํ•ด์„œ 'userId'๋ฅผ prop์œผ๋กœ ์ „๋‹ฌ๋ฐ›์Šต๋‹ˆ๋‹ค. +// ๋งŒ์•ฝ Counter๊ฐ€ ๋” ๊นŠ์€ ๊ณณ์— ์žˆ๋‹ค๋ฉด, prop drilling์€ ๋”์šฑ ์‹ฌํ•ด์ง‘๋‹ˆ๋‹ค. +function Counter({ userId }: { userId: string }) { + const [count, setCount] = useState(0); + const { track } = useTrackEvent(); // ๊ฐ€์ƒ์˜ ํŠธ๋ž˜ํ‚น ํ›… + + const handleIncrement = () => { + const newCount = count + 1; + setCount(newCount); + + // ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง (์นด์šดํŠธ ์ฆ๊ฐ€)๊ณผ ํŠธ๋ž˜ํ‚น ๋กœ์ง์ด ํ˜ผ์žฌํ•ฉ๋‹ˆ๋‹ค. + track({ + event: "click_increment", + params: { + type: "count", + value: newCount, + userId, // ์ƒ์œ„๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ userId ์‚ฌ์šฉ + }, + }); + }; + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +## Event Tracker๊ฐ€ ์ œ์‹œํ•˜๋Š” ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„ + +Event Tracker๋Š” ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์„ ์œ„ํ•œ ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค. +Event Tracker๊ฐ€ ์ œ์‹œํ•˜๋Š” ์„ ์–ธ์  ๋ฐฉ์‹์€ ์ „ํ†ต์ ์œผ๋กœ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น๊ณผ ๊ด€๋ จ๋œ ๋ณต์žก์„ฑ์„ ๋‹จ์ˆœํ™”ํ•˜์—ฌ, ๋ชจ๋“  ๊ฐœ๋ฐœ์ž๋“ค์ด ์‰ฝ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. + + + +### ์„ ์–ธ์  ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น + +```tsx {7, 10, 12, 32, 34} +function Page() { + const { user, userId } = useUser(); + + // Track.Provider๋ฅผ ํ†ตํ•ด ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ํŠธ๋ž˜ํ‚น ์ปจํ…์ŠคํŠธ(userId)๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + // ๋” ์ด์ƒ prop drilling์ด ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค. + return ( + +
+

User: {user.name}

+ {/* userId๋ฅผ prop์œผ๋กœ ์ „๋‹ฌํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. */} +
+
+ ); +} + +function Counter() { + const [count, setCount] = useState(0); + + const handleIncrement = () => { + // ์ด์ œ handleIncrement ํ•จ์ˆ˜๋Š” ์ˆœ์ˆ˜ํ•˜๊ฒŒ ์นด์šดํŠธ ์ฆ๊ฐ€ ๋กœ์ง๋งŒ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. + setCount(count + 1); + }; + + return ( +
+

Count: {count}

+ {/* + Track.Click ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์‹ธ๊ณ , + ํด๋ฆญ ๋ฐœ์ƒ ์‹œ ์ •์˜๋œ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ํ•จ๊ป˜ ์ด๋ฒคํŠธ๋ฅผ ํŠธ๋ž˜ํ‚นํ•ฉ๋‹ˆ๋‹ค. + ์ปจํ…์ŠคํŠธ๋กœ ์ œ๊ณต๋œ userId๋Š” ์ž๋™์œผ๋กœ ํŠธ๋ž˜ํ‚น ๋ฐ์ดํ„ฐ์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. + */} + + + +
+ ); +} +``` + +Event Tracker๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„ ์–ธ์  ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์ด ๊ฐ€๋Šฅํ•ด์ ธ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ํ–ฅ์ƒ๋˜๊ณ  ๋ณต์žก์„ฑ์ด ๊ฐ์†Œํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœ์ž๋“ค์ด ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์„ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•์Šต๋‹ˆ๋‹ค. + +์ด์ œ `handleIncrement` ํ•จ์ˆ˜๋Š” ์นด์šดํŠธ ์ฆ๊ฐ€์—๋งŒ ์ฑ…์ž„์ด ์žˆ๊ณ , ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น์€ `` ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. +**์ด๋Ÿฌํ•œ ์„ ์–ธ์  ์ ‘๊ทผ ๋ฐฉ์‹์€ ๊ฐœ๋ฐœ์ž๊ฐ€ '์–ด๋–ป๊ฒŒ ํŠธ๋ž˜ํ‚นํ• ์ง€'๊ฐ€ ์•„๋‹Œ '๋ฌด์—‡์„ ํŠธ๋ž˜ํ‚นํ• ์ง€'์— ์ง‘์ค‘ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.** +์–ด๋–ป๊ฒŒ ํŠธ๋ž˜ํ‚นํ• ์ง€๋Š” React ์•ฑ ์™ธ๋ถ€์—์„œ ์ •์˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +### ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ์‘์ง‘๋„ ๊ฐœ์„  + +```tsx {4-10, 14-20} +const [Track, useTracker] = createTracker({ + // DOM ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ์‹คํ–‰๋  ์ฝœ๋ฐฑ ํ•จ์ˆ˜ + DOMEvents: { + onClick: (params, context) => { + // ์‹ค์ œ ํŠธ๋ž˜ํ‚น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Google Analytics, Amplitude ๋“ฑ) ํ˜ธ์ถœ + logEvent("click_event", { + ...params, // { value: ..., type: "count" } + userId: context.userId, // Provider๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ userId + }); + }, + // ํ•„์š”์— ๋”ฐ๋ผ onMouseOver, onFocus ๋“ฑ ๋‹ค์–‘ํ•œ DOM ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์ •์˜ ๊ฐ€๋Šฅ + }, + // ํ™”๋ฉด ๋…ธ์ถœ(Impression) ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ์‹คํ–‰๋  ์ฝœ๋ฐฑ ํ•จ์ˆ˜ + onImpression: (params, context) => { + logEvent("impression_event", { + ...params, + userId: context.userId, + pagePath: window.location.pathname, + }); + }, +}); +``` + +์ด์ œ '์–ด๋–ป๊ฒŒ ์ถ”์ ํ• ์ง€'์— ๋Œ€ํ•œ ์ฝ”๋“œ๊ฐ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ„๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์™ธ๋ถ€์— ์œ„์น˜ํ•˜๋ฏ€๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๋กœ์ง์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### ๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ + +```tsx {13-20, 24-33} +import { z } from "zod"; +import { createTracker } from "@offlegacy/Event Tracker"; + +interface Context { + /* ... */ +} + +interface Params { + /* ... */ +} + +// ์Šคํ‚ค๋งˆ ์ •์˜ +const schemas = { + page_view: z.object({ + title: z.string(), + }), + click_button: z.object({ + target: z.string(), + }), +}; + +// ํŠธ๋ž˜์ปค ์„ค์ • +const [Track] = createTracker({ + schema: { + schemas: { + page_view, + click_button, + }, + onSchemaError: (error) => { + console.error("Schema validation error:", error); + }, + abortOnError: true, + }, +}); + +// ์Šคํ‚ค๋งˆ ์‚ฌ์šฉํ•˜๊ธฐ +; +; +``` + +Event Tracker๋Š” ์„ ํƒ์ ์œผ๋กœ [Zod](https://zod.dev/) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ†ตํ•ฉํ•˜์—ฌ ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜์˜ ๊ฐ•๋ ฅํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฒ€์ฆ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ , ํŠธ๋ž˜ํ‚น ๋ฐ์ดํ„ฐ์˜ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +