From 8e29a1f9dabb4814641707b7ac05700cfaa65e09 Mon Sep 17 00:00:00 2001 From: chpark Date: Wed, 20 May 2026 23:28:10 +0900 Subject: [PATCH] =?UTF-8?q?fix(orders/admin):=20=EB=9D=BD=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=A6=89=EC=8B=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20=E2=80=94=20=EC=98=B5=ED=8B=B0=EB=AF=B8?= =?UTF-8?q?=EC=8A=A4=ED=8B=B1=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문제: 발주 A 클릭 → B 클릭 시 A 의 release/acquire 응답을 기다리지 않아 리스트는 30초 자동 갱신 전까지 stale (A 에 여전히 "내가 수정 중" 표시) 수정: - 이전 락 release 시 setOrders 로 previousLocked 행의 EDITING_BY 즉시 null - 새 락 acquire 성공 시 activeId 행의 EDITING_BY 즉시 본인으로 세팅 - 다른 사람 락이라 거부된 경우도 그 정보로 즉시 업데이트 - 30초 자동 갱신은 안전망(누락된 변화 동기화)으로 유지 Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/(main)/m/admin/orders/page.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/app/(main)/m/admin/orders/page.tsx b/src/app/(main)/m/admin/orders/page.tsx index 1bdb68a..69f9a88 100644 --- a/src/app/(main)/m/admin/orders/page.tsx +++ b/src/app/(main)/m/admin/orders/page.tsx @@ -173,6 +173,10 @@ export default function AdminOrdersPage() { const previousLocked = lockedOrderRef.current; if (previousLocked && previousLocked !== activeId) { releaseLock(previousLocked); + // 옵티미스틱: 리스트에서 이전 락 즉시 제거 + setOrders((prev) => prev.map((o) => + o.OBJID === previousLocked ? { ...o, EDITING_BY: null, EDITING_BY_NAME: null } : o + )); lockedOrderRef.current = ""; } // 이전 heartbeat 정지 @@ -205,6 +209,10 @@ export default function AdminOrdersPage() { setLockedByMe(true); setLockedByOther(null); lockedOrderRef.current = activeId; + // 옵티미스틱: 리스트에 내 락 즉시 반영 + setOrders((prev) => prev.map((o) => + o.OBJID === activeId ? { ...o, EDITING_BY: lj.lockedBy || myUserId, EDITING_BY_NAME: lj.lockedByName || "(나)" } : o + )); // 30초마다 heartbeat heartbeatRef.current = setInterval(async () => { try { @@ -224,6 +232,10 @@ export default function AdminOrdersPage() { } else if (lj.locked) { setLockedByMe(false); setLockedByOther({ name: lj.lockedByName || lj.lockedBy || "다른 담당자" }); + // 옵티미스틱: 리스트에 락 정보 반영 + setOrders((prev) => prev.map((o) => + o.OBJID === activeId ? { ...o, EDITING_BY: lj.lockedBy || "other", EDITING_BY_NAME: lj.lockedByName || "다른 담당자" } : o + )); Swal.fire({ icon: "warning", title: "수정 중인 발주", @@ -236,7 +248,7 @@ export default function AdminOrdersPage() { })(); return () => { cancelled = true; }; - }, [activeId, releaseLock]); + }, [activeId, releaseLock, myUserId]); // 페이지 떠날 때 락 해제 (best-effort) — sendBeacon 으로 keepalive useEffect(() => {