PageSourceSearch

https://www.asseystore.co.kr/js/product_tracker.js?1745208073

js asseystore.co.kr collected 2026-09-24 15:05:45 UTC 5,049 bytes, 153 lines download raw bytes

1/**
2 * 유저의 행동에 따른 상품 데이터 수집
3 * 
4 * 1. 클릭 이벤트 attachOnClickEvent
5 * 2. 노출 이벤트 trackImpression
6 */
7class ProductTracker {
8    constructor($list_wrap = null) {
9        // $list_wrap이 전달되지 않았거나 유효하지 않은 경우 document 사용
10        this.$list_wrap = $list_wrap || $(document);
11        
12        // jQuery 객체가 아닌 경우 jQuery로 변환
13        if (this.$list_wrap && typeof this.$list_wrap.jquery === 'undefined') {
14            this.$list_wrap = $(this.$list_wrap);
15        }
16        
17        this.impressedProducts = [];
18        this.debounceTimer = null;       // Debounce 타이머 ID
19        this.DEBOUNCE_DELAY = 1000;      // Debounce 지연 시간 (밀리초)
20        this.debouncedTrackImpression = null; // Debounced 함수를 저장할 속성
21    }
22
23    init() {
24        if (typeof BrandScope === 'undefined') {
25            throw new Error('BrandScope is undefined');
26        }
27
28        this.debouncedTrackImpression = () => {
29            clearTimeout(this.debounceTimer);
30            this.debounceTimer = setTimeout(() => {
31                this.trackImpression();
32            }, this.DEBOUNCE_DELAY);
33        }
34
35        this.attachOnClickEvent();
36        this.observeImpression();
37    }
38
39    attachOnClickEvent() {
40        this.$list_wrap.find('._shop_item').one('mouseup', function(e) {
41            // 마우스 왼쪽 또는 휠 클릭이 아니면 이벤트 종료
42            if (e.which !== 1 && e.which !== 2) { 
43                return;
44            }
45
46            const productData = $(this).data('product-properties');
47
48            if (productData === undefined) {
49                throw new Error('productData is undefined');
50            } 
51
52            BrandScope.track({
53                eventType: 'Select',
54                props: {
55                    ownership: 'behavior-tracking-analytics',
56                    selectedType: 'PRODUCT',
57                    selectedNo: productData.idx,
58                    selectedCode: productData.code,
59                    selectedName: productData.name,
60                    selectedImageUrl: productData.image_url,
61                    productPrice: productData.price,
62                    productOriginPrice: productData.original_price,
63                }
64            });
65        });
66    }
67
68    trackImpression() {
69        const productsToTrack = [...this.impressedProducts];
70
71        if (productsToTrack.length === 0) {
72            return;
73        }
74
75        this.impressedProducts = [];
76
77        const data = productsToTrack.map(product => ({
78            productNo: product.idx,
79            productCode: product.code,
80            productName: product.name,
81            productImageUrl: product.image_url,
82            productPrice: product.price,
83            productOriginPrice: product.original_price,
84        }))
85
86        BrandScope.track({
87            eventType: 'ProductImpression',
88            props: {
89                ownership: 'behavior-tracking-analytics',
90                impressedTarget: 'product_item',
91                impressionType: 'PRODUCT',
92                products: data
93            }
94        });
95    }
96
97    /**
98     * 노출 추적 관찰자 설정
99     * DELAY 이상 노출 된 상품에 대해서만 노출 추적 이벤트 발생
100     */
101    observeImpression() {
102        const DELAY = 500;
103        const intersectionTimer = new Map(); // 각 상품의 타이머를 저장할 Map
104
105        const observer = new IntersectionObserver((entries, observer) => {
106            entries.forEach(entry => {
107                try {
108                    const productData = JSON.parse(entry.target.dataset.productProperties);
109
110                    const idx = productData.idx;
111
112                    if (idx === undefined) {
113                        return;
114                    }
115                    
116                    if (entry.isIntersecting) {
117                        if (intersectionTimer.has(idx)) {
118                            return;
119                        }
120
121                        intersectionTimer.set(idx, setTimeout(() => {
122                            this.impressedProducts.push(productData);
123
124                            if (this.impressedProducts.length > 0) {
125                                this.debouncedTrackImpression();
126                            }
127
128                            observer.unobserve(entry.target);
129                        }, DELAY));
130
131                    } else {
132                        if (!intersectionTimer.has(idx)) {
133                            return;
134                        }
135
136                        clearTimeout(intersectionTimer.get(idx));
137                        intersectionTimer.delete(idx);
138                    }
139                } catch (_) {
140                    // 오류 발생 시 무시
141                }
142            });
143        }, {
144            threshold: 0.8
145        });
146
147        const shopItems = this.$list_wrap.find('._shop_item');
148        
149        shopItems.each(function(){
150            observer.observe($(this)[0]);
151        });
152    }
153}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.