주의: 본 글은 일본어 원문을 기계번역한 것입니다. 번역 오류를 발견하시면 알려주시기 바랍니다.
LOPPO Art Supply 시부야점(무인 판매소) 개설에 맞춰 단 2일 만에 구축한 캐시리스 완전 셀프 계산대 시스템에 대해 해설합니다.
서론: LOPPO Art Supply 무인 판매소 프로젝트
LOPPO Art Supply는 타카하시의 취미에서 시작된 셀화 용품 브랜드로, 지금까지 온라인 판매를 중심으로 전개해 왔습니다. 올해 초, LOPPO Art Supply의 이벤트 공간으로 작은 점포를 임대하게 되었는데, 한쪽 구석을 재고 보관소로 사용할 예정이었던 곳에서 "어차피 재고를 두는 거라면 그대로 판매소로 만들 수 없을까"라는 아이디어가 떠올랐습니다.
실제 매장에 대한 요청은 이전부터 있었지만, 매장 운영에 필요한 인적 자원 확보가 어려웠습니다. 그래서 떠올린 것이 "화구 무인 판매소"라는 형태입니다. 채소 무인 판매소 같은 느낌으로, 셀화 용품을 24시간 365일 살 수 있는 꿈같은(미친!) 장소입니다.
화구 무인 판매소 (이미지)
1. 기존 셀프 계산대의 과제와 해결책
상주 직원이 필요 없는 무인 판매소를 실현하려면 완전 셀프 계산대 시스템이 필요합니다. 이번에는 도난 대책으로 캐시리스 결제만 지원하는 것을 전제로 합니다.
처음에는 시판 셀프 계산대 솔루션을 검토했지만, 두 가지 큰 과제가 있었습니다.
- 초기 비용에 더해 월정액 고정비가 높음
- 입금까지의 주기가 김
지속 가능성을 높이려면 고정비를 최소한으로 억제하면서 캐시플로를 양호하게 유지하는 것이 중요합니다. 그래서 주목한 것이 과거 이벤트 출점 시 도입했던 Square 결제 시스템입니다.
Square는 다양한 결제 수단을 지원할 뿐만 아니라, 월정액 고정비가 제로(결제 수수료만)이며, 최단 다음 영업일에 입금되는 신속한 입금 주기가 매력적입니다. 또한 Square API를 이용하면 커스텀 애플리케이션 개발이 가능합니다. 이미 Square Terminal도 보유하고 있어서 이를 활용하면 초기 비용도 억제할 수 있다고 판단했습니다.
다만 개발에는 큰 제약이 있었습니다. 매장 오픈을 위한 상품 제조 업무만으로도 벅찬 상황이라, 셀프 계산대 시스템 개발에는 단 2일밖에 시간을 할애할 수 없었습니다.
2. 시스템 설계: 안전하고 사용하기 쉬운 셀프 계산대
전체 아키텍처
시스템은 크게 다음과 같은 컴포넌트로 구성되어 있습니다.
- Linux 애플리케이션 서버: React 프론트엔드와 Express 백엔드
- 매장 내 시스템: Windows 11 Pro 키오스크 모드 클라이언트 단말기, 주변기기, Square Terminal
- Square 서비스: Square API, 상품 마스터 데이터
- 모니터링 및 운영 시스템: 감시 카메라, 무정전 전원 장치, 네트워크 이중화
이용자에게 보이는 것은 터치 대응 모니터와 바코드 스캐너, 그리고 Square Terminal뿐입니다. 클라이언트는 Windows 11 Pro의 키오스크 모드로 동작하며, 메인 애플리케이션 로직은 매장 외부의 Linux 머신 위에 있습니다.
보안과 네트워크
네트워크는 Tailscale을 사용한 VPN 환경에서 운영되며, 이를 통해 클라이언트 단말기와 Linux 서버 간의 통신이 보호됩니다. 또한 모든 기기의 전원은 무정전 전원 장치에 연결하여 낙뢰 및 정전 대책을 실시하고, 네트워크도 이중화하여 안정적인 가동을 확보하고 있습니다.
Tailscale의 도입으로 원격지에서의 유지보수도 용이해졌습니다. 각 단말기에는 로컬 데이터를 일절 저장하지 않으며, 모든 것을 Square 데이터에서 가져오는 구조입니다.
하드웨어 구성
- 터치 대응 모니터
- USB 바코드 스캐너
- Square Terminal (결제 처리 및 영수증 인쇄)
- 감시 카메라 (실시간 모니터링용)
프론트엔드 구현
결제 수단 선택 화면
프론트엔드는 React로 구현하였으며, 다음과 같은 주요 화면으로 구성되어 있습니다.
- 상품 스캔 화면
- 결제 수단 선택 화면
- 결제 처리 중 화면
- 결제 완료 화면
기계 번역이지만 다국어화도 진행하여, 일본어, 영어, 프랑스어, 스페인어, 번체 중국어, 간체 중국어의 6개 언어를 지원하고 있습니다. 이를 통해 방일 외국인 고객도 안심하고 이용할 수 있게 되었습니다.
// 언어 설정 예시
const translations = {
ja: {
title: 'セルフレジシステム',
scanTitle: '商品スキャン',
// ...생략
},
en: {
title: 'Self-Checkout System',
scanTitle: 'Product Scan',
// ...생략
},
// 기타 언어...
};
또한 바코드 스캐너의 입력을 최우선으로 받도록 설계하여, 사용자가 조작에 헤매지 않는 인터페이스를 지향했습니다.
3. Square API 활용 포인트
Terminal API를 이용한 결제 처리
Square API 중에서도 특히 중요한 것이 Terminal API입니다. 이를 통해 Square Terminal에 결제 처리를 요청할 수 있습니다.
// Terminal 체크아웃 생성
app.post("/api/create-terminal-checkout", async (req, res) => {
try {
const { order, amountMoney, paymentType = "CARD_PRESENT" } = req.body;
const ALLOWED = new Set([
"CARD_PRESENT",
"FELICA_TRANSPORTATION_GROUP",
"FELICA_ID",
"FELICA_QUICPAY",
"QR_CODE"
]);
if (!ALLOWED.has(paymentType)) {
return res.status(400).json({ error: "지원되지 않는 결제 수단이 지정되었습니다" });
}
// 주문을 먼저 생성
const orderId = await createOrder(order);
// Square Terminal Checkout 생성
const checkoutResponse = await squareClient.terminal.checkouts.create({
idempotencyKey: randomUUID(),
checkout: {
amountMoney: {
// 금액은 BigInt 필수
amount: BigInt(amountMoney.amount),
currency: amountMoney.currency,
},
deviceOptions: {
deviceId: SQUARE_DEVICE_ID,
skip_receipt_screen: true,
show_itemized_cart: false,
},
referenceId: orderId,
orderId,
note: "LOPPO 셀프 계산대에서의 결제",
paymentType: paymentType
},
});
res.json(checkoutResponse);
} catch (error) {
handleError("Terminal Checkout 생성 오류", error, res);
}
});
다양한 결제 수단
Square Terminal은 다양한 결제 수단을 지원하므로, 고객은 원하는 방법으로 결제할 수 있습니다.
- 신용카드/체크카드
- 교통 IC 카드 (Suica/PASMO 등)
- iD
- QUICPay
- QR 코드 결제 (PayPay 등)
참고로 UnionPay 카드는 지원하지 않습니다.
결제 상태 확인 폴링
결제 처리는 Square Terminal에서 이루어지므로, 결제 완료나 취소 등의 상태를 폴링으로 확인할 필요가 있습니다.
// 결제 상태를 폴링으로 확인
const checkPaymentStatus = async () => {
try {
const statusResponse = await fetch(`/api/get-checkout-status?checkoutId=${data.checkout.id}`);
const statusData = await statusResponse.json();
if (statusData.status === 'COMPLETED') {
setPaymentStatus(t.paymentCompleted);
// 완료 처리
setTimeout(() => {
setStatus('complete');
setCart([]);
}, 2000);
} else if (statusData.status === 'CANCELED' || statusData.status === 'CANCEL_REQUESTED') {
setPaymentStatus(t.paymentCanceled);
setTimeout(() => {
setStatus('ready');
}, 3000);
} else {
// 아직 완료되지 않은 경우 재확인
setPaymentStatus(t.processing);
setTimeout(checkPaymentStatus, 2000);
}
} catch (error) {
console.error(t.statusCheckFailed, error);
setPaymentStatus(t.statusCheckFailed);
setTimeout(() => {
setStatus('ready');
}, 3000);
}
};
상품 마스터 데이터 관리
상품 정보는 모두 Square 관리 화면에서 등록하고 API를 통해 가져옵니다. 이를 통해 상품 추가나 가격 변경 등의 운영 작업을 간소화하고 있습니다.
app.get("/api/catalog-items", async (_req, res) => {
try {
const TYPES = "ITEM,ITEM_VARIATION,CATEGORY,IMAGE"; // 필요한 타입을 모두 나열
//------------------------------------------------------------------
// 1. 전부 불러오기
//------------------------------------------------------------------
const objects = [];
for await (const obj of await squareClient.catalog.list({ types: TYPES }))
objects.push(obj);
//------------------------------------------------------------------
// 2. CATEGORY / IMAGE / VARIATION을 먼저 맵으로 변환
//------------------------------------------------------------------
const imageMap = {};
const categoryMap = {};
const variationMap = {};
// ...생략 (맵 생성 처리)
//------------------------------------------------------------------
// 3. ITEM을 전개하고 위에서 만든 맵으로 정보를 삽입
//------------------------------------------------------------------
const filtered = objects
.filter((o) => o.type === "ITEM")
.map((item) => {
// ...생략 (데이터 변환 처리)
})
// -- 여기서 요건 필터 적용 --
.filter(
(item) =>
!item.isArchived &&
item.categoryNames.includes("六方画材")
);
res.json(filtered);
} catch (error) {
handleError("카탈로그 아이템 가져오기 오류", error, res);
}
});
4. LLM 활용을 통한 초고속 개발
이 프로젝트의 가장 큰 특징은 단 2일이라는 짧은 기간에 개발을 완료한 것입니다. 이를 가능하게 한 것이 LLM(대규모 언어 모델)의 활용이었습니다.
개발 시간 내역
- 기본 시스템 개발: 약 2시간
- 개선 및 UI 조정: 약 4시간
- 테스트 및 배포: 나머지 시간
Claude 3.7 Sonnet 활용 방법
개발에서는 주로 Claude 3.7 Sonnet을 활용하여 구현의 효율화를 도모했습니다. 애플리케이션 로직뿐만 아니라 UI 설계도 능숙하게 처리하고, 셋업 문서까지 준비해 주는 등 매우 유용했습니다. 특히 다국어 대응 코드와 Square API 연동 부분에서 LLM의 제안이 매우 도움이 되었습니다.
ChatGPT 4o나 ChatGPT o3 등도 조합하여 사용했지만, 웹 애플리케이션에 대한 이해도에서는 3.7 Sonnet에 전혀 미치지 못했습니다.
LLM 활용의 구체적인 사례
LLM을 개발에 활용할 때의 당연한 주의점이지만, 생성된 코드를 그대로 사용하기는 어렵고 반드시 이해한 후에 필요한 수정을 가하는 것이 중요합니다. 예를 들어 Square Terminal API와의 연동 부분에서는 다음과 같은 수정이 필요했습니다.
- 결제 수단 추가: LLM이 생성한 코드는 신용카드 결제만 지원했기 때문에, 결제 수단을 선택하는 화면을 추가할 필요가 있었습니다
- 오류 처리: Terminal에서의 결제 취소 이벤트 처리 방식이 잘못되어 있어, API 문서를 바탕으로 올바른 처리로 수정했습니다
- 보안: 일부에서 취약한 프로토콜로 앱 간 통신을 하고 있어, 프라이빗 VPN을 구축하여 통신 경로의 안전성을 확보했습니다
LLM은 기본적인 코드 구조를 제공해 주었지만, 실제 운영에 견딜 수 있도록 하기 위한 조정은 수작업으로 해야 했습니다.
5. 국제화와 사용 편의성
다국어 대응 구현
방일 외국인도 이용할 수 있도록 시스템은 일본어, 영어, 프랑스어, 스페인어, 번체 중국어, 간체 중국어의 6개 언어를 지원합니다. React 컴포넌트 내에서 언어 설정을 관리하고, 화면상의 모든 텍스트를 번역 객체에서 가져오는 방식을 채택했습니다.
// 언어 선택 상태 관리
const [language, setLanguage] = useState('ja'); // 기본 언어를 일본어로 설정
// 언어 설정 가져오기
const t = translations[language];
// 사용 예시
<h1 className="text-4xl font-bold">{t.title}</h1>
<p className="text-lg text-gray-700 mb-6">
{t.scanDescription}
</p>
사용 편의성 개선
슈퍼마켓이나 편의점의 셀프 계산대와 동일한 사용 편의성을 목표로 다음과 같은 궁리를 했습니다.
- 바코드 스캔 우선: 페이지의 어느 위치에서든 키보드 입력을 받으며, 바코드 스캐너 입력을 항상 우선
- 큰 버튼: 터치 조작이 쉬운 큰 버튼 크기
- 명확한 피드백: 조작 결과를 알기 쉬운 메시지 표시
이러한 궁리를 통해, 이용자가 헤매지 않고 조작할 수 있는 인터페이스를 실현할 수 있었다고 생각합니다.
6. 운영 측면의 궁리
실시간 모니터링과 장애 대응
매장 내에 네트워크 카메라를 설치하여 판매소의 상황을 실시간으로 확인할 수 있도록 하고 있습니다. 문제가 발생할 경우, 고객이 매장에 게시된 전화번호로 연락하여 대응할 수 있습니다.
운영상 이상이 감지되면 30분~2시간 이내에 현장에 도착하여 대응할 수 있는 체제를 갖추고 있습니다. 또한 만일 결제 단말기가 작동하지 않을 경우, 사후에 결제 링크를 전달하여 결제를 완료하는 대체 수단도 마련되어 있습니다.
안정적인 가동을 위해 무정전 전원 장치에 의한 전원 백업, 네트워크 이중화, 무조작 시 정기적인 리부팅도 설정되어 있습니다.
한 번 클라이언트의 전원 케이블이 느슨하게 꽂혀 있어 빠지는 트러블이 있었지만, 그 이후로 시스템은 매우 안정적으로 운영되고 있습니다.
7. 실제 성과와 효과
판매 기회의 확대
무인 판매소 개설로 도심 역 근처의 24시간 영업이라는 귀중한 판매 기회를 확보했습니다. 실제 매장 개설 요청에 부응하면서도 인적 자원의 제약을 극복한 것이 가장 큰 성과입니다.
결제 방법 경향
도입한 모든 결제 방법이 고르게 이용되고 있지만, 특히 인기 있는 순서는 다음과 같습니다.
- 교통 IC 카드
- QR 코드 결제 (PayPay 등)
- 신용카드 (터치 결제)
8. 향후 전망과 확장 계획
온라인 쇼핑으로의 확장
현재 LOPPO Art Supply 온라인 스토어는 BASE로 구축되어 있지만, Square API를 이용한 것으로 변경할 예정입니다. 이를 통해 결제 수수료를 절약할 수 있을 뿐만 아니라, 상품 구매 흐름을 개선하고 매장과 온라인 판매의 재고 및 매출 관리를 일원화할 수 있는 장점이 있습니다.
나아가 온라인으로 구매하고 매장에서 수령하는 수단도 제공하고 싶습니다.
마무리: 물감을 만들 수 있다면 계산대 시스템도 만들 수 있다
다양한 셀화 관련 화구를 수제로 복각해 온 LOPPO이지만, 이번에는 계산대 시스템을 수제로 만들었습니다.
소프트웨어 애플리케이션은 이처럼 기존 API의 활용이나 LLM의 지원으로 비교적 쉽게 구축할 수 있는 경우가 있습니다. 그렇지만 계산대 시스템이 이렇게 쉽게 만들어질 줄은 생각지도 못했기에, 매우 좋은 공부가 되었습니다.
주력한 점
- 기존 서비스 활용: Square API 등 기존 플랫폼을 최대한 활용
- LLM 등의 지원 도구 활용: 개발 효율을 높이는 도구의 적극적인 도입
- 필요 최소한의 범위에 집중: 필요한 기능에 집중하여 심플하게 구현
셀프 계산대 시스템 구축에 관심이 있는 분이나, Square API 활용을 검토하고 있는 분의 참고가 되면 기쁘겠습니다.
