Company Project@ 플럭시티2025.12 — 2026.09

VGOLF F&B

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

Role: Frontend Developer · F&B 제품 전반 단독 개발

Overview

골프장 F&B 플랫폼의 주문·관리자·주방 영수증 출력·매장 사이니지 서브시스템을 초기 보일러플레이트 위에서 설계·구현했습니다. 앱 로직을 넘어 매장 하드웨어 연동, 오프라인·실시간 견고성, 상태별 조건 렌더링처럼 실제 배포 현장에서 깨지는 지점들을 붙잡아 해결한 것이 핵심입니다. 초기 인프라(axios·recoil·SSE 훅·login shell)와 일부 모듈은 선행 팀원 기반이며, 2025년 12월 합류 이후 F&B 제품 전반을 맡아 개발했습니다.

이 프로젝트의 핵심

10개월 · 520커밋

Next.js App Router 정적 export, TypeScript strict

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

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

클라우드→LAN 브릿지 설계

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

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

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

나머지 3개 더 보기

WYSIWYG 사이니지 편집기

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

이중 실시간 전략

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

다축 조건 렌더링 설계

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

Screens

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

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

자세히

하드웨어 연동 — ESC/POS 주방 영수증5

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

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

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

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

구조가 가장 큰 난점

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

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

디지털 사이니지(DID) — 오프라인 우선4

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

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

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

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

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

WYSIWYG 사이니지 편집기4

1~4분할 레이아웃을 grid-template-areas로 구성해 분할 수가 바뀌어도 같은 렌더 경로를 씀

텍스트 세트 드래그 위치를 중앙 기준 % 오프셋으로 저장

편집 해상도와 표출 해상도가 달라도 위치가 유지됨

container-query 기반 폰트 자동 스케일로 화면 크기에 따라 글자가 함께 커지고 작아짐

정렬·폰트·크기·색상·PDF 오버레이·전체화면 미리보기까지 편집기와 실제 표출이 동일하게 렌더

실시간 · 무인 운영 견고성5

사이니지는 SSE 채널을 구독해 콘텐츠 변경만 수신

불필요한 폴링 트래픽 제거

주문 화면은 React Query 폴링에 야간모드 상태머신(off / idle 30s / active 5s)을 얹어 영업 시간대별로 주기를 조정

연속에러 서킷브레이커 가드로 백엔드 장애 시 폴링을 자동 중단, 24시간 무인 환경에서 요청이 쌓이지 않게 함

visibilitychange 연동으로 화면이 보이지 않을 때의 동작을 분리

캐시 무효화는 ERP 연동 여부를 아는(ERP-aware) 중앙집중 방식으로 관리

실시간 골퍼 코스진행 관제 바3

각 홀의 실제 너비·간격 비율로 트랙을 구성해 코스 형태가 바 위에 그대로 반영되게 함

골퍼를 progress·gap 기반으로 배치

홀 내부 진행률과 홀 사이 이동 구간을 구분해 표시

정방향·역방향 진행 순서까지 계산해 한눈에 코스 상황을 읽을 수 있게 구성

매장 배치도 캔버스 에디터3

pan / zoom과 페이지 간 드래그 이동(포인터 히트테스트)을 지원하는 캔버스 편집기

회전 시 w/h 스왑까지 반영한 AABB 충돌검출로 테이블이 겹치지 않게 배치

근사정사각 멀티페이지 그리드 구성, 좌표는 "x,y,r" · "1920,1080,page" 형태로 인코딩해 저장·복원

조건별 렌더링 설계6

하나의 화면이 여러 축의 상태를 동시에 만족해야 해, 상태를 형태(칩·컬러·레이아웃)로 인코딩하고 각 축을 독립 분기해 조합 폭발을 관리

주문 상태

접수(R) · 재주문(RN) · 수락(P) · 취소(N) · 완료(Y)에 따라 카드/즉석조리/알림 분기

ERP 연동

연동 시 공급가·VAT·전표 컬럼을 조건부 주입하고 orderErp가 있는 행만 필터

화면 분할

1~4분할, 일반 ↔ PDF, 텍스트 오버레이 조합

네트워크

online(최신) / offline(캐시본) / connecting(백오프 재시도)

권한

canPrintBill · canManageGoods · canCancelOrder 기반 RBAC와 역할별 랜딩 라우팅

정산 · ERP3

ExcelJS로 서식·병합·테두리를 적용한 매출 시트 export

ERP 연동 시 공급가·VAT·전표 컬럼을 조건부로 주입하고, orderErp가 있는 행만 필터링

그룹 행 forward-fill로 회계 대사에 바로 쓸 수 있는 형태로 정리

Tech Stack

Next.jsReactTypeScriptReact QueryRecoilPWAWorkboxSSEdnd kitpdf.jsExcelJSESC/POSIndexedDBSCSS