react native appsflyer deep link 완벽 가이드 - 초기 설정 (1)

Choi Boo·2025년 3월 13일
1

appsflyer-deep-link

목록 보기
1/4
post-thumbnail

Google Firebase Dynamic Links는 2025년 8월 25일에 중단되기 때문에 대체안인 “AppsFlyer”를 찾았습니다.

AppsFlyer는 기본적으로 모바일 앱 마케팅 및 광고 성과 분석을 위한 모바일 측정하기 위한 솔루션입니다.

딥링크란 사용자를 일반적인 앱 홈페이지나 모바일 웹사이트가 아닌 앱 내부의 특정 위치로 직접 연결하는 것을 말합니다.

딥링크는 일반 웹 링크와 매우 유사한 방식으로 작동하며, 클릭하면 웹사이트 내의 기사나 페이지로 이동합니다. 차이점은 모바일에서는 이러한 링크가 앱 내부의 콘텐츠로 이동한다는 점입니다.

AppsFlyer 딥링크는 앱에 AppsFlyer SDK를 설치하고 딥 링크를 이용해 앱의 특정 화면으로 이동할 수 있습니다.

⚠️ AppsFlyer 시작하기 전 주의사항 ⚠️

  1. AppsFlyer 회원가입하기 위해서는 업무용 이메일이 필요합니다.
  2. AppsFlyer 딥링크 iOS 테스트하기 위해서는 Apple Developer Program을 가입해야 합니다.
    https://developer.apple.com/kr/programs/

왜 AppsFlyer를 선택했는가?

Firebase Dynamic Links를 대체할 수 있는 딥링크 솔루션은 여러 있지만 그 중에서 AppsFlyer를 선택한 이유는 토스를 이용할 때 토스에서 카톡 알림이 오는 경우 링크를 클릭하면 *.onelink.me라는 도메인을 사용하고 있어서 그것에 대해 찾아보니 아래에 대한 정보입니다.

  1. AppsFlyer에서 지원하는 도메인
  2. 토스에서 이미 검증된 솔루션이라 판단
  3. React Native 지원
  4. 딥링크 & 유니버셜 링크 무료

새로나온 수파링크라는 것도 있는데 한국인이 만든 솔루션입니다. 이것도 무료라서 가볍게 사용하기에 좋아보입니다.

Supalink - The Perfect Alternative to Firebase Dynamic Links

Deep linking | AppsFlyer mobile glossary

프로젝트 초기 설정

npx @react-native-community/cli@latest init appsflyer_deep_link_app
// package.json

"dependencies": {
  "react": "19.0.0",
  "react-native": "0.78.0"
},

Android - new architecture 비활성화

// android/gradle.properties

newArchEnabled=false

iOS - new architecture 비활성화

// ios/Podfile

ENV['RCT_NEW_ARCH_ENABLED'] = '0' // 최상단에 위치

...

네비게이션 설정

Getting started | React Navigation

설치

yarn add @react-navigation/native

yarn add react-native-screens react-native-safe-area-context

yarn add @react-navigation/native-stack

yarn add @react-navigation/elements

yarn add -D babel-plugin-module-resolver
// package.json

"dependencies": {
  "@react-navigation/native": "^7.0.15",
  "@react-navigation/native-stack": "^7.2.1",
  "react": "19.0.0",
  "react-native": "0.78.0",
  "react-native-safe-area-context": "^5.3.0",
  "react-native-screens": "^4.9.1"
},
"devDependencies": {
  "babel-plugin-module-resolver": "^5.0.2",
}

Android MainActivity 수정

// android/app/src/main/java/com/appsflyer_deep_link_app/MainActivity.kt

package com.appsflyer_deep_link_app

import android.os.Bundle // add this
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate

class MainActivity : ReactActivity() {

  /**
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
   */
  override fun getMainComponentName(): String = "appsflyer_deep_link_app"

  // add this
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(null)
  }

  /**
   * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
   * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
   */
  override fun createReactActivityDelegate(): ReactActivityDelegate =
      DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
}

babel alias 설정

// babel.config.js

module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    [
      'module-resolver',
      {
        root: ['.'],
        extensions: [
          '.ios.ts',
          '.android.ts',
          '.ts',
          '.ios.tsx',
          '.android.tsx',
          '.tsx',
          '.jsx',
          '.js',
          '.json',
        ],
        alias: {
          '@screens': './src/screens',
          '@utils': './src/utils',
          '@hooks': './src/hooks',
          '@typings': './src/typings',
          '@components': './src/components',
          '@config': './config/config.json',
        },
      },
    ],
  ],
};

tsconfig 설정

// tsconfig.json

{
  "extends": "@react-native/typescript-config/tsconfig.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@screens/*": ["src/screens/*"],
      "@utils/*": ["src/utils/*"],
      "@hooks/*": ["src/hooks/*"],
      "@typings/*": ["src/typings/*"],
      "@components/*": ["src/components/*"],
      "@config": ["config/config.json"]
    }
  }
}

네비게이션 타입 설정

// src/typings/navigation.ts

import type {NativeStackScreenProps} from '@react-navigation/native-stack';

export type RootStackNavigatorTypes = {
  Home: undefined;
  DeepLink: {
    id: string;
  };
};

export type DeepLinkScreenProps = NativeStackScreenProps<
  RootStackNavigatorTypes,
  'DeepLink'
>;

declare global {
  namespace ReactNavigation {
    interface RootParamList extends RootStackNavigatorTypes {}
  }
}

네비게이션 linking 설정

// src/utils/consts.ts

import type {LinkingOptions} from '@react-navigation/native';
import type {RootStackNavigatorTypes} from '@typings/navigation';

export const NAVIGATION_LINKS = {
  HOME: '/home',
  DEEP_LINK: '/deep-link',
} as const;

export const navigationLinking: LinkingOptions<RootStackNavigatorTypes> = {
  prefixes: ['appsflyer-deep-link://'],
  config: {
    screens: {
      Home: NAVIGATION_LINKS.HOME,
      DeepLink: {
        path: `${NAVIGATION_LINKS.DEEP_LINK}/:id`,
        parse: {
          id: String,
        },
      },
    },
  },
};

Configuring links | React Navigation

App.tsx

// src/App.tsx

import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import DeepLinkScreen from '@screens/DeepLink';
import HomeScreen from '@screens/Home';
import {RootStackNavigatorTypes} from '@typings/navigation';
import {navigationLinking} from '@utils/consts';

const RootStack = createNativeStackNavigator<RootStackNavigatorTypes>();

function App() {
  return (
    <NavigationContainer linking={navigationLinking}>
      <RootStack.Navigator initialRouteName="Home">
        <RootStack.Screen name="Home" component={HomeScreen} />
        <RootStack.Screen name="DeepLink" component={DeepLinkScreen} />
      </RootStack.Navigator>
    </NavigationContainer>
  );
}

export default App;

화면 설정

// src/screens/Home.tsx

import type {FC} from 'react';
import {SafeAreaView, Text, View} from 'react-native';

const HomeScreen: FC = () => {
  return (
    <SafeAreaView>
      <View>
        <Text>Home Screen</Text>
      </View>
    </SafeAreaView>
  );
};

export default HomeScreen;
// src/screens/DeepLink.tsx

import {DeepLinkScreenProps} from '@typings/navigation';
import type {FC} from 'react';
import {SafeAreaView, Text, View} from 'react-native';

const DeepLinkScreen: FC<DeepLinkScreenProps> = ({route}) => {
  return (
    <SafeAreaView>
      <View>
        <Text>DeepLink Screen</Text>
        <Text>deep link id: {route.params?.id || 'empty'}</Text>
      </View>
    </SafeAreaView>
  );
};

export default DeepLinkScreen;

실행

npx pod-install ios

yarn start
yarn ios

참고자료

[RN CheatSheet] New architecture 비활성화 하기

Deep linking | AppsFlyer mobile glossary

Getting started | React Navigation

Configuring links | React Navigation

노션 링크

react native appsflyer deep link 완벽 가이드 - 초기 설정 (1) - 현재
react native appsflyer deep link 완벽 가이드 - SDK 설치 (2)
react native appsflyer deep link 완벽 가이드 - OneLink (3)
react native appsflyer deep link 완벽 가이드 - 코드 (4)

profile
https://qnrjs42.notion.site

0개의 댓글