조용진의 개발 블로그
← 글 목록

키오스크 하드웨어 이벤트를 Custom Hook으로 분리한 과정

ReactCustom Hook키오스크하드웨어

지문 인식기·소켓·QR·원격 제어 로직을 역할별 Custom Hook으로 분리하고 이벤트 구독과 정리 책임을 명확히 한 과정을 정리합니다.

키오스크 화면은 일반 웹 화면보다 외부 상태가 많다. 지문 인식기 연결, 소켓 메시지, 스캔 결과, QR 코드와 원격 제어 이벤트가 한 페이지에 모이면 렌더링 코드와 장비 생명주기가 강하게 결합된다.

기존 페이지에 섞여 있던 하드웨어 로직을 역할별 Custom Hook으로 나눠 연결·이벤트 등록·정리 책임을 화면 밖으로 이동했다.

장비별 책임을 먼저 분리했다

페이지에서 분리한 주요 Hook은 다음과 같다.

flowchart LR
    A[키오스크 페이지] --> B[지문 연결 Hook]
    A --> C[지문 소켓 Hook]
    A --> D[데이터·스캔 Hook]
    A --> E[QR Hook]
    A --> F[원격 제어 Hook]
    B --> G[장비 API]
    C --> H[소켓 이벤트]
    D --> G
    E --> I[키보드 입력]
    F --> H

아래 코드 예시는 설명을 위해 식별자와 운영 설정값을 일반화했습니다.

페이지는 Hook에서 받은 상태와 동작을 UI에 연결하고, 각 Hook은 자신이 만든 타이머와 이벤트 구독을 관리하도록 바꿨다.

이벤트 함수의 참조를 안정화했다

이벤트 기반 코드는 등록할 때와 해제할 때 같은 함수 참조가 필요하다. 렌더링마다 새 함수가 만들어지면 off가 기존 리스너를 제거하지 못할 수 있다.

const handleDeviceEvent = useCallback(() => navigateToDeviceStatus(), [navigateToDeviceStatus]);
useEffect(() => {
  socket.on(eventName, handleDeviceEvent);
  return () => socket.off(eventName, handleDeviceEvent);
}, [socket, eventName, handleDeviceEvent]);

주요 이벤트 핸들러를 useCallback으로 감싸고 의존성을 명시했다. 이렇게 하면 구독과 해제가 같은 참조를 사용하면서도, 핸들러가 참조하는 최신 상태가 의존성 변경에 맞춰 갱신된다.

재연결과 중복 실행을 제어했다

장비 연결이 끊겼을 때는 60초 간격으로 재연결을 시도했다. 타이머는 Hook이 해제될 때 정리하고, 이미 연결 중인 작업과 겹치지 않도록 연결 상태를 확인했다.

연결 Hook은 현재 포트가 열려 있는지 확인한 뒤 연결을 시도했고, 연결 중 상태와 재연결 상태가 겹치지 않도록 조건을 뒀다.

const ensureDeviceConnection = useCallback(() => {
  if (shouldReconnect && !isConnected) return connectDevice();
}, [connectDevice, isConnected, shouldReconnect]);
useInterval(ensureDeviceConnection, checkInterval);

이 재시도 함수도 useCallback으로 유지하고 실제 상태를 의존성에 포함했다. 화면 렌더링과 무관한 중복 연결 방지는 기존 fingerConnectTrigger ref로 관리했다.

정리 코드까지 Hook의 일부로 봤다

소켓과 IPC 이벤트는 화면을 떠난 뒤에도 살아 있으면 중복 호출의 원인이 된다. 각 Hook의 effect cleanup에서 등록한 이벤트를 해제했고, 해당 구현이 제공하는 경우 removeAllListeners도 사용했다. 타이머 역시 cleanup에서 제거했다.

Custom Hook 분리의 효과는 파일을 작게 만드는 데 그치지 않았다. 장비 이벤트를 어디에서 등록하고 언제 정리하는지, 재연결과 데이터 동기화가 겹치지 않는지를 역할별로 추적할 수 있게 됐다.

하드웨어 연동 화면에서 중요한 것은 Hook의 개수가 아니라 생명주기의 소유권이다. 연결을 만든 코드가 재시도와 해제까지 책임지고, 이벤트를 등록한 코드가 같은 함수 참조로 정리하도록 구성해야 화면 전환과 재렌더링 이후에도 안정적으로 동작한다.