ABOUT ME

-

  • [ React Native Expo ] Expo-Camera QR Scan
    Application/React Native Expo 2024. 12. 8. 15:33
    반응형

    Expo camera를 활용하여 QR 코드 인식하기

    Expo 에서 BarcodeScanner를 사용할려고했는데 오류가 발생하면서 실행이 안되어서 Camera를 사용해서 기능을 구현

     

    https://docs.expo.dev/versions/latest/sdk/camera/

     

    Camera

    A React component that renders a preview for the device's front or back camera.

    docs.expo.dev


    Expo Camera Setup

    우선 Expo-camera를 설치

    npx expo install expo-camera

     

    앱을 사용할때 사용자의 카메라 혹은 오디오의 권한을 요청하기 위해 플러그인을 추가

    app.json 파일에 plugins를 추가

    {
      "expo": {
        "plugins": [
          [
            "expo-camera",
            {
              "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
              "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone",
              "recordAudioAndroid": true
            }
          ]
        ]
      }
    }

    Camera View

    Expo Document에 있는 예제를 보며 하나씩 차근차근 진행

     

    우선 필요한 변수들을 세팅

     

    여기서 facing은 카메라의 전면과 후면 카메라 여부 ( back인 경우 후면 카메라, front인 경우 전면 카메라 )

    permission은 앱이 켜졌을 때 해당 사용자가 카메라 권한을 승인을 했는지 않했는지 체크하는 변수

     

    scanned는 현재 카메라가 QR코드를 스캔했는지 않했는지 판별하여 true, false로 나눈다.

    -> 카메라가 QR코드를 빠르게 읽기때문에 이를 통해 flag를 세워 더이상 QR인식을 하지 못하도록 한다.

     

    qrData는 해당 QR code가 가지고 있는 데이터를 저장

        const [facing, setFacing] = useState<CameraType>('back')
        const [permission, requestPermission] = useCameraPermissions();
    
        const [scanned, setScanned] = useState(false);
        const [qrData, setQrData] = useState<string | null>(null);

     

    useEffect를 넣어 처음 화면에 들어왔을 때 권한 승인 없다면 권한 요청을 하기 위해서 추가

     

    /* 카메라 권한이 없는 경우 권한 요청 */
        useEffect(() => {
            if(!permission) {
                requestPermission();
            }
        }, []);

     

    만약 권한이 없다면 화면에 카메라가 아닌 권한 요청 페이지를 보여준다.

        if(!permission?.granted) {
            return(
                <ScreenInner>
                    <Text>카메라 사용 권한이 필요합니다.</Text>
                    <TouchableOpacity onPress={requestPermission}>
                        <Text>권한 요청</Text>
                    </TouchableOpacity>
                </ScreenInner>
            )
        }

     

    이때 ScreenInner는 단순히 View 컴포넌트를 재사용하기 위해서 따로 만들어서 사용한 컴포넌트

    import {StyleSheet, View} from "react-native";
    
    export default function ScreenInner({children}) {
        return(
            <View style={styles.container}>
                {children}
            </View>
        )
    }
    
    const styles = StyleSheet.create({
        container: {
            flex: 1,
            backgroundColor: '#fff',
            alignItems: 'center',
            justifyContent: 'center',
        },
    })

     

    테스트를 하기 위해 함수를 추가

    toggleCameraFacing : 카메라 전면 / 후면 변환 함수

    handleResetScanner : 다시 scan을 할 수 있도록 scanned 변수를 초기화

    handleAlertData : alert 테스트를 하면서 읽어온 qrdata를 확인

    handleBarcodeScanned : QR code를 스캔을 할때 인식이 되면 해당 함수가 호출되며 data를 보내줌.

     

        function toggleCameraFacing() {
           setFacing(current => (current === 'back' ? 'front' : 'back'))
        }
    
        function handleResetScanner() {
            setScanned(false);
        }
    
        function handleAlertData() {
            alert(`QR DATA : ${qrData}`)
        }
    
        function handleBarcodeScanned(data: string) {
            setScanned(true);
            setQrData(data);
            alert(`QR DATA : ${qrData}`)
        }

     

    이제 화면에 카메라를 보여준다.

    CameraView 컴포넌트를 사용하며

     

    onBarCodeScanned는 QR코드를 인식하면 실행이 되면 이때 scanned 값을 가져와서 이미 한번 스캔이 되었다면 실행이 안되게 하며, 스캔을 안했다면 스캔이 되도록 함수를 호출

     

    barcodeScannerSettings는 어떤 바코드를 인식할지 타입을 지정한다.

    QR코드 말고 일반 상품에 붙어있는 바코드등 다양한 바코드를 인식할 수 있다.

     

    Expo document에 해당 내용을 참고

    QR scan만 할 것이기에 barcodeTypes는 "qr"만 지정

    return(
            <ScreenInner>
                <CameraView style={styles.camera} 
                	onBarcodeScanned={({data}) => scanned ? undefined : handleBarcodeScanned(data)} 
                    barcodeScannerSettings={{barcodeTypes:["qr"]}}>
                    <View style={styles.overlay}>
                       <View style={[styles.corner, styles.topLeft]} />
                       <View style={[styles.corner, styles.topRight]} />
                       <View style={[styles.corner, styles.bottomLeft]} />
                       <View style={[styles.corner, styles.bottomRight]} />
                    </View>
                </CameraView>
    
                <TouchableOpacity onPress={handleResetScanner}>
                    <Text>Reset</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={handleAlertData}>
                    <Text>Alert</Text>
                </TouchableOpacity>
            </ScreenInner>
        )

     

    이후 화면에 QR코드를 인식하는 부분을 표시해주고 싶어서 overlay를 추가

     

    테스트를 위해서 Reset 버튼과 alert 버튼을 추가

     

    [ 전체 코드 ]

    import {StyleSheet, Text, TouchableOpacity, View} from "react-native";
    import ScreenInner from "../component/screen_inner";
    import {Camera, CameraView, useCameraPermissions} from "expo-camera";
    import {useEffect, useState} from "react";
    
    export default function QRscanner() {
        // const [facing, setFacing] = useState<CameraType>('back')
        const [permission, requestPermission] = useCameraPermissions();
    
        const [scanned, setScanned] = useState(false);
        const [qrData, setQrData] = useState<string | null>(null);
    
        /* 카메라 권한이 없는 경우 권한 요청 */
        useEffect(() => {
            if(!permission) {
                requestPermission();
            }
        }, []);
    
        if(!permission?.granted) {
            return(
                <ScreenInner>
                    <Text>카메라 사용 권한이 필요합니다.</Text>
                    <TouchableOpacity onPress={requestPermission}>
                        <Text>권한 요청</Text>
                    </TouchableOpacity>
                </ScreenInner>
            )
        }
    
        // function toggleCameraFacing() {
        //   setFacing(current => (current === 'back' ? 'front' : 'back'))
        // }
    
        function handleResetScanner() {
            setScanned(false);
        }
    
        function handleAlertData() {
            alert(`QR DATA : ${qrData}`)
        }
    
        function handleBarcodeScanned(data: string) {
            setScanned(true);
            setQrData(data);
            alert(`QR DATA : ${qrData}`)
        }
    
        return(
            <ScreenInner>
                <CameraView style={styles.camera} onBarcodeScanned={({data}) => scanned ? undefined : handleBarcodeScanned(data)} barcodeScannerSettings={{barcodeTypes:["qr"]}}>
                    <View style={styles.overlay}>
                       <View style={[styles.corner, styles.topLeft]} />
                       <View style={[styles.corner, styles.topRight]} />
                       <View style={[styles.corner, styles.bottomLeft]} />
                       <View style={[styles.corner, styles.bottomRight]} />
                    </View>
                </CameraView>
    
                <TouchableOpacity onPress={handleResetScanner}>
                    <Text>Reset</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={handleAlertData}>
                    <Text>Alert</Text>
                </TouchableOpacity>
            </ScreenInner>
        )
    }
    
    const styles = StyleSheet.create({
        camera: {
            width: '100%',
            height: '80%',
        },
        overlay: {
            position: 'absolute',
            top: '40%',
            left: '50%',
            width: '80%',
            height: '50%',
            justifyContent: 'center',
            alignItems: 'center',
            marginLeft: '-40%',
            marginTop: '-30%',
            backgroundColor: 'transparent',  // 이 부분만 투명하게 설정
        },
        corner: {
            width: 40,
            height: 40,
            borderColor: 'white',
            position: 'absolute',
        },
        topLeft: {
            borderTopWidth: 5,
            borderLeftWidth: 5,
            top: 0,
            left: 0,
        },
        topRight: {
            borderTopWidth: 5,
            borderRightWidth: 5,
            top: 0,
            right: 0,
        },
        bottomLeft: {
            borderBottomWidth: 5,
            borderLeftWidth: 5,
            bottom: 0,
            left: 0,
        },
        bottomRight: {
            borderBottomWidth: 5,
            borderRightWidth: 5,
            bottom: 0,
            right: 0,
        },
    })

     

    이렇게 하면 QR코드를 인식하기 위한 영역을 표시할 수 있고,

    조건을 추가하면 해당 영역에서만 QR을 인식하게 할 수 도 있다.

     

    현재는 QR인식을 하기 위해서

    QR을 읽고 난 후의 데이터는 alert으로 표시가 나오게 했고,

    reset버튼을 누르면 다시 QR을 찍을 수 있고 alert을 누르면 현재 읽은 QR의 데이터를 alert으로 볼 수 있도록 구현을 했다.

    728x90
    반응형