Skip to content

Feat/ga4 mp#71

Merged
Turtle-Hwan merged 30 commits into
Turtle-Hwan:mainfrom
ArticPenguin:feat/ga4-MP
Apr 28, 2026
Merged

Feat/ga4 mp#71
Turtle-Hwan merged 30 commits into
Turtle-Hwan:mainfrom
ArticPenguin:feat/ga4-MP

Conversation

@ArticPenguin
Copy link
Copy Markdown
Contributor

No description provided.

ArticPenguin and others added 26 commits April 27, 2026 17:47
- Rename helpers to match GA4-Data-Taxonomy.md naming convention
  - sendTabChange → sendNavigationTabSelect
  - sendLinkClick → sendLinkOpen (+ linkGroup, sameHostVariant params)
  - sendGAEvent("search") → sendSearchSubmit
  - sendSettingChange → sendSettingsCredentialsSaved / Deleted
  - sendButtonClick("google_login/logout") → sendAuthLoginStart / sendAuthLogout
  - sendError → sendSystemError
- Add sendExtensionOpen: single call emits extension_first_open (최초 1회),
  extension_session_start (새 세션 시), extension_open (매번) in one fetch
- getOrCreateSessionId now returns { sessionId, isNewSession } to support
  session_start detection without an extra storage read
- Restore and add JSDoc for all exported functions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- sendButtonClick("google_login") → sendAuthLoginStart("google", ...)
- sendButtonClick("google_logout") → sendAuthLogout(...)
- sendSettingChange("credentials", "saved") → sendSettingsCredentialsSaved()
- sendSettingChange("credentials", "deleted") → sendSettingsCredentialsDeleted()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auth: sendAuthLoginSuccess, sendAuthLoginFail,
      sendAuthEmailVerificationStart, sendAuthEmailVerificationSuccess

Template (editor): sendTemplateEditorOpen, sendTemplateItemAdd,
      sendTemplateSaveSuccess/Fail, sendTemplateSyncSuccess/Fail,
      sendTemplatePublishSuccess/Fail, sendTemplateApply

Template (gallery): sendTemplateGalleryOpen, sendTemplateGallerySearch,
      sendTemplateGallerySortChange, sendTemplateCloneSuccess/Fail,
      sendTemplateLikeToggle

Alerts: sendAlertsViewOpen, sendAlertsItemOpen
Todo: sendTodoViewOpen, sendTodoItemCreate, sendTodoItemComplete, sendTodoItemDelete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
EditorHeader: save/sync/publish success+fail events with templateId,
  origin (owned|cloned), itemCount, errorCode, errorMessage
EditorPage: extension_editor_open on mount (origin derived from route params),
  template_item_add on staging→canvas drag
TemplateListPage: template_apply with origin (default|owned|cloned)
  and isDefault flag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SettingsDialog: auth_login_success (with is_guest flag) and
  auth_login_fail (error_code: login_failed | exception) after
  startGoogleLogin resolves
EmailVerificationDialog: auth_email_verification_start when dialog
  opens, auth_email_verification_success after verifyEmailCode succeeds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Alerts: alerts_view_open on mount (viewMode, category resolved from storage)
AlertItem: alerts_item_open on click (alertId, category, source: general|department)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TodoList: todo_view_open after initial load (1회, useRef guard),
  todo_item_complete and todo_item_delete on custom todo actions
TodoAddDialog: todo_item_create after addCustomTodo succeeds
  (source: "dialog", has_due_date: always true by form validation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- template_gallery_open on mount (entry_point: "popup")
- template_gallery_search on debounced query (query_length, sort_option)
- template_gallery_sort_change on sort dropdown select
- template_clone_success (posted_template_id, author_id_present) after clone
- template_clone_fail (posted_template_id, error_code) on error
- template_like_toggle (posted_template_id, liked) after like resolves

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…or call sites

ItemPropertiesPanel: sendTemplateItemAdd('button', templateItemId) in
  handleMoveToCanvas — covers the button path alongside the existing drag path
App: connect sendSystemError to ErrorBoundary onError so uncaught React
  errors are reported as system_error events (error_code: react_error_boundary)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
'sort'를 useEffect 의존성에 추가해 stale 클로저로 인한 잘못된
sort_option 전송 방지 및 eslint exhaustive-deps 경고 제거.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sendTodoViewOpen 전송을 별도 useEffect로 분리해 loadTodoList의
useCallback deps에서 allTodos.length를 제거함. 기존 구조에서는
setECampusTodos → allTodos 변경 → loadTodoList 재계산 → useEffect
재실행으로 eCampus API가 불필요하게 재호출되는 문제가 있었음.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
standardCategories 집합으로 category 필드를 검사해 학과명이 들어온
경우를 학과 공지(source=department)로 정확히 분류함. 기존에는
department 필드 유무만 확인해 category에 학과명이 들어온 형태를
general로 잘못 보고했음.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sendTemplateCloneFail에 errorMessage 선택 파라미터를 추가해
sendTemplateSaveFail, sendTemplateSyncFail 등 다른 fail 헬퍼와
시그니처를 통일함. 호출부에서 error.message를 errorCode로 넘기던
잘못된 패턴도 수정.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ments

taxonomy 강제를 위해 sendGAEvent의 export를 제거함. 모든 이벤트 정의는
이 파일 안에서만 이루어지므로 외부에서 직접 호출할 이유가 없음.

모듈 상단 JSDoc에 설계 원칙·도메인 헬퍼 목록·전송 흐름 추가.
getOrCreateSessionId, sendGAEvent, sendExtensionOpen 세 함수에
비자명한 동작(세션 fallback 부작용, engagement_time_msec 이유,
lifecycle 이벤트 배열 누적 조건)에 대한 인라인 주석 추가.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/debug/mp/collect는 페이로드 유효성 검증만 하고 GA4에 기록하지 않아
DebugView에 이벤트가 나타나지 않는 문제가 있었음.

DebugView 표시는 엔드포인트가 아닌 debug_mode: 1 파라미터로 제어되므로
항상 /mp/collect(production endpoint)로 전송하도록 수정함.
GA_DEBUG_ENDPOINT 상수도 함께 제거.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
모든 이벤트 상태를 '신규 제안' 대신 '구현됨' / '미구현'으로 명확히 표기.
주요 변경사항:
- 구현된 이벤트 전체를 '구현됨'으로 업데이트
- template_clone_fail 파라미터에 error_message? 추가 반영
- navigation_tab_select에서 ui_location 미전송 명시
- button_click을 Core Product Events에 추가
- 'Current Event Mapping'을 'Migration History'로 대체
  (구 이벤트는 이미 모두 제거됐으므로 이력 표로 정리)
- MVP Recommendation 표에 상태 컬럼 추가
- Identity Model에 DebugView 동작 방식 설명 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MV3에서 popup close 시 async fetch 신뢰성 문제로 수집 불가.
세션 종료 추정은 session_id 30분 타임아웃으로 충분하며
정의된 Explore Reports에서도 사용처가 없어 제거.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/mp/collect는 204 No Content를 반환하므로 response.json() 호출 시
SyntaxError가 throw되어 catch 블록으로 빠지면서 실제 전송은 성공했음에도
[GA] Error sending event 로그가 찍히는 문제 수정.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
미구현 상태였던 이벤트들의 헬퍼 함수 추가:
- template_create_start, template_name_edit, template_item_update,
  template_item_delete, template_delete
- settings_open
- banner_open
- alerts_subscription_change
- labs_view_open, labs_feature_use

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e events

- banner_open: ImageCarousel 배너 클릭 시 img/alt/position 전송
- settings_open: MainLayout 설정 아이콘 클릭 시 전송
- template_create_start: 기본/빈 템플릿 생성 진입 시 origin 구분 전송
- template_delete: 로컬/서버 삭제 성공 후 origin·sync_status 전송

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- template_name_edit: 1초 debounce 후 전송 (keystroke 과다 방지)
- template_item_update: 아이템 속성 저장 성공 시 전송
- template_item_delete: 영구삭제(staging) / 캔버스→임시저장(canvas) 구분 전송

templateId를 ItemPropertiesPanelForm props로 추가해 이벤트 맥락 제공.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- labs_view_open: LabsDialog 진입 시 전송
- labs_feature_use: QR 생성 성공/실패, 도서관 열람실 클릭 시 전송
- alerts_subscription_change: 학과 구독/취소 성공 시 category·result 전송

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
banner_open, template_create_start, template_name_edit,
template_item_update, template_item_delete, template_delete,
settings_open, alerts_subscription_change, labs_view_open,
labs_feature_use 모두 구현 완료.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
구현된 전체 47개 이벤트를 알파벳 순으로 정리한 레퍼런스 표 추가.
이벤트명 / 수집 목적 / 수집 속성 / 사용 코드 / 잔존이슈 포함.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 897efb1776

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const handleMoveToCanvas = () => {
if (!isFromStaging) return;
dispatch({ type: 'MOVE_TO_CANVAS', payload: selectedItem.templateItemId });
sendTemplateItemAdd('button', selectedItem.templateItemId);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Send template ID, not item ID, in template_item_add

This call passes selectedItem.templateItemId as the second argument, but sendTemplateItemAdd maps that argument to GA4 template_id. In the staging→canvas button flow, that records an item identifier as a template identifier, so template_item_add data from this path is mis-keyed and cannot be reliably analyzed with template-level events like save/apply.

Useful? React with 👍 / 👎.

Comment thread src/utils/analytics.ts Outdated
Comment on lines +214 to +216
if (!firstOpenSent) {
events.push({ name: "extension_first_open", params: baseParams });
await setStorage({ firstOpenSent: true });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist first-open marker only after lifecycle send succeeds

firstOpenSent is stored before the lifecycle payload is posted. If the first popup open occurs while offline (or the fetch fails), the marker remains set and extension_first_open will never be retried for that client, permanently undercounting first-open cohorts and retention baselines.

Useful? React with 👍 / 👎.

Comment thread src/pages/EditorPage.tsx Outdated
Comment on lines +194 to +195
const origin = templateId ? 'owned' : startFrom === 'default' ? 'default' : 'local_only';
sendTemplateEditorOpen(origin, templateId ? parseInt(templateId) : undefined);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Derive editor-open origin from real template source

When templateId exists, origin is hardcoded to 'owned'. Cloned templates are also opened via /editor/:templateId, so those sessions will be reported as owned and distort template_editor_open breakdowns by origin. This should use actual template metadata (or explicit navigation state) instead of inferring from presence of an ID.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a comprehensive GA4 Measurement Protocol taxonomy for the LinKU Chrome Extension, replacing generic event tracking with structured, domain-specific event helpers. It includes a new taxonomy documentation file and updates various components to use these specific helpers for lifecycle, navigation, search, authentication, template management, and other product features. A critical issue was identified in src/components/Tabs/Alerts/AlertItem.tsx where standardCategories is used but not defined, which will cause a runtime error. Additionally, the use of eslint-disable-next-line in EditorPage.tsx and the hardcoded engagement_time_msec in analytics.ts were noted as areas for potential improvement.

import type { Alert, AlertCategory } from "@/types/api";
import { ExternalLink, Calendar } from "lucide-react";
import { cn } from "@/lib/utils";
import { sendAlertsItemOpen } from "@/utils/analytics";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The variable standardCategories is used in the handleClick function but is not defined or imported in this file. This will cause a ReferenceError at runtime when an alert item is clicked. Please define it locally or import it from a shared constants file.

import { sendAlertsItemOpen } from "@/utils/analytics";

const standardCategories = new Set(["일반", "학사", "학생", "장학", "취창업", "국제"]);

ArticPenguin and others added 2 commits April 28, 2026 11:10
…, remove duplicates and noise

- 레거시 이벤트 5건 복구 (v1.5.46 연속성 유지)
  · link_open → link_click (sendLinkOpen → sendLinkClick)
  · navigation_tab_select → tab_change (sendNavigationTabSelect → sendTabChange)
  · system_error → error (sendSystemError → sendError)
  · page_view 재도입 (App.tsx에서 sendExtensionOpen과 함께 호출)
  · setting_change 재도입 (sendSettingsCredentialsSaved/Deleted 내부에서 병렬 발송)

- 신규 이벤트 41건에 MP_ prefix + camelCase 컨벤션 적용
  · 이벤트명 형식: MP_{object}_{action}
  · 복합어 camelCase: MP_authLogin_start, MP_templateEditor_view 등
  · 파라미터 정합성: author_id_present → is_author_id_present, liked → is_liked,
    subscription result → subscription_result

- 중복 트래킹 3건 제거
  · settings_icon 클릭: sendSettingsOpen 제거 (button_click 레거시로 대체, 직접 중복)
  · labs_icon 클릭: sendButtonClick 제거 (labs_open이 다이얼로그 진입 시점 포착, 간접 중복)
  · open_template_editor 클릭: sendButtonClick 제거 (editor_view가 진입 포착, 간접 중복)

- P3 이벤트 2건 제거 (분석 가치 낮음)
  · template_name_edit (결과는 template_save_success에 함축됨)
  · template_gallery_sort_change (gallery_search의 sort_option 파라미터에 포함됨)

- EditorHeader.tsx에서 debounce ref 제거 (template_name_edit 제거에 따라)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ention

- GA4-Data-Taxonomy.md
  · Migration History 2단계로 분리 (v1.5.46→GA4-MP, 컨벤션 정립)
  · 이벤트 레퍼런스 표를 레거시/신규 두 그룹으로 재구성
  · 신규 이벤트 39개 MP_ prefix 및 새 이름 반영
  · 제거된 이벤트 2건(template_name_edit, template_gallery_sort_change) 명시

- GA4-Taxonmy_Conventioning.md
  · 동일 구조로 레거시/신규 분리 재작성
  · 항목 컬럼 유지 (트래킹 시점 단문 설명)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ArticPenguin
Copy link
Copy Markdown
Contributor Author

@gemini-code-assist review this PR

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request establishes a comprehensive GA4 Data Taxonomy and implements it using the Measurement Protocol to enable product and retention analysis within the Chrome Extension. Key changes include a refactored analytics utility with session management and lifecycle tracking, alongside the integration of domain-specific event helpers across the application's core features like templates, authentication, and user interactions. Feedback focuses on resolving inconsistencies between the taxonomy documentation and the actual implementation, correcting a filename typo, and ensuring that gallery sort changes are captured even when no search query is present.

Comment thread docs/GA4-Data-Taxonomy.md
@@ -0,0 +1,309 @@
# GA4 Data Taxonomy
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

이 택소노미 문서와 실제 구현 사이에 몇 가지 불일치하는 부분이 보입니다. 유지보수성을 높이기 위해 문서를 코드와 일치시키는 것을 권장합니다.

  • template_clone_success 이벤트: 문서(133행)에는 author_id_present 파라미터로, 구현된 레퍼런스(293행)와 코드에는 is_author_id_present로 되어 있습니다.
  • template_like_toggle 이벤트: 문서(135행)에는 liked 파라미터로, 구현된 레퍼런스(291행)와 코드에는 is_liked로 되어 있습니다.
  • MP_settingsCredentials_save/delete 이벤트(186행): '과거형 제거'가 사유로 언급되었지만, 코드의 함수명은 ...Saved/...Deleted로 과거형을 사용하고 있어 혼란을 줄 수 있습니다.
  • MP_templateEditor_view 이벤트(294행): cloned origin이 미분류된다는 비고가 있지만, EditorPage.tsx의 구현을 보면 cloned origin을 잘 처리하고 있는 것으로 보입니다. 문서 업데이트가 필요해 보입니다.

Comment thread docs/GA4-Taxonmy_Conventioning.md Outdated
@@ -0,0 +1,61 @@
## 구현된 이벤트 전체 레퍼런스
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

  1. 파일명에 오타가 있습니다 (Taxonmy -> Taxonomy).
  2. 이 파일의 내용("구현된 이벤트 전체 레퍼런스")이 GA4-Data-Taxonomy.md 파일의 마지막 섹션과 거의 중복됩니다. 두 개의 유사한 문서를 유지하면 혼란을 야기하고 유지보수 비용을 증가시킬 수 있습니다. GA4-Data-Taxonomy.md 파일로 내용을 통합하고 이 파일은 삭제하여 단일 정보 소스를 유지하는 것을 제안합니다.

Comment thread src/pages/GalleryPage.tsx Outdated
Comment on lines +97 to +99
if (searchQuery.trim()) {
sendTemplateGallerySearch(searchQuery.trim().length, sort);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

현재 구현에서는 검색어(searchQuery)가 있을 때만 sendTemplateGallerySearch 이벤트가 전송됩니다. 이로 인해 사용자가 검색어 없이 정렬 옵션만 변경하는 경우에는 분석 이벤트가 누락될 수 있습니다.

template_gallery_sort_change 이벤트를 이 이벤트에 통합하려는 본래 의도를 살리려면, 검색어가 없더라도 정렬 옵션 변경 시 이벤트를 보내는 것이 좋아 보입니다.

setTimeout 콜백 내에서 searchQuery 확인 조건을 제거하여 항상 이벤트를 전송하도록 수정하는 것을 고려해 보세요.

      sendTemplateGallerySearch(searchQuery.trim().length, sort);

Copy link
Copy Markdown
Owner

@Turtle-Hwan Turtle-Hwan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for attach GA4 taxonomy

@Turtle-Hwan Turtle-Hwan merged commit 26aee06 into Turtle-Hwan:main Apr 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants