일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 페이지네이션
- async
- 글또10기x코드트리
- meatadata
- generic
- map
- javascript
- reactnative
- TS
- supabase authentication
- 슬라이딩윈도우
- extends
- react
- Next.js
- Spring
- array
- set
- 상속
- 스크롤이벤트
- Filter
- supabase auth
- code-push-standalone
- 코드푸시
- interface
- codepush
- app.post
- 타입스크립트
- 이진탐색
- supabase 페이지네이션
- xlsx-js-style
- Today
- Total
rhanziy
ReactNative - 딥링크(feat.Cold start) 트러블 슈팅 과정 본문
☕️ 사건의 배경
현재 운영중인 서비스의 선물하기 기능은 상품을 구매하고 카카오톡 메세지 템플릿을 통해 보내고 싶은 사람에게 공유할 수 있다.
공유 받은 사용자는 카카오톡 메세지 템플릿에 설정한 scheme과 parameter를 통해 url로 앱을 실행한다.
앱에서는 Linking을 통해 url을 분기 처리하고 원하는 화면으로 이동한 후 유저 상태를 판단하고, 쿼리파라미터의 code를 통해 선물을 등록할 수 있다.


Hot/Warm Start
앱이 백그라운드거나 실행 중인 경우 Linking.subscribe를 통해 새로운 딥링크 이벤트를 감지하고 처리한다.
Cold Start
앱이 종료된 상태인 경우 실행될 때 한 번 Linking.getInitialURL()의 Promise객체에서 url을 반환처리하고 있다.
🚨 문제 상황
줄곧 앱이 포그라운드거나 백그라운드 상태에서 테스트를 진행했었기에 별다른 이슈가 없는줄 알았다. 그런데 앱 스키마를 변경할 일이 있어서 다시한번 테스트를 진행하던 중, 앱이 꺼져있는 상태에서 URL을 통해 앱을 실행하게 되면 원하는 화면이아닌 홈화면으로 이동되는 상황을 마주했다. 원인을 모르겠어서 몇번 더 테스트를 진행하다가 문득 이상함(?)을 발견했다.
로그인 O -> 앱 실행 시 로그인 화면으로 이동했다가 홈화면 이동
로그인 X -> 앱 실행 시 로그인 화면으로 이동
즉, 유저의 로그인 여부와 상관없이 로그인 화면으로 이동되는게 문제였다.
🔍 원인 파악
결과적으로 원인은 바로 유저 데이터가 초기에 무조건 undefined로 처리됐기 때문이였다.
리액트 네이티브는 NavigationContainer의 linking옵션을 통해 딥링크와 통합 URL이 처리된다. NavigationContainer의 설정 시점은 앱이 실행될 때 가장 먼저 초기화해야 하는 단계이다. 즉, 앱의 루트컴포넌트를 설정하는 시점에서 네비게이터를 셋팅하는데, 그때는 기기에 저장된 로그인 정보를 전역데이터로 셋팅하기 전이기 때문에 딥링크 실행시점에 user data는 undefined일 수 밖에 없는 것이였다.
📌 해결 과정
❎ 그럼 앱이 초기화 되기 전에 AsyncStorage에서 로그인 정보를 확인하고 그 후에 네비게이션 셋팅을 해볼까?
-> 그렇게 되면 딥링크를 통해 앱을 실행했을때 앱 셋팅 과정에서 예상치 못한 문제가 발생할 수 있을 것 같은데... 로그인 되지 않은 유저의
경우 로그인 후 url처리 방식을 다시 한번 고민 해봐야겠는걸,
✅ 그럼 딥링크를 통해 들어온 url을 AsyncStorage에 저장해 두었다가 앱 설정을 마친 후 user값을 판단할때 꺼내쓸까?
-> 네비게이션이 초기화 되기 전에 딥링크가 실행됐을 때 존재하지 않는 화면으로 이동되는 문제를 방지할 수 있고, 로그인 상태에 따라 적절한 처리가 가능하겠네. 또, 앱이 예상치 못하게 종료되더라도 AsyncStorage에 저장된 정보로 일관성을 유지할 수 있겠다!
🔥 결과
getInitialURL이 실행되었을때 url에서 필요한 정보 저장
async getInitialURL() {
const url = await Linking.getInitialURL();
Logger.log('>>>> INITIAL URL : ' + url);
if (url?.includes('kakaolink')) {
const parsed = queryString.parseUrl(url);
await AsyncStorage.setItem('kakaolinkCode', parsed.query.code as string);
}
return url;
}
앱 셋팅이 완료 된 후 useEffect를 통해 딥링킹 처리했고, 정상적으로 원하는 동작이 실행된다.
const openedDeepLinkUrl = useCallback(async () => {
try {
const kakaoLinkCode = await AsyncStorage.getItem('kakaolinkCode');
if (!kakaoLinkCode || !user) return;
navigation.navigate('EnrollGifticonScreen', {code: kakaoLinkCode});
await AsyncStorage.removeItem('kakaolinkCode');
} catch (error) {
Logger.error(error);
}
}, [user, navigation]);
useEffect(() => {
openedDeepLinkUrl();
}, [openedDeepLinkUrl]);
마무리하며 글또10기 작은 회고...
처음 글또 다짐글에서 작성했던 글을 다시 한번 읽어보았는데 공약(?)과 다르게 이행된건 많지 않았다. ㅋㅋㅋ 그래도 작성하고자 했던 글을 다 완성시킨 점이 놀라웠고, 한달에 한번은 아니였지만 벌써 세 번이나 모임에도 참석했다~! 좋은 사람들과 대화하며 오프라인 모임에 대한 내 편견이 한꺼풀 벗겨지게 되는 계기가 되었다. 하지만 제일 뿌듯하고 자랑스러운 점은 단 한번도 제출패스를 한 적이 없다는 것.
거듭된 고민 끝에 더 나은 코드를 완성해 가듯, 글또를 통해 나의 글쓰기 습관과 경험치를 차곡차곡 쌓아 올려왔다. 그리고 지난 6개월간 배운 가장 큰 가치는 과정을 기록하고 공유하는 힘이었다.
기술적인 문제를 해결하는 과정 자체가 성장의 기회라고 생각했지만, 이번에는 글로 정리하면서 더 깊게 고민해보고 많은 것을 느낄 수 있는 시간이 되었다. 돌이켜보면, 완벽한 글이 아니더라도 꾸준히 작성해온 습관이 이 과정에서 큰 도움이 되었다. 그동안은 공부했거나 구현한 내용을 정리하는 목적의 글을 주로 작성했는데, 이번에는 단순히 '해결했다.'에서 끝나는 것이 아니라, 그 과정 속에서 무엇을 배웠는지, 어떤 시행착오를 겪었는지를 서술하며 나의 성장도 기록할 수 있는 개발자로 진화한 것 같다.
(무엇보다 '글또'라는 좋은 기회를 소개해주고, 문제를 해결할 때 접근하는 사고방식을 알려준 지노에게 고맙다는 말을 꼭 전하고 싶다...ㅎㅎㅎ 지노는 부족한 나를 끊임없이 성장할 수 있도록 이끌어준 기폭제이자 나에게 단순히 물고기를 주는 것에 그치지 않고, 스스로 잡을 수 있는 방법까지 알려준 영원히 든든한 선배님이다. 항상 반짝반짝 빛나길.)