React Native Deep Linking (2)

React Native Deep Linking 연결

딥링크

  • 웹링크 URL이 사용자를 특정 웹사이트로 이동시키는 것처럼, 딥링크는 사용자를 특정 앱이나 앱의 원하는 화면으로 이동하도록 유도하는 링크이다.

딥링크 유형

post image

출처 tosspayments

  • 딥링크는 AOS, IOS 모두 사용할 수 있고, 커스텀 스킴은 앱 링크, URI 스킴으로 불리기도 한다.

딥링크 구현

  • 진행중인 사이드프로젝트에서는 SEO를 위한 웹사이트와 앱을 연결하기 위해 딥링크를 구현하였다.

1. 웹에서 앱으로 이동

const useAppOpen = () => {
  const [appOpened, setAppOpened] = useState(false);
  const { os } = useFetchCookie();
  const router = useRouter();

  useVisibilityChange({
    onVisible: () => setAppOpened(true),
    onHidden: () => setAppOpened(false),
  });

  const handleAppOpen = ({ appScheme }: { appScheme: string }) => {
    router.replace(
      os === 'ios'
        ? `${APP_SCHEME}${appScheme}`
        : `intent://${appScheme}#Intent;scheme=myapp;package=com.myapp.app;end;`,
    );

    setTimeout(() => {
      if (!appOpened) {
        window.location.href =
          os === 'ios' ? APPLE_STORE_URL : GOOGLE_PLAY_STORE_URL;
      }
    }, 500);
  };

  return {
    handleAppOpen,
  };
};

export default useAppOpen;

웹에서는 우선 os를 저장했고, os별로 다른 경로를 지정했다.

ios 에서는 myapp://로 바로 Deep Link를 이용해 이동하고, aos에서는 intent://로 시작하는 Intent스킴을 사용해 서로 다른 경로를 사용하고 있다.

그리고 딥링크에 앱이 열리지 않으면 setTimeout을 이용해 앱스토어로 이동하도록 구현했다.

setTimeout을 사용한 이유는 앱이 설치되어 있다면, 브라우저는 즉시 앱을 실행하고, 반대로 앱이 설치되지 않으면 아무런 변화도 일어나지 않는다. 이 상태를 감지하기위해서 500ms의 시간동안 앱이 실행되지 않는다면 앱스토어로 이동하는 방식으로 구현했다.

2. 앱에서 링크 이동

AOS

// android/app/src/main/AndroidManifest.xml

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="myapp" />
    </intent-filter>

위 경로에서 schememyapp로 지정해주면 된다.

IOS

// ios/MyApp/Info.plist

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.myapp.app</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
    </dict>
</array>

CFBundleURLSchemesmyapp을 추가해주면 된다.


react-navigation

현재 프로젝트에서는 @react-navigation/native 라이브러리를 사용하고 있어서, NavigationContainer에서 지원하는 linking props사용해 딥링크를 구현했다.

// App.tsx

{...}
<NavigationContainer
  linking={linkingConfig}
  ref={navigationRef}
  {...}
>
    {...}
</NavigationContainer>
{...}

// ../linkingConfig.ts

export const linkingConfig: LinkingOptions<NavigationProps> = {
  prefixes: ['myapp://'],
  config: {
    screens: {
      [RootNavigatorName.rootTab]: {
        screens: {
          [TabNavigatorName.HOME_TAB]: {
            path: 'open',
            screens: {
              [StackNavigatorName.HOME_SCREEN]: '',
            },
          },
          [TabNavigatorName.ECOMMERCE_TAB]: {
            path: 'ecommerce',
            screens: {
              [StackNavigatorName.ECOMMERCE_SCREEN]: '',
            },
          },
        },
      },
      [StackNavigatorName.AUTH_ONBOARDING_SCREEN]: 'onboarding',
      [StackNavigatorName.AUTH_SCREEN]: 'auth',
    },
  },
  {...}
};

먼저 prefixesmyapp://을 추가해주고, screens에 각 스크린에 등록한 경로들을 path로 지정해주면 된다.

  async getInitialURL() {
    const url = await Linking.getInitialURL();

    if (url) {
      const redirectUrl = await handleGetUserInfo();
      return redirectUrl || url;
    }
  },

getInitialURL을 이용해 앱이 처음 실행될 때, 딥링크를 처리할 수 있도록 구현했다. 여기서 handleGetUserInfo 함수는 앱이 딥링크를 통해 진입했을 경우 스플래쉬스크린을 거치지않아, 사용자 정보를 가져와 로그인 여부를 확인하는 함수이다.

  subscribe(listener) {
    const onReceiveURL = async (event: { url: any }) => {
      if (event.url) {
        const redirectUrl = await handleGetUserInfo();
        listener(redirectUrl || event.url);
      }
    };

    const linkingListener = Linking.addEventListener('url', onReceiveURL);
    const appStateListener = AppState.addEventListener('change', async nextAppState => {
      if (nextAppState === 'active') {
        const url = await Linking.getInitialURL();

        if (url) {
          const redirectUrl = await handleGetUserInfo();
          listener(redirectUrl || url);
        }
      }
    });

    return () => {
      linkingListener.remove();
      appStateListener.remove();
    };
  },

subscribe를 이용해 앱이 실행중일 때, 딥링크를 처리할 수 있도록 구현했다.


  • 앱이 처음 실행될 때 : getInitialURL을 이용해 딥링크 처리
  • 앱이 백그라운드 -> 포그라운드 전환될 때 : subscribeAppstate를 이용해 딥링크 처리
  • 앱이 실행중일 때 : subscribeLinking을 이용해 딥링크 처리