diff --git a/LEGAL.md b/LEGAL.md index 058819c1..45f86e33 100644 --- a/LEGAL.md +++ b/LEGAL.md @@ -379,50 +379,3 @@ GitHub Issues: https://github.com/tryCatchPing/it-contest/issues Developer: tryCatchPing ``` - ---- - -## ✅ 다음 단계 - -1. **내용 검토 및 수정** - - - `[PLACEHOLDER]` 부분을 실제 날짜로 교체 - - 관할 법원 명시 (서울중앙지방법원 등) - - 필요시 법률 전문가와 상담 - -2. **앱에 적용** - - - 최종 버전을 `legal_text_dialog.dart`의 상수에 복사 - - 한국어 버전을 기본으로 사용 (영어 버전은 추후 국제화 시 사용) - -3. **웹사이트 호스팅 (선택사항)** - - - GitHub Pages나 다른 호스팅 서비스에 업로드 - - URL을 앱스토어 제출 시 제공 - -4. **앱스토어 제출 시** - - App Store: App Privacy 섹션에서 "Data Not Collected" 선택 - - Google Play: Data Safety 섹션에서 "No data collected" 명시 - - 개인정보 처리방침 URL 제공 (선택사항, 앱 내 표시로 충분) - ---- - -## 📝 주요 특징 - -### 개인정보 보호 정책 하이라이트 - -- ✅ 완전한 오프라인 앱임을 명시 -- ✅ 외부 서버 없음을 강조 -- ✅ 제3자 공유 없음을 명확히 -- ✅ 사용자 권리 명시 - -### 이용약관 하이라이트 - -- ✅ 명확한 서비스 범위 -- ✅ 라이선스 제한 명시 -- ✅ 책임 제한 조항 -- ✅ 오픈소스 라이선스 준수 - ---- - -**참고**: 본 템플릿은 일반적인 가이드라인이며, 실제 법적 효력을 위해서는 전문 법률 자문을 받으시기 바랍니다. diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index e0b90122..c534f640 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -24,7 +24,8 @@ val hasReleaseKeystore = if (keystorePropertiesFile.exists()) { android { namespace = "com.clustudy.clustudy" compileSdk = flutter.compileSdkVersion - ndkVersion = "29.0.13599879" + // Target the latest stable NDK with 16 KB page support (Android 15 requirement). + ndkVersion = "29.0.14206865" compileOptions { sourceCompatibility = JavaVersion.VERSION_11 @@ -45,6 +46,13 @@ android { versionName = flutter.versionName } + packaging { + // Keep JNI libs uncompressed so Play can align them for 16 KB page sizes. + jniLibs { + useLegacyPackaging = false + } + } + signingConfigs { if (hasReleaseKeystore) { create("release") { @@ -76,4 +84,5 @@ flutter { dependencies { implementation("com.android.installreferrer:installreferrer:2.2") + implementation("androidx.core:core-ktx:1.13.1") } diff --git a/android/app/src/main/kotlin/com/clustudy/clustudy/MainActivity.kt b/android/app/src/main/kotlin/com/clustudy/clustudy/MainActivity.kt index 4c0f74aa..e5253f85 100644 --- a/android/app/src/main/kotlin/com/clustudy/clustudy/MainActivity.kt +++ b/android/app/src/main/kotlin/com/clustudy/clustudy/MainActivity.kt @@ -2,6 +2,10 @@ package com.clustudy.clustudy import android.os.Handler import android.os.Looper +import android.os.Bundle +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsControllerCompat import com.android.installreferrer.api.InstallReferrerClient import com.android.installreferrer.api.InstallReferrerStateListener import io.flutter.embedding.android.FlutterActivity @@ -15,6 +19,15 @@ private const val INSTALL_REFERRER_CHANNEL = class MainActivity : FlutterActivity() { private val mainHandler = Handler(Looper.getMainLooper()) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + WindowCompat.setDecorFitsSystemWindows(window, false) + ViewCompat.getWindowInsetsController(window.decorView)?.let { controller -> + controller.systemBarsBehavior = + WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE + } + } + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml index 06952be7..edd19a88 100644 --- a/android/app/src/main/res/values-night/styles.xml +++ b/android/app/src/main/res/values-night/styles.xml @@ -14,5 +14,9 @@ This Theme is only used starting with V2 of Flutter's Android embedding. --> diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index cb1ef880..257ab871 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -14,5 +14,9 @@ This Theme is only used starting with V2 of Flutter's Android embedding. --> diff --git a/docs/install_attribution_guide.md b/docs/install_attribution_guide.md new file mode 100644 index 00000000..c8558e0f --- /dev/null +++ b/docs/install_attribution_guide.md @@ -0,0 +1,450 @@ +# 앱 설치 경로 추적 가이드 + +## 빠른 참조 + +**기본 링크**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share +``` + +**마케팅 파라미터 추가 예시** (인스타그램): + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dinstagram%26utm_medium%3Dsocial%26utm_campaign%3Dlaunch_event +``` + +**지원 파라미터**: + +- UTM: `utm_source`, `utm_medium`, `utm_campaign`, `utm_content` +- 커스텀: `source`, `medium`, `campaign`, `content` + +**Firebase Analytics 자동 기록**: + +- User Properties: `install_source`, `install_medium`, `install_campaign` +- Event: `install_attribution` + +--- + +## 개요 + +Google Play Store의 Install Referrer API를 통해 앱 설치 경로를 추적하고, Firebase Analytics에 기록하는 시스템입니다. + +--- + +## 파라미터 종류 + +### 1. UTM 파라미터 (표준 마케팅 파라미터) + +UTM(Urchin Tracking Module)은 웹/앱 마케팅에서 표준으로 사용하는 파라미터입니다. + +#### `utm_source` (필수) + +- **의미**: 설치가 발생한 출처/채널 +- **예시**: + - `instagram` - 인스타그램 + - `naver_blog` - 네이버 블로그 + - 'everytime' - 에브리타임 + - `google_search` - 구글 검색 + - `youtube` - 유튜브 + - `partner_app` - 제휴 앱 + +#### `utm_medium` (권장) + +- **의미**: 마케팅 매체/방식 +- **예시**: + - `social` - 소셜 미디어 + - `email` - 이메일 + - `community` - 커뮤니티 + - `banner` - 배너 광고 + - `video` - 동영상 + - `organic` - 자연 유입 + +#### `utm_campaign` (권장) + +- **의미**: 특정 캠페인/이벤트 이름 +- **예시**: + - `launch_event` - 런칭 이벤트 + - `q1_2025` - 2025년 1분기 캠페인 + - `summer_sale` - 여름 세일 + - `influencer_collab` - 인플루언서 협업 + +#### `utm_content` (선택) + +- **의미**: 동일한 캠페인 내에서 구체적인 콘텐츠 구분 +- **예시**: + - `banner_top` - 상단 배너 + - `banner_bottom` - 하단 배너 + - `video_intro` - 인트로 영상 + - `post_promotion` - 프로모션 포스트 + +### 2. 커스텀 파라미터 (대체 파라미터) + +UTM 파라미터를 사용하지 않을 때 사용할 수 있는 대체 파라미터입니다. + +#### `source` + +- **의미**: `utm_source`와 동일 (출처) +- **사용 시점**: UTM을 사용하지 않을 때 + +#### `medium` + +- **의미**: `utm_medium`과 동일 (매체) +- **사용 시점**: UTM을 사용하지 않을 때 + +#### `campaign` + +- **의미**: `utm_campaign`과 동일 (캠페인) +- **사용 시점**: UTM을 사용하지 않을 때 + +#### `content` + +- **의미**: `utm_content`와 동일 (콘텐츠) +- **사용 시점**: UTM을 사용하지 않을 때 + +--- + +## Firebase Analytics 프로퍼티 + +코드에서 자동으로 Firebase Analytics에 설정되는 User Properties입니다. + +### `install_source` + +- **의미**: 설치 출처 +- **값**: `utm_source` 또는 `source` 파라미터 값 +- **용도**: 사용자 세그먼트 분석, 채널별 성과 비교 +- **예시**: `instagram`, `naver_blog`, `google_search` + +### `install_medium` + +- **의미**: 설치 매체 +- **값**: `utm_medium` 또는 `medium` 파라미터 값 +- **용도**: 매체별 효과 분석 +- **예시**: `social`, `email`, `banner` + +### `install_campaign` + +- **의미**: 설치 캠페인 +- **값**: `utm_campaign` 또는 `campaign` 파라미터 값 +- **용도**: 캠페인별 성과 추적 +- **예시**: `launch_event`, `q1_2025`, `summer_sale` + +--- + +## 파라미터 우선순위 + +코드에서 파라미터를 읽을 때의 우선순위: + +```dart +// InstallAttributionPayload 클래스에서 +String? get source => parameters['utm_source'] ?? parameters['source']; +String? get medium => parameters['utm_medium'] ?? parameters['medium']; +String? get campaign => parameters['utm_campaign'] ?? parameters['campaign']; +String? get content => parameters['utm_content'] ?? parameters['content']; +``` + +**규칙**: UTM 파라미터가 있으면 우선 사용, 없으면 커스텀 파라미터 사용 + +--- + +## 기본 링크 + +**실제 Google Play Store 링크**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share +``` + +이 링크에 `referrer` 파라미터를 추가하여 마케팅 추적을 할 수 있습니다. + +--- + +## 사용 예시 + +### 예시 1: UTM 파라미터 사용 (권장) + +**기본 링크에 `referrer` 파라미터 추가**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dinstagram%26utm_medium%3Dsocial%26utm_campaign%3Dlaunch_event +``` + +**파라미터**: + +- `utm_source=instagram` +- `utm_medium=social` +- `utm_campaign=launch_event` + +**Firebase Analytics에 기록되는 값**: + +- User Property `install_source`: `instagram` +- User Property `install_medium`: `social` +- User Property `install_campaign`: `launch_event` +- Event `install_attribution`의 파라미터: 모든 UTM 파라미터 포함 + +### 예시 2: 커스텀 파라미터 사용 + +**기본 링크에 `referrer` 파라미터 추가**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=source%3Dpartner_app%26campaign%3Dq1_2025 +``` + +**파라미터**: + +- `source=partner_app` +- `campaign=q1_2025` + +**Firebase Analytics에 기록되는 값**: + +- User Property `install_source`: `partner_app` +- User Property `install_campaign`: `q1_2025` +- User Property `install_medium`: 설정되지 않음 (없음) +- Event `install_attribution`의 파라미터: `source`, `campaign` 포함 + +### 예시 3: UTM과 커스텀 혼합 (UTM 우선) + +**기본 링크에 `referrer` 파라미터 추가**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dnaver_blog%26source%3Dfallback%26utm_campaign%3Dlaunch_event +``` + +**파라미터**: + +- `utm_source=naver_blog` +- `source=fallback` (무시됨) +- `utm_campaign=launch_event` + +**Firebase Analytics에 기록되는 값**: + +- User Property `install_source`: `naver_blog` (UTM 우선) +- User Property `install_campaign`: `launch_event` + +--- + +## Clustudy 실제 마케팅 채널별 설정 가이드 + +### 케이스 1: 네이버 블로그 공식 블로그 (직접 글 작성) + +**권장 파라미터**: + +- `utm_source=naver_blog` - 네이버 블로그 출처 명시 +- `utm_medium=blog` - 블로그 매체 +- `utm_campaign={포스트_제목_또는_시기}` - 특정 포스트나 시기 구분 +- `utm_content={포스트_날짜_또는_주제}` (선택) - 같은 캠페인 내에서 구체적 구분 + +**예시 1: 런칭 포스트**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dnaver_blog%26utm_medium%3Dblog%26utm_campaign%3Dlaunch_post +``` + +**예시 2: 기능 소개 포스트 (날짜별 구분)**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dnaver_blog%26utm_medium%3Dblog%26utm_campaign%3Dfeature_intro%26utm_content%3D251107 +``` + +**Firebase Analytics 분석**: + +- `install_source`: `naver_blog`로 네이버 블로그에서 온 설치 추적 +- `install_medium`: `blog`로 블로그 매체 효과 측정 +- `install_campaign`: 포스트별 성과 비교 가능 + +--- + +### 케이스 2: 에브리타임 대학교 커뮤니티 (홍보글 작성) + +**권장 파라미터**: + +- `utm_source=everytime` - 에브리타임 출처 명시 +- `utm_medium=community` - 커뮤니티 매체 +- `utm_campaign={대학교명_또는_이벤트명}` - 대학교별 또는 이벤트별 구분 +- `utm_content={게시판_또는_날짜}` (선택) - 게시판별 구분 + +**예시 1: 숭실대학교 홍보**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Deverytime%26utm_medium%3Dcommunity%26utm_campaign%3Dsoongsil_promotion +``` + +**예시 2: 연세대학교 + 특정 게시판**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Deverytime%26utm_medium%3Dcommunity%26utm_campaign%3Dyonsei_promotion%26utm_content%3Dfree_board +``` + +**Firebase Analytics 분석**: + +- `install_source`: `everytime`로 에브리타임에서 온 설치 추적 +- `install_medium`: `community`로 커뮤니티 매체 효과 측정 +- `install_campaign`: 대학교별 또는 이벤트별 성과 비교 가능 + +--- + +### 케이스 3: 인스타그램 공식 계정 (직접 글 작성) + +**권장 파라미터**: + +- `utm_source=instagram` - 인스타그램 출처 명시 +- `utm_medium=social` - 소셜 미디어 매체 +- `utm_campaign={포스트_주제_또는_시기}` - 포스트 주제나 시기 구분 +- `utm_content={포스트_타입}` (선택) - 피드/스토리/릴스 등 구분 + +**예시 1: 기능 소개 포스트**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dinstagram%26utm_medium%3Dsocial%26utm_campaign%3Dfeature_intro +``` + +**예시 2: 릴스 콘텐츠**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dinstagram%26utm_medium%3Dsocial%26utm_campaign%3Dq1_2025%26utm_content%3Dreels +``` + +**예시 3: 스토리**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dinstagram%26utm_medium%3Dsocial%26utm_campaign%3Ddaily_update%26utm_content%3Dstory +``` + +**Firebase Analytics 분석**: + +- `install_source`: `instagram`로 인스타그램에서 온 설치 추적 +- `install_medium`: `social`로 소셜 미디어 매체 효과 측정 +- `install_campaign`: 포스트 주제별 성과 비교 가능 +- `utm_content`: 포스트 타입별(피드/스토리/릴스) 효과 비교 가능 + +--- + +## 일반적인 사용 시나리오 (참고) + +### 시나리오 1: 네이버 블로그 배너 광고 + +**파라미터**: + +- `utm_source=naver_blog` +- `utm_medium=banner` +- `utm_campaign=summer_sale` +- `utm_content=banner_top` + +**완성된 링크**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dnaver_blog%26utm_medium%3Dbanner%26utm_campaign%3Dsummer_sale%26utm_content%3Dbanner_top +``` + +### 시나리오 2: 유튜브 동영상 설명란 + +**파라미터**: + +- `utm_source=youtube` +- `utm_medium=video` +- `utm_campaign=tutorial_series` +- `utm_content=episode_1` + +**완성된 링크**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=utm_source%3Dyoutube%26utm_medium%3Dvideo%26utm_campaign%3Dtutorial_series%26utm_content%3Depisode_1 +``` + +### 시나리오 3: 제휴 앱 (UTM 없이) + +**파라미터**: + +- `source=partner_app` +- `campaign=q1_partnership` + +**완성된 링크**: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer=source%3Dpartner_app%26campaign%3Dq1_partnership +``` + +--- + +## Firebase Analytics에서 확인 방법 + +### Events 탭 + +- 이벤트명: `install_attribution` +- 파라미터: 모든 UTM/커스텀 파라미터가 포함됨 + +### User Properties 탭 + +- `install_source`: 설치 출처 +- `install_medium`: 설치 매체 +- `install_campaign`: 설치 캠페인 + +### 활용 방법 + +1. **채널별 설치 수**: `install_source`로 그룹화 +2. **매체별 효과**: `install_medium`으로 분석 +3. **캠페인 성과**: `install_campaign`으로 추적 +4. **세그먼트 분석**: User Properties를 조합하여 사용자 세그먼트 생성 + +--- + +## 링크 생성 팁 + +### URL 인코딩 방법 + +`referrer` 파라미터 값은 반드시 URL 인코딩해야 합니다. + +**인코딩 전**: + +``` +utm_source=instagram&utm_medium=social&utm_campaign=launch_event +``` + +**인코딩 후**: + +``` +utm_source%3Dinstagram%26utm_medium%3Dsocial%26utm_campaign%3Dlaunch_event +``` + +**인코딩 규칙**: + +- `=` → `%3D` +- `&` → `%26` +- 공백 → `%20` (필요한 경우) + +### 온라인 도구 활용 + +- [URL Encoder/Decoder](https://www.urlencoder.org/) +- [Google URL Builder](https://ga-dev-tools.google/campaign-url-builder/) (UTM 파라미터 전용) + +### 링크 구조 + +기본 구조: + +``` +https://play.google.com/store/apps/details?id=com.clustudy.clustudy&pcampaignid=web_share&referrer={인코딩된_파라미터} +``` + +--- + +## 주의사항 + +1. **URL 인코딩 필수**: `referrer` 파라미터 값은 반드시 URL 인코딩해야 함 +2. **앱 재설치 필요**: 테스트 시 앱을 완전히 삭제 후 재설치해야 함 +3. **첫 실행 시에만**: Install Referrer는 앱 설치 후 첫 실행 시에만 사용 가능 +4. **UTM 우선**: UTM과 커스텀 파라미터가 동시에 있으면 UTM이 우선됨 +5. **기존 파라미터 유지**: `pcampaignid=web_share` 같은 기존 파라미터는 그대로 유지하고 `referrer`만 추가 + +--- + +## 요약 + +| 항목 | UTM 파라미터 | 커스텀 파라미터 | Firebase User Property | +| ------------- | -------------------------- | ------------------ | ---------------------------------- | +| **의미** | 표준 마케팅 파라미터 | UTM 대체 파라미터 | 사용자 속성 (영구 저장) | +| **예시** | `utm_source`, `utm_medium` | `source`, `medium` | `install_source`, `install_medium` | +| **용도** | 마케팅 추적 표준 | 간단한 추적 | 사용자 세그먼트 분석 | +| **우선순위** | 높음 | 낮음 | - | +| **저장 위치** | Event 파라미터 | Event 파라미터 | User Property | + +**권장**: UTM 파라미터 사용을 권장합니다. 표준이므로 다른 도구와의 호환성이 좋고, 마케팅 분석이 용이합니다. diff --git "a/docs/screenshots/\354\233\220\354\212\244\355\206\240\354\226\264.png" "b/docs/screenshots/\354\233\220\354\212\244\355\206\240\354\226\264.png" new file mode 100644 index 00000000..bc9a8909 Binary files /dev/null and "b/docs/screenshots/\354\233\220\354\212\244\355\206\240\354\226\264.png" differ diff --git a/pubspec.yaml b/pubspec.yaml index f11e8e23..f949a737 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,7 +21,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # - 마이너(x.1.x): 호환성 유지하면서 의미 있는 기능 추가나 UX 개선이 있을 때. 기존 사용 흐름은 유지되지만 “새 기능”이라 말할 수 있는 수준 # - 패치(x.x.1): 버그 수정, 안정화, 성능 조정처럼 기존 기능을 깨지 않고 다듬을 때. 핫픽스 # - 빌드 번호(+n): 같은 앱 버전을 여러 번 스토어에 제출할 때 -version: 1.0.3+4 +version: 1.0.4+5 environment: sdk: ^3.8.1