데이터 개인정보 보호법 준수
GDPR, CCPA, COPPA 및 기타 소비자 개인정보 보호 규정에 대한 사용자 동의 선택 사항을 Singular에 알림으로써 개인정보 보호 규정을 준수하는 데이터 수집을 구현하세요.
사용자가 제3자와의 정보 공유에 동의하거나 거부할 경우, Singular의 개인정보 보호 방법을 사용하여 사용자의 선택 사항을 전달하세요. 이를 통해 캘리포니아 소비자 개인정보 보호법(CCPA)과 같은 규정을 준수하고 파트너가 사용자의 개인정보 보호 기본 설정을 존중할 수 있습니다.
자세히 알아보기: Singular가 개인정보 동의를 처리하는 방법에 대한 자세한 내용은 사용자 개인정보 보호 및 데이터 공유 제한을참조하세요.
데이터 공유 제한
타사 데이터 공유 제어
limitDataSharing() 방법을 사용하여 사용자가 타사 파트너와 개인 데이터를 공유하는 데 동의했는지 여부를 Singular에 알립니다.
메소드 서명:
static limitDataSharing(shouldLimitDataSharing: boolean): void
매개변수:
- false: 사용자가 데이터 공유에 동의하고 옵트인했습니다.
- true: 사용자가 옵트아웃했으며 데이터 공유에 동의하지 않음
중요: 선택 사항이지만, 이 방법은 어트리뷰션 데이터 공유에 영향을 줍니다. 일부 파트너는 사용자가 옵트인했음을 명시적으로 통지한 경우에만 전체 어트리뷰션 정보를 공유합니다.
전체 메서드 설명서는 limitDataSharing 참조를 참조하세요.
사용 예시
사용자 개인정보 기본 설정에 따라 데이터 공유 제어를 구현합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
// User has opted in to share their data
export const onUserOptedInToDataSharing = () => {
NativeSingular.limitDataSharing(false);
console.log('Data sharing enabled');
};
// User has opted out and declined to share their data
export const onUserOptedOutOfDataSharing = () => {
NativeSingular.limitDataSharing(true);
console.log('Data sharing limited');
};
// Set based on user preference
export const handlePrivacyConsent = (userConsented) => {
// Pass inverse: false = opted in, true = opted out
NativeSingular.limitDataSharing(!userConsented);
console.log(`Data sharing: ${userConsented ? 'Enabled' : 'Limited'}`);
};
import { Singular } from 'singular-react-native';
// User has opted in to share their data
export const onUserOptedInToDataSharing = () => {
Singular.limitDataSharing(false);
console.log('Data sharing enabled');
};
// User has opted out and declined to share their data
export const onUserOptedOutOfDataSharing = () => {
Singular.limitDataSharing(true);
console.log('Data sharing limited');
};
// Set based on user preference
export const handlePrivacyConsent = (userConsented) => {
// Pass inverse: false = opted in, true = opted out
Singular.limitDataSharing(!userConsented);
console.log(`Data sharing: ${userConsented ? 'Enabled' : 'Limited'}`);
};
작동 방식:
Singular는 사용자 개인정보 보호 포스트백에서이 설정을 사용하여 규정 준수를 위해 이 설정이 필요한 파트너에게 전달합니다.
GDPR 준수 방법
사용자 추적 동의를 관리하고 SDK 기능을 제어하여 GDPR(일반 데이터 보호 규정) 및 기타 개인정보 보호 규정을 준수합니다.
추적 동의 관리
TrackingOptIn
Singular 서버에 GDPR 옵트인 이벤트를 전송하여 추적에 대한 명시적인 사용자 동의를 기록합니다.
메서드 서명:
static trackingOptIn(): void
사용 시기:
- GDPR 준수: 사용자가 GDPR 규제 지역에서 추적에 명시적으로 동의할 때 호출합니다.
- 동의 기록: 사용자가 Singular의 시스템에서 GDPR 동의를 제공한 것으로 표시합니다.
- 기본 동작: 이 호출이 없으면 SDK는 추적을 계속하지만 동의를 구체적으로 기록하지 않습니다.
전체 메서드 문서는 trackingOptIn 참조를 참조하세요.
구현 예시
사용자가 앱의 동의 대화 상자를 통해 추적 동의를 수락한 후 trackingOptIn() 으로 호출합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useState, useEffect } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';
export default function GDPRManager() {
const [hasConsent, setHasConsent] = useState(null);
useEffect(() => {
checkStoredConsent();
}, []);
const checkStoredConsent = async () => {
try {
const consent = await AsyncStorage.getItem('gdpr_consent');
if (consent === null) {
// Show consent dialog
showGDPRConsentDialog();
} else {
setHasConsent(consent === 'true');
}
} catch (error) {
console.error('Error checking consent:', error);
}
};
const showGDPRConsentDialog = () => {
Alert.alert(
'Privacy Consent',
'We would like your permission to track app usage and improve your experience.',
[
{
text: 'Accept',
onPress: onUserAcceptedTracking
},
{
text: 'Decline',
style: 'cancel',
onPress: () => setHasConsent(false)
}
]
);
};
const onUserAcceptedTracking = async () => {
// Record user consent
NativeSingular.trackingOptIn();
console.log('User opted in to tracking');
// Save preference
await AsyncStorage.setItem('gdpr_consent', 'true');
setHasConsent(true);
};
return (
<View>
{hasConsent !== null && (
<Text>Privacy Status: {hasConsent ? 'Opted In' : 'Declined'}</Text>
)}
</View>
);
}
import React, { useState, useEffect } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import { Singular } from 'singular-react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
export default function GDPRManager() {
const [hasConsent, setHasConsent] = useState(null);
useEffect(() => {
checkStoredConsent();
}, []);
const checkStoredConsent = async () => {
try {
const consent = await AsyncStorage.getItem('gdpr_consent');
if (consent === null) {
// Show consent dialog
showGDPRConsentDialog();
} else {
setHasConsent(consent === 'true');
}
} catch (error) {
console.error('Error checking consent:', error);
}
};
const showGDPRConsentDialog = () => {
Alert.alert(
'Privacy Consent',
'We would like your permission to track app usage and improve your experience.',
[
{
text: 'Accept',
onPress: onUserAcceptedTracking
},
{
text: 'Decline',
style: 'cancel',
onPress: () => setHasConsent(false)
}
]
);
};
const onUserAcceptedTracking = async () => {
// Record user consent
Singular.trackingOptIn();
console.log('User opted in to tracking');
// Save preference
await AsyncStorage.setItem('gdpr_consent', 'true');
setHasConsent(true);
};
return (
<View>
{hasConsent !== null && (
<Text>Privacy Status: {hasConsent ? 'Opted In' : 'Declined'}</Text>
)}
</View>
);
}
추적 제어 메서드
StopAllTracking
이 디바이스에서 현재 사용자에 대한 모든 SDK 추적 활동을 완전히 비활성화합니다.
메소드 서명:
static stopAllTracking(): void
심각 중요: 이 메서드는 resumeAllTracking() 이 호출될 때까지 SDK를 영구적으로 비활성화합니다. 비활성화 상태는 앱을 다시 시작할 때에도 유지되며 프로그래밍 방식으로만 되돌릴 수 있습니다.
동작:
- 즉각적인 효과: 모든 추적, 이벤트 보고 및 데이터 수집을 즉시 중지합니다.
- 영구 상태: 앱을 닫았다가 다시 열어도 비활성화 상태로 유지됨
-
자동 재설정 없음: 다시 활성화하려면
resumeAllTracking()을 명시적으로 호출해야 합니다.
전체 메서드 문서는 stopAllTracking 참조를 참조하세요.
구현 예시
사용자가 동의를 거부하거나 개인정보 설정을 통해 옵트아웃하면 추적을 중지합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';
// User declined all tracking
export const onUserDeclinedTracking = async () => {
NativeSingular.stopAllTracking();
console.log('All tracking stopped');
// Store preference
await AsyncStorage.setItem('tracking_enabled', 'false');
};
// Handle user opt-out from settings menu
export const handlePrivacySettingsChange = (trackingEnabled) => {
if (!trackingEnabled) {
NativeSingular.stopAllTracking();
console.log('Privacy settings: Tracking disabled');
}
};
import { Singular } from 'singular-react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
// User declined all tracking
export const onUserDeclinedTracking = async () => {
Singular.stopAllTracking();
console.log('All tracking stopped');
// Store preference
await AsyncStorage.setItem('tracking_enabled', 'false');
};
// Handle user opt-out from settings menu
export const handlePrivacySettingsChange = (trackingEnabled) => {
if (!trackingEnabled) {
Singular.stopAllTracking();
console.log('Privacy settings: Tracking disabled');
}
};
ResumeAllTracking
stopAllTracking() 으로 추적을 중지한 후 다시 활성화합니다.
메서드 서명:
static resumeAllTracking(): void
사용 사례:
- 동의 변경: 사용자가 개인정보 기본 설정을 변경하고 추적을 다시 선택함
- 개인정보 설정: 사용자가 앱 설정 메뉴를 통해 동의를 업데이트합니다.
- 지역 규정 준수: 사용자가 비규제 지역으로 이동하는 경우 추적을 다시 활성화합니다.
전체 메서드 설명서는 resumeAllTracking 참조를 참조하세요.
구현 예시
사용자가 다시 옵트인하거나 개인정보 기본 설정을 업데이트하면 추적을 재개합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';
// User opted back in to tracking
export const onUserResumedTracking = async () => {
NativeSingular.resumeAllTracking();
console.log('Tracking resumed');
// Update stored preference
await AsyncStorage.setItem('tracking_enabled', 'true');
};
// Handle consent update from settings
export const handlePrivacySettingsChange = (trackingEnabled) => {
if (trackingEnabled) {
NativeSingular.resumeAllTracking();
console.log('Privacy settings: Tracking enabled');
}
};
import { Singular } from 'singular-react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
// User opted back in to tracking
export const onUserResumedTracking = async () => {
Singular.resumeAllTracking();
console.log('Tracking resumed');
// Update stored preference
await AsyncStorage.setItem('tracking_enabled', 'true');
};
// Handle consent update from settings
export const handlePrivacySettingsChange = (trackingEnabled) => {
if (trackingEnabled) {
Singular.resumeAllTracking();
console.log('Privacy settings: Tracking enabled');
}
};
IsAllTrackingStopped
현재 사용자에 대한 추적이 비활성화되었는지 확인합니다.
메서드 서명:
static isAllTrackingStopped(): Promise<boolean>
반환합니다:
-
Promise<true>: 현재
stopAllTracking()을 통해 추적이 중지되었습니다. - Promise<false>: 추적이 활성 상태입니다(중지되지 않았거나 재개되지 않았습니다).
전체 메서드 문서는 isAllTrackingStopped 참조를 참조하세요.
구현 예시
추적 상태를 확인하여 UI 상태를 SDK 추적 상태와 동기화합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useState, useEffect } from 'react';
import { View, Text, Switch } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';
export default function PrivacySettingsUI() {
const [trackingEnabled, setTrackingEnabled] = useState(false);
const [statusText, setStatusText] = useState('Checking...');
useEffect(() => {
updatePrivacyUI();
}, []);
// Check current tracking status
const isTrackingEnabled = async () => {
const isStopped = await NativeSingular.isAllTrackingStopped();
return !isStopped;
};
// Display privacy status in settings
const getPrivacyStatusText = async () => {
const isStopped = await NativeSingular.isAllTrackingStopped();
return isStopped ? 'Tracking: Disabled' : 'Tracking: Enabled';
};
// Sync UI with tracking state
const updatePrivacyUI = async () => {
const isStopped = await NativeSingular.isAllTrackingStopped();
setTrackingEnabled(!isStopped);
const status = await getPrivacyStatusText();
setStatusText(status);
console.log(`Current tracking state: ${isStopped ? 'Stopped' : 'Active'}`);
};
// Handle toggle change from UI
const onTrackingToggleChanged = async (enabled) => {
if (enabled) {
NativeSingular.resumeAllTracking();
} else {
NativeSingular.stopAllTracking();
}
await updatePrivacyUI();
};
return (
<View>
<Text>{statusText}</Text>
<Switch
value={trackingEnabled}
onValueChange={onTrackingToggleChanged}
/>
</View>
);
}
import React, { useState, useEffect } from 'react';
import { View, Text, Switch } from 'react-native';
import { Singular } from 'singular-react-native';
export default function PrivacySettingsUI() {
const [trackingEnabled, setTrackingEnabled] = useState(false);
const [statusText, setStatusText] = useState('Checking...');
useEffect(() => {
updatePrivacyUI();
}, []);
// Check current tracking status
const isTrackingEnabled = async () => {
const isStopped = await Singular.isAllTrackingStopped();
return !isStopped;
};
// Display privacy status in settings
const getPrivacyStatusText = async () => {
const isStopped = await Singular.isAllTrackingStopped();
return isStopped ? 'Tracking: Disabled' : 'Tracking: Enabled';
};
// Sync UI with tracking state
const updatePrivacyUI = async () => {
const isStopped = await Singular.isAllTrackingStopped();
setTrackingEnabled(!isStopped);
const status = await getPrivacyStatusText();
setStatusText(status);
console.log(`Current tracking state: ${isStopped ? 'Stopped' : 'Active'}`);
};
// Handle toggle change from UI
const onTrackingToggleChanged = async (enabled) => {
if (enabled) {
Singular.resumeAllTracking();
} else {
Singular.stopAllTracking();
}
await updatePrivacyUI();
};
return (
<View>
<Text>{statusText}</Text>
<Switch
value={trackingEnabled}
onValueChange={onTrackingToggleChanged}
/>
</View>
);
}
어린이 개인정보 보호
TrackingUnder13
사용자가 13세 미만인 경우 COPPA(아동 온라인 개인 정보 보호법) 및 기타 아동 개인 정보 보호 규정을 준수하기 위해 Singular에 알립니다.
방법 서명:
static trackingUnder13(): void
준수 요구 사항:
- COPPA 준수: 미국에서 13세 미만 아동의 데이터를 수집하는 앱에 필수입니다.
- 연령 제한 콘텐츠: 등록 또는 연령 확인 시 사용자가 13세 미만임을 확인할 때 사용합니다.
- 제한된 추적: 아동 개인정보 보호법을 준수하기 위해 데이터 수집을 제한합니다.
전체 메서드 문서는 13세 미만 추적 참조를 참조하세요.
구현 예시
연령 확인을 통해 사용자가 13세 미만임을 확인한 후 즉시 trackingUnder13() 으로 전화합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useState } from 'react';
import { View, TextInput, Button, Alert } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';
export default function COPPAManager() {
const [age, setAge] = useState('');
// User identified as under 13
const onUserUnder13 = async () => {
NativeSingular.trackingUnder13();
console.log('COPPA mode enabled for user under 13');
// Store age category for app restart
await AsyncStorage.setItem('user_age_category', 'under_13');
};
// Call after age verification
const onAgeVerified = async (userAge) => {
if (userAge < 13) {
NativeSingular.trackingUnder13();
console.log('COPPA restrictions applied');
// Also limit advertising identifiers
NativeSingular.setLimitAdvertisingIdentifiers(true);
await AsyncStorage.setItem('user_age_category', 'under_13');
Alert.alert('Child Account', 'Special privacy protections have been applied.');
} else {
await AsyncStorage.setItem('user_age_category', 'adult');
console.log('Adult account - standard tracking');
}
};
const handleAgeSubmit = () => {
const userAge = parseInt(age, 10);
if (isNaN(userAge) || userAge < 1 || userAge > 120) {
Alert.alert('Invalid Age', 'Please enter a valid age.');
return;
}
onAgeVerified(userAge);
};
return (
<View>
<TextInput
placeholder="Enter your age"
value={age}
onChangeText={setAge}
keyboardType="number-pad"
/>
<Button title="Submit" onPress={handleAgeSubmit} />
</View>
);
}
import React, { useState } from 'react';
import { View, TextInput, Button, Alert } from 'react-native';
import { Singular } from 'singular-react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
export default function COPPAManager() {
const [age, setAge] = useState('');
// User identified as under 13
const onUserUnder13 = async () => {
Singular.trackingUnder13();
console.log('COPPA mode enabled for user under 13');
// Store age category for app restart
await AsyncStorage.setItem('user_age_category', 'under_13');
};
// Call after age verification
const onAgeVerified = async (userAge) => {
if (userAge < 13) {
Singular.trackingUnder13();
console.log('COPPA restrictions applied');
// Also limit advertising identifiers
Singular.setLimitAdvertisingIdentifiers(true);
await AsyncStorage.setItem('user_age_category', 'under_13');
Alert.alert('Child Account', 'Special privacy protections have been applied.');
} else {
await AsyncStorage.setItem('user_age_category', 'adult');
console.log('Adult account - standard tracking');
}
};
const handleAgeSubmit = () => {
const userAge = parseInt(age, 10);
if (isNaN(userAge) || userAge < 1 || userAge > 120) {
Alert.alert('Invalid Age', 'Please enter a valid age.');
return;
}
onAgeVerified(userAge);
};
return (
<View>
<TextInput
placeholder="Enter your age"
value={age}
onChangeText={setAge}
keyboardType="number-pad"
/>
<Button title="Submit" onPress={handleAgeSubmit} />
</View>
);
}
중요: 사용자가 13세 미만임을 확인한 후 가능한 한 빨리, 즉 앱 초기화 중 또는 연령 확인 직후에 이 메서드를 호출하세요. 이렇게 하면 이후 모든 추적에서 아동의 개인정보 보호 규정을 준수할 수 있습니다.
설정한도 광고 식별자
혼합 오디언스 앱에 대해 SDK 초기화 후 광고 식별자(Android의 경우 GAID, iOS의 경우 IDFA)의 수집 및 사용을 제한합니다.
메소드 서명:
static setLimitAdvertisingIdentifiers(isEnabled: boolean): void
파라미터:
- true: 제한된 광고 식별자 모드 활성화(수집 제한)
- false: 제한된 광고 식별자 모드 비활성화(일반 수집)
사용 사례:
- 혼합 대상 앱: 앱 출시 후 연령이 결정되는 성인과 어린이 모두에게 서비스를 제공하는 앱
- 동적 개인정보 제어: 사용자 행동 또는 액세스하는 콘텐츠에 따라 추적 조정
- 런타임 제한: 초기 SDK 설정 후 광고 식별자 제한을 적용합니다.
전체 메서드 설명서는 setLimitAdvertisingIdentifiers 참조를 참조하세요.
구현 예시
사용자 연령, 콘텐츠 유형 또는 앱 모드에 따라 광고 식별자를 동적으로 제한합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
// Limit advertising identifiers after initialization
export const enableLimitedMode = () => {
NativeSingular.setLimitAdvertisingIdentifiers(true);
console.log('Advertising identifiers limited');
};
// Mixed audience app with age gate
export const onKidsModeSwitched = (isKidsMode) => {
if (isKidsMode) {
NativeSingular.setLimitAdvertisingIdentifiers(true);
console.log('Advertising identifiers limited for kids mode');
} else {
NativeSingular.setLimitAdvertisingIdentifiers(false);
console.log('Advertising identifiers unrestricted for adult mode');
}
};
// Content-based restrictions
export const onViewingChildrensContent = () => {
NativeSingular.setLimitAdvertisingIdentifiers(true);
console.log('Ad identifiers restricted for children\'s content');
};
// Combined with TrackingUnder13
export const onAgeVerified = (age) => {
if (age < 13) {
NativeSingular.trackingUnder13();
NativeSingular.setLimitAdvertisingIdentifiers(true);
console.log('Full COPPA restrictions applied');
}
};
import { Singular } from 'singular-react-native';
// Limit advertising identifiers after initialization
export const enableLimitedMode = () => {
Singular.setLimitAdvertisingIdentifiers(true);
console.log('Advertising identifiers limited');
};
// Mixed audience app with age gate
export const onKidsModeSwitched = (isKidsMode) => {
if (isKidsMode) {
Singular.setLimitAdvertisingIdentifiers(true);
console.log('Advertising identifiers limited for kids mode');
} else {
Singular.setLimitAdvertisingIdentifiers(false);
console.log('Advertising identifiers unrestricted for adult mode');
}
};
// Content-based restrictions
export const onViewingChildrensContent = () => {
Singular.setLimitAdvertisingIdentifiers(true);
console.log('Ad identifiers restricted for children\'s content');
};
// Combined with TrackingUnder13
export const onAgeVerified = (age) => {
if (age < 13) {
Singular.trackingUnder13();
Singular.setLimitAdvertisingIdentifiers(true);
console.log('Full COPPA restrictions applied');
}
};
구성 대안: SDK가 시작되기 전에 개인정보 보호 요구 사항을 알고 있는 경우 limitAdvertisingIdentifiers 구성 옵션을 설정하여 SDK 초기화 중에 광고 식별자를 제한할 수도 있습니다.
구성 속성
개인정보 보호 요구 사항을 미리 알고 있는 앱의 경우 SDK 초기화 중에 광고 식별자 제한을 설정합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
// Limit advertising identifiers at initialization
const config: SingularConfig = {
apikey: 'YOUR_SDK_KEY',
secret: 'YOUR_SDK_SECRET',
limitAdvertisingIdentifiers: true
};
NativeSingular.init(config);
import { Singular, SingularConfig } from 'singular-react-native';
// Limit advertising identifiers at initialization
const config = new SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
)
.withLimitAdvertisingIdentifiers(true);
Singular.init(config);
전체 구성 설명서는 withLimitAdvertisingIdentifiers 참조를 참조하세요.
구현 모범 사례
전체 개인정보 관리 예시
사용자 기본 설정을 존중하고 규정을 준수하는 포괄적인 개인정보 관리 기능을 구현하세요.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import AsyncStorage from '@react-native-async-storage/async-storage';
const PREF_USER_CONSENT = 'privacy_user_consent';
const PREF_DATA_SHARING = 'privacy_data_sharing';
const PREF_AGE_CATEGORY = 'user_age_category';
/**
* Initialize privacy settings on app startup based on stored preferences
*/
export const initializePrivacySettings = async () => {
const hasUserConsent = await getUserConsent();
const allowDataSharing = await getDataSharingPreference();
const ageCategory = await getAgeCategory();
// Apply age-based restrictions first
if (ageCategory === 'under_13') {
NativeSingular.trackingUnder13();
NativeSingular.setLimitAdvertisingIdentifiers(true);
console.log('COPPA restrictions applied on startup');
}
// Apply stored tracking preference
if (hasUserConsent) {
NativeSingular.trackingOptIn();
NativeSingular.resumeAllTracking();
console.log('Privacy initialized: Tracking enabled with consent');
} else {
NativeSingular.stopAllTracking();
console.log('Privacy initialized: Tracking disabled');
}
// Set data sharing preference (inverse logic)
NativeSingular.limitDataSharing(!allowDataSharing);
console.log(`Privacy initialized: consent=${hasUserConsent}, sharing=${allowDataSharing}`);
};
/**
* User accepts tracking via consent dialog
*/
export const onUserAcceptedTracking = async () => {
await saveUserConsent(true);
NativeSingular.trackingOptIn();
NativeSingular.resumeAllTracking();
console.log('User accepted tracking');
};
/**
* User declines tracking
*/
export const onUserDeclinedTracking = async () => {
await saveUserConsent(false);
NativeSingular.stopAllTracking();
console.log('User declined tracking');
};
/**
* User updates data sharing preference
*/
export const setDataSharingEnabled = async (enabled) => {
await saveDataSharingPreference(enabled);
// Note: LimitDataSharing uses inverse logic
// false = data sharing enabled, true = data sharing limited
NativeSingular.limitDataSharing(!enabled);
console.log(`Data sharing: ${enabled ? 'Enabled' : 'Limited'}`);
};
/**
* Check if tracking is currently enabled
*/
export const isTrackingEnabled = async () => {
const isStopped = await NativeSingular.isAllTrackingStopped();
return !isStopped;
};
/**
* Get current privacy status as readable text
*/
export const getPrivacyStatus = async () => {
const isEnabled = await isTrackingEnabled();
const dataSharingEnabled = await getDataSharingPreference();
const ageCategory = await getAgeCategory();
let status = `Tracking: ${isEnabled ? 'Enabled' : 'Disabled'}\n`;
status += `Data Sharing: ${dataSharingEnabled ? 'Enabled' : 'Limited'}`;
if (ageCategory === 'under_13') {
status += '\nCOPPA: Restrictions Applied';
}
return status;
};
// Private helper methods for AsyncStorage
const getUserConsent = async () => {
const value = await AsyncStorage.getItem(PREF_USER_CONSENT);
return value === 'true';
};
const saveUserConsent = async (consent) => {
await AsyncStorage.setItem(PREF_USER_CONSENT, consent.toString());
};
const getDataSharingPreference = async () => {
const value = await AsyncStorage.getItem(PREF_DATA_SHARING);
return value === 'true';
};
const saveDataSharingPreference = async (enabled) => {
await AsyncStorage.setItem(PREF_DATA_SHARING, enabled.toString());
};
const getAgeCategory = async () => {
return await AsyncStorage.getItem(PREF_AGE_CATEGORY);
};
// React component example
export default function PrivacyInitializer() {
useEffect(() => {
initializePrivacySettings();
}, []);
return null;
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
const PREF_USER_CONSENT = 'privacy_user_consent';
const PREF_DATA_SHARING = 'privacy_data_sharing';
const PREF_AGE_CATEGORY = 'user_age_category';
/**
* Initialize privacy settings on app startup based on stored preferences
*/
export const initializePrivacySettings = async () => {
const hasUserConsent = await getUserConsent();
const allowDataSharing = await getDataSharingPreference();
const ageCategory = await getAgeCategory();
// Apply age-based restrictions first
if (ageCategory === 'under_13') {
Singular.trackingUnder13();
Singular.setLimitAdvertisingIdentifiers(true);
console.log('COPPA restrictions applied on startup');
}
// Apply stored tracking preference
if (hasUserConsent) {
Singular.trackingOptIn();
Singular.resumeAllTracking();
console.log('Privacy initialized: Tracking enabled with consent');
} else {
Singular.stopAllTracking();
console.log('Privacy initialized: Tracking disabled');
}
// Set data sharing preference (inverse logic)
Singular.limitDataSharing(!allowDataSharing);
console.log(`Privacy initialized: consent=${hasUserConsent}, sharing=${allowDataSharing}`);
};
/**
* User accepts tracking via consent dialog
*/
export const onUserAcceptedTracking = async () => {
await saveUserConsent(true);
Singular.trackingOptIn();
Singular.resumeAllTracking();
console.log('User accepted tracking');
};
/**
* User declines tracking
*/
export const onUserDeclinedTracking = async () => {
await saveUserConsent(false);
Singular.stopAllTracking();
console.log('User declined tracking');
};
/**
* User updates data sharing preference
*/
export const setDataSharingEnabled = async (enabled) => {
await saveDataSharingPreference(enabled);
// Note: LimitDataSharing uses inverse logic
// false = data sharing enabled, true = data sharing limited
Singular.limitDataSharing(!enabled);
console.log(`Data sharing: ${enabled ? 'Enabled' : 'Limited'}`);
};
/**
* Check if tracking is currently enabled
*/
export const isTrackingEnabled = async () => {
const isStopped = await Singular.isAllTrackingStopped();
return !isStopped;
};
/**
* Get current privacy status as readable text
*/
export const getPrivacyStatus = async () => {
const isEnabled = await isTrackingEnabled();
const dataSharingEnabled = await getDataSharingPreference();
const ageCategory = await getAgeCategory();
let status = `Tracking: ${isEnabled ? 'Enabled' : 'Disabled'}\n`;
status += `Data Sharing: ${dataSharingEnabled ? 'Enabled' : 'Limited'}`;
if (ageCategory === 'under_13') {
status += '\nCOPPA: Restrictions Applied';
}
return status;
};
// Private helper methods for AsyncStorage
const getUserConsent = async () => {
const value = await AsyncStorage.getItem(PREF_USER_CONSENT);
return value === 'true';
};
const saveUserConsent = async (consent) => {
await AsyncStorage.setItem(PREF_USER_CONSENT, consent.toString());
};
const getDataSharingPreference = async () => {
const value = await AsyncStorage.getItem(PREF_DATA_SHARING);
return value === 'true';
};
const saveDataSharingPreference = async (enabled) => {
await AsyncStorage.setItem(PREF_DATA_SHARING, enabled.toString());
};
const getAgeCategory = async () => {
return await AsyncStorage.getItem(PREF_AGE_CATEGORY);
};
// React component example
export default function PrivacyInitializer() {
useEffect(() => {
initializePrivacySettings();
}, []);
return null;
}
주요 구현 가이드라인
모범 사례
- 영구 저장소: 비동기 스토리지 또는 보안 스토리지 솔루션을 사용하여 사용자 환경설정을 저장하세요.
- 조기 초기화: 가능하면 SDK 초기화 전에 개인정보 보호 설정을 적용하세요.
-
UI 동기화:
isAllTrackingStopped()를 사용하여 설정 UI를 실제 SDK 상태와 동기화합니다. - 명확한 커뮤니케이션: 앱 설정에서 명확하고 접근 가능한 개인정보 보호 컨트롤 제공
-
역논리:
limitDataSharing(false)은 데이터 공유가 활성화되어 있음을 의미하고true은 제한됨을의미합니다. -
COPPA 우선순위: 다른 개인정보 보호 설정보다 어린이 개인정보 보호(
trackingUnder13())를 먼저 적용하세요. - 규정 준수 문서: 사용자가 동의를 제공하거나 철회한 시기와 방법에 대한 기록을 유지합니다.