> ## Documentation Index
> Fetch the complete documentation index at: https://help.airbridge.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 트러블 슈팅 - 플러터 SDK

## iOS

<AccordionGroup>
  <Accordion title="저장된 암호의 도메인이 airbridge.io 또는 abr.ge로 보이는 문제">
    #### 문제 현상

    [Password AutoFill](https://developer.apple.com/documentation/security/password-autofill) 기능으로 저장된 암호의 도메인이 airbridge.io 또는 abr.ge로 보이는 현상이 앱을 사용하는 유저에게 발생할 수 있습니다.

    #### 발생 원인

    에어브릿지 SDK의 딥링크를 설정한 후에 Password AutoFill 기능을 활용하면 도메인이 에어브릿지 딥링크의 앱 링크(applinks) 도메인 airbridge.io 또는 abr.ge로 저장됩니다.

    #### 해결 방법

    Password AutoFill에 사용하는 webcredentials 도메인을 설정하면 문제를 해결할 수 있습니다.

    1. 암호를 저장하는 도메인을 준비해주세요.

    2. 아래 JSON을 `https://YOUR_DOMAIN/.well-known/apple-app-site-association`에 `Content-Type: application/json`으로 호스팅합니다. 준비한 도메인이 `YOUR_DOMAIN`입니다.

    [애플 개발자 대시보드](https://developer.apple.com/account/resources)의 \[Identifiers]>\[YOUR\_APP]에서 App ID Prefix, Bundle ID를 확인할 수 있습니다.

    ```json lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    {
        "webcredentials": {
            "apps": ["YOUR_APP_ID_PREFIX.YOUR_BUNDLE_ID"]
        }
    }
    ```

    3. Xcode의 \[YOUR\_PROJECT]>\[Signing & Capabilities]로 이동합니다.

    4. '+ Capability'를 클릭해 Associated Domains를 추가합니다. Associated Domains에 `webcredentials:YOUR_DOMAIN`을 입력합니다.
  </Accordion>

  <Accordion title="Xcode에서 Upload Symbol Failed 경고 메시지가 뜨는 현상">
    #### 문제 현상

    App Store에 앱 업로드 시 Xcode에 Airbridge framework에 dSYM이 포함되어 있지 않다는 경고 메세지가 나타나는 현상

    #### 발생 원인

    Airbridge iOS SDK에서 dSYM을 지원하고 있지 않습니다.

    #### 해결 방법

    다음 버전에서 dSYM 지원을 개발 중에 있습니다. 해당 경고는 무시해 주세요.
  </Accordion>

  <Accordion title="트래킹 링크로 앱을 열어도 의도한 페이지로 이동하지 않거나 딥링크가 전달되지 않습니다.">
    #### 문제 현상

    App Store에 앱 업로드 시 Xcode에 Airbridge framework에 dSYM이 포함되어 있지 않다는 경고 메세지가 나타나는 현상

    #### 발생 원인

    Flutter는 v3.7부터 외부 플러그인 없이 프레임워크의 Router API로 딥링크를 직접 처리하는 기능이 개선되었습니다. iOS에서는 `Info.plist`의 `FlutterDeepLinkingEnabled` 키 값이 `true`이거나 기본 핸들러가 동작하는 경우, Flutter 엔진이 들어온 유니버설 링크/스킴을 가로채 앱의 Router로 라우팅합니다.

    에어브릿지 SDK는 `AppDelegate`(`application(_:open:options:)`, `application(_:continue:restorationHandler:)`)에서 `AirbridgeFlutter.trackDeeplink`로 에어브릿지 딥링크를 받아 스킴 딥링크로 변환한 뒤 앱에 전달합니다. Flutter 기본 딥링크 핸들러와 이 처리 흐름이 충돌하면, 변환된 스킴 딥링크가 앱에 정상 전달되지 않습니다.

    #### 해결 방법

    `Info.plist`의 `FlutterDeepLinkingEnabled` 키 값을 Boolean `false`로 지정합니다. (Xcode의 plist 편집기에서는 `Boolean` 타입의 값을 `NO`로 표시합니다.)

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <key>FlutterDeepLinkingEnabled</key>
    <false/>
    ```

    설정 후 `AppDelegate`에 에어브릿지 SDK의 딥링크 네이티브 연동(`AirbridgeFlutter.trackDeeplink(url:)`, `AirbridgeFlutter.trackDeeplink(userActivity:)`)이 누락되지 않았는지도 함께 확인해 주세요.
  </Accordion>

  <Accordion title="[Xcode 27] SceneDelegate 전환 필수화 및 Airbridge 딥링크 추가 설정 안내">
    <Note>
      **알립니다**

      이 문서는 기존 AppDelegate 기반 앱을 UIScene(SceneDelegate)로 전환해야 하는 고객을 위한 트러블슈팅 가이드입니다.
    </Note>

    #### **문제 현상**

    * Xcode 27에서 빌드한 앱을 실행하면 GeneratedPluginRegistrant.register(with:) 부근에서 EXC\_BAD\_ACCESS (code=1, address=0x0) 크래시가 발생하며 앱이 즉시 종료됩니다.
    * 스킴 딥링크(Scheme Deeplink) 또는 유니버설 링크(Universal Links)로 앱에 진입해도 Airbridge 딥링크 이벤트가 수집되지 않습니다.
    * 개발자 가이드의 [SDK 초기화하기](/ko/developers/flutter-sdk-v4#sdk-초기화하기)와 앱에서 [딥링크 이벤트 수집하기](/ko/developers/flutter-sdk-v4#앱에서-딥링크-이벤트-수집하기) 예제대로 `AppDelegate`에 코드를 작성했는데도 동작하지 않습니다.

    #### **발생 원인**

    Apple은 앱 생명주기를 `UIApplicationDelegate`(AppDelegate) 단독 방식에서 `UIScene` 기반(SceneDelegate) 방식으로 전환하고 있으며, 최신 Xcode/iOS에서는 Scene 생명주기 채택이 사실상 필수가 되었습니다.

    앱이 `UIApplicationSceneManifest`를 통해 Scene 생명주기를 채택하면, 다음 `AppDelegate` 콜백은 **더 이상 호출되지 않습니다.**

    | 기존 `AppDelegate` 콜백                                         | Scene 생명주기에서의 대체 콜백(`SceneDelegate`)                       |
    | ----------------------------------------------------------- | ---------------------------------------------------------- |
    | `application:didFinishLaunchingWithOptions:`에서의 `window` 생성 | `scene:willConnectToSession:options:`                      |
    | 앱 콜드 스타트 시점의 딥링크                                            | `scene:willConnectToSession:options:`의 `connectionOptions` |
    | `application:openURL:options:`                              | `scene:openURLContexts:`                                   |
    | `application:continueUserActivity:restorationHandler:`      | `scene:continueUserActivity:`                              |

    #### **해결 방법**

    <Tabs>
      <Tab title="Airbridge Flutter SDK 4.10.0 이상">
        | 앱이 꺼진 상태에서 딥링크 | `scene:willConnectToSession:options:`에서 `connectionOptions`를 `trackDeeplink(connectionOptions:)` 호출 |
        | -------------- | --------------------------------------------------------------------------------------------------- |
        | 스킴 딥링크(백그라운드)  | `scene:openURLContexts:` 에서 `URLContexts` 를`trackDeeplink(openURLContexts:)`호출                      |
        | 유니버설 링크(백그라운드) | `scene:continueUserActivity:`에서 `NSUserActivity` 를`trackDeeplink(userActivity:)` 호출                 |
      </Tab>

      <Tab title="Airbridge Flutter SDK 4.10.0 미만">
        | 앱이 꺼진 상태에서 딥링크 | `scene:willConnectToSession:options:`에서 `connectionOptions`<br />- `URLContexts`에서 `URL`을 꺼내 `trackDeeplinkWithUrl:` 호출<br />- `userActivities`에서 `NSUserActivity`를 꺼내 `trackDeeplinkWithUserActivity:` 호출 |
        | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
        | 스킴 딥링크(백그라운드)  | `URLContexts`에서 `URL`을 꺼내 `trackDeeplinkWithUrl:` 호출                                                                                                                                                       |
        | 유니버설 링크(백그라운드) | `userActivities`에서 `NSUserActivity`를 꺼내 `trackDeeplinkWithUserActivity:` 호출                                                                                                                                |
      </Tab>
    </Tabs>

    ##### 1. Info.plist에 Scene Manifest 추가

    Info.plist에 아래 UIApplicationSceneManifest를 추가합니다. UISceneDelegateClassName은 사용하는 모듈에 맞게 지정하고, **UISceneStoryboardFile에 Main을 반드시 지정**합니다. (이 값이 없으면 FlutterViewController가 scene의 ootViewController로 생성되지 않아 플러그인 등록이 실패합니다.)

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <key>UIApplicationSceneManifest</key>
    <dict>
        <key>UIApplicationSupportsMultipleScenes</key>
        <false/>
        <key>UISceneConfigurations</key>
        <dict>
            <key>UIWindowSceneSessionRoleApplication</key>
            <array>
                <dict>
                    <key>UISceneConfigurationName</key>
                    <string>Default Configuration</string>
                    <key>UISceneDelegateClassName</key>
                    <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                    <key>UISceneStoryboardFile</key>
                    <string>Main</string>
                </dict>
            </array>
        </dict>
    </dict>
    ```

    ##### 2. SceneDelegate 생성 및 딥링크 수집 코드를 SceneDelegate로 이전

    <Tabs>
      <Tab title="Swift SDK 4.10.0 이상">
        ```swift lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        import UIKit
        import Flutter
        import airbridge_flutter_sdk

        class SceneDelegate: UIResponder, UIWindowSceneDelegate {
            var window: UIWindow?

            // when terminated app is opened with scheme deeplink or universal links
            func scene(_ scene: UIScene,
                       willConnectTo session: UISceneSession,
                       options connectionOptions: UIScene.ConnectionOptions) {
                guard let windowScene = scene as? UIWindowScene else { return }

                // In the Scene lifecycle, the window is owned by the SceneDelegate,
                // so plugins must be registered against the FlutterViewController, not the AppDelegate.
                if let controller = window?.rootViewController as? FlutterViewController {
                    GeneratedPluginRegistrant.register(with: controller)
                }

                // track deeplink
                AirbridgeFlutter.trackDeeplink(connectionOptions: connectionOptions)
            }

            // when backgrounded app is opened with scheme deeplink
            func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
                // track deeplink
                AirbridgeFlutter.trackDeeplink(openURLContexts: URLContexts)
            }

            // when backgrounded app is opened with universal links
            func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
                // track deeplink
                AirbridgeFlutter.trackDeeplink(userActivity: userActivity)
            }
        }
        ```
      </Tab>

      <Tab title="Objective-C SDK 4.10.0 이상">
        ```objective-c lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        // SceneDelegate.h
        #import <UIKit/UIKit.h>

        API_AVAILABLE(ios(13.0))
        @interface SceneDelegate : UIResponder <UIWindowSceneDelegate>

        @property (strong, nonatomic) UIWindow *window;

        @end

        // SceneDelegate.m
        #import "SceneDelegate.h"
        #import "AppDelegate.h"
        #import <Flutter/Flutter.h>
        #import "GeneratedPluginRegistrant.h"
        #import <airbridge_flutter_sdk/AirbridgeFlutter.h>

        @implementation SceneDelegate

        // when terminated app is opened with scheme deeplink or universal links
        - (void)scene:(UIScene *)scene
            willConnectToSession:(UISceneSession *)session
                         options:(UISceneConnectionOptions *)connectionOptions
        {
          // In the Scene lifecycle, the window is owned by the SceneDelegate,
          // so plugins must be registered against the FlutterViewController, not the AppDelegate.
          if ([self.window.rootViewController isKindOfClass:[FlutterViewController class]]) {
            FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController;
            [GeneratedPluginRegistrant registerWithRegistry:controller];
          }

          // track deeplink
          [AirbridgeFlutter trackDeeplinkWithConnectionOptions:connectionOptions];
        }

        // when backgrounded app is opened with scheme deeplink
        - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
        {
          // track deeplink
          [AirbridgeFlutter trackDeeplinkWithOpenURLContexts:URLContexts];
        }

        // when backgrounded app is opened with universal links
        - (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity
        {
          // track deeplink
          [AirbridgeFlutter trackDeeplinkWithUserActivity:userActivity];
        }

        @end
        ```
      </Tab>

      <Tab title="Swift SDK 4.10.0 미만">
        ```swift lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        import UIKit
        import Flutter
        import airbridge_flutter_sdk

        class SceneDelegate: UIResponder, UIWindowSceneDelegate {
            var window: UIWindow?

            // when terminated app is opened with scheme deeplink or universal links
            func scene(_ scene: UIScene,
                       willConnectTo session: UISceneSession,
                       options connectionOptions: UIScene.ConnectionOptions) {
                guard let windowScene = scene as? UIWindowScene else { return }

                // In the Scene lifecycle, the window is owned by the SceneDelegate,
                // so plugins must be registered against the FlutterViewController, not the AppDelegate.
                if let controller = window?.rootViewController as? FlutterViewController {
                    GeneratedPluginRegistrant.register(with: controller)
                }

                // track deeplink
                if let context = connectionOptions.urlContexts.first {
                    AirbridgeFlutter.trackDeeplink(url: context.url)
                }
                if let userActivity = connectionOptions.userActivities.first {
                    AirbridgeFlutter.trackDeeplink(userActivity: userActivity)
                }
            }

            // when backgrounded app is opened with scheme deeplink
            func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
                // track deeplink
                if let context = URLContexts.first {
                    AirbridgeFlutter.trackDeeplink(url: context.url)
                }
            }

            // when backgrounded app is opened with universal links
            func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
                // track deeplink
                AirbridgeFlutter.trackDeeplink(userActivity: userActivity)
            }
        }
        ```
      </Tab>

      <Tab title="Objective-C SDK 4.10.0 미만">
        ```objective-c lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
        // SceneDelegate.h
        #import <UIKit/UIKit.h>

        API_AVAILABLE(ios(13.0))
        @interface SceneDelegate : UIResponder <UIWindowSceneDelegate>

        @property (strong, nonatomic) UIWindow *window;

        @end

        // SceneDelegate.m
        #import "SceneDelegate.h"
        #import "AppDelegate.h"
        #import <Flutter/Flutter.h>
        #import "GeneratedPluginRegistrant.h"
        #import <airbridge_flutter_sdk/AirbridgeFlutter.h>

        @implementation SceneDelegate

        // when terminated app is opened with scheme deeplink or universal links
        - (void)scene:(UIScene *)scene
            willConnectToSession:(UISceneSession *)session
                         options:(UISceneConnectionOptions *)connectionOptions
        {
          // In the Scene lifecycle, the window is owned by the SceneDelegate,
          // so plugins must be registered against the FlutterViewController, not the AppDelegate.
          if ([self.window.rootViewController isKindOfClass:[FlutterViewController class]]) {
            FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController;
            [GeneratedPluginRegistrant registerWithRegistry:controller];
          }

          // track deeplink
          UIOpenURLContext *context = connectionOptions.URLContexts.allObjects.firstObject;
          if (context != nil) {
            [AirbridgeFlutter trackDeeplinkWithUrl:context.URL];
          }
          NSUserActivity *userActivity = connectionOptions.userActivities.allObjects.firstObject;
          if (userActivity != nil) {
            [AirbridgeFlutter trackDeeplinkWithUserActivity:userActivity];
          }
        }

        // when backgrounded app is opened with scheme deeplink
        - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
        {
          // track deeplink
          UIOpenURLContext *context = URLContexts.allObjects.firstObject;
          if (context != nil) {
            [AirbridgeFlutter trackDeeplinkWithUrl:context.URL];
          }
        }

        // when backgrounded app is opened with universal links
        - (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity
        {
          // track deeplink
          [AirbridgeFlutter trackDeeplinkWithUserActivity:userActivity];
        }

        @end
        ```
      </Tab>
    </Tabs>

    ##### 3. AppDelegate 정리

    * SDK 초기화 코드(`initializeSDK`)는 `application:didFinishLaunchingWithOptions:`에 **그대로 둡니다.**
    * 딥링크 수집을 위해 작성했던 `application:openURL:options:`와 `application:continueUserActivity:restorationHandler:`는 Scene 생명주기에서 호출되지 않으므로,  `SceneDelegate`로 이전한 뒤 `AppDelegate`에서는 삭제합니다.
    * `GeneratedPluginRegistrant.register(with: self)`는 `FlutterAppDelegate`가 `self.window.rootViewController`(FlutterViewController)를 통해 플러그인 레지스트라를 만듭니다. Scene 생명주기에서는 `window`가 `SceneDelegate` 소유라 `AppDelegate`의 `self.window`가 `nil`이 됩니다. 그 결과 레지스트라가 `nil`

      로 반환되고, 이를 받은 플러그인이 `nil`을 역참조하면서 `EXC_BAD_ACCESS (address=0x0)` 크래시가 발생합니다. 따라서 플러그인 등록(`GeneratedPluginRegistrant.register`)을 **반드시** **`FlutterViewController`가 존재하는** **`SceneDelegate`로 이전**해야 합니다.

    ```swift lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    import UIKit
    import Flutter
    import airbridge_flutter_sdk

    @main
    @objc class AppDelegate: FlutterAppDelegate {
        override func application(
            _ application: UIApplication,
            didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
        ) -> Bool {
            AirbridgeFlutter.initializeSDK(name: "YOUR_APP_NAME", token: "YOUR_APP_TOKEN")

    				// Moved to SceneDelegate (Deleted)
            // GeneratedPluginRegistrant.register(with: self)

            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
    }
    ```
  </Accordion>
</AccordionGroup>

## 안드로이드

<AccordionGroup>
  <Accordion title="빌드하면 Dependencies coroutines 오류가 발생합니다">
    #### 문제 현상

    빌드하면 아래 메시지와 함께 Dependencies coroutines 오류가 발생합니다.

    ```html Text lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    java.lang.NoClassDefFoundError: kotlin/coroutines/AbstractCoroutineContextKey
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
      ...
    ```

    #### 발생 원인

    kotlinx-coroutines-core 라이브러리 버전이 v1.3.5 이상이면 [kotlin-stdlib 라이브러리 버전이 일정 레벨 이상](https://github.com/Kotlin/kotlinx.coroutines/issues/1879)이어야 합니다.

    #### 해결 방법

    `gradlew dependencies` 커멘드로 kotlin-stdlib 라이브러리 버전이 v1.3.70 이상인지 확인해 주세요. 해당 버전 미만이라면 업데이트를 진행해야 합니다.
  </Accordion>

  <Accordion title="브레이즈 SDK를 사용한 푸시 알람으로 발생한 딥링크 이벤트가 수집되지 않습니다">
    #### 문제 현상

    브레이즈 SDK를 사용한 푸시 알람으로 발생한 딥링크 실행(Deeplink Open) 이벤트가 수집되지 않습니다. 대신 실행(Open) 이벤트가 수집됩니다.

    #### 발생 원인

    에어브릿지 안드로이드 SDK는 activity의 action에 있는 dataString과 intent의 dataString으로 딥링크 실행 이벤트와 실행 이벤트를 판별합니다.

    브레이즈 SDK를 사용한 푸시 알람으로 앱을 실행하면 `NotificationTrampolineActivity`을 사용합니다. 브레이즈 SDK를 사용한 푸시 알람으로 앱을 실행하면 `NotificationTrampolineActivity`의 activity의 action과 intent로부터 dataString을 확인할 수 없습니다. 이로 인해 딥링크 실행 이벤트와 실행 이벤트를 판별할 수 없습니다.

    #### 해결 방법

    <CodeGroup>
      ```kotlin Kotlin lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
      import co.ab180.airbridge.flutter.AirbridgeFlutter
      ...
      AirbridgeFlutter.setLifecycleIntegration { activity ->
          return@setLifecycleIntegration activity
              .takeIf { it.javaClass.name == "com.braze.push.NotificationTrampolineActivity" }
              ?.run { intent?.extras?.getString("uri") }
      }
      ```

      ```java Java lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
      import co.ab180.airbridge.flutter.AirbridgeFlutter;
      import co.ab180.airbridge.flutter.common.AirbridgeLifecycleIntegration;
      ...
      AirbridgeFlutter.setLifecycleIntegration(new AirbridgeLifecycleIntegration() {
          @Nullable
          @Override
          public String getDataString(@NonNull Activity activity) {
              if (
                  activity.getClass().getName().equals("com.braze.push.NotificationTrampolineActivity")
                  && activity.getIntent() != null
                  && activity.getIntent().getExtras() != null
              ) {
                  return activity.getIntent().getExtras().getString("uri");
              }
              return null;
          }
      });
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="빌드하면 Manifest merger failed 오류가 발생합니다">
    #### 문제 현상

    빌드하면 Manifest merger failed 오류가 발생합니다.

    #### 발생 원인

    에어브릿지 SDK의 `AndroidManifest.xml`에는 공유 환경 설정 데이터 백업을 옵트아웃하는 규칙이 포함되어 있습니다. 이는 재설치 중에 동일한 에어브릿지 설정 값들을 유지하지 않도록 하여 새로운 설치 또는 재설치를 정확하게 감지하도록 하기 위해 수행됩니다.

    에어브릿지 SDK 백업 규칙과 앱 백업 규칙의 병합 과정에서 충돌이 발생할 수 있습니다.

    #### 해결 방법

    에어브릿지 SDK에서 정의하는 옵트아웃하는 규칙은 다음과 같습니다.

    <CodeGroup>
      ```xml Backup on Android 12 or later lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
      <?xml version="1.0" encoding="utf-8"?>
      <data-extraction-rules>
          <cloud-backup>
              <exclude domain="sharedpref" path="airbridge-internal" />
              <exclude domain="sharedpref" path="airbridge-install" />
              <exclude domain="sharedpref" path="airbridge-user-info" />
              <exclude domain="sharedpref" path="airbridge-user-alias" />
              <exclude domain="sharedpref" path="airbridge-user-attributes" />
              <exclude domain="sharedpref" path="airbridge-device-alias" />
              <exclude domain="database" path="airbridge.db" />
          </cloud-backup>
          <device-transfer>
              <exclude domain="sharedpref" path="airbridge-internal" />
              <exclude domain="sharedpref" path="airbridge-install" />
              <exclude domain="sharedpref" path="airbridge-user-info" />
              <exclude domain="sharedpref" path="airbridge-user-alias" />
              <exclude domain="sharedpref" path="airbridge-user-attributes" />
              <exclude domain="sharedpref" path="airbridge-device-alias" />
              <exclude domain="database" path="airbridge.db" />
          </device-transfer>
      </data-extraction-rules>
      ```

      ```xml Backup on Android 11 and earlier lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
      <?xml version="1.0" encoding="utf-8"?>
      <full-backup-content>
          <exclude domain="sharedpref" path="airbridge-internal" />
          <exclude domain="sharedpref" path="airbridge-install" />
          <exclude domain="sharedpref" path="airbridge-user-info" />
          <exclude domain="sharedpref" path="airbridge-user-alias" />
          <exclude domain="sharedpref" path="airbridge-user-attributes" />
          <exclude domain="sharedpref" path="airbridge-device-alias" />
          <exclude domain="database" path="airbridge.db" />
      </full-backup-content>
      ```
    </CodeGroup>

    ##### fullBackupContent="string"과의 충돌 방지

    `android:fullBackupContent="string"`를  `AndroidManifest.xml`에 추가하면 다음과 같은 오류가 발생할 수 있습니다.

    ```html Build Output lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    Manifest merger failed : Attribute application@fullBackupContent value=(string) from AndroidManifest.xml
    ```

    위 오류를 해결하려면 `AndroidManifest.xml` 파일의

    * `<manifest>` 태그에  `xmlns:tools="http://schemas.android.com/tools"`를 추가 해주세요.
    * `<application>` 태그에  `tools:replace="android:fullBackupContent"`를 추가 해주세요.

    ##### dataExtractionRules="string resource"과의 충돌 방지

    `android:dataExtractionRules="string resource"`를  `AndroidManifest.xml`에 추가하면 다음과 같은 오류가 발생할 수 있습니다.

    ```html Build Output lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    Manifest merger failed : Attribute application@dataExtractionRules value=(string resource) from AndroidManifest.xml

    ```

    위 오류를 해결하려면 `AndroidManifest.xml` 파일의

    * `<manifest>` 태그에  `xmlns:tools="http://schemas.android.com/tools"`를 추가 해주세요.
    * `<application>` 태그에  `tools:replace="android:dataExtractionRules"`를 추가 해주세요.

    ##### allowBackup="false"과의 충돌 방지

    `android:allowBackup="false"`를  `AndroidManifest.xml`에 추가하면 다음과 같은 오류가 발생할 수 있습니다.

    ```html Build Output lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    Manifest merger failed : Attribute application@allowBackup value=(false) from AndroidManifest.xml:32:9-36
    	is also present at [:airbridge] AndroidManifest.xml:27:9-35 value=(true).
    	Suggestion: add 'tools:replace="android:allowBackup"' to <application> element at AndroidManifest.xml:30:5-250:19 to override.
    ```

    위 오류를 해결하려면 `AndroidManifest.xml` 파일의

    * `<manifest>` 태그에  `xmlns:tools="http://schemas.android.com/tools"`를 추가 해주세요.
    * `<application>` 태그에  `tools:replace="android:allowBackup"`를 추가 해주세요.

    ##### compileSdkVersion이 31 미만일 경우

    `android:dataExtractionRules` 기능이 API Level 31 부터 추가되었기 때문에 compileSdkVersion이 31 미만일 경우, 다음과 같은 오류가 발생할 수 있습니다.

    ```html Build Output lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    AndroidManifest.xml: AAPT: error: attribute android:dataExtractionRules not found.
    ```

    위 오류를 해결하려면 `AndroidManifest.xml` 파일의

    * `<manifest>` 태그에  `xmlns:tools="http://schemas.android.com/tools"`를 추가 해주세요.
    * `<application>` 태그에  `tools:remove="android:dataExtractionRules"`를 추가 해주세요.

    아래 가이드를 함께 참고해 주세요.

    * [안드로이드 가이드](https://developer.android.com/guide/topics/data/autobackup)
    * [에어브릿지 가이드](https://airbridge.readme.io/page/auto-backup-%EC%A4%91%EB%B3%B5-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-%EA%B0%80%EC%9D%B4%EB%93%9C)
  </Accordion>

  <Accordion title="에어브릿지 SDK 백업 규칙 병합 충돌 문제">
    #### 문제 현상

    에어브릿지 SDK 백업 규칙과 다른 서드파티 SDK (예. Appsflyer SDK) 백업 규칙이 중복 적용되는 경우 아래와 같은 빌드 에러가 표시됩니다.

    ```html lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    Attribute application@fullBackupContent value=(@xml/appsflyer_backup_rules) from [com.appsflyer:af-android-sdk:6.6.1] AndroidManifest.xml:14:18-73
    is also present at [io.airbridge:sdk-android:2.14.0] AndroidManifest.xml:27:18-78 value=(@xml/airbridge_auto_backup_rules).
    Suggestion: add 'tools:replace="android:fullBackupContent"' to <application> element at AndroidManifest.xml:7:5-13:19 to override.
    ```

    #### 발생 원인

    에어브릿지 SDK 백업 규칙과 서드파티 SDK 백업 규칙 중복 적용으로 인해 빌드 에러가 발생할 수 있습니다.

    #### 해결 방법

    ##### `backup_rules.xml` 설정

    1. `android/app/src/main/res/xml` 폴더를 생성해주세요.
    2. 생성된 xml 폴더 내부에 (e.g. `custom_backup_rules.xml`) 파일을 생성해주세요.
    3. 에어브릿지 SDK에서 정의하는 데이터 백업 규칙을 다음과 같이 추가해 주세요.

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <?xml version="1.0" encoding="utf-8"?>
    <full-backup-content>
        <!-- Airbridge Backup Rules -->
        <exclude domain="sharedpref" path="airbridge-internal" />
        <exclude domain="sharedpref" path="airbridge-install" />
        <exclude domain="sharedpref" path="airbridge-user-info" />
        <exclude domain="sharedpref" path="airbridge-user-alias" />
        <exclude domain="sharedpref" path="airbridge-user-attributes" />
        <exclude domain="sharedpref" path="airbridge-device-alias" />
        <exclude domain="database" path="airbridge.db" />
      
        <!-- Appsflyer Backup Rules -->
        <exclude domain="sharedpref" path="appsflyer-data"/>
    	
    	<!-- Your Custom Backup Rules -->
    </full-backup-content>
    ```

    ##### `data_extraction_rules.xml` 설정

    <Info>
      **알립니다**

      data\_extraction\_rules.xml 설정은 Airbridge Flutter SDK v4.1.5 이상부터 필요로 합니다.
    </Info>

    1. 생성된 xml 폴더 내부에 (e.g. `custom_data_extraction_rule.xml`) 파일을 생성해주세요.
    2. 에어브릿지 SDK에서 정의하는 데이터 백업 규칙을 다음과 같이 추가해 주세요.

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <?xml version="1.0" encoding="utf-8"?>
    <data-extraction-rules>
        <cloud-backup>
            <!-- Airbridge Backup Rules -->
            <exclude domain="sharedpref" path="airbridge-internal" />
            <exclude domain="sharedpref" path="airbridge-install" />
            <exclude domain="sharedpref" path="airbridge-user-info" />
            <exclude domain="sharedpref" path="airbridge-user-alias" />
            <exclude domain="sharedpref" path="airbridge-user-attributes" />
            <exclude domain="sharedpref" path="airbridge-device-alias" />
            <exclude domain="database" path="airbridge.db" />

            <!-- Appsflyer Backup Rules -->
            <exclude domain="sharedpref" path="appsflyer-data"/>

    	    <!-- Your Custom Backup Rules -->
        </cloud-backup>
        <device-transfer>
            <!-- Airbridge Backup Rules -->
            <exclude domain="sharedpref" path="airbridge-internal" />
            <exclude domain="sharedpref" path="airbridge-install" />
            <exclude domain="sharedpref" path="airbridge-user-info" />
            <exclude domain="sharedpref" path="airbridge-user-alias" />
            <exclude domain="sharedpref" path="airbridge-user-attributes" />
            <exclude domain="sharedpref" path="airbridge-device-alias" />
            <exclude domain="database" path="airbridge.db" />

            <!-- Appsflyer Backup Rules -->
            <exclude domain="sharedpref" path="appsflyer-data"/>

    	    <!-- Your Custom Backup Rules -->
        </device-transfer>
    </data-extraction-rules>
    ```

    ##### `AndroidManifest.xml` 설정

    ###### Airbridge Flutter SDK v4.1.5 이상

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <manifest
        ...
        xmlns:tools="http://schemas.android.com/tools">

        <application
            ...
    		android:allowBackup="true"
    		android:fullBackupContent="@xml/custom_backup_rules"
            android:dataExtractionRules="@xml/custom_data_extraction_rules"
    		tools:replace="android:fullBackupContent,android:dataExtractionRules">
    ```

    ###### Airbridge Flutter SDK v4.1.5 미만

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <manifest
        ...
        xmlns:tools="http://schemas.android.com/tools">

        <application
            ...
    		android:allowBackup="true"
    		android:fullBackupContent="@xml/custom_backup_rules"
    		tools:replace="android:fullBackupContent">
    ```
  </Accordion>

  <Accordion title="GAID가 00000000-0000-0000-0000-000000000000으로 수집됩니다">
    #### 발생 현상

    LAT(LimitAdTracking)를 비활성화한 상태에서 정상적으로 수집되어야 하는 GAID가 00000000-0000-0000-0000-000000000000으로 수집됩니다.

    #### 발생 원인

    다른 서드파티 라이브러리 등으로 인해 AD\_ID 권한이 제외되었습니다.

    #### **해결 방법**

    AD\_ID 권한을 추가합니다.

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <manifest ...>
      ...
      <uses-permission android:name="com.google.android.gms.permission.AD_ID" />
      ...
    </manifest>
    ```
  </Accordion>

  <Accordion title="Google Play 업로드 시 16KB 페이지 크기 관련 경고 또는 거부가 발생합니다">
    #### 문제 현상

    Google Play Console에 앱을 업로드할 때 다음과 같은 경고 또는 거부 메시지를 받을 수 있습니다:

    * "Your app doesn't support 16 KB page size"
    * "16 KB page size compatibility required"
    * 앱 업로드가 거부되거나 경고 상태로 표시됩니다

    이는 Android 15(API 수준 35) 이상을 타겟팅하는 앱이 16KB 페이지 크기를 지원해야 한다는 Google Play 정책 요구사항 때문입니다.

    #### 발생 원인

    16KB 페이지 크기 지원을 위해서는 앱 전체(APK/AAB)가 16KB zipalign으로 패키징되어야 Google Play 정책을 통과합니다.

    APK/AAB가 16KB zipalign으로 빌드되지 않으면:

    * Google Play Console 업로드 시 경고 또는 거부 메시지를 받습니다
    * 2025년 11월 1일 이후에는 업로드가 완전히 차단됩니다
    * 16KB 페이지 크기 기기에서 향후 정상 작동하지 않을 수 있습니다

    **Airbridge SDK 알려진 이슈: PT\_GNU\_RELRO 정렬**

    Airbridge SDK는 v4.6부터 16KB 페이지 크기를 지원하고 있습니다. 다만 SDK 빌드에 NDK r21을 사용한 버전에서는 네이티브 라이브러리(`.so`)의 LOAD 세그먼트는 16KB 경계로 정렬되지만 `PT_GNU_RELRO` 세그먼트는 정렬되지 않습니다.

    * **앱 동작에는 영향이 없습니다.** 16KB 페이지 크기 기기에서도 정상적으로 동작합니다.
    * 다만 Google Play Console 업로드 시 16KB 페이지 크기 관련 경고 메시지가 표시될 수 있습니다.
    * Android Studio의 lint, APK Analyzer, `check_elf_alignment.sh` 등 정렬 검사 도구에서도 경고로 표시될 수 있습니다.

    해당 이슈는 SDK 빌드 툴체인을 NDK r27로 업그레이드하여 수정했으며, 수정 이후 16KB 페이지 크기 지원에 이상이 없음을 확인했습니다. 플랫폼별 16KB 페이지 크기 지원 버전과 권장 버전은 아래 표를 참고해 주세요.

    | Platform      | 16KB 지원 시작 버전 | 권장 버전 (PT\_GNU\_RELRO 이슈 수정) |
    | ------------- | ------------- | ---------------------------- |
    | Android       | v4.6          | v4.9.4                       |
    | React Native  | v4.6          | v4.9.0                       |
    | Cordova Ionic | v4.4.1        | x(지원 예정)                     |
    | Flutter       | v4.6          | v4.9.0                       |
    | Expo          | v4.6          | v4.9.0                       |
    | Unity         | v4.6          | v4.9.2                       |
    | Unreal        | v4.6          | v4.9.0                       |

    #### 해결 방법

    **Step 1: Airbridge SDK 버전 확인**

    사용 중인 Airbridge SDK 버전을 위 표와 비교해 확인해 주세요.

    * **`16KB 지원 시작 버전`보다 낮은 버전을 사용 중인 경우:**

      16KB 페이지 크기를 지원하지 않습니다. 반드시 업데이트해 주세요.
    * **`16KB 지원 시작 버전`** **이상,** **`권장 버전`** **미만을 사용 중인 경우:**

      위에 안내한 `PT_GNU_RELRO` 정렬 이슈로 인해 Google Play Console 경고가 계속 표시될 수 있습니다. 앱 동작에는 영향이 없지만, 경고를 해소하려면 `권장 버전` 이상으로 업데이트해 주세요.

    **Step 2: Android Gradle Plugin(AGP) 버전 확인 및 설정**

    현재 프로젝트의 AGP 버전을 확인하세요.

    AGP 8.5.1 이상을 사용하는 경우:

    * 별도의 추가 설정 없이 자동으로 16KB zipalign이 적용됩니다.
    * Step 3으로 바로 이동하세요.

    AGP 8.5.0 이하를 사용하는 경우:

    * 옵션 A: AGP를 8.5.1 이상으로 업그레이드
    * 옵션 B: 현재 AGP 버전 유지 + 설정 추가

      ```groovy lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
      android {
          ...
          packagingOptions {
              jniLibs {
                  useLegacyPackaging true
              }
          }
      }
      ```

    **Step 3: 앱 다시 빌드 및 확인**

    설정을 변경한 후 앱을 클린 빌드하세요.

    **Step 4: 16KB zipalign 적용 확인**

    빌드된 APK/AAB가 올바르게 16KB zipalign되었는지 확인하세요.
  </Accordion>

  <Accordion title="트래킹 링크로 앱을 열어도 의도한 페이지로 이동하지 않거나 딥링크가 전달되지 않습니다.">
    **문제 현상**

    에어브릿지 트래킹 링크(앱 링크/URI 스킴)로 앱을 실행했을 때 다음과 같은 현상이 발생할 수 있습니다.

    * 트래킹 링크에 설정한 목적지(스킴 딥링크)로 이동하지 않고 홈 화면이나 빈 화면으로 진입합니다.
    * 딥링크 리스너(

      `Airbridge.setOnDeeplinkReceived`

      )로 스킴 딥링크가 전달되지 않습니다.
    * 딥링크 실행(Deeplink Open) 이벤트 또는 디퍼드 딥링크의 목적지 전달이 동작하지 않습니다.

    ##### 발생 원인

    Flutter는 v3.7부터 외부 플러그인 없이 프레임워크의 Router API로 딥링크를 직접 처리하는 기능이 개선되었고, Flutter v3.27 이상에서는 이 기본 딥링크 핸들러가 기본적으로 활성화됩니다.

    에어브릿지 SDK는 네이티브 레벨(`MainActivity`의 `onResume`/`onNewIntent`에서 호출하는 `AirbridgeFlutter.trackDeeplink(intent)`)에서 들어온 에어브릿지 딥링크를 가로채, 트래킹 링크에 설정된 스킴 딥링크(`YOUR_SCHEME://...`)로 변환한 뒤 앱에 전달합니다.

    이때 Flutter 기본 딥링크 핸들러가 활성화되어 있으면, Flutter 엔진이 동일한 URI를 가로채 앱의 Router로 먼저 라우팅합니다. 앱에 전달되는 값이 에어브릿지 SDK가 변환한 스킴 딥링크가 아니라 원본 형태의 에어브릿지 딥링크(HTTP 앱 링크 등)이므로, 앱의 라우터가 해당 경로를 매칭하지 못하고 에어브릿지 SDK의 변환·전달 흐름과도 충돌합니다.

    ##### 해결 방법

    `AndroidManifest.xml`에서 딥링크를 처리하는 `<activity>`(일반적으로 `MainActivity`)에 `flutter_deeplinking_enabled` 메타데이터를 `false`로 설정해 Flutter 기본 딥링크 핸들러를 비활성화합니다.

    ```xml lines theme={"theme":{"light":"github-dark-dimmed","dark":"github-dark-dimmed"}}
    <activity
        android:name=".MainActivity"
        ...>
        <meta-data
            android:name="flutter_deeplinking_enabled"
            android:value="false" />
        ...
    </activity>
    ```

    설정 후 에어브릿지 SDK의 딥링크 네이티브 연동(`MainActivity`의 `AirbridgeFlutter.trackDeeplink(intent)` 호출)이 누락되지 않았는지도 함께 확인해 주세요.
  </Accordion>
</AccordionGroup>

<link rel="alternate" hrefLang="en" href="https://help.airbridge.io/en/developers/troubleshooting-flutter-sdk-v4" />

<link rel="alternate" hrefLang="ko" href="https://help.airbridge.io/ko/developers/troubleshooting-flutter-sdk-v4" />
