2025.12 — 2026.09회사 프로젝트

VGOLF F&B

주방 프린터부터 디지털 사이니지까지, 매장을 돌리는 F&B 시스템

맡은 일
Frontend Developer · F&B 제품 전반 단독 개발
기간
2025.12 — 2026.09
소속
플럭시티클라우드팀

골프장 F&B 플랫폼의 주문·관리자·주방 영수증 출력·매장 사이니지 서브시스템을 초기 보일러플레이트 위에서 설계·구현했습니다. 앱 로직 외에도 매장 하드웨어 연동, 오프라인·실시간 견고성, 상태별 조건 렌더링처럼 실제 현장에서 문제가 생기는 지점을 하나씩 해결했습니다.

초기 인프라(axios·recoil·SSE 훅·login shell)와 일부 모듈은 선행 팀원이 만들어 둔 기반이고, 2025년 12월 합류 이후 그 위에서 F&B 제품 전반을 맡아 개발했습니다.

  1. i.Problem

    주문 앱은 클라우드에 정적으로 배포되고, 영수증을 뽑을 프린터는 매장 사설망 안에 있었습니다. 둘은 서로 닿지 못합니다. 사이니지 미니 PC는 망이 끊긴 채로 부팅되는 일이 잦았습니다.

  2. ii.Action

    로컬 프린트 에이전트를 두고 raw TCP 로 잇는 경로를 설계했고, ESC/POS 바이트열을 직접 만들었습니다. 80mm 영수증 폭에 맞추느라 한글을 폭 2로 계산하는 고정폭 레이아웃까지 짰습니다. 사이니지는 서비스워커로 문서를 미리 받아 두어 네트워크 없이 부팅해도 화면이 뜨게 했습니다.

  3. iii.Result

    10개월 520커밋으로 주문·관리자·주방 출력·사이니지까지 매장이 도는 데 필요한 것을 붙였습니다. 사이니지는 SSE 로, 주문 화면은 폴링에 야간모드와 연속오류 차단기를 붙여 24시간 무인으로 돕니다.

Pl. 1주문 화면 — 카테고리 탭으로 나눈 메뉴판과 테이블별 주문서. NEW·BEST·시그니처 같은 태그와 품절 여부를 상태값으로 받아 조건부로 그립니다.

핵심

  1. 01

    10개월 · 520커밋

    Next.js App Router 정적 export, TypeScript strict

  2. 02

    ESC/POS 열전사 프린팅 직접 구현

    80mm 주방 영수증 바이트열 생성 + EUC-KR 인코딩, CJK를 폭 2로 계산하는 고정폭 레이아웃 엔진

  3. 03

    클라우드→LAN 브릿지 설계

    정적 배포 앱과 매장 사설망 프린터가 서로 닿지 못하는 구조를, 로컬 프린트 에이전트를 경유해 raw TCP로 해결

  4. 04

    오프라인 부팅 사이니지(DID)

    망분리 미니 PC가 네트워크 없이 부팅해도 화면이 뜨도록 서비스워커 문서 프리캐시 설계

나머지 3개 더 보기
  1. 05

    WYSIWYG 사이니지 편집기

    편집 화면과 실제 표출이 동일하게 렌더되도록 %오프셋 저장 + container-query 폰트 자동 스케일

  2. 06

    이중 실시간 전략

    사이니지는 SSE 구독, 주문 화면은 폴링 + 야간모드 상태머신 + 연속에러 서킷브레이커로 24시간 무인 운영

  3. 07

    다축 조건 렌더링 설계

    주문 상태·ERP·권한·화면 분할·네트워크·운영 시간대 6개 축을 독립 분기해 조합 폭발을 관리

화면

좌우로 넘겨 보실 수 있습니다

Pl. 2of14

Pl. 2진행중 오더 보드 — 상단에 홀 진행 상황과 캐디 위치가 흐르고, 주문은 테이블 카드로 쌓입니다. 주방·홀에서 종일 띄워 두는 화면이라 새 주문과 상태 변화가 SSE로 바로 꽂히고 새로고침이 필요 없습니다.
Pl. 3정산이 끝난 주문을 모아 보는 화면. 한 팀이 여러 번 나눠 주문한 것을 카드 하나로 묶고, 묶음별 소계와 주문 합계를 같이 보여줍니다.
Pl. 4캐디와 라운지가 주고받는 메시지. "경기과에 무전이 안 되서 여기로 보냅니다" 같은 실제 사용 기록이 남아 있습니다. 무전이 닿지 않는 코스에서 주문 변경·취소가 이 경로로 들어옵니다.
Pl. 5티오프 예약을 불러와 내장객을 테이블에 붙입니다. 부·코스로 걸러 찾고 단체 예약도 함께 처리합니다. 예약 데이터는 골프장 기간계에서 넘어옵니다.
Pl. 6메뉴 관리 — 124개 메뉴를 분류·태그·판매 상태·채널로 관리합니다. 상품 정보는 ERP에서 받아 갱신하고, 매장에서는 판매/중지만 바로 바꿀 수 있게 했습니다.
Pl. 7매출 조회 — 날짜와 캐디로 걸러 공급가·VAT·판매가를 전표 단위까지 봅니다. 정산 담당자가 그대로 쓰도록 엑셀 내보내기를 붙였습니다.
Pl. 8만든 사이니지가 실제로 걸려 있는 모습. 편집기에서 지정한 배경·제목·영문 표기가 그대로 매장 벽면 디스플레이에 표출됩니다.
Pl. 9DID 관리 — 1~4분할을 고르고 칸마다 일반/PDF를 지정합니다. 기기별 연결 탭, 배경 이미지, 글자 색, 정렬, 텍스트 3종의 크기를 조절하면 오른쪽 미리보기가 실제 표출과 같은 방식으로 다시 그려집니다.
Pl. 10편집기에서 만든 화면이 실제로 표출된 상태. 해상도가 달라도 위치가 유지되도록 텍스트를 중앙 기준 %오프셋으로 저장하고, 글자 크기는 container-query로 화면 폭을 따라갑니다.
Pl. 11사이니지 기기는 코드 한 번으로 연결합니다. 설치하러 간 사람이 계정도 주소도 몰라도 되도록, 관리자가 발급한 코드만 넣으면 그 매장 화면이 뜨게 했습니다.
Pl. 12설치 담당자를 위한 안내를 화면 안에 넣었습니다. 기기를 껐다 켜도 같은 화면이 다시 뜨게 하는 절차와, 반대로 빠져나오는 방법(Alt+F4·모서리 5번 탭)까지 적어 두었습니다. 현장에 개발자가 다시 갈 일을 줄이려고 만든 화면입니다.
Pl. 13배치도 관리 — 좌측 팔레트에서 4·6·8·10·12인 테이블을 끌어다 놓고 캔버스에서 이동·회전·페이지 추가를 합니다. 겹치지 않도록 충돌을 검사하고, 우측 번호 목록은 ERP와 연동됩니다.
Pl. 14같은 배치가 매장 운영 화면으로 그대로 이어집니다. 편집기에서 옮긴 자리가 주문·경과시간이 뜨는 실제 테이블 화면이 됩니다.

실제 운영 화면입니다. 손님·직원의 이름과 CCTV에 잡힌 이용객은 알아볼 수 없게 처리했고, 내부 접속 주소는 잘라냈습니다.

자세히

궁금한 항목을 펼치면 그때 무엇이 문제였고 어떻게 판단했는지 나옵니다.

80mm 열전사 프린터용 ESC/POS 바이트열을 직접 생성하고, 프린터가 이해하는 EUC-KR로 인코딩

CJK 문자를 폭 2로 계산하는 고정폭 레이아웃 엔진으로 한/영 혼용 표 정렬을 맞춤

빌지번호 클라이언트 채번 + IndexedDB 일자 단위 중복방지

재시작해도 같은 영수증이 다시 출력되지 않음

구조가 가장 큰 난점

앱은 클라우드 정적배포인데 프린터는 매장 사설망(192.168.x.x)이라 서로 닿지 못함

브라우저가 로컬 프린트 에이전트(127.0.0.1:9110)로 명령을 보내고, 에이전트가 raw TCP:9100으로 프린터에 쏘는 브릿지로 해결

망분리 미니 PC가 네트워크 없이 부팅해도 화면이 뜨도록 서비스워커 캐싱을 설계

문서 프리캐시를 빌드ID revision과 함께 주입

CDN 폰트가 오프라인에서 누락되는 문제를 자체 호스팅으로 해결

PDF는 브라우저 기본 뷰어를 피하고 canvas 직접 렌더 + 로컬 워커로 처리해 오프라인에서도 표출

배경이 지정되지 않은 경우 템플릿 fallback으로 빈 화면이 뜨지 않게 방어

배운 것

끝난 뒤에 남은 것들을 적었습니다.

브라우저가 할 줄 아는 통신이 HTTP뿐이라는 걸 이 프로젝트에서 처음 체감했습니다. 프린터는 요청과 응답을 주고받는 상대가 아니라, 정해진 바이트열을 정해진 순서로 밀어 넣으면 그대로 찍는 장치였습니다. 요청을 보내고 응답을 기다리는 사고방식에서 벗어나 '지금 이 바이트가 어디까지 갔는가'를 따라가는 법을 배웠고, 포트가 열려 있는지·어느 구간에서 끊기는지를 보게 되면서 HTTP 바깥의 통신이 겁나지 않게 됐습니다. 이 프로젝트에서 가장 많이 자란 부분이 여기입니다.

폐쇄망이라 사무실에서는 재현이 안 됐습니다. 현장에 가서야 알 수 있는 것을, 가기 전에 미리 대비해 두어야 했습니다. 무엇이 막힐지 모르는 상태에서 대비책을 만드는 일이라 준비 자체가 쉽지 않았습니다

매장 망이 어떻게 생겼는지, 프린터가 어떤 규약을 쓰는지, 방화벽이 무엇을 막는지를 가능한 경우로 늘어놓고 각각에 맞는 수를 준비해 들어갔습니다. 힘든 구간이었지만 가장 많이 배운 구간이기도 했습니다.

프론트만 알았다면 현장에서 아무것도 못 했을 거라고 생각합니다. 화면이 안 뜰 때 그게 화면 문제인지, 에이전트가 안 떠 있는 건지, 포트가 막힌 건지, 서버가 안 주는 건지를 그 자리에서 갈라낼 수 있었던 건 백엔드와 네트워크를 함께 봐 뒀기 때문이었습니다. 덕분에 현장에서 기다리지 않고 바로 손을 댈 수 있었고, 올라오는 오류에 그때그때 대처할 수 있었습니다. 범위를 넓게 둔 게 언제 쓸모가 되는지를 알게 된 경험입니다.

로컬 에이전트도 배치 파일도 이때 처음 만들어 봤습니다. 혼자 알아낸 게 아니라 팀장님과 선배님들께 물어 가며 만들었고, 시니어분들이 짚어 주신 것이 결정적이었습니다. 모르는 영역에 들어갈 때 혼자 오래 붙잡고 있는 것보다 먼저 묻고 빨리 배우는 쪽이 훨씬 멀리 간다는 걸 이 프로젝트에서 배웠습니다. 제가 자란 자리에 그분들의 몫이 분명히 있습니다.

쓴 기술

Next.jsReactTypeScriptReact QueryRecoilPWAWorkboxSSEdnd kitpdf.jsExcelJSESC/POSIndexedDBSCSS