React Native + Expo 앱에서 QR 코드를 읽고 JSON 획득

이 기사는 "【연재】 최초의 React Native + Expo 개발 환경 구축 입문"의 자식 기사입니다. 환경과 같은 조건은 상위 기사를 참조하십시오.

지난번 까지, 송장 정보를 서버로부터 취득해 표시할 수 있게 되었으므로, 이번에는 호출할 서버의 정보를 QR 코드로부터 취득할 수 있도록(듯이) 합니다.

QR코드에 미리 JSON을 넣어 두면, 복수의 설정 항목이 있는 설정을 앱의 설정 방법 설명서 없이 유저에게 실시해 줄 수 있게 되는 등, 편리성 업이나 업무의 효율화를 전망할 수 있습니다.

【주의】현시점에서, QR 스캐너 기능이 잘 동작하지 않는 경우가 있는 것 같습니다. 자세한 내용은 포럼 참조.

목표 : Import 버튼을 누른다 → QR 코드 리더 → QR에 들어있는 URL로부터 청구서를 읽어들입니다


QR 리더 설치


BarCodeScanner 모듈은, QR 뿐만이 아니라 여러 기획의 바코드도 읽을 수 있는 바코드·QR 리더입니다. 빨리 설치합니다. Visual Studio Code에서 React Native 프로젝트를 열고 Ctrl + @에서 PowerShell을 열고 다음 명령을 실행합니다.
expo install expo-barcode-scanner

QR 코드 로딩 화면 설치



QR코드 불러오기 화면은, Expo 바코드 API 예제 로부터 카피해, unstated를 위한 HOC화를 실시합니다.

/components/BarcodeScannerScreen.js
import * as React from "react";
import { Text, View, StyleSheet, Button } from "react-native";
import Constants from "expo-constants";
import * as Permissions from "expo-permissions";
import { Subscribe } from "unstated";

import { BarCodeScanner } from "expo-barcode-scanner";
import InvoiceContainer from "../containers/InvoiceContainer";

class BarcodeScannerScreenContent extends React.Component {
  state = {
    hasCameraPermission: null,
    scanned: false
  };

  async componentDidMount() {
    this.getPermissionsAsync();
  }

  getPermissionsAsync = async () => {
    const { status } = await Permissions.askAsync(Permissions.CAMERA);
    this.setState({ hasCameraPermission: status === "granted" });
  };

  render() {
    const { hasCameraPermission, scanned } = this.state;

    if (hasCameraPermission === null) {
      return <Text>Requesting for camera permission</Text>;
    }
    if (hasCameraPermission === false) {
      return <Text>No access to camera</Text>;
    }
    return (
      <View
        style={{
          flex: 1,
          flexDirection: "column",
          justifyContent: "flex-end"
        }}
      >
        <BarCodeScanner
          onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
          style={StyleSheet.absoluteFillObject}
        />

        {scanned && <Button title={"Tap to Scan Again"} onPress={() => this.setState({ scanned: false })} />}
      </View>
    );
  }

  handleBarCodeScanned = ({ type, data }) => {
    this.setState({ scanned: true });
    // alert(`Bar code with type ${type} and data ${data} has been scanned!`);
    let jsondata = {};
    if (this.isJson(data) && (jsondata = JSON.parse(data)).invoice_endpoint_url) {
      this.props.globalState.getDataFromServer(jsondata.invoice_endpoint_url);
      this.props.navigation.goBack();
    } else {
      alert("This is not a valid QR code.");
    }
  };

  isJson(str) {
    try {
      JSON.parse(str);
    } catch (e) {
      return false;
    }
    return true;
  }
}

const BarcodeScannerScreen = ({ navigation }) => {
  return (
    <Subscribe to={[InvoiceContainer]}>
      {globalState => <BarcodeScannerScreenContent globalState={globalState} navigation={navigation} />}
    </Subscribe>
  );
};

export default BarcodeScannerScreen;

unstated와 navigation을 위해 HOC화 이외에 실시하고 있는 것은, QR코드를 읽으면 내용을 체크해, 만약 JSON로, 한층 더 안에 invoice_endpoint_url 가 있으면 지정 URL로부터 청구서 로드 실시해 화면 돌아가서, 그렇지 않으면 "This is not a valid QR code."라고 경고합니다. 경고의 경우, 네비게이션의 Back 버튼으로 화면을 되돌릴 수도 있고, 「Tap to Scan Again」버튼으로 재도전할 수도 있습니다.

또, 글로벌 State의 InvoiceContainer의 getDataFromServer()를 호출할 때, QR코드로부터 받은 invoice_endpoint_url 를 건네주기로 했습니다. 따라서 InvoiceContainer를 수정해야 합니다.

만든 화면 컴포넌트를 navigation에 등록.

App.js
...
import BarcodeScannerScreen from "./components/BarcodeScannerScreen";
...
const RootStack = createStackNavigator(
  {
    Home: HomeScreen,
    InvoiceEdit: InvoiceEditScreen,
    Summary: SummaryScreen,
    BarcodeScanner: BarcodeScannerScreen
  },
  {
    initialRouteName: "Home"
  }
);
...

지정된 URL에서 인보이스를 로드하는 동작 구현



송장 취득의 엔드포인트는 원래 INVOICE_API_ENDPOINT에 정수화하고 있었지만, 인수로 받아들이도록(듯이) 변경합니다.

containers/InvoiceContainer.js
...
// const INVOICE_API_ENDPOINT = "http://192.168.1.8:8080/invoice.js";
...
  getDataFromServer(endpoint) {
    this.setState({ isDataLoading: true });
    axios
      .get(endpoint, { timeout: 3000 })
...

※이어서 통신 타임 아웃을 3 초로 설정했습니다.

JSON에 엔드포인트 URL이 포함된 QR 코드 작성



온라인 QR 코드 생성 앱 QR Code Generator 등으로, 이하의 JSON을 QR코드화. ※여기에서 지정하는 URL은, 마지막 기사 의 청구서 JSON 파일을 취할 수 있는 주소를 지정합니다.
{"invoice_endpoint_url":"http://192.168.1.8:8080/invoice.js"}



송장 불러오기 버튼으로 QR 스캔 화면으로 날 수 있도록 합니다.



HomeScreen의 Import 버튼으로 QR 리더 화면으로 날 수 있습니다. (이전의 동작은 코멘트 아웃)

components/HomeScreen.js
  onImportClick() {
    // this.props.globalState.getDataFromServer();
    this.props.navigation.navigate("BarcodeScanner");
  }

컴포넌트가 아닌 글로벌 State측에 서버와의 통신 동작을 기술하고 있으므로, 변경은 매우 간단하네요.

결과





Import 버튼을 누르면 QR 스캔 화면으로.



잘못된 QR 코드 (또는 바코드)를 읽으면 경고.



올바른 QR을 로드하면 서버 송장을 로드하여 원래 화면으로 전환합니다.

좋은 웹페이지 즐겨찾기