데이터 개인정보 보호법 준수
GDPR, CCPA 및 기타 소비자 개인정보 보호 규정에 따른 사용자의 동의 선택을 Singular에 알려, 개인정보 보호를 준수하는 데이터 수집을 구현하세요.
사용자가 제3자와의 정보 공유에 동의하거나 거부할 때, Singular의 개인정보 보호 메서드를 사용해 사용자의 선택을 전달하세요. 이를 통해 California Consumer Privacy Act (CCPA) 와 같은 규정을 준수하고, 파트너가 사용자의 개인정보 보호 설정을 존중할 수 있도록 할 수 있습니다.
자세히 알아보기: Singular의 개인정보 동의 처리 방식에 대한 자세한 내용은 유저 개인정보 보호 및 데이터 공유 제한 문서를 참고하세요.
데이터 공유 제한
제3자 데이터 공유 제어
사용자가 제3자 파트너와 개인 데이터를 공유하는 데 동의했는지를
limitDataSharing
메서드를 사용해 Singular에 알립니다.
메서드 시그니처:
public static void limitDataSharing(boolean shouldLimitDataSharing);
파라미터:
- false: 유저가 데이터 공유에 옵트인하고 동의함
- true: 유저가 옵트아웃하고 데이터 공유에 동의하지 않음
중요: 이 메서드는 선택 사항이지만 어트리뷰션 데이터 공유에 영향을 미칩니다. 일부 파트너는 사용자가 옵트인했음이 명시적으로 통보되었을 때에만 완전한 어트리뷰션 정보를 공유합니다.
사용 예시
// User has opted in to share their data
Singular.limitDataSharing(false)
// User has opted out and declined to share their data
Singular.limitDataSharing(true)
// Example: Set based on user preference
fun handlePrivacyConsent(userConsented: Boolean) {
// Pass inverse: false = opted in, true = opted out
Singular.limitDataSharing(!userConsented)
Log.d("Privacy", "Data sharing: ${if (userConsented) "Enabled" else "Limited"}")
}
// User has opted in to share their data
Singular.limitDataSharing(false);
// User has opted out and declined to share their data
Singular.limitDataSharing(true);
// Example: Set based on user preference
public void handlePrivacyConsent(boolean userConsented) {
// Pass inverse: false = opted in, true = opted out
Singular.limitDataSharing(!userConsented);
Log.d("Privacy", "Data sharing: " + (userConsented ? "Enabled" : "Limited"));
}
동작 방식:
Singular은 이 설정을 유저 개인정보 포스트백 에 사용하며, 규제 준수를 위해 이를 필요로 하는 파트너에게 전달합니다.
이벤트별 데이터 공유 제한
Singular 이벤트에 대한 데이터 공유 재정의
다른 이벤트 인수와 함께
Attributes.sngAttrLimitDataSharing
속성을 전달하면, Singular 이벤트 또는 커스텀 매출 호출에 대해 전역
limitDataSharing
설정을 재정의할 수 있습니다. 이는 사용자가 어떤 동작과 함께 동의를 갱신하고, 후속 이벤트에 대한 SDK 전역 설정을 변경하지 않고도 해당 특정 이벤트에 새로운 선택을 즉시 반영하려는 경우에 유용합니다.
허용 값:
- false: 해당 이벤트에 한해 유저가 옵트인함
- true: 해당 이벤트에 한해 유저가 옵트아웃함
중요:
이 속성은 반드시
Boolean
이어야 합니다. 불리언이 아닌 입력은 무시되며, 요청은 전역
limitDataSharing
설정으로 폴백됩니다. 어느 경우든 이 속성은 요청이 전송되기 전에 이벤트 인수에서 제거되므로 이벤트 페이로드에는 표시되지 않습니다.
사용 예시
// Example 1: Standard event, opt out only for this event
val args = JSONObject()
.put(Attributes.sngAttrLimitDataSharing.toString(), true)
.put("order_id", "12345")
Singular.event("checkout_completed", args.toString())
// Example 2: Custom revenue, opt in only for this event
val attributes = mutableMapOf<String, Any>().apply {
put(Attributes.sngAttrLimitDataSharing.toString(), false)
put("sku", "premium_monthly")
}
Singular.customRevenue("premium_purchase", "USD", 9.99, attributes)
// Example 1: Standard event, opt out only for this event
JSONObject args = new JSONObject();
args.put(Attributes.sngAttrLimitDataSharing.toString(), true);
args.put("order_id", "12345");
Singular.event("checkout_completed", args.toString());
// Example 2: Custom revenue, opt in only for this event
Map<String, Object> attributes = new HashMap<>();
attributes.put(Attributes.sngAttrLimitDataSharing.toString(), false);
attributes.put("sku", "premium_monthly");
Singular.customRevenue("premium_purchase", "USD", 9.99, attributes);
동작 방식:
유효한 불리언 값이 제공되면, SDK는 해당 속성을 이벤트 인수에서 제거하고 요청의
data_sharing_options.limit_data_sharing
필드에 기록합니다 — 이는 해당 Singular 요청에 한해 전역
limitDataSharing
설정을 재정의합니다. 전역 설정과
getLimitDataSharing
의 결과는 변경되지 않으므로, 속성을 제공하지 않는 모든 후속 이벤트는 계속해서 전역 선택을 사용합니다.
GDPR 준수 메서드
GDPR (일반 데이터 보호 규정) 및 기타 개인정보 보호 규정을 준수하기 위해 사용자 트래킹 동의를 관리하고 SDK 기능을 제어합니다.
트래킹 동의 관리
trackingOptIn
Singular 서버로 GDPR 옵트인 이벤트를 전송하여 사용자의 명시적인 트래킹 동의를 기록합니다.
메서드 시그니처:
Singular.trackingOptIn()
사용 시점:
- GDPR 준수: GDPR 규제 지역에서 유저가 트래킹에 명시적으로 동의할 때 호출
- 동의 기록: Singular 시스템에서 유저를 GDPR 동의를 제공한 것으로 표시함
- 기본 동작: 이 호출 없이도 SDK는 트래킹을 계속하지만, 동의를 별도로 기록하지는 않음
// User accepted tracking consent
Singular.trackingOptIn()
// Example: Call after consent dialog
fun onUserAcceptedTracking() {
Singular.trackingOptIn()
Log.d("GDPR", "User opted in to tracking")
}
// User accepted tracking consent
Singular.trackingOptIn();
// Example: Call after consent dialog
public void onUserAcceptedTracking() {
Singular.trackingOptIn();
Log.d("GDPR", "User opted in to tracking");
}
트래킹 제어 메서드
stopAllTracking
이 디바이스의 현재 유저에 대한 모든 SDK 트래킹 활동을 완전히 비활성화합니다.
메서드 시그니처:
Singular.stopAllTracking()
중요 중요:
이 메서드는
resumeAllTracking()
이 호출될 때까지 SDK를 영구적으로 비활성화합니다. 비활성화 상태는 앱 재시작 후에도 유지되며, 프로그래밍 방식으로만 되돌릴 수 있습니다.
동작:
- 즉시 적용: 모든 트래킹, 이벤트 보고 및 데이터 수집을 즉시 중단함
- 지속 상태: 앱이 종료되고 다시 열린 후에도 비활성화 상태가 유지됨
-
자동 초기화 없음:
재활성화하려면
resumeAllTracking()을 명시적으로 호출해야 함
// User declined all tracking
Singular.stopAllTracking()
// Example: Handle user opt-out
fun onUserDeclinedTracking() {
Singular.stopAllTracking()
Log.d("Privacy", "All tracking stopped")
// Optionally store preference
saveUserTrackingPreference(false)
}
// User declined all tracking
Singular.stopAllTracking();
// Example: Handle user opt-out
public void onUserDeclinedTracking() {
Singular.stopAllTracking();
Log.d("Privacy", "All tracking stopped");
// Optionally store preference
saveUserTrackingPreference(false);
}
resumeAllTracking
stopAllTracking()
으로 트래킹을 중단한 후 다시 활성화합니다.
메서드 시그니처:
Singular.resumeAllTracking()
사용 사례:
- 동의 변경: 유저가 개인정보 보호 설정을 변경하고 트래킹에 다시 옵트인함
- 개인정보 보호 설정: 유저가 앱 설정 메뉴를 통해 동의를 업데이트함
- 지역별 준수: 유저가 규제 대상이 아닌 지역으로 이동할 때 트래킹을 다시 활성화함
// User opted back in to tracking
Singular.resumeAllTracking()
// Example: Handle consent update
fun onUserResumedTracking() {
Singular.resumeAllTracking()
Log.d("Privacy", "Tracking resumed")
// Optionally update stored preference
saveUserTrackingPreference(true)
}
// User opted back in to tracking
Singular.resumeAllTracking();
// Example: Handle consent update
public void onUserResumedTracking() {
Singular.resumeAllTracking();
Log.d("Privacy", "Tracking resumed");
// Optionally update stored preference
saveUserTrackingPreference(true);
}
isAllTrackingStopped
현재 유저에 대해 트래킹이 비활성화되었는지 확인합니다.
메서드 시그니처:
public static boolean isAllTrackingStopped();
반환값:
-
true:
stopAllTracking()을 통해 현재 트래킹이 중단된 상태 - false: 트래킹이 활성 상태 (중단된 적이 없거나 재개됨)
SDK가 초기화되지 않은 경우, 이전 트래킹 상태와 관계없이
false
를 반환합니다. 이 값을 신뢰하기 전에 항상
Singular.init()
이 완료되었는지 확인하고,
false
를 "트래킹 중"이 아니라 "중단되지 않음 또는 아직 초기화되지 않음"으로 해석하세요.
// Check current tracking status
val isTrackingStopped = Singular.isAllTrackingStopped()
// Example: Display privacy status in settings
fun getPrivacyStatusText(): String {
return if (Singular.isAllTrackingStopped()) {
"Tracking: Disabled"
} else {
"Tracking: Enabled"
}
}
// Example: Sync UI with tracking state
fun updatePrivacyToggle() {
val isStopped = Singular.isAllTrackingStopped()
privacyToggle.isChecked = !isStopped
Log.d("Privacy", "Current tracking state: ${if (isStopped) "Stopped" else "Active"}")
}
// Check current tracking status
boolean isTrackingStopped = Singular.isAllTrackingStopped();
// Example: Display privacy status in settings
public String getPrivacyStatusText() {
if (Singular.isAllTrackingStopped()) {
return "Tracking: Disabled";
} else {
return "Tracking: Enabled";
}
}
// Example: Sync UI with tracking state
public void updatePrivacyToggle() {
boolean isStopped = Singular.isAllTrackingStopped();
privacyToggle.setChecked(!isStopped);
Log.d("Privacy", "Current tracking state: " + (isStopped ? "Stopped" : "Active"));
}
아동 개인정보 보호
trackingUnder13
COPPA (아동 온라인 개인정보 보호법) 및 기타 아동 개인정보 보호 규정을 준수하기 위해 사용자가 13세 미만임을 Singular에 알립니다.
메서드 시그니처:
Singular.trackingUnder13()
준수 요구사항:
- COPPA 준수: 미국에서 13세 미만 아동의 데이터를 수집하는 앱에 필수
- 연령 제한 콘텐츠: 유저가 회원가입 또는 연령 확인 과정에서 13세 미만으로 식별될 때 사용
- 트래킹 제한: 아동 개인정보 보호법을 준수하기 위해 데이터 수집을 제한함
// User identified as under 13
Singular.trackingUnder13()
// Example: Call after age verification
fun onAgeVerified(userAge: Int) {
if (userAge < 13) {
Singular.trackingUnder13()
Log.d("Privacy", "COPPA mode enabled for user under 13")
}
}
// User identified as under 13
Singular.trackingUnder13();
// Example: Call after age verification
public void onAgeVerified(int userAge) {
if (userAge < 13) {
Singular.trackingUnder13();
Log.d("Privacy", "COPPA mode enabled for user under 13");
}
}
중요: 사용자가 13세 미만임을 확인한 후 가능한 한 빨리 이 메서드를 호출하세요. 가급적 앱 초기화 시 또는 연령 확인 직후가 좋습니다. 이렇게 하면 이후의 모든 트래킹이 아동 개인정보 보호 규정을 따르게 됩니다.
setLimitAdvertisingIdentifiers
혼합 사용자 대상 앱의 경우 SDK 초기화 이후 광고 식별자 (Android의 GAID) 수집 및 사용을 제한합니다.
메서드 시그니처:
public static void setLimitAdvertisingIdentifiers(boolean enabled);
파라미터:
-
enabled:
광고 식별자 수집을 제한하려면
true를 전달하고, 일반 수집으로 복원하려면false를 전달합니다.
구성 시점 변형과 혼동하지 마세요.
런타임 메서드
Singular.setLimitAdvertisingIdentifiers(boolean)
는 불리언 인수가 필요하지만, 구성 시점 메서드
SingularConfig.withLimitAdvertisingIdentifiers()
는 인수를 받지 않고 무조건 제한을 활성화합니다. 런타임 메서드를 인수 없이 호출하면 컴파일되지 않습니다.
사용 사례:
- 혼합 사용자 대상 앱: 성인과 아동을 모두 대상으로 하며 앱 실행 후 연령이 결정되는 앱
- 동적 개인정보 제어: 사용자 행동이나 접근하는 콘텐츠에 따라 트래킹을 조정
- 런타임 제한: 초기 SDK 설정 이후 광고 식별자 제한을 적용
// Limit advertising identifiers after initialization
Singular.setLimitAdvertisingIdentifiers(true)
// Example: Mixed audience app with age gate
fun onKidsModeSwitched(isKidsMode: Boolean) {
if (isKidsMode) {
Singular.setLimitAdvertisingIdentifiers(true)
Log.d("Privacy", "Advertising identifiers limited for kids mode")
}
}
// Example: Content-based restrictions
fun onViewingChildrensContent() {
Singular.setLimitAdvertisingIdentifiers(true)
Log.d("Privacy", "Ad identifiers restricted for children's content")
}
// Limit advertising identifiers after initialization
Singular.setLimitAdvertisingIdentifiers(true);
// Example: Mixed audience app with age gate
public void onKidsModeSwitched(boolean isKidsMode) {
if (isKidsMode) {
Singular.setLimitAdvertisingIdentifiers(true);
Log.d("Privacy", "Advertising identifiers limited for kids mode");
}
}
// Example: Content-based restrictions
public void onViewingChildrensContent() {
Singular.setLimitAdvertisingIdentifiers(true);
Log.d("Privacy", "Ad identifiers restricted for children's content");
}
구성 대안:
SDK 시작 전에 개인정보 보호 요구사항을 알고 있는 경우,
SingularConfig.withLimitAdvertisingIdentifiers()
를 사용하여 SDK 초기화 시점에 광고 식별자를 제한할 수도 있습니다.
구성 메서드
개인정보 보호 요구사항을 미리 알고 있는 앱의 경우, SDK 초기화 시 광고 식별자 제한을 설정합니다.
// Limit advertising identifiers at initialization
val config = SingularConfig("SDK_KEY", "SDK_SECRET")
.withLimitAdvertisingIdentifiers()
Singular.init(applicationContext, config)
// Limit advertising identifiers at initialization
SingularConfig config = new SingularConfig("SDK_KEY", "SDK_SECRET")
.withLimitAdvertisingIdentifiers();
Singular.init(getApplicationContext(), config);
구현 모범 사례
전체 개인정보 관리 예시
유저 선호도를 존중하고 규정을 준수하는 포괄적인 개인정보 보호 제어를 구현합니다.
class PrivacyManager(private val context: Context) {
private val prefs = context.getSharedPreferences("privacy_prefs", Context.MODE_PRIVATE)
// Initialize privacy settings on app start
fun initializePrivacy() {
val hasUserConsent = getUserConsent()
val allowDataSharing = getDataSharingPreference()
val userAge = getUserAge()
// Apply stored preferences
if (hasUserConsent) {
Singular.trackingOptIn()
Singular.resumeAllTracking()
} else {
Singular.stopAllTracking()
}
// Set data sharing preference
Singular.limitDataSharing(!allowDataSharing)
// Handle children's privacy
if (userAge > 0 && userAge < 13) {
Singular.trackingUnder13()
Singular.setLimitAdvertisingIdentifiers(true)
}
Log.d("Privacy", "Initialized: consent=$hasUserConsent, sharing=$allowDataSharing, age=$userAge")
}
// User accepts tracking via consent dialog
fun userAcceptedTracking() {
saveUserConsent(true)
Singular.trackingOptIn()
Singular.resumeAllTracking()
Log.d("Privacy", "User accepted tracking")
}
// User declines tracking
fun userDeclinedTracking() {
saveUserConsent(false)
Singular.stopAllTracking()
Log.d("Privacy", "User declined tracking")
}
// User updates data sharing preference
fun setDataSharingEnabled(enabled: Boolean) {
saveDataSharingPreference(enabled)
// Note: limitDataSharing uses inverse logic
Singular.limitDataSharing(!enabled)
Log.d("Privacy", "Data sharing: ${if (enabled) "Enabled" else "Limited"}")
}
// Handle age verification result
fun onAgeVerified(age: Int) {
saveUserAge(age)
if (age < 13) {
Singular.trackingUnder13()
Singular.setLimitAdvertisingIdentifiers(true)
Log.d("Privacy", "COPPA restrictions applied for user under 13")
}
}
// Get current tracking status
fun isTrackingEnabled(): Boolean {
return !Singular.isAllTrackingStopped()
}
// Private helper methods
private fun getUserConsent(): Boolean {
return prefs.getBoolean("user_consent", false)
}
private fun saveUserConsent(consent: Boolean) {
prefs.edit().putBoolean("user_consent", consent).apply()
}
private fun getDataSharingPreference(): Boolean {
return prefs.getBoolean("data_sharing", false)
}
private fun saveDataSharingPreference(enabled: Boolean) {
prefs.edit().putBoolean("data_sharing", enabled).apply()
}
private fun getUserAge(): Int {
return prefs.getInt("user_age", 0)
}
private fun saveUserAge(age: Int) {
prefs.edit().putInt("user_age", age).apply()
}
}
public class PrivacyManager {
private Context context;
private SharedPreferences prefs;
public PrivacyManager(Context context) {
this.context = context;
this.prefs = context.getSharedPreferences("privacy_prefs", Context.MODE_PRIVATE);
}
// Initialize privacy settings on app start
public void initializePrivacy() {
boolean hasUserConsent = getUserConsent();
boolean allowDataSharing = getDataSharingPreference();
int userAge = getUserAge();
// Apply stored preferences
if (hasUserConsent) {
Singular.trackingOptIn();
Singular.resumeAllTracking();
} else {
Singular.stopAllTracking();
}
// Set data sharing preference
Singular.limitDataSharing(!allowDataSharing);
// Handle children's privacy
if (userAge > 0 && userAge < 13) {
Singular.trackingUnder13();
Singular.setLimitAdvertisingIdentifiers(true);
}
Log.d("Privacy", "Initialized: consent=" + hasUserConsent + ", sharing=" + allowDataSharing + ", age=" + userAge);
}
// User accepts tracking via consent dialog
public void userAcceptedTracking() {
saveUserConsent(true);
Singular.trackingOptIn();
Singular.resumeAllTracking();
Log.d("Privacy", "User accepted tracking");
}
// User declines tracking
public void userDeclinedTracking() {
saveUserConsent(false);
Singular.stopAllTracking();
Log.d("Privacy", "User declined tracking");
}
// User updates data sharing preference
public void setDataSharingEnabled(boolean enabled) {
saveDataSharingPreference(enabled);
// Note: limitDataSharing uses inverse logic
Singular.limitDataSharing(!enabled);
Log.d("Privacy", "Data sharing: " + (enabled ? "Enabled" : "Limited"));
}
// Handle age verification result
public void onAgeVerified(int age) {
saveUserAge(age);
if (age < 13) {
Singular.trackingUnder13();
Singular.setLimitAdvertisingIdentifiers(true);
Log.d("Privacy", "COPPA restrictions applied for user under 13");
}
}
// Get current tracking status
public boolean isTrackingEnabled() {
return !Singular.isAllTrackingStopped();
}
// Private helper methods
private boolean getUserConsent() {
return prefs.getBoolean("user_consent", false);
}
private void saveUserConsent(boolean consent) {
prefs.edit().putBoolean("user_consent", consent).apply();
}
private boolean getDataSharingPreference() {
return prefs.getBoolean("data_sharing", false);
}
private void saveDataSharingPreference(boolean enabled) {
prefs.edit().putBoolean("data_sharing", enabled).apply();
}
private int getUserAge() {
return prefs.getInt("user_age", 0);
}
private void saveUserAge(int age) {
prefs.edit().putInt("user_age", age).apply();
}
}
모범 사례:
- 지속적인 저장: 사용자 선호도를 SharedPreferences 또는 안전한 저장소에 저장
- 조기 초기화: 가능한 경우 SDK 초기화 전에 개인정보 보호 설정을 적용
-
UI 동기화:
isAllTrackingStopped()을 사용해 설정 UI를 실제 SDK 상태와 동기화 유지 - 명확한 커뮤니케이션: 앱 설정에서 명확하고 접근하기 쉬운 개인정보 보호 제어 제공
- 연령 확인: 아동을 대상으로 하는 앱에는 강력한 연령 확인을 구현
-
결합된 제어:
13세 미만 유저에게는
trackingUnder13()과setLimitAdvertisingIdentifiers(true)을 함께 적용