바코드 스캔 기능 개발

신재욱·2024년 8월 8일
post-thumbnail

개요

최근 프로젝트에서 바코드 스캔 기능을 구현하면서, 페이지에 들어왔을 때 인풋을 클릭하지 않아도 바코드를 스캔하면 자동으로 값이 입력되도록 하는 기능을 만들었다. 이는 포스기 등에서 바코드 스캐너로 바코드를 찍으면 자동으로 값이 입력되는 방식을 참고하여 구현한 것이다.

필요성

자재를 확인하고 등록하기 위해 바코드 스캔 기능이 필요했다. 일반적으로 인풋에 커서를 두고 바코드 스캐너로 바코드를 찍으면 키보드로 값을 입력하고 엔터를 치는 것과 같은 방식으로 작동한다. 그러나 바코드 스캐너 기능이 포함된 페이지에서 특정 인풋에만 커서를 둘 수 있는 상황이 아니었고, 스캐너가 없을 때도 수동으로 타이핑하여 등록할 수 있어야 했다. 이러한 점을 고려하여 다음과 같은 바코드 스캔 기능을 구현했다.

구현 코드

1. 자동으로 인풋에 커서가 위치하도록 설정

바코드 스캐너가 입력하는 인풋에 자동으로 커서가 위치하도록 하기 위해 useRef와 useEffect를 사용하여 컴포넌트가 마운트되었을 때와 키 입력이 발생했을 때 인풋에 포커스를 설정했다.

import React, { useState, useEffect, useRef } from 'react';

const BarcodeScannerComponent = () => {
  const inputRef = useRef(null);
  const [focus, setFocus] = useState(false);
  const [inputValue, setInputValue] = useState('');

  // 키 입력 시 인풋에 포커스 설정
  useEffect(() => {
    if (focus === false) {
      const focusInput = () => {
        inputRef.current.focus();
      };
      window.addEventListener('keydown', focusInput);
      return () => {
        window.removeEventListener('keydown', focusInput);
      };
    }
  }, [focus]);

  const handleInputChange = (event) => {
    setInputValue(event.target.value);
  };

  const handleKeyDown = (event) => {
    if (event.key === 'Enter') {
      // 바코드 스캔 시 처리 로직
      console.log("바코드 스캔 완료:", inputValue);
      setInputValue('');
    }
  };

  return (
    <input
      ref={inputRef}
      value={inputValue}
      onChange={handleInputChange}
      onKeyDown={handleKeyDown}
      autoFocus
      type="text"
    />
  );
};
export default BarcodeScannerComponent;

2. 자동 입력 시 화면에서 인풋이 보이지 않도록 설정

자동 입력 인풋이 화면에 보이지 않도록 style={{ opacity: 0 }} 속성을 사용했다.

return (
  <input
    ref={inputRef}
    value={inputValue}
    onChange={handleInputChange}
    onKeyDown={handleKeyDown}
    autoFocus
    type="text"
    style={{ opacity: 0 }}  // 화면에서 보이지 않도록 설정
  />
);

3. 한글 입력 방지

한글이 입력되지 않도록 인풋의 type을 "password"로 설정하여 한글 모드에서도 영어로 값이 입력되도록 했다.

한글이 아예 입력되지 않도록 제안하지 않고 type="password"로 한 이유는, 그렇게 했을 때 사용자가 한글 모드인지 모르고 바코드를 찍으면 아무런 값이 입력되지 않기 때문이다. 따라서 한글 모드이든 영문 모드이든 관계없이 바코드 스캐너가 항상 영어로 입력되도록 하기 위해 type="password"를 사용했다.

return (
  <input
    ref={inputRef}
    value={inputValue}
    onChange={handleInputChange}
    onKeyDown={handleKeyDown}
    autoFocus
    type="password"  // 한글 입력 방지
    style={{ opacity: 0 }}  // 화면에서 보이지 않도록 설정
  />
);

4. 수동 입력 지원

바코드 스캐너가 없을 때는 수동으로 인풋에 값을 입력할 수 있도록 별도의 인풋 필드를 제공했다.

const ManualInputComponent = () => {
  const [inputDirectlyValue, setInputDirectlyValue] = useState('');

  const handleInputDirectlyChange = (event) => {
    setInputDirectlyValue(event.target.value);
  };

  const handleDirectlyKeyDown = (event) => {
    if (event.key === 'Enter') {
      // 수동 입력 시 처리 로직
      console.log("수동 입력 완료:", inputDirectlyValue);
      setInputDirectlyValue('');
    }
  };

  return (
    <input
      value={inputDirectlyValue}
      placeholder="Bar Code 직접 입력"
      onChange={handleInputDirectlyChange}
      onKeyDown={handleDirectlyKeyDown}
      type="text"
    />
  );
};
export default ManualInputComponent;

5. 수동 입력 시 포커스 제어

수동 입력 시 포커스가 자동 입력 인풋으로 가지 않도록 onFocusonBlur를 사용하여 포커스를 제어했다.

onFocus : 포커스를 받은 경우 이벤트 설정
onBlur : 포커스가 해지될 때 이벤트 설정

onFocus 이벤트 핸들러는 인풋에 포커스가 갔을 때 setFocus(true)를 호출하여 자동 입력 인풋으로 포커스가 가지 않도록 하고,
onBlur 이벤트 핸들러는 인풋에서 포커스가 벗어났을 때 setFocus(false)를 호출하여 다시 자동 입력 인풋으로 포커스가 갈 수 있도록 한다.

const ManualInputComponent = () => {
  const [inputDirectlyValue, setInputDirectlyValue] = useState('');
  const [focus, setFocus] = useState(false);

  const handleInputDirectlyChange = (event) => {
    setInputDirectlyValue(event.target.value);
  };

  const handleDirectlyKeyDown = (event) => {
    if (event.key === 'Enter') {
      // 수동 입력 시 처리 로직
      console.log("수동 입력 완료:", inputDirectlyValue);
      setInputDirectlyValue('');
    }
  };

  return (
    <input
      value={inputDirectlyValue}
      placeholder="Bar Code 직접 입력"
      onChange={handleInputDirectlyChange}
      onKeyDown={handleDirectlyKeyDown}
      type="text"
      onFocus={() => setFocus(true)}  // 포커스 제어
      onBlur={() => setFocus(false)}  // 포커스 제어
    />
  );
};
export default ManualInputComponent;

6. 전역 상태 관리를 통한 포커스 제어

다른 인풋을 사용할 때 무조건 onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}를 넣어서 포커스를 제어해야 한다. 처음에는 focus를 useState로 관리했지만, 나중에 컴포넌트 밖에서도 인풋에 접근해야 하는 상황이 있을 것을 고려하여 전역 상태 관리 라이브러리인 Recoil을 사용해 focus 상태를 전역적으로 관리했다.

import { atom } from 'recoil';

// Recoil 상태 정의
export const focusAtom = atom({
  key: 'focusAtom',
  default: false,
});

// 인풋 컴포넌트에서 포커스 제어
import { useRecoilState } from 'recoil';
import { focusAtom } from '../../../../store/recoil';

const ManualInputComponent = () => {
  const [inputDirectlyValue, setInputDirectlyValue] = useState('');
  const [focus, setFocus] = useRecoilState(focusAtom);

  const handleInputDirectlyChange = (event) => {
    setInputDirectlyValue(event.target.value);
  };

  const handleDirectlyKeyDown = (event) => {
    if (event.key === 'Enter') {
      console.log("수동 입력 완료:", inputDirectlyValue);
      setInputDirectlyValue('');
    }
  };

  return (
    <input
      value={inputDirectlyValue}
      placeholder="Bar Code 직접 입력"
      onChange={handleInputDirectlyChange}
      onKeyDown={handleDirectlyKeyDown}
      type="text"
      onFocus={() => setFocus(true)}  // 포커스 제어
      onBlur={() => setFocus(false)}  // 포커스 제어
    />
  );
};
export default ManualInputComponent;

결론

이번 프로젝트에서 개발 중인 MES(Mesurement Execution System)나 WMS(Warehouse Management System) 특성상 다른 일반적인 웹과는 달리 바코드 스캔 기능이 필요했다. 하지만 바코드 스캔 기능에 대한 레퍼런스가 많지 않아 개인적인 생각으로 기능을 구현하게 되었다. 이를 통해 기능은 완성했지만, 좀 더 고도화할 필요성이 있다.

이 기능이 들어갈 때 모든 인풋 요소에 반드시 onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}를 적용하여 포커스를 제어해야 하는 번거로움이 있다. 바코드가 찍히는 인풋을 화면에서 시각적으로 숨긴 상태로 사용하기 때문에, 실제로는 인풋이 존재하여 화면에서 타이핑을 하면 바코드 스캔한 것처럼 값이 입력된다.

이러한 문제점들이 존재하므로 지속적인 고도화 작업을 통해 더욱 견고한 시스템을 개발해야 할 필요성이 있다.

profile
3년차 개발자

0개의 댓글