투케이2K

475. (javaScript) 자바스크립트 zxing browser , library 라이브러리 사용해 QR 및 Barcode 이미지 스캔 수행 본문

JavaScript

475. (javaScript) 자바스크립트 zxing browser , library 라이브러리 사용해 QR 및 Barcode 이미지 스캔 수행

투케이2K 2025. 12. 22. 20:39
728x90

[개발 환경 설정]

개발 툴 : Edit++

개발 언어 : JavaScript

 

[소스 코드]

-----------------------------------------------------------------------------------------
[사전 설명 및 설정 사항]
-----------------------------------------------------------------------------------------

- 개발 환경 : Web


- 개발 기술 : JavaScript (자바스크립트) / zxing / QR / Barcode / Scan


- 사전) @zxing/browser 라이브러리 간략 설명 : 

  >> @zxing/browser 는 ZXing (JS 포트)의 브라우저 전용 유틸리티 레이어입니다
        
  >> @zxing/browser 는 <video> 카메라 스트림, <img> 이미지, 파일/URL, <canvas> 등 브라우저의 미디어 요소를 다루며, 내부적으로 ZXing 디코더를 호출해 다양한 바코드(특히 QR)를 읽습니다

  >> @zxing/browser 핵심 특징 : 

    - BrowserQRCodeReader 는 브라우저 미디어 요소를 받아 디코딩 루프를 돌려 줍니다

    - decodeFromVideoDevice : 지정/자동 선택된 카메라에서 연속 스캔. 콜백은 (result, error, controls)로 호출됩니다

    - decodeFromStream : 직접 생성한 MediaStream(예: getUserMedia)을 넘겨 연속 스캔. 해상도/카메라 제약을 세밀히 지정할 때 유용

    - decodeOnceFromVideoDevice : 한 번만 읽고 Promise로 결과를 반환. 실패/예외는 catch로 처리

  >> @zxing/browser 사용 시 주의 사항 :

    - 해상도/크기 : QR이 프레임에서 너무 작은 경우 주의 (모듈 수가 충분히 픽셀로 표현되지 않음)

    - 초점/블러 : 초점이 맞지 않거나 흔들림 주의

    - 조명/노이즈 : 어두움, 노이즈, 난반사(유광 인쇄)로 패턴 경계가 흐려짐 주의

    - 잘림/여백 부족 : QR 주변 quiet zone (여백) 이 최소 4 모듈 이상 필요

    - 기하학적 왜곡 : 과도한 기울어짐/원근 왜곡으로 세 눈의 상대 위치가 성립하지 않음 주의

    - 반전 색상 : 밝은 QR(화이트 패턴/어두운 배경) 등 색상 반전 케이스 주의


>> 사전) 바코드 종류 예시 : 

    - CODE128 (가장 추천) : 영문, 숫자, 특수문자 모두 지원 , 길이 제한 거의 없음

    - EAN-13 : 13자리 숫자만 가능 , 유통 상품 표준 바코드

    - EAN-8 : 8자리 숫자 , 소형 상품용

    - UPC-A : 12자리 숫자 , 북미 지역 상품 코드

    - ITF (Interleaved 2 of 5) : 숫자 전용 , 물류/박스 바코드

    - MSI : 숫자 전용 , 창고 관리, 내부 시스템

-----------------------------------------------------------------------------------------





-----------------------------------------------------------------------------------------
[소스 코드]
-----------------------------------------------------------------------------------------

<!DOCTYPE HTML>
<html lang="ko">
<head>
    <title>javaScriptTest</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">


    <!-- 반응형 구조 만들기 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">


    <!-- 내부 CSS 스타일 지정 -->
    <style>

        html, body {
            width: 100%;
            height: 100%;
            margin : 0 auto;
            padding : 0;
            border : none;
            background-color: #666;
        }

    </style>





    <!-- [CDN 주소 설정] -->    
    <!-- <script src="https://sdk.amazonaws.com/js/aws-sdk-2.1416.0.min.js"></script> -->
    <!-- <script src="https://unpkg.com/amazon-kinesis-video-streams-webrtc/dist/kvs-webrtc.min.js"></script> -->  







    <!-- [자바스크립트 코드 지정] : ESM Import 방식 사용 -->
    <script type="module">

        // --------------------------------------------------------------------------------------------------------------

        
        /*
        -----------------------------------------------------------
        import { BrowserQRCodeReader } from 'https://cdn.jsdelivr.net/npm/@zxing/browser@latest/+esm'; 설명 정리
        -----------------------------------------------------------
        1. @zxing/browser: ZXing(JS 포트)의 브라우저 전용 유틸리티 레이어입니다
        -----------------------------------------------------------
        2. @zxing/browser 는 <video> 카메라 스트림, <img> 이미지, 파일/URL, <canvas> 등 브라우저의 미디어 요소를 다루며, 내부적으로 ZXing 디코더를 호출해 다양한 바코드(특히 QR)를 읽습니다
        -----------------------------------------------------------
        */
       
        
        /*
        -----------------------------------------------------------
        import { BarcodeFormat, DecodeHintType } from 'https://cdn.jsdelivr.net/npm/@zxing/library@latest/+esm'; 설명 정리
        -----------------------------------------------------------
        1. @zxing/library: ZXing(JS 포트) 의 순수 디코딩 엔진 + 포맷/힌트 상수 유틸 모음입니다
        -----------------------------------------------------------
        2. @zxing/library 는 브라우저/Node.js 공통 사용 가능합니다
        -----------------------------------------------------------
        */


        // 리더는 browser 패키지에서
        import { BrowserMultiFormatReader } from 'https://cdn.jsdelivr.net/npm/@zxing/browser@latest/+esm';

        
        // 포맷/힌트 상수는 library 패키지에서
        import { BarcodeFormat, DecodeHintType } from 'https://cdn.jsdelivr.net/npm/@zxing/library@latest/+esm';


        // --------------------------------------------------------------------------------------------------------------

        // [전역 변수 선언]        
        var controls = null;
        var scanWatingCnt = 1;

        // --------------------------------------------------------------------------------------------------------------

        // [html 최초 로드 및 이벤트 상시 대기 실시]
        window.onload = async function() {
            console.log("[window onload] : [Start]");

            try {


                // -----------------------------------------
                // [video 태그 객체 정의]
                // -----------------------------------------

                const videoEl = document.getElementById('preview');

                
                // -----------------------------------------
                // ✅ 힌트로 가능한 포맷 제한 (예: QR + CODE_128 + EAN_13)
                // -----------------------------------------
                const hints = new Map();
                hints.set(DecodeHintType.POSSIBLE_FORMATS, [
                    BarcodeFormat.QR_CODE,
                    BarcodeFormat.CODE_128,
                    BarcodeFormat.EAN_13,
                ]);


                // -----------------------------------------
                // ✅ [BrowserMultiFormatReader : 바코드(여러 포맷) + QR 모두 스캔 리더 생성]
                // -----------------------------------------
                const reader = new BrowserMultiFormatReader(hints);


                // -----------------------------------------
                // [카메라(디바이스)를 자동 선택해 연속 스캔을 시작]
                // -----------------------------------------
                console.log("[decodeFromVideoDevice] : [Scan] : Start"); 

                try {
                    controls = await reader.decodeFromVideoDevice(
                        undefined, // deviceId
                        videoEl, // preview <video>
                        (result, err) => { // ✅ callback
                            if (err != null && err != undefined) {

                                // 디코딩 실패 등 반복적으로 발생할 수 있음                        
                                //console.error(`[decodeFromVideoDevice] : [Scan] : [Error] : 예외 상황 발생 : ${err.name} : `, err);

                                if (err.name == 'e' || err.stack.indexOf("selectBestPatterns") >= 0){ // 스캔 한 QR 을 찾지 못하고 있는 상태
                                    console.log(`[decodeFromVideoDevice] : [Scan] : [Waiting ..] : 스캔 대기 중 : ${err.name} : ${scanWatingCnt}`);

                                    scanWatingCnt ++; // ✅ QR 및 바코드 스캔 대기 카운트 증가

                                    if (scanWatingCnt > 50){ // ✅ QR 및 바코드 스캔 종료 처리 (무한 대기 방지)

                                        if (controls != null && controls != undefined){
                                            controls.stop();

                                            console.error("[decodeFromVideoDevice] : [Scan] : TimeOut Stop");
                                        }

                                    }
                                }                                
                                else {
                                    console.error(`[decodeFromVideoDevice] : [Scan] : [Error] : 예외 상황 발생 : ${err.name} : `, err);

                                    if (controls != null && controls != undefined){
                                        controls.stop();

                                        console.error("[decodeFromVideoDevice] : [Scan] : Exception Stop");
                                    }                                    
                                }
                            }
                            else {

                                /*
                                {
                                    "text": "123456",
                                    "rawBytes": {
                                        "0": 16,
                                        "1": 24,
                                        "2": 123,
                                        "3": 114,
                                        "4": 0,
                                        "5": 236,
                                        "6": 17,
                                        "7": 236,
                                        "8": 17,
                                        "9": 236,
                                        "10": 17,
                                        "11": 236,
                                        "12": 17,
                                        "13": 236,
                                        "14": 17,
                                        "15": 236,
                                        "16": 17,
                                        "17": 236,
                                        "18": 17
                                    },
                                    "numBits": 152,
                                    "resultPoints": [
                                        {
                                            "x": 243.5,
                                            "y": 309,
                                            "estimatedModuleSize": 8.428571428571429,
                                            "count": 2
                                        },
                                        {
                                            "x": 243.5,
                                            "y": 180.5,
                                            "estimatedModuleSize": 8.428571428571429,
                                            "count": 2
                                        },
                                        {
                                            "x": 361,
                                            "y": 182,
                                            "estimatedModuleSize": 8,
                                            "count": 2
                                        }
                                    ],
                                    "format": 11,
                                    "timestamp": 1765780101127,
                                    "resultMetadata": {}
                                }
                                */

                                console.warn("[decodeFromVideoDevice] : [Scan] : [Data] : ", JSON.stringify(result)); // ✅ QR 및 바코드 스캔 전문 출력

                                const scanText = result.getText();

                                console.log("[decodeFromVideoDevice] : [Scan] : [Text] : ", scanText); 

                                if (scanText != null && scanText != undefined && scanText != ''){

                                    // 필요 시 한 번만 읽고 중지하려면 controls.stop(); 설정
                                    if (controls != null && controls != undefined){
                                        controls.stop();

                                        console.error("[decodeFromVideoDevice] : [Scan] : Success Stop");
                                    }
                                    
                                }

                            }

                        }
                    );
                }
                catch (decodeFromVideoDeviceException){
                    console.error("[decodeFromVideoDeviceException] : 예외 상황 발생 : ", decodeFromVideoDeviceException);

                    if (decodeFromVideoDeviceException.name == "NotAllowedError"){
                        console.error(`[decodeFromVideoDeviceException] : 카메라 퍼미션 권한 비허용 : ${decodeFromVideoDeviceException.name}`);
                    }

                    if (controls != null && controls != undefined){ // ✅ QR 및 바코드 스캔 종료 처리
                        controls.stop();

                        console.error("[decodeFromVideoDeviceException] : [Scan] : Exception Stop");
                    }

                }

            }
            catch (exception) {
              console.error("[window onload] : [Exception] : 예외 상황 발생 : ", exception);
            }

        };

        // --------------------------------------------------------------------------------------------------------------

    </script>


</head>


<body>

    <!-- video Tag Setting -->
    <video id="preview" autoplay playsinline style="width: 360px; height: auto;"></video>

</body>

</html>

-----------------------------------------------------------------------------------------





-----------------------------------------------------------------------------------------
[참고 사이트]
-----------------------------------------------------------------------------------------

[자바스크립트 zxing browser 라이브러리 사용해 video 태그 사용 실시간 QR 이미지 (png , jpeg) 스캔 수행 실시]

https://kkh0977.tistory.com/8501

https://blog.naver.com/kkh0977/224110786770


[@zxing/browser 라이브러리 사용 참고 사이트]

https://www.npmjs.com/package/@zxing/browser


[qrcode 라이브러리 사용해 QR 코드 생성 실시]

https://blog.naver.com/kkh0977/222395438877?trackingCode=blog_bloghome_searchlist

-----------------------------------------------------------------------------------------
 

[결과 출력]

 
728x90
반응형
Comments