바코드 스캐너(물리적 장비)를 사용하여 사용자의 QR코드인식 기능 개발
<script th:src="@{/static/js/webserial-barcode-scanner.umd.js}"></script>
<script th:src="@{/static/js/event-emitter.js}"></script>
<script th:src="@{/static/js/main.js}"></script>
아래 라이브러리는 파일 첨부가 안되서 코드로 올림
class t{constructor(t){this._events={}}on(t,e){this._events[t]=this._events[t]||[],this._events[t].push(e)}emit(t,...e){let n=this._events[t];n&&n.forEach((t=>{t(...e)}))}}class e{constructor(e){this._internal={emitter:new t,port:null,reader:null,options:Object.assign({baudRate:9600,bufferSize:255,dataBits:8,flowControl:"none",parity:"none",stopBits:1},e)},navigator.serial.addEventListener("disconnect",(t=>{this._internal.port==t.target&&this._internal.emitter.emit("disconnected")}))}async connect(){try{let t=await navigator.serial.requestPort();t&&await this.open(t)}catch(t){console.log("Could not connect! "+t)}}async reconnect(t){if(!t.vendorId||!t.productId)return;let e=(await navigator.serial.getPorts()).filter((e=>{let n=e.getInfo();return n.usbVendorId==t.vendorId&&n.usbProductId==t.productId}));1==e.length&&await this.open(e[0])}async disconnect(){this._internal.port&&(this._internal.reader.releaseLock(),await this._internal.port.close(),this._internal.port=null,this._internal.reader=null,this._internal.emitter.emit("disconnected"))}async open(t){this._internal.port=t,await this._internal.port.open(this._internal.options);let e=this._internal.port.getInfo();this._internal.emitter.emit("connected",{type:"serial",vendorId:e.usbVendorId||null,productId:e.usbProductId||null});let n="";for(;t.readable;){this._internal.reader=t.readable.getReader();try{for(;;){const{value:t,done:e}=await this._internal.reader.read();if(e){this._internal.reader.releaseLock();break}if(t)for(let e=0;e<t.length;e++){let r=t[e];13!==r?n+=String.fromCharCode(r):(this._internal.emitter.emit("barcode",{value:n}),n="")}}}catch(t){n=""}}}addEventListener(t,e){this._internal.emitter.on(t,e)}}export{e as default};
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).WebSerialBarcodeScanner=t()}(this,(function(){"use strict";class e{constructor(e){this._events={}}on(e,t){this._events[e]=this._events[e]||[],this._events[e].push(t)}emit(e,...t){let n=this._events[e];n&&n.forEach((e=>{e(...t)}))}}return class{constructor(t){this._internal={emitter:new e,port:null,reader:null,options:Object.assign({baudRate:9600,bufferSize:255,dataBits:8,flowControl:"none",parity:"none",stopBits:1},t)},navigator.serial.addEventListener("disconnect",(e=>{this._internal.port==e.target&&this._internal.emitter.emit("disconnected")}))}async connect(){try{let e=await navigator.serial.requestPort();e&&await this.open(e)}catch(e){console.log("Could not connect! "+e)}}async reconnect(e){if(!e.vendorId||!e.productId)return;let t=(await navigator.serial.getPorts()).filter((t=>{let n=t.getInfo();return n.usbVendorId==e.vendorId&&n.usbProductId==e.productId}));1==t.length&&await this.open(t[0])}async disconnect(){this._internal.port&&(this._internal.reader.releaseLock(),await this._internal.port.close(),this._internal.port=null,this._internal.reader=null,this._internal.emitter.emit("disconnected"))}async open(e){this._internal.port=e,await this._internal.port.open(this._internal.options);let t=this._internal.port.getInfo();this._internal.emitter.emit("connected",{type:"serial",vendorId:t.usbVendorId||null,productId:t.usbProductId||null});let n="";for(;e.readable;){this._internal.reader=e.readable.getReader();try{for(;;){const{value:e,done:t}=await this._internal.reader.read();if(t){this._internal.reader.releaseLock();break}if(e)for(let t=0;t<e.length;t++){let r=e[t];13!==r?n+=String.fromCharCode(r):(this._internal.emitter.emit("barcode",{value:n}),n="")}}}catch(e){n=""}}}addEventListener(e,t){this._internal.emitter.on(e,t)}}}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).WebSerialBarcodeScanner=t()}(this,(function(){"use strict";class e{constructor(e){this._events={}}on(e,t){this._events[e]=this._events[e]||[],this._events[e].push(t)}emit(e,...t){let n=this._events[e];n&&n.forEach((e=>{e(...t)}))}}return class{constructor(t){this._internal={emitter:new e,port:null,reader:null,options:Object.assign({baudRate:9600,bufferSize:255,dataBits:8,flowControl:"none",parity:"none",stopBits:1},t)},navigator.serial.addEventListener("disconnect",(e=>{this._internal.port==e.target&&this._internal.emitter.emit("disconnected")}))}async connect(){try{let e=await navigator.serial.requestPort();e&&await this.open(e)}catch(e){console.log("Could not connect! "+e)}}async reconnect(e){if(!e.vendorId||!e.productId)return;let t=(await navigator.serial.getPorts()).filter((t=>{let n=t.getInfo();return n.usbVendorId==e.vendorId&&n.usbProductId==e.productId}));1==t.length&&await this.open(t[0])}async disconnect(){this._internal.port&&(this._internal.reader.releaseLock(),await this._internal.port.close(),this._internal.port=null,this._internal.reader=null,this._internal.emitter.emit("disconnected"))}async open(e){this._internal.port=e,await this._internal.port.open(this._internal.options);let t=this._internal.port.getInfo();this._internal.emitter.emit("connected",{type:"serial",vendorId:t.usbVendorId||null,productId:t.usbProductId||null});let n="";for(;e.readable;){this._internal.reader=e.readable.getReader();try{for(;;){const{value:e,done:t}=await this._internal.reader.read();if(t){this._internal.reader.releaseLock();break}if(e)for(let t=0;t<e.length;t++){let r=e[t];13!==r?n+=String.fromCharCode(r):(this._internal.emitter.emit("barcode",{value:n}),n="")}}}catch(e){n=""}}}addEventListener(e,t){this._internal.emitter.on(e,t)}}}));
class WebSerialBarcodeScanner {
constructor(options) {
this._internal = {
emitter: new EventEmitter(),
port: null,
reader: null,
isConnected: false,
usbVendorId: null,
productId: null,
options: Object.assign(
{
baudRate: 9600,
bufferSize: 255,
dataBits: 8,
flowControl: "none",
parity: "none",
stopBits: 1,
},
options,
),
};
navigator.serial.addEventListener("disconnect", (event) => {
if (this._internal.port == event.target) {
this._internal.emitter.emit("disconnected");
this._internal.isConnected = false;
}
});
}
async connect() {
try {
let port = await navigator.serial.requestPort();
if (port) {
await this.open(port);
}
} catch (error) {
console.error("Could not connect! " + error);
}
}
async reconnect(previousPort, callback) {
if (!previousPort.vendorId || !previousPort.productId) {
return;
}
let ports = await navigator.serial.getPorts();
let matches = ports.filter((port) => {
let info = port.getInfo();
return (
info.usbVendorId == previousPort.vendorId &&
info.usbProductId == previousPort.productId
);
});
if (matches.length === 1) {
await this.open(matches[0], callback);
}
}
async disconnect() {
if (!this._internal.port) {
return;
}
this._internal.reader.releaseLock();
await this._internal.port.close();
this._internal.port = null;
this._internal.reader = null;
this._internal.emitter.emit("disconnected");
}
async open(port, callback) {
this._internal.port = port;
await this._internal.port.open(this._internal.options);
let info = this._internal.port.getInfo();
this._internal.emitter.emit("connected", {
type: "serial",
vendorId: info.usbVendorId || null,
productId: info.usbProductId || null,
});
localStorage.setItem("usbVendorId", info.usbVendorId);
localStorage.setItem("usbProductId", info.usbProductId);
let buffer = "";
this._internal.isConnected = true;
while (port.readable) {
this._internal.reader = port.readable.getReader();
try {
while (true) {
const { value, done } = await this._internal.reader.read();
const reconstituted = String.fromCharCode.apply(null, value);
console.log("reconstituted", reconstituted);
callback(reconstituted);
if (done) {
this._internal.reader.releaseLock();
break;
}
if (value) {
for (let i = 0; i < value.length; i++) {
let character = value[i];
if (character !== 13) {
buffer += String.fromCharCode(character);
} else {
this._internal.emitter.emit("barcode", {
value: buffer,
});
buffer = "";
}
}
}
}
} catch (error) {
buffer = "";
}
}
}
addEventListener(n, f) {
this._internal.emitter.on(n, f);
}
}
const barcodeScanner = new WebSerialBarcodeScanner();
let lastUsedDevice = {
type: "serial",
vendorId: localStorage.getItem("usbVendorId") || null,
productId: localStorage.getItem("usbProductId") || null,
};
function onStartQR() {
setInterval(async function () {
if (lastUsedDevice.productId &&
lastUsedDevice.vendorId &&
!barcodeScanner._internal.isConnected) {
await barcodeScanner.reconnect(lastUsedDevice, onQRCode);
}
}, 2000);
}
function onQRCode(value){
// QR스캔시 실행할 로직
}
window.onload = function () {
onStartQR();
}
시리얼 같은 경우 브라우저를 켤때마다 물어보는데 이전에 설정했던 시리얼을 사용자에게 물어보지 않고 사용하고싶으면 크롬/엣지 실행시 아규먼트에 --user-data-dir="C:\path\to\your\chrome_profile" 추가