오프라인 매장 무인화 프로젝트, 개발부터 운영까지

올해 전반기, PO님과 개발 팀장님께서 주간 회의를 통해 대규모 프로젝트를 진행할 예정이라는 소식을 공유해 주셨다.

프로젝트의 핵심은 기존 오프라인 스크린 골프 매장을 모두 무인화 시스템으로 전환하는 것이었다.

프로젝트 내용을 듣자마자 가장 먼저 든 생각은 “IoT 기반 기술에 대한 이해가 많이 필요하겠는데?” 였다. 전공이 전자공학이긴 하지만 IoT 분야를 다룬 지는 벌써 4~5년이 지났으며 이전 회사에서 인턴으로 근무할 당시 관련 개발을 맡았던 경험이 있었지만, 이후에는 IT 서비스 개발 업무에만 줄곧 해왔었다.

IT와 IoT에 대한 다양한 지식이 복합적으로 요구될 것이라는 생각에, 벌써부터 걱정이 앞서는 프로젝트는 정말 오랜만이다.

한동안 서랍 속에 오래 넣어 두었던 걱정인형을 다시 내 침대 옆에 놔둘 만큼, 개인적으로 많은 우려가 있었던 프로젝트였었다. 이번 글에서는 몇 개월 동안 프로젝트를 진행하며 겪었던 진행 과정과 시행착오, 그리고 그 속에서 얻은 경험을 기록해 보려 한다.


초기 고민과 방향성

1-1 무인화 매장 진짜 괜찮은게 맞을까..?

골프 이미지1

회사에서 운영 중인 스크린 골프 매장은 일반적인 스크린 골프와는 몇 가지 차별점이 있다. 고급스러운 인테리어와 값비싼 스윙 센서 장비, 방문 고객을 VIP로 모시는 프리미엄 서비스. 이처럼 차별화된 서비스는 꾸준한 방문을 이끄는 고객층을 형성하는 기반이 되었으며 골프가 프리미엄 레저 스포츠로 분류되는 만큼, 고급스러운 공간과 정교한 서비스는 매장의 강력한 경쟁력이 되었다.

그런데 갑자기 무인화로 바뀌고 기존 매장의 매니저들은 오전 시간대에만 운영한다라… 분명 타격이 있을 것이다.

프로젝트를 처음 접했을 때 가장 먼저 떠올랐던 걱정은 크게 네 가지로 분류하였다.

첫 번째 고객 경험 , 고객이 직접 키오스크와 패드(태블릿)를 이용해야 하는 환경은 이전 매니저 직원들에게 제공 받던 VIP 서비스의 경험이 크게 반감된다.

두 번째 이용이 끝난 뒤의 운영 방식 , 골프 이용 종료 후 다음 예약을 위해 골프공을 직접 수거·정리해야 하는 상황은, 서비스 만족도를 떨어뜨리고 상당한 불편함을 유발한다.

세 번째 기존 고객들의 적응 문제 , 오랫동안 직원의 안내를 받으며 이용하던 매장이 갑작스럽게 무인화된다면, 장기 고객일수록 많이 당황스럽거나 혼란을 겪게 된다.

마지막 네 번째 비용과 매출 , 단기적으로는 매니저 인력을 줄여 인건비를 절감할 수 있겠지만, 장기적으로 이러한 단점들이 고객 경험 저하로 이어져 매출 감소 및 고객 이탈을 초래하지 않을지 우려스럽다.

즉 우리 스크린 골프 서비스는 ‘사치스러움’이 가장 큰 무기였으나, 무인화 전환은 ‘가성비’라는 이미지로 퇴색시킬 위험이 있어 보였다.

그러나 사업 운영 방향에 관해 위 사항들은 나의 주관적인 생각이므로 어쩌면 현장에서 보이는 근시안적인 변화만 바라보고 걱정하는 것일 수도 있다는 생각이 들기도 한다.

결국 내가 할 수 있는 일은 프로젝트의 방향을 의심하는 것이 아니라, 정해진 목표 안에서 오프라인 매장 무인화 서비스 만들기 위해 최선을 다하는 것이라고 생각하며 다시 개발에 집중하기로 했다.


1-2 웹앱 기반으로 갈까? 네이티브로 갈까?

비교

다행히 내가 맡게 된 역할은 IoT 시스템과 연동해 실내 타석 룸의 TV, PC, 출입문을 제어하는 작업은 맡지 않았다. 키오스크와 패드를 통해 고객이 타석을 현장 예약하고 이용을 연장할 수 있는 애플리케이션 과 매장 각 룸의 실시간 예약 현황을 보여주는 DID(Digital Information Display) 화면을 개발하는 담당을 맡게 되었다.

DID는 직영점 매장에 따라 각 룸의 정보를 대시보드 형태로 보여주는 구조였기에, React 만으로 쉽게 기술 스택을 정할 수 있었다.

반면 키오스크와 태블릿에 설치되는 현장 예약 앱은 사정이 달랐다. 기술 스택 선정부터 디바이스 환경 특성 등 고려해야 할 요소가 다수 존재했다.

무인화 전환에 설치되는 키오스크와 패드는 Android 기반이며 벤더 업체에서 제공하는 장비를 납품 받을 예정이고, 결제 시스템은 KICC(한국정보통신주식회사) 결제 리더기를 연동하는 구조다. 고민이 많았던 부분은 어떤 기반으로 애플리케이션을 개발할 것인가 였다. 프로젝트는 매주 개발 현황을 공유해야 할 만큼 일정이 빠듯했으며 네이티브 앱으로 개발하는 방법도 고려했지만, 당시 나에게는 네이티브 앱 개발 경험이 부족해 일정 리스크가 부담스러웠다.

결국 예약 APP 서비스에서 사용하던 모바일 웹앱(React framework + Native App Wrapper) 형식을 똑같이 적용하기로 결정하였다. 이 방식은 이전 개발 경험을 바탕으로 작업 속도를 높이는 데 효과적일 것이고, 기존에 사용하던 디자인 시스템의 UI도 상당 부분 재활용할 수 있었기에 프로젝트 일정을 지키는데 가장 적합한 선택이었다.

React SPA 기반 웹 애플리케이션을 Native App Wrapper로 감싸는 방식으로 React Native와 Capacitor 중 어떤 기술을 선택할지 비교·분석해 보았다.

분석표

위 표를 통해 어느 환경이 더 적합할지 고려한 결과, 나의 최종 선택은 Capacitor 였다.

  • React Native의 장점을 활용하기 어려운 프로젝트 , React Native는 다양한 모바일 UI 라이브러리와 생태계를 갖추고 있지만, 이번 프로젝트는 화면을 모두 React JS로 구현하는 구조다. 따라서 React Native의 UI 생태계를 활용할 일이 거의 없고, 오히려 웹 기술을 그대로 유지하면서 필요한 네이티브 기능만 사용할 수 있는 Capacitor가 더 합리적이었다.
  • 웹과 네이티브 간 통신 구조가 더 단순 , React Native는 브리지를 통해 네이티브 기능을 연결해야 하지만, Capacitor는 Plugin을 통해 웹앱과 네이티브 기능을 연결할 수 있다. 기존 React 웹앱을 그대로 활용하면서 필요한 네이티브 기능만 연결할 수 있다는 점에서 적합하다고 판단했다.
  • 이전 프로젝트 경험 영향 , 이전 예약 서비스에서는 React와 React Native 간 브리지 연결을 위해 비슷한 코드를 반복해서 작성해야 했고, 보일러플레이트 코드가 많아질수록 구조가 복잡해지는 느낌이 있었다. 반면 Capacitor는 기능별로 Plugin이 명확하게 구분되어 있어 코드 관리와 유지보수 측면에서 유리할 것으로 기대했다.


나에겐 웹 애플리케이션과 네이티브 SDK를 얼마나 간결하고 직관적으로 연결할 수 있느냐가 더 중요했기에 Capacitor 특성이 더 적합하다고 판단하였다.

시스템 구상

2-1 키오스크/패드 예약 APP 아키텍처

앱 아키텍처 앱 아키텍처


하나의 앱으로 두 디바이스 환경 대응하기

키오스크와 패드, 두 디바이스 환경에 맞춰 앱을 어떻게 구성할지 고민이 깊었다.

두 환경을 각각 별도의 앱으로 개발한다면 작업량이 상당히 많아질 것으로 예상되었다. 현장 예약, 이용권 구매, 사전 예약 등 주요 기능의 구현 방식이 대부분 유사했기 때문에, 별도의 앱으로 나누는 것은 불필요하게 개발 공수를 늘리는 일이다.

여러 방안을 숙고한 끝에 하나의 앱에서 키오스크와 패드 환경을 모두 지원하는 방식 으로 방향을 잡았다.

웹뷰 기반으로 개발하는 구조였기 때문에 URL의 path를 ‘/kiosk’, ‘/pad’ 형태로 역할 분리하면 하나의 앱 안에서 각 환경을 명확하게 구분하면서도 공통 기능은 재사용할 수 있어 관리하기 편안해 보였다.

키오스크와 패드는 화면 크기와 해상도가 서로 다르므로 각 path에 해당하는 최상위 layout 에서 환경별 스케일링 설정을 적용하여 디바이스 특성에 맞게 화면을 구성했다.

path 플로우차트 path 흐름도


const DESIGN_WIDTH = 2160;
const DESIGN_HEIGHT = 3840;

export const KioskLayout = ({ children }: { children: ReactNode }) => {
  const { scale, offsetX, offsetY } = useFitTransform(); // 해상도 리사이즈 계산 hook

  return (
    <div className="fixed inset-0 overflow-hidden bg-black">
      <div
        className="absolute left-0 top-0 bg-[#fafafa]"
        style={
         {
           width: `${DESIGN_WIDTH}px`,
           height: `${DESIGN_HEIGHT}px`,
           transform: `translate(${offsetX}px, ${offsetY}px) scale(${scale})`,
           transformOrigin: "top left",
         }
        }
      >
        {children}
      </div>
    </div>
  );
};


예약 앱과 kicc 결제 앱 간 App to App 연동

이번 프로젝트에서 가장 어려웠던 부분은 결제 연동이었다.

Capacitor 브릿지 구현을 위해 Java를 사용해야 했는데, 학부 시절 이후 거의 다뤄보지 않아 문법과 구조를 다시 익히는 데 시간이 꽤 걸렸다. 다행히 AI의 도움을 받아 Java 문법과 브릿지 구현 방식에 대해 수시로 질문하며 부족한 부분을 보완했고, 이를 바탕으로 내가 만든 앱과 KICC 결제 앱을 연동하는 방식을 구현했다.

키오스크와 패드에는 KICC 결제 앱(EasyCardA)이 기본 셋팅으로 설치되어 있다. 카드 리더기 제어는 전부 결제 앱이 담당하므로, 현장 예약 앱은 결제 시점에 결제 앱을 호출하고 승인 결과만 돌려받으면 된다. SDK를 붙이는 방식이 아니라 Android의 Intent를 이용한 App to App 방식이다.

// 결제 앱의 결제 Activity를 명시적 Intent로 호출
Intent intent = new Intent();
intent.setClassName(PAYMENT_APP_PACKAGE, PAYMENT_ACTIVITY);
intent.putExtra(/* 거래 유형, 금액 등 연동 규격 파라미터 */);
startActivityForResult(call, intent, "paymentResult");

React(웹뷰)에서 이 네이티브 코드를 부를 수 있도록 Capacitor 커스텀 플러그인으로 감쌌고, 결제 앱이 돌려주는 응답의 결과 코드로 승인 성공 여부를 판정한다.

// lib/native/kiccPayment.ts
export interface KiccPaymentPlugin {
  startPayment(input: { amount: number; tranNo: string }): Promise<{
    approved: boolean; // 결과 코드 기반 승인 여부
    raw: Record<string, string>; // 결제 앱 응답 원본 (승인번호 등)
  }>;
}
const KiccPayment = registerPlugin<KiccPaymentPlugin>("KiccPayment");

// src/paymentContainer.tsx
const van = await KiccPayment.startPayment({ amount, tranNo });
if (!van.approved) throw { kind: "DECLINED" };
// van.raw의 승인 정보로 서버 확정 진행


다만 Intent만 쏜다고 바로 동작하지는 않았고 두 가지 설정이 더 필요했다.

  1. 패키지 가시성(Android 11+) — 매니페스트에 로 결제 앱 패키지를 선언해야 상대 앱을 찾을 수 있다.
  <!-- KICC EasyCardA -- 호출용(Android 11+ 패키지 가시성). -->
    <queries>
        <package android:name="kr.co.kicc.easycarda" />
    </queries>
  1. 잠금 모드 화이트리스트 — 키오스크/패드는 Lock Task Mode 로 화면이 잠겨 있어 외부 앱 실행이 차단되는데, 결제 앱을 화이트리스트에 등록해야 결제 화면이 뜬다.
dpm.setLockTaskPackages(admin, new String[]{getPackageName(), KICC_PACKAGE});


카드 결제 VAN 승인시 위험 부분 발견

결제 플로우차트 결제 진행 흐름도

confirm API 완료 처리 중 만약 에러(네트워크 끊김, 다른 고객이 가로챔)가 발생하게 되면 다시 결제 버튼을 눌러 이용자가 중복 결제 를 하게 되는 크리티컬한 버그를 발견하였다.

중복 결제 시나리오

1. 고객이 카드를 긁음 → startPayment → VAN 승인 성공 → 1차 출금 완료
2. 이어서 confirm 호출 → 하지만 네트워크 끊킴 / 서버 타임아웃 / 5xx로 실패
3. "결제 실패" 안내
4. 다시 결제하기 재시도 → prepare → startPayment 또 호출 → VAN 승인 -> 2차 출금 발생
5. 결제 금액 두 번 청구


테스트 중 confirm API 단계에 일부러 네트워크 오류를 발생시켜 해당 상황을 재현해 보았는데 결제 플로우가 처음부터 다시 진행 되면서 카드 승인이 한 번 더 발생되었다. 즉, 35,000원짜리 결제를 재시도했을 뿐인데 고객의 카드에는 70,000원이 승인되는 문제가 발생할 수 있는 것이였다.

처음에는 confirm API 과정에 에러가 발생될 경우, APP to APP 방식을 통해 자동 VAN 취소를 하는 방향으로 구현을 생각했다.

KICC EasyCardA APP에서 제공된 파리미터 메뉴얼에는 아래와 같이 승인 취소를 구현할 수 있었다.

  1. 당일취소/전일취소(승인번호)
  2. 직전거래통신취소(망취소)

위와 같은 결제 오류 상황에서는 2번 ‘직전거래통신취소’ 방식이 적합하였지만 매장에서는 키오스크와 패드가 하나의 단말기 번호를 공유하고 있기 때문에 문제가 발생할 수 있다. 로비 키오스크의 결제 오류 시점과 룸 내 패드의 정상 결제 시점이 겹칠 경우, 결제 취소 과정에서 어떤 거래를 취소해야 하는지 정확하게 구분하기 어려워 패드에서 정상 처리된 결제가 잘못 환불될 위험이 있었다.

그래서 2번 방식 대신 1번 ‘당일취소/전일취소’ 방식을 적용하고, 해당 방식으로 자동 환불이 정상적으로 처리되는지 테스트를 진행해 보았다.

[결제하기 클릭 — 단 한 번]
│
├─ prepare API
├─ KICC VAN 승인 (카드 리더 삽입) ← 카드 꽂는 건 여기 딱 1회
│
├─ confirm API 최초 요청
│   실패(애매)
├─ confirm API 요청 자동 재시도
│   실패 → 더 이상 재시도 안 함
│
└─ VAN 취소(D4) → 자동 환불


이 방식은 카드 결제 시에는 문제가 없었으나, 키오스크 바코드 스캐너를 이용한 QR 결제 과정에서는 치명적인 문제가 발견 되었다. 카드는 리더기 투입구에 계속 꽂아두면 되지만, QR 결제는 바코드 스캐너에 스마트폰을 최소 6~7초간 태그하고 있어야 자동 취소가 진행된다.

그 어떤 이용자가 QR을 저렇게 사용하지 않을 것이라고 생각을 했다.

… 근데 아무리 봐도 벤더 업체가 판매한 키오스크가 이상하게 느껴졌다.

PG 결제도 아닌 VAN 결제 환경인데, 대체 왜 키오스크에 바코드 스캐너가 장착되어 있던 걸까? -.-


PG는 온라인 결제를 기반으로 하기 때문에 환불 처리가 비교적 자유로운 반면, VAN 결제 시스템은 실물 카드나 QR을 결제한 기기에 다시 제시해야 환불할 수 있는 구조다. 따라서 VAN 결제 방식에서는 환불을 위해 카드나 바코드 리더기를 직접 거쳐야 했고, App-to-App 연동만으로 자동 환불을 처리하는 데에는 여러 제약이 있었다.

결국 사용자 행동에 따라 오류가 제각각 이므로 자동 환불은 안정적 대안이 되지 못한다는 점을 고려하여 위 작업 과정들은 모두 전면취소 하였다.

다른 방안으로는 VAN 승인 결과를 Ref 참조 객체에 저장해두고, confirm API 요청 과정에서 오류가 발생해 결제를 재시도 시 카드 재승인 없이 confirm API 과정만 다시 수행 하도록 구현했다. 이후에도 여전히 API 오류가 발생하면 사용자에게 관리자 문의를 안내하는 모달을 노출하도록 하였다.

const approvedRef = useRef<Approved | null>(null); // VAN 승인 캐시(재시도 시 재사용)

const pay = async (params: Params) => {
  // 1) 승인 확보 — 최초 1회만 카드 출금. 재시도면 이 블록을 통째로 건너뛴다.
  if (!approvedRef.current) {
    const { amount, context } = await config.prepare(params);
    const van = await KiccPayment.startPayment({ amount, tranNo });
    if (!van.approved) throw { kind: "DECLINED" };
    approvedRef.current = { vanApproval: normalizeVanApproval(van.raw), amount, context };
  }
  // 2) 확정 — '결제하기' 버튼 다시 누르며 재시도 시 여기만 반복된다(이미 출금됐으므로 confirm만 재수행).
  return config.confirm({ ...approvedRef.current, params });
};


개선 후 결제 시나리오

1. 결제 화면에 에러 실패 되었다는 문구 발생
2. 결제 버튼 다시 누름
3. prepare API, kicc 결제 승인 모두 건너가고 바로 confirm 과정 재시도 (카드를 다시 꽂거나 나둬도 출금 발생 X)
4. confirm 에러 또 다시 발생
5. 관리자 문의 모달 안내 


사실 이러한 예외 상황이 실제 현장에서 발생할 확률은 극히 낮다. 하지만 결제 영역에서의 작은 오류는 곧바로 이용자의 금전적 피해로 직결되기에 발생 가능한 모든 예외 변수를 고려한 방어 로직이 반드시 필요하다고 생각한다.


2-2 전체 흐름도

매장 이용 흐름도 스크린 골프 이용 흐름도

고객이 매장을 방문하면 가장 처음 맞이하는 서비스들은 모두 내가 담당한 부분들이다.

  1. 키오스크
  2. 실시간 타석 룸 현황 보드 화면(DID)
  3. 출입 인증 벽면 패드
  4. 룸 이용 현황 패드

키오스크부터 벽면 패드는 고객이 방문하자마자 직접 사용하는 무인화 서비스 시작점이기 때문에 작은 오류 하나만 발생해도 매장 이용에 대한 신뢰도가 크게 떨어질 수 있다. 그래서 단위 테스트는 물론, 유저 테스트 도 꼼꼼하게 준비했다.

특히 유저 테스트에 대해서는, 내부 개발진보다 실제 현장 매니저들이 고객 시선에서 불편함이나 예기치 못한 오류를 훨씬 잘 발견할 수 있는 위치에 있다고 바라보았다. 이에 무인화 설치가 예정될 각 직영점의 매니저들에게 Slack으로 이용 테스트를 부탁드렸다. (무인화 시공 전 기기들은 미리 각 직영점 창고에 이미 보관 중)

매장 매니저 채팅 키오스크 기기 매뉴얼 안내

무작정 매니저들에게 정해진 순서대로 동작을 테스트 하도록 안내하는 방식은 적절하지 않다고 생각했다.

개발자나 기획자는 시스템 구조를 이미 완벽히 이해하고 있어, 정해진 테스트 시나리오대로 쉽게 검증을 진행할 수 있다. 하지만 매니저들은 처음 접하는 서비스 기기인 만큼 오히려 사용 메뉴얼 자료 를 보면서 자유롭게 사용해 보는 쪽이 더 적합해 보였다.

또한 이번 무인화 프로젝트는 현장 매니저들의 근무 형태와 인력 운영에도 민감한 영향을 주고 있었다. 따라서 단순 기기 테스트를 요청할 때도 체크리스트 같은 딱딱한 방식보다는, 이용 매뉴얼과 함께 정중한 태도로 조심스럽게 협조를 구하는 과정이 무엇보다 중요했다.

테스트 요청 결과, 현장 매니저분들로부터 앱 내 예약·충전 가이드 방식은 물론, 연령대가 높은 고객을 위한 안내 문구 개선안까지 다양한 사용자 관점의 피드백을 받을 수 있었다.


2-3 APP / WEB 배포 과정

배포 과정 키오스크 기기 매뉴얼 안내

웹앱(WebView) 기반으로 운영되는 서비스이므로 배포는 APP과 WEB 두 갈래로 나뉜다.

먼저 앱 배포 절차는 수월한 편이었다. 빌드된 APK 파일과 버전 정보(versionName, versionCode)를 KICC 개발팀에 전달하면, 내부 심사를 거쳐 KICC 전용 스토어에 앱이 최종 등록되는 구조였다.

반면 웹 배포는 CI/CD 파이프라인이 구축되어 있어, main 브랜치로 머지(Merge)되면 Vercel을 통해 자동으로 배포되는 구조다.

이때 APK는 빌드 시 환경 변수로 주입된 Vercel 도메인을 웹뷰(WebView)로 원격 로드하는 방식이다. 즉, APK는 패키징을 위한 네이티브 셸(Native Shell) 역할만 수행하고, 실제 서비스 화면은 항상 Vercel의 최신 배포본을 바라보도록 구현했다.

덕분에 네이티브 SDK 영역(키오스크 화면 잠금, KICC 결제 연동 등)이 아닌 단순 UI 수정이나 API 연동 작업이라면, KICC에 앱 재배포를 요청하거나 각 기기마다 업데이트를 거칠 필요 없이 웹 배포만으로 전 매장에 즉시 반영할 수 있었다.

이 구조가 특히 빛을 발한 이유는 운영 규모에 있다. 직영점만 수십 곳에 달하고, 매장에 설치된 키오스크와 패드를 합치면 관리 대상 기기가 수십 대에 이르기 때문이다. 만약 원격 로드 방식이 아니었다면 사소한 UI 수정 하나에도 매장 매니저들이 기기를 일일이 업데이트해야 했을 텐데, 웹 배포 단 한 번 으로 전 매장에 동시 적용되므로 현장에 번거로운 작업을 요청하지 않고도 서비스를 유연하게 개선할 수 있었다.

2-4 무인화 기기 간략 소개

키오스크와 패드

벤더 업체에게 납품 받는 키오스크와 패드 이미지는 위와 같다.

키오스크에는 바코드 레이저 스캐너와 영수증 프린터, 결제 리더기가 탑재되어 있는 반면, 패드 기기에는 전용 바코드 센서가 별도로 장착되어 있지 않았다.

따라서 패드 환경에서 QR 인증이 필요한 경우, WebRTC API를 활용해 전면 카메라로 QR을 인식하는 방식으로 대체 구현을 하였다.


최종 결과


3-1 실외 골프장 시공 (2026-07-09)

실외 골프 매장 실외 골프장 배치도

키오스크

실외 골프장 매장은 ‘타석 룸’이라는 별도 공간이 없어 키오스크 단 1대만 설치하면 되는 구조였다.

키오스크 환경 설정에서 해당 직영점을 선택하면 실시간 타석 현황이 조회되며, 원하는 타석 번호를 선택해 현장 예약을 곧바로 진행할 수 있도록 구현했다.

이 작업은 한 달 뒤 예정된 대규모 실내 스크린 골프장 시공에 앞서, 무인 시스템이 현장에 어떻게 빠르게 안착할 수 있을지 미리 파악하고 대비하는 사전 연습에 가까웠다.

키오스크-예약-1

키오스크-예약-2

(작업이 다 끝나자마자 팀장님의 ‘빨리 퇴근하자!’ 한마디에 급하게 찍었더니, 본의 아니게 밤티 영상만 남았다. 😅)


3-2 실내 스크린 골프장 시공 (2026-08-14)

실내 스크린 매장 실내 스크린 배치도

pad 벽면 패드와 이용 현황 패드

DID

실내 스크린 매장은 각 타석 룸이 구조적으로 연결되어 있어, 이전에 진행했던 실외 매장보다 시공 과정에서 점검하고 기기별 세팅을 진행해야 할 작업이 훨씬 많다.

특히 타석 룸 내부에는 IoT 기반의 자동화 설비까지 구성되어 있어, 자동화 작업을 위해 인테리어 컨설팅 및 시공 업체와 함께 체크해야 할 항목들도 있었다.

예를 들면 벽면 패드에서 예약 입장을 검증한 후 성공 API가 호출되면 동시에 해당 타석 룸 내부의 TV, 프로젝터, 카메라 센서, 에어컨 등이 정상적으로 동작하는지 꼼꼼하게 확인해야 했다.

실내 매장에서 내가 별도로 확인해야 할 사항은 다음과 같았다.

  1. 입장 인증 및 출입문 제어
    • 벽면 패드에서 이용자가 본인의 전화번호를 입력했을 때 정상적으로 입장이 가능
    • 예약된 이용자가 아닐 경우 출입문이 열리지 않는지 확인
  2. 이용 연장 결제
    • 스크린 골프 이용 중 벽면 패드를 통해 이용 연장 결제 및 크레딧 충전
  3. 매장 각 스크린 룸 현황 표시
    • 각 스크린 룸의 이용 현황이 매장 로비 TV에 실시간으로 정상 표시

wall-pad

room-pad


운영 후 드러난 문제점들

무인화 운영을 시작한 지 몇 주가 지나면서 예상하지 못했던 문제점들이 하나둘씩 드러나기 시작했다.


갑자기 훅 들어오는 다음 예약자

나깜짝놀랬네

수많은 사전 테스트를 거쳐도 결코 예측할 수 없는 단 하나의 변수가 있다면 그것은 바로 ‘사람’이었다.

105번 방 룸 예약 현황)
11:00 ~ 11:55 A 이용자
(5분 간 대기)
12:00 ~ 13:55 B 이용자


A 이용자가 이용을 종료한 후 다음 이용자의 12:00 전까지 남은 5분이라는 자투리 시간 동안 정리를 하거나 조금 더 골프를 이용하는(?) 경우가 있다. 이때 5분 사이에 B 이용자가 벽면 패드에 전화번호를 입력하면 잠겨 있던 문이 열리면서 입장할 수 있어, A 이용자와 서로 난처한 상황이 발생하는 경우가 종종 나타난다고 하였다.

연예인들도 자주 방문할 만큼 프라이빗한 스크린 골프 이용 환경을 중요하게 생각하는 서비스였기에, 이용 고객끼리 서로 마주치는 상황은 단순한 불편을 넘어 서비스의 장점을 훼손할 수 있는 치명적인 문제였다.

현재 이용자와 다음 이용자가 입·퇴실 과정에서 서로 마주치는 상황을 방지하기 위해, 아래와 같은 규칙을 적용해 입장을 제한하기로 했다.

  1. 현재 이용자가 있는 경우, 다음 이용자의 예약 시간이 되기 전까지는 ‘이용 중’ 화면을 노출한다. 다음 이용자 이용 시작 1분 전인 59분부터 ‘입장하기’ 화면으로 전환한다.
  2. 현재 이용자가 없는 경우, ‘입장하기’ 화면을 계속 노출한다. 다음 이용자가 예약 시작 전인 55분에 전화번호 인증을 완료하더라도 즉시 ‘이용 중’ 화면으로 전환한다.


월패드화면 입장 화면과 이용 중 화면

5분이라는 짧은 시간에도 입·퇴실 과정에서 여러 상황이 발생할 수 있었고, 이를 방지하기 위해 입장 규칙을 더 엄격히 적용해 문제를 해결했다.


인수인계 받은 적이 없어요.

아예 몰라요

직영점 무인화 세팅이 완료된 후, 각 매장 슬랙 계정으로 기기 사용 매뉴얼 PDF를 개별 안내했다. 그러나 여전히 현장에서 매니저분들이 기기 조작에 어려움을 겪는 일이 빈번하게 발생했었다. Slack 개인 메세지로 하나하나 설명하다 보면 나도 점점 답답해져 결국 전화로 직접 안내하는 경우도 많았고 사용법을 이해시키는 과정에서 시간과 에너지를 상당히 소모하기도 했다. 더 큰 문제는 어렵게 사용법을 알려드렸던 매니저가 퇴사한 뒤였다. 새로 채용된 매니저가 전임자에게 인수인계를 제대로 받지 못한 채 들어오면서, 나에게 다시 기기 사용법을 문의하는 ‘뫼비우스의 띠’가 끊임없이 반복된 것이다.

개발자가 매니저들에게 계속 직접 기기 사용법을 알려주는 방식은 적절하지 않다고 생각했고 팀장님께도 현재 운영 방식에 문제가 있다는 의견을 전달드렸다.

팀 내 논의 끝에 서비스팀 실장님들이 키오스크와 패드 매뉴얼을 전담 관리하고 각 매장 매니저분들을 직접 교육하는 방향으로 프로세스를 개편했다. 덕분에 개발팀은 기기 고장이나 이슈 대응 등 하드웨어·시스템 차원의 문제에만 집중할 수 있도록 역할을 명확히 재정리했다.


큰 제약이 걸린 환불 과정

환불문의

무인화 운영과 VAN 결제 방식의 한계가 실제 환불 상황을 거치며 서서히 드러나기 시작했다.

오후 시간대에는 매장 근무자 없이 무인으로 운영되다 보니, 고객의 환불 요청이 들어와도 본사에서 원격으로 즉시 처리해 줄 수 있는 방법이 없었다.

결국 매니저가 고객과 별도로 다음 방문 일정을 조율해 현장에서 직접 결제를 취소해야만 환불이 가능했다. 당장 환불을 원했던 고객에게는 큰 불편을 느꼈을 것이다. 그렇다고 환불 건 때문에 매니저가 매장으로 다시 출근하는 것 역시 무인화의 취지에 어긋날 뿐만 아니라, 운영 측면에서도 매우 비효율적이었다.

이전부터 VAN 결제 방식의 환불 처리에 대한 문제점을 인지하고 있었지만, 실제 무인 매장에서 환불 상황을 겪어보니 무인화 서비스에 VAN 결제 방식은 잘 맞지 않는다 는 것을 확실히 체감했다.

수수료가 더 부과되더라도 환불 처리 측면에서는 PG 기반 결제 방식이 무인 환경에 더 적합 해 보였다.

해당 문제점을 팀장님과 PO분께 공유해 보니, 이미 다양한 결제 업체들과 미팅을 진행했던 상황이었다. 우리 서비스는 키오스크와 패드용 앱을 직접 커스터마이징 하여 운영하는 구조였지만, 일반적인 PG 결제 기기 환경에서는 커스텀 앱을 탑재하고 운용하는데 제약이 컸다고 한다. 커스텀 앱을 올리려면 결제 업체의 개발팀에 별도로 개발을 의뢰해야 했고, 이로 인한 추가 비용 리스크도 부담이었다. 자체 개발팀을 갖춘 우리 회사 입장에서는 제어권도 없는 채 외주 개발에 의존하는 방식이 오히려 비효율적이었던 것이다.

아무리 고민해 봐도 실물 카드가 반드시 태깅되어야만 취소가 가능한 VAN 방식의 한계상, 당장 이 환불 문제를 깔끔하게 해결할 뚜렷한 실마리는 보이지 않았다. 우선 고객들 대부분 매주에 여러 번 방문하는 단골 고객이 많은 만큼, 환불이 필요한 상황에서는 매니저가 고객과 일정을 조율해 직접 응대하는 방식이 현재로서는 가장 현실적인 대안이다.

위 환불 문제를 겪으며 무인화 서비스는 개발보다, 사람이 하던 판단과 대응을 시스템으로 대체하는 과정이 더 어렵다는 것을 느끼게 되었다.


마무리하며

마무리

요근래 이렇게 현장의 디테일까지 꼼꼼하게 글로 남겨본 건 정말 오랜만이다.

심지어 이 글 하나를 완성하는 데만 거의 2주가 걸렸다. 그 와중에도 직영점 매장을 찾아가 무인화 시공을 진행했고, 현장 예약 앱의 추가 업데이트 작업도 끊임없이 이어졌다. 그때마다 새로운 이슈와 맥락이 더해져 글을 고쳐 쓰는 일이 반복되었다. 글을 작성하는 순간에도 프로젝트는 여전히 순항 중이었다. 어쩌면 이 글 또한 완성된 후기라기보다, 끊임없이 치열하게 흘러갔던 현장의 과정을 그대로 담아낸 생생한 기록에 가까울지도 모르겠다.

그동안 소프트웨어 중심으로 개발 업무를 해왔지만, 이번 프로젝트에서는 하드웨어와 서비스를 연동하는 것은 물론 직접 현장 시공까지 참여하면서 현장 운영에서 발생하는 다양한 고충과 과정들을 직접 경험할 수 있었다.