본문 바로가기
  • Survival Plan
개발 이야기/React-Native

리액트 네이티브 - 앱 처음 실행 확인 하기

by IT/머신러닝 엔지니어의 독서/경제/육아 공부 리치윈드 - windFlex 2020. 3. 27.

대부분 모바일 앱이 그렇겠지만,

리액트 네이티브 (React Native) 역시 최초 실행 시에만 처리해 주어야 할 일이 있는데요.

예를 들면, View Pager 등이 대표 적인 예입니다. 

최초 실행 시에만, 앱에 대한 사용설명 Page를 출력해 주는 경우입니다. 

 

또한, 로컬 데이터를 저장하거나 PUSH Notification 등 에도 , 최초 실행시점에만 초기화 해주는 경우 발생하지요.

이 밖에 많은 경우에 최초 실행 여부를 점검 해야 할 필요성이 존재 합니다. 

 

지금 까지는 발생하는 그 때 그때 component에 적용을 했었습니다만, 

시간을 내어서 utils로 정리를 해 봤습니다. 

 

 


기본적인 방식은

Local Storage에 keyFirstLaunch라는 변수를 만들어 최초 실행 시점에는 true로 저장을 해 두는 것입니다. 

최초 실행에는 기존에 keyFirstLaunch 라는 값이 존재 하지 않기 때문에, null 값을 리턴 하고,  이 때 true 값을 저장합니다. 

그리고 두번째 부터는 keyFirstLaunch 값이 존재 하면, 바로 false를 리턴하는 함수로 작성해 두면, 

두고 두고 사용할 수 있습니다. 

 

정리 하면 아래와 같습니다. 

키값이 존재 하는가?  키값이 없으면 true라는 값을 저장하고, 키값이 있으면 false 를 반환

로컬 디스크에 값을 저장하기 가장 쉬운 방법은 React-native의 AsyncStorage를 사용하는 것입니다. 

그 밖에 SQLite와 Realm DB 등을 사용하는 방법도 있겠으나,

단순 저장에는 AsyncStorage가 가장 빠르고 별다른 수고 없이 그냥 사용할 수 있기 때문에, Async를 사용하도록 하겠습니다. 

 

더보기

AsyncStorage는 기존에 'react-native'에 공식으로 지원 받는 모듈이었으나, 최근 community 모듈로 빠져 나왔습니다. 기존 AsyncStorage는 Deprecated 되었습니다. 참조 바랍니다. 

위 내용을 코드화 해보면 아래와 같습니다. 

//import { AsyncStorage } from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';

const KEY_VALUE = 'keyFirstLaunch';

// 키값에 true로 저장한다.
function setAppLaunched() {
  AsyncStorage.setItem(KEY_VALUE, 'true');
}

export default async function checkFirstLaunch() {
  try {
    const isFirstLaunched = await AsyncStorage.getItem(KEY_VALUE); //우선 값을 읽자.
    if (isFirstLaunched === null) {  // 값이 없다면,
      setAppLaunched();  // 키값에 true로고 저장하고,
      return true; // true를 반환 ==> 최초 실행 !!
    }
    return false;  // 값이 없다면, 최초 실행 아님 !!
  } catch (error) {
  	  // 에러 발생 시에도 false 로 반환 
      console.log(' [chk first launch] :' + error);  
    return false; // Error
  }
}

여기에서 유의할 점이 몇가지 있는데요.

1) AsysncStrage는 String value만 저장이 가능하다. 

따라서, setItem에 저장되는 변수는 true 값이 아니라, string으로써의 'true'라는 것 입니다. 

값을 불러오고 확인 할 때도 주의가 필요합니다. 

 

2) AsyncStorage는 Async 이다

모듈의 이름에서도 알 수 있지만, Async Storage는 비동기 적으로 실행 된다는 것이죠.

따라서, 값을 읽어와 변수에 값을 저장하지만, 다음 코드에서 변수값을 확인 했을 때, 원하는 값이 아닐 수 있습니다. 

 

위의 예제 코드에서 async 와 await 를 제외 했거나, async 만 사용하고 await 를 제외했다면 어떻게 될까요?

export default async function checkFirstLaunch() {
  try {
    const isFirstLaunched = await AsyncStorage.getItem(KEY_VALUE); //우선 값을 읽자.
    if (isFirstLaunched === null) {  // 값이 없다면,
      setAppLaunched();  // 키값에 true로고 저장하고,
      return true; // true를 반환 ==> 최초 실행 !!
    }
    return false;  // 값이 없다면, 최초 실행 아님 !!
  } catch (error) {
     // 에러 발생 시에도 false 로 반환 
      console.log(' [chk first launch] :' + error);  
    return false; // Error
  }
}

예상과는 다른 결과를 확인 할 수 있는데, 디버깅을 해 보면 "isfirstLaunched" 가 이상한 값을 가지는 것을 확인 할 수 있습니다. 

만약  AsyncStorage.getItem() 다음 라인에 아래와 같이 디버깅을 위한 로그를 찍으면?

console.log(isFirstLaunched);

에러가 발생 합니다.  ==> isfirstLaunched is undefined ....... 뭐 이런 것일 겁니다.

AsyncStorage 가 비동기로 실행이 되기 때문에, 그 다음 라인을 실행할 시점에 Storage에서 값을 읽어와 isFirstLaunched 에 저장하지 않은 시점이기 때문입니다. 

 

에러가 발생하지 않지만, 값이 예상치 못하는 [ object ] 을 가질 때가 있습니다. 

주로 Async를 직접 사용하는 쪽보다는, 활용/적용되는 쪽에서 많이 발생 하는데, 대표적인 경우는 아래와 결과 값을 받을 때 입니다. 

 {"_40":0,"_65":0,"_55":null,"_72":null}

이러한 Dict 값은  React-native 에서 fetch 사용할 때 많이 발생하는 경우이지요.

Async 값 리턴 받기 전에 할당해 놓은 dictionary 값 입니다. 이런 값을 보면, 비동기 문제가 발생했다고 생각하시면 되겠습니다. 

 

따라서, 여기에서는 동기화를 맞추어 주기 위해서, 결과를 기다렸다가 실행하는  await 를 사용해 주어야 합니다. 

(await 는 async 함수 안에서만 동작합니다.)


[App 에 최초 실행 적용하기]

다음으로, 언제 어디에서 사용할 것인가에 대한 것을 결정해야 할 차례입니다. 

 

최초 실행 여부를 점검하는 부분은 주요 로직과 별개인 경우가 많으니, 최초 컴포넌트 (App)에 적용해 보도록 하겠습니다. 

 

state 값에 First Launch 인지 확인하는 상태변수를 하나 등록하고, App가 처음으로 시작할 때, 확인을 하는 것이 적절해 보입니다. 

따라서, componentDidMount() 라이프 사이클에 추가합니다.

 

import React from 'react';
import {View } from 'react-native';
import checkFirstLaunch from './src/utils/checkFirstLaunch';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isFirstLaunch: false,
    };
  }

 async componentDidMount() {
    const isFirstLaunch = await checkFirstLaunch();
    //console.log('[App() check]: ' + JSON.stringify(isFirstLaunch));
    if(isFirstLaunch){
      console.log( ' [App()] : This is the first Launch..!'+ isFirstLaunch.toString() );
      this.setState({isFirstLaunch: true});
    }
  }

  render() {
    return (
      <AppContainer />
    );
  }
}
export default App;

이전에 설명한 이유와 동일한 이유로

async / await 에 주의하여 주시기 바랍니다. 

만약 await 가 적용되지 않으면, isFirstLauch의 값에 "[object Object]" 을 보게 될것 입니다. 

JSON.stringify() 를 주석으로 달아 놓아습니다. 만약 값을 확인하길 원하시면, 주석을 해제하여 보시기 바랍니다. 

 

async / await 가 잘 적용이 되었다면, 

어플리케이션이 최초 실행할 경우에만, 동작하는 것을 확인 할 수 있습니다. 

댓글2