ghdtjgus

지난 글에서 안드로이드 딥링크 구현과 관련된 부분을 알아봤다.

그때 앱 링크와 url 스킴에 대해서 알아봤었는데 그 외에도 알게 된 점이 많아서 그 부분을 좀 알아보려고 한다.

 

OS, 브라우저 여부에 따른 분기 처리

이 부분이 필요한 이유는 다음 이미지를 보면 알 수 있다.

 

왼쪽은 웹 기록 챌린지 페이지이고, 오른쪽은 앱 기록 탭이다.

 

물론 두 페이지는 동일한 웹페이지이다.

즉, 왼쪽은 웹페이지를 그대로 안드로이드 에뮬레이터 크롬 창에서 접속한 거고, 오른쪽은 웹페이지를 웹뷰 앱에서 그대로 띄워준 거다.

다만 브라우저에서 접속한 경우인지를 판단하여 분기 처리를 해주었다.

 

이렇듯, 우리는 웹과 앱에서 접속한 OS 환경 (Android, iOS 등), 브라우저 환경인지 여부에 따라 분기 처리를 해줄 수 있다.

아래 경우에서는 안드로이드, iOS, macOS이면서 브라우저에서 접속한 경우에만 버튼을 보여주도록 했다.

macOS에서는 앱스토어에서 앱을 다운로드할 수 있기 때문에 모바일 기기와 같이 추가해 주었다.

const userAgent = navigator.userAgent;
const {
  os: { name: osName },
  isWebView,
} = getEnvironment(userAgent);

if ((osName === "Android" || "macOS" || "iOS") && !isWebView) {
  setShowButton(true);
}

 

내비게이션 구조

현재 App.tsx에서 딥링크 및 내비게이션 관련 처리를 해주고 있다.

deepLink 함수에서는 내비게이션 하는 로직도 있기 때문에 전적으로 NavigationProvider 내부에서 모든 내비게이션 로직을 다룰 수 없다.

const App = () => {
  const deepLink = useCallback((url: string) => {
    if (url) {
      try {
        const urlObject = new URL(url);
        let route: string | null = null;

        if (urlObject.protocol === 'https:') {
          route = urlObject.pathname.split('/')[1];
        } else if (urlObject.protocol === 'ohouseapp:') {
          route = url.split('://')[1];
        }

        if (route) {
          const routeName = urlMap[route];
          if (routeName) {
            navigate(routeName as keyof RootParamList);
          } else {
            Alert.alert('urlMap에서 경로를 찾을 수 없습니다:', route);
          }
        } else {
          Alert.alert('경로가 null이거나 navigationRef를 사용할 수 없습니다');
        }
      } catch (error) {
        Alert.alert(
          '링크 처리 오류',
          '링크를 처리하는 도중 오류가 발생했습니다.',
        );
      }
    }
  }, []);

  useEffect(() => {
    Linking.getInitialURL().then(url => {
      if (url) {
        deepLink(url);
      }
    });

    const urlListener = Linking.addEventListener('url', ({url}) => {
      if (url) {
        deepLink(url);
      }
    });

    return () => {
      urlListener.remove();
    };
  }, [deepLink]);

  return <NavigationProvider />;
};
const BottomTabs = createBottomTabNavigator();

const NavigationProvider = () => {
  return (
    <NavigationContainer ref={navigationRef}>
      <BottomTabs.Navigator screenOptions={{headerShown: false}}>
        <BottomTabs.Screen name="홈" component={HomeScreen} />
        <BottomTabs.Screen name="행운출첵" component={LuckyAttendanceScreen} />
        <BottomTabs.Screen
          name="기록챌린지"
          component={RecordChallengeScreen}
        />
        <BottomTabs.Screen name="로그인" component={LoginScreen} />
      </BottomTabs.Navigator>
    </NavigationContainer>
  );
};

export default NavigationProvider;

 

NavigationContainer 외부에 있는 deepLink 함수에서도 내비게이션을 문제없이 하기 위해서 NavigationProvider에서 모든 내비게이션 로직을 관리하지 않는다.

navigationRef를 별도의 파일에서 생성해서 관리하고 NavigationContainer에 ref로 주입해 주게 되면 NavigationProvider 외부에서도 내비게이션 로직을 실행할 수 있게 된다.

export const navigationRef = createRef<NavigationContainerRef<RootParamList>>();

export const navigate = (name: keyof RootParamList, params?: any) => {
  navigationRef.current?.navigate(name, params);
};

 

이렇게 하면 우리는 딥링크 관련 처리를 할 수 있게 된다.

이 부분을 처리하지 않으면, 우리가 웹 기록 챌린지 페이지에서 버튼을 눌렀을 때 앱 접속까지는 문제없이 된다.

하지만, 우리가 기대했던 것처럼 앱 기록 챌린지 탭으로는 접속이 되지 않기 때문에 처리를 해준 것이라고 생각하면 된다.

 

URL Polyfill

App.tsx 가장 상단에 보면 URLPolyfill을 진행하는 것을 볼 수 있다.

이 코드는 왜 넣어준 걸까?

import {setupURLPolyfill} from 'react-native-url-polyfill';

setupURLPolyfill();

 

우리가 지금 딥링크 관련 로직이 존재하기 때문에 url을 다뤄야 하는 일이 있다.

그런데, react native에서는 우리가 기대한 것처럼 웹에서 사용했던 일부 web api가 지원되지 않는다.

즉 url 객체 부분을 제대로 지원해주지 않아서 우리가 따로 어떤 처리를 해주어야 문제 없이 url 객체 관련 처리를 할 수 있다.

 

그 처리를 우리는 위 함수를 통해서 하는데, setupURLPolyfill 함수로 웹 환경에서 지원하는 url 객체 관련 기능을 react-native에서도 사용할 수 있게 해 준다.

 

Linking

이제 마지막으로 Linking API를 활용해서 딥링크를 처리하는 로직이다.

useEffect(() => {
    Linking.getInitialURL().then(url => {
      if (url) {
        deepLink(url);
      }
    });

    const urlListener = Linking.addEventListener('url', ({url}) => {
      if (url) {
        deepLink(url);
      }
    });

    return () => {
      urlListener.remove();
    };
  }, [deepLink]);

 

사실 코드만 봐도 대략적으로 어떤 로직인지 파악하는 건 어렵지 않을 거 같다.

일단 앱이 처음 실행될 때 url을 전달받은 부분이 있거나 실행 중 url 이벤트가 발생하면 deepLink 함수를 통해서 이벤트를 처리한다.

 

우리 로직에서는 웹 기록 챌린지 버튼을 누르면 앱이 실행되고, 위 로직의 Linking.getInitialURL()에서 잡힌다.

이때 url인 ohouseapp://record-challenge가 deepLink 함수에 전달되고 경로를 파싱 한 후 내비게이션이 이루어져 결국 앱 기록 챌린지 탭으로 전환이 된다.

profile

ghdtjgus

@gugu76

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그