주 콘텐츠로 건너뛰기
버전: 1.x

동기화 엔진의 작동 방식

v1.10.0 신규 기능

이 페이지는 WCPOS v1.10.0에서 도입된 동기화 엔진을 설명합니다. 이전 버전은 화면별 복제라는 다른 모델을 사용합니다. 이 페이지 끝의 v1.10.0에서 달라진 점을 참조하세요.

WCPOS는 로컬 우선(local-first) 방식입니다. 모든 화면은 기기에 있는 데이터베이스를 읽고 쓰며, 동기화 엔진이 백그라운드에서 그 데이터베이스와 WooCommerce 스토어를 수렴시킵니다. 이 페이지에서는 엔진이 무엇을 언제 가져올지 결정하는 방식과 판매 내역이 서버로 돌아가는 방식을 설명합니다. POS가 호스팅에 어떤 부하를 주는지 이해하려는 개발자, 통합 담당자, 스토어 소유자에게 유용한 수준의 세부 정보를 담고 있습니다.

스토어와 계산원마다 하나의 엔진

POS는 각 기기에서 사이트 + 스토어 + 계산원 조합마다 하나의 동기화 엔진을 실행합니다. 각 엔진은 자체 로컬 데이터베이스를 소유하므로, 스토어나 계산원을 전환하면 공유된 레코드 더미를 필터링하는 것이 아니라 데이터 평면 전체가 바뀝니다. 계산원별 격리는 의도된 설계입니다. 같은 기기의 두 계산원은 로컬 데이터를 절대 공유하지 않습니다.

업그레이드 시 로컬 데이터베이스는 제자리에서 마이그레이션되지 않습니다. 앱은 새 데이터베이스를 시작하고 항상 정본 사본을 보유한 서버에서 다시 다운로드합니다(v1.9에서 업그레이드 참조).

Pro 다중 스토어 설치에서는 모든 동기화 요청이 자신의 스토어를 식별하므로, 계산대에서 수정한 가격은 웹 스토어의 가격이 아니라 해당 스토어의 가격을 업데이트합니다.

백그라운드에서 실행되는 작업

엔진이 일정에 따라 수행하는 모든 작업은 레인(lane), 즉 이름이 지정되고 범위가 제한된 백그라운드 작업 단위입니다. 레인은 세 그룹으로 나뉩니다:

그룹레인기본 주기
풀(Pull)변경 확인(변경 신호)10초 ~ 5분, 동기화 프리셋에 따라 설정
최근 주문, 제품 카탈로그 시드, 참조 데이터 시드약 5분
고객 트리클(유휴 시간 전용)약 5분
푸시(Push)쓰기 드레인 — 대기 중인 로컬 변경 사항 전송약 10초
유지 관리무결성 및 삭제 감사, 서버 총계 갱신수 분 ~ 약 17분

모든 레인에는 두 가지 속성이 적용됩니다:

  • 각 레인은 실행당 요청 상한을 선언합니다. 어떤 레인도 한 번의 실행에서 무제한으로 요청을 확산할 수 없으며, 큰 작업은 재개 가능한 커서와 함께 제한된 배치로 처리됩니다. 이는 핵심 설계 불변식이며 동기화 성능에서 자세히 다룹니다.
  • 유지 관리는 계산원에게 양보합니다. 감사와 백그라운드 사전 준비 작업은 계산대가 판매를 시작할 수 있게 만드는 대화형 작업보다 항상 나중에 실행되며, 서버에 부하 징후가 보이면 가장 먼저 일시 중지됩니다.

위 주기는 기본값입니다. 확인 간격과 요청당 레코드 수는 POS의 스토어 상태 → 성능에서 기기별로 판매자가 조정할 수 있습니다. 스토어 상태를 참조하세요.

POS가 변경 사항을 파악하는 방법

엔진은 변경 여부를 알아내기 위해 데이터를 다시 다운로드하지 않습니다. 서버는 변경 로그를 유지하고, POS는 “내 마지막 위치 이후 변경된 것이 있는가?”라는 하나의 질문에 답하는 가벼운 변경 확인을 폴링합니다.

  • 한 번의 확인으로 여덟 개 컬렉션을 처리합니다. 제품, 변형, 세금 비율, 고객, 쿠폰, 카테고리, 브랜드, 태그가 대상입니다. 주문은 의도적으로 변경 확인에 포함되지 않으며, 주문의 최신성은 별도의 최근 주문 레인이 담당합니다. 그래서 제품 업데이트와 주문 업데이트가 서로 다른 리듬으로 도착할 수 있습니다.
  • 유휴 상태의 계산대는 비용이 거의 들지 않습니다. 엔진은 조건부 요청을 보내므로, 변경된 것이 없으면 서버는 본문 없는 304 Not Modified 하나로 응답합니다. 조용한 스토어는 확인당 아주 작은 응답 하나로 안정화됩니다.
  • 확인 간격에는 ±20%의 지터가 적용되어, 한 사이트의 여러 계산대가 동시에 서버로 몰리지 않고 서로 시차를 두게 됩니다.
  • 유휴 감쇠: 10분 동안 아무런 조작이 없으면 확인 간격이 늘어납니다(최대 60초 하한까지). 터치, 키 입력, 인식된 바코드 스캔 등 실제 활동이 있으면 즉시 원래 주기로 복귀하고 곧바로 따라잡기 확인을 실행합니다. 감쇠는 간격을 늘리기만 하며, 설정된 값보다 빠르게 폴링하는 일은 없습니다.
  • 며칠 동안 닫혀 있던 계산대는 이력을 재생하는 대신 기준선을 다시 잡습니다. 변경 로그가 계산대의 마지막 위치보다 너무 멀리 진행되었다면 모든 행을 재생하는 데 수백 건의 요청이 듭니다. 대신 엔진은 커서를 최신 위치로 건너뛰고, 기기가 이미 보유한 데이터의 현재 서버 상태를 다시 확인합니다. 따라서 비용은 계산대가 얼마나 오래 쉬었는지가 아니라 로컬 사본의 크기에 비례합니다.

화면이 데이터를 받는 방법

v1.10.0에서 화면은 자체적으로 동기화를 실행하지 않습니다. 화면은 자신이 무엇을 표시하는지(검색어, 필터, 정렬, 페이지)를 선언하고, 요청이 필요한지 여부는 엔진이 판단합니다. 각 선언은 다음 세 가지 중 하나로 처리됩니다:

  • 가져옴(Fetched) — 엔진이 이를 충족하기 위해 네트워크 작업을 수행했습니다.
  • 로컬에서 제공됨(Served locally) — 기기가 이미 답을 가지고 있었거나, 최근에 동일한 요청으로 이미 가져왔습니다.
  • 대체됨(Superseded) — 화면이 이동해(스크롤하거나 필터를 변경해) 더 새로운 선언이 이를 대체했습니다. 이는 오류가 아니라 일상적인 동작입니다.

로컬에서만 답할 수 있는 필터는 서버로 전달되지 않습니다. 또한 선언이 성공했다고 해서 컬렉션이 완전히 다운로드되었다는 뜻은 아닙니다. 완전성은 별도로 추적됩니다(다음 섹션 참조).

모든 것이 미리 다운로드되지는 않으며, 부팅 시점에 기기에 무엇이 있는지는 컬렉션마다 다릅니다:

  1. 시드됨 — 제품은 범위가 제한된 카탈로그 시드를 통해 채워지고, 세금 비율은 부팅 시 가져옵니다(세금 비율 없이는 POS가 장바구니 계산을 할 수 없습니다).
  2. 요청 시 + 유휴 트리클 — 고객은 미리 시드되지 않습니다. 고객 트리클은 유휴 간격마다 소량의 배치를 다운로드하며, 계산원이 활동 중일 때는 완전히 건너뜁니다. 신규 고객이나 변경된 고객은 변경 확인을 통해 도착합니다.
  3. 처음 열 때 가져옴 — 카테고리, 태그, 브랜드, 쿠폰은 계산원이 처음 열 때 가져옵니다. 아무도 열지 않는 컬렉션은 요청을 단 한 건도 생성하지 않습니다.

변형 선택기는 추가로 열 때마다 가격과 재고를 한 번 갱신하므로, 이미 기기에 있는 변형은 즉시 표시되면서도 며칠 지난 재고를 보여주는 일은 없습니다.

“모두 다운로드되었나요?” — 정직한 커버리지

“제품 5,000개 중 1,240개” 같은 표시에는 분모로 쓸 서버 측 총계가 필요합니다. 엔진은 컬렉션마다 총계를 유지하며(약 15분마다 갱신되고, 실제 동기화 응답이 이미 총계를 포함하고 있어 대개 별도 요청이 필요 없으므로 비용이 들지 않습니다) 다음의 엄격한 정직성 원칙을 따릅니다:

  • 서버 총계가 오래되었거나 없으면 *확인 중…*으로 표시하며, 로컬 개수로 몰래 대체하지 않습니다. 로컬 값을 분모로 쓰면 항상 100%로 표시되어, 이 숫자가 드러내야 할 격차를 정확히 가려 버립니다.
  • 엔진이 완전성을 보장할 수 없으면 판정은 알 수 없음이 되고, UI는 해당 수치를 로컬 개수로 표시합니다.
  • 주문 커버리지 막대는 전체 서버 주문 이력을 기준으로 측정하지만, 계산대는 의도적으로 열린 주문과 최근 주문만 보관합니다. 따라서 정상적인 계산대에서도 주문은 설계상 부분적으로 표시됩니다.

이 수치들은 스토어 상태 → 데이터베이스에 표시되며, 첫 제품이 기기에 도착하는 즉시 전환되는 판매 준비 완료 마일스톤도 함께 표시됩니다. 오프라인이어도 이를 막지 않습니다. 오프라인 판매가 바로 이 기능의 목적이기 때문입니다. 스토어 상태를 참조하세요.

변경 사항이 스토어로 전달되는 방법

판매, 제품 수정, 고객 업데이트 등 모든 로컬 쓰기는 먼저 기기의 영구 발신 대기열에 들어가고, 드레인 레인이 몇 초마다 대기열을 WooCommerce로 보냅니다. 이것이 오프라인 판매를 안전하게 만드는 요소입니다. 연결이 없는 상태에서 등록한 판매는 대기열에 머물다가 연결이 복구되면 전송됩니다.

중요한 세부 사항은 다음과 같습니다:

  • 장바구니 쓰기는 주문별로 직렬화됩니다. 빠르게 연속되는 스캐너 입력은 하나씩 차례로 적용되며, 같은 항목을 다시 추가하면 두 번째 줄로 대기열에 쌓이지 않고 기존 줄에 병합됩니다.
  • 주문 확인 응답은 서버의 사본을 채택합니다. WooCommerce는 주문 생성 시 주문 항목에 ID를 부여하며, 엔진은 확인된 주문을 채택하여 이후 업데이트가 중복 항목을 추가하지 않고 해당 항목과 일치하도록 합니다. 채택은 보수적으로 이루어집니다. 서버가 아직 보지 못한 로컬 편집을 절대 덮어쓰지 않으며, 주문에만 적용됩니다.
  • 서버가 영구적으로 거부한 쓰기는 조용히 재시도되지 않습니다. 해당 쓰기는 서버가 제시한 사유와 함께 보류되고 스토어 상태 → 데이터베이스에 *“서버에 전달되지 않은 변경 사항”*으로 표시되며, 두 가지 명시적 조치를 제공합니다. 다시 보내기(현재 상태의 레코드에서 요청을 다시 구성하므로 이후의 수정 사항이 반영됩니다)와 삭제입니다. 자동 재시도 루프는 없으며, 복구는 언제나 눈에 보이는 의도적인 동작입니다. 판매자 관점의 안내는 스토어 상태를 참조하세요.

여러 브라우저 탭

같은 스토어의 웹 POS를 여러 탭에서 실행하는 것은 지원됩니다. 모든 탭에서 판매를 등록할 수 있으며(쓰기는 공유 대기열에 추가됩니다), 전송은 선출된 한 탭이 담당합니다. 이는 스토어 + 계산원 범위마다 적용됩니다. 그 탭이 닫히면 브라우저가 다음 탭을 자동으로 승격시킵니다. 서로 다른 계산원으로 로그인한 두 탭은 별개의 범위이며 각자 자신의 대기열을 관리합니다.

로컬 저장소

웹 앱과 데스크톱 앱은 OPFS(Origin Private File System) 워커를 통해 데이터를 저장하고, iOS 및 Android 앱은 파일 시스템 엔진을 통해 동일한 디스크 형식을 사용합니다. 네 플랫폼 모두 하나의 저장 형식과 동일한 손상 복구 도구를 공유합니다. (이전 버전은 웹에서 IndexedDB를, 네이티브에서 SQLite를 사용했습니다.)

쿼리는 선택자, 정렬, 페이지 모두 데이터베이스 계층 내부에서 실행되므로 화면에 보이는 페이지의 행만 앱으로 전달됩니다. 합성된 주문 10,000건 픽스처에서 이 푸시다운은 라이브 구독의 쓰기당 업데이트 비용을 약 27ms에서 약 0.06ms로 낮췄습니다.

v1.9에서 업그레이드

v1.10.0은 로컬 데이터베이스를 마이그레이션하지 않고 콜드 재동기화를 수행합니다:

  1. 처음 실행할 때 앱은 새 로컬 데이터베이스를 열고 스토어에서 다시 다운로드합니다. 모든 기기에서 일회성 전체 재다운로드가 발생합니다.
  2. 손실되는 것은 없습니다. 동기화된 모든 데이터의 정본 사본은 WooCommerce 서버입니다. 전송되지 않은 대기 중 변경 사항은 이전 데이터가 정리되기 전에 전송되거나 표시되므로, 업그레이드가 미전송 판매를 삭제할 수 없습니다.
  3. 앱과 플러그인은 함께 배포됩니다. v1.10.0 클라이언트는 플러그인의 v2 동기화 API를 사용합니다. 한쪽만 업그레이드하면 요청이 rest_no_route 오류로 실패합니다. 이 신호는 설치가 손상되었다는 뜻이 아니라 “나머지 한쪽을 업데이트하세요”라는 뜻입니다.

v1.10.0에서 달라진 점

v1.9.xv1.10.0
동기화 단위마운트된 화면 쿼리마다 하나의 복제 스트림사이트 + 스토어 + 계산원마다 하나의 엔진
가져오기를 유발하는 요인화면 마운트화면이 필요한 것을 선언
변경 감지주기적 폴링 + 시간별 전체 감사조건부 304 응답을 사용하는 변경 로그 커서
서버 총계추적하지 않음컬렉션별 총계와 정직한 확인 중… 상태
실패한 쓰기불투명하게 재시도영구 대기열, 눈에 보이는 복구 패널, 조용한 재시도 없음
다중 탭 웹조율되지 않음범위마다 선출된 전송 탭 하나
로컬 검색단어 접두사 일치부분 문자열 일치(최소 3자)
저장소IndexedDB(웹), SQLite(네이티브)모든 플랫폼에서 OPFS 형식 저장소
동기화 튜닝고정스토어 상태의 기기별 프리셋과 조절 항목

요청 상한, 서버 부하 시의 백오프, 실측 수치 등 엔진의 성능 철학은 동기화 성능을 참조하세요.