투케이2K

544. (javaScript) [간단 소스] 자바스크립트 sweetAlert 및 zxing 라이브러리 사용해 QR 및 바코드 스캔 카메라 팝업창 생성 수행 본문

JavaScript

544. (javaScript) [간단 소스] 자바스크립트 sweetAlert 및 zxing 라이브러리 사용해 QR 및 바코드 스캔 카메라 팝업창 생성 수행

투케이2K 2026. 6. 2. 19:23
728x90
반응형

[개발 환경 설정]

개발 툴 : Edit++ / Vscode

개발 언어 : JavaScript

 

[소스 코드]

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

- 개발 환경 : Web


- 개발 기술 : 자바스크립트 / sweetAlert / zxing 


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

  >> zxing/browser 는 ZXing (JS 포트) 의 브라우저 전용 QR 스캔 라이브러리입니다

  >> 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(화이트 패턴/어두운 배경) 등 색상 반전 케이스 주의

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





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

<!DOCTYPE HTML>
<html lang="ko" translate="no">
<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">

    <!-- Chrome / Edge (Chromium)에서 자동 번역 기능을 완전히 비활성화 -->
    <meta name="google" content="notranslate">

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

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

    </style>





    <!-- [CDN 주소 설정] --> 
    <script src="https://code.jquery.com/jquery-latest.min.js"></script>

    <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>






    <!-- [자바스크립트 코드 지정] -->
    <script type="module"> 

      // -----------------------------------------------------------------
      // 🟦 <script type="module"> QR 및 바코드 스캔 라이브러리 불러오기
      // -----------------------------------------------------------------
      // 리더는 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;



      // -----------------------------------------------------------------
      // 🟦 [Window.onload 웹 브라우저 로드 완료]
      // -----------------------------------------------------------------
      window.onload = async function() {
        console.log("[window onload] : [html 최초 로드 및 이벤트 상시 대기 실시] : [start]");

        try {

          
          // -----------------------------------------
          // ✅ SweetAlert 팝업 생성          
          // -----------------------------------------
          swal({
            title: "QR 및 Barcode 스캔",
            content: {
              element: "video", 
              attributes: {
                id: "qrVideo",
                style: `
                  width: 360px; 
                  height: 320px;

                  display:flex;
                  justify-content:center;
                  align-items:center;

                  margin:0 auto; /* ✅ 가로 중앙 */

                  background-color: #444;
                `
              }
            },
            buttons: {                          
              confirm: { // 기본 확인 버튼
                text: "확인",
                value: "confirmBtnClick",
                visible: true 
              },              
              cancel: { // 기본 취소 버튼
                text: "취소",
                value: "cancelBtnClick",
                visible: true 
              }
            },
          }).then((value) => { // ✅ 버튼 클릭 이벤트 확인
            console.log('value callback : ', value);

            if (controls !== null && controls !== undefined){
                controls.stop();
                controls = null;
                console.error("[decodeFromVideoDevice] : [Scan] : Button Click Stop");
            }

            switch (value) {
              case "cancelBtnClick":

                console.error('btn click : 취소를 클릭했습니다.');
                break;
              default:
                console.log('btn click : 확인을 클릭했습니다.');                
            }

          });


          // -----------------------------------------
          // ✅ 렌더 이후 실행          
          // -----------------------------------------
          setTimeout(async() => {

            // -----------------------------------------
            // ✅ SweetAlert 팝업창에서 지정한 qrVideo 객체 지정
            // -----------------------------------------
            const videoEl = document.getElementById("qrVideo");

            if (!videoEl) {
              console.error('[Error] : videoEl dom is null');
              return;
            }


            // -----------------------------------------
            // ✅ 힌트로 가능한 포맷 제한 (예: 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 및 바코드 스캔 대기 카운트 증가
                            }                                
                            else {
                                console.error(`[decodeFromVideoDevice] : [Scan] : [Error] : 예외 상황 발생 : ${err.name} : `, err);

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

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

                                    alert('[Scan] : Exception Stop [1] : ' + err.name);
                                }                                    
                            }
                        }
                        else {
                            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();
                                    controls = null;

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

                                    swal.close();   // ✅ SweetAlert 팝업창 강제 닫기

                                    alert('[Scan] : Success : ' + scanText);
                                }
                                
                            }

                        }

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

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

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

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

                    alert('[Scan] : Exception Stop [2] : ' + decodeFromVideoDeviceException.name);
                }

            }

          }, 200);


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

        }

      };
      

    </script>


</head>


<body>


</body>

</html>

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





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

▶️ [자바스크립트 zxing browser , library 라이브러리 사용해 QR 및 Barcode 이미지 스캔 수행]

https://kkh0977.tistory.com/8525

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


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

https://kkh0977.tistory.com/8501

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


▶️ [라이브러리] [Web] 자바스크립트 zxing browser 실시간 웹 QR 코드 스캔 라이브러리

https://kkh0977.tistory.com/8504

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


▶️ [자바스크립트 SweetAlert 라이브러리 사용해 input 입력 받기 팝업창 생성 - prompt , text , number , password]

https://kkh0977.tistory.com/8788

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


▶️ [자바스크립트 SweetAlert 라이브러리 사용해 팝업창 활성 시 확인 및 취소 버튼 외 동적 버튼 추가 생성 , 팝업창 자동 닫힘 방지 수행]

https://kkh0977.tistory.com/8784

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

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

[결과 출력]

 
728x90
반응형
Comments