diff --git a/src/components/main/MainUtm.jsx b/src/components/main/MainUtm.jsx
index 41788c4..efd29a5 100644
--- a/src/components/main/MainUtm.jsx
+++ b/src/components/main/MainUtm.jsx
@@ -27,9 +27,6 @@ const DETAIL = {
date: "2025-06-10 14:30",
};
-const PHASES = ["list", "detail", "confirm", "done"];
-const PHASE_DURATION = { list: 1800, detail: 2200, confirm: 1800, done: 2000 };
-
function UtmSystemPanel({ phase, activeRow }) {
return (
@@ -72,13 +69,8 @@ function UtmSystemPanel({ phase, activeRow }) {
))}
- {/* 상세: 아래로 펼쳐짐 */}
비행계획 상세
@@ -109,9 +101,7 @@ function UtmSystemPanel({ phase, activeRow }) {
상태
{phase === "done" ? "승인" : "대기"}
@@ -142,7 +132,6 @@ function UtmSystemPanel({ phase, activeRow }) {
- {/* 컨펌: 패널 안에서 블러 오버레이 */}
{phase === "confirm" && (
{
- if (!inView) return;
- let t;
- const run = (current) => {
- const next = PHASES[(PHASES.indexOf(current) + 1) % PHASES.length];
- t = setTimeout(() => {
- setPhase(next);
- run(next);
- }, PHASE_DURATION[current]);
- };
- t = setTimeout(() => run("list"), 1000);
- return () => clearTimeout(t);
- }, [inView]);
-
- // 커서 이동 로직
+ // ← useEffect 딱 하나만!
useEffect(() => {
if (!inView) return;
@@ -207,7 +182,13 @@ function MainUtm() {
const cursor = cursorRef.current;
if (!showcase || !cursor) return;
- let timers = [];
+ let stopped = false;
+ const ids = [];
+
+ function sid(id) {
+ ids.push(id);
+ return id;
+ }
function getPos(selector) {
const el = ref.current?.querySelector(selector);
@@ -224,64 +205,68 @@ function MainUtm() {
cursor.style.transform = `translate(${x}px, ${y}px)`;
}
- function click() {
+ function doClick() {
cursor.classList.add("utm-cursor--click");
- setTimeout(() => cursor.classList.remove("utm-cursor--click"), 250);
+ sid(setTimeout(() => cursor.classList.remove("utm-cursor--click"), 250));
}
- function after(ms, fn) {
- const t = setTimeout(fn, ms);
- timers.push(t);
+ function schedule(ms, fn) {
+ sid(
+ setTimeout(() => {
+ if (!stopped) fn();
+ }, ms),
+ );
}
- function loop() {
- timers.forEach(clearTimeout);
- timers = [];
+ function run() {
+ if (stopped) return;
setPhase("list");
- after(1000, () => {
+ moveTo(180, 160);
+
+ // 상세보기로 이동
+ schedule(1000, () => {
const p = getPos(".utm-panel__row--active .utm-panel__btn");
if (p) moveTo(p.x, p.y);
});
- after(2000, () => {
- click();
- after(200, () => setPhase("detail"));
- });
+ // 상세보기 클릭 → detail
+ schedule(2000, () => doClick());
+ schedule(2200, () => setPhase("detail"));
- after(3200, () => {
+ // 승인처리로 이동
+ schedule(3200, () => {
const p = getPos(".utm-panel__approve-btn");
if (p) moveTo(p.x, p.y);
});
- after(4200, () => {
- click();
- after(200, () => setPhase("confirm"));
- });
+ // 승인처리 클릭 → confirm
+ schedule(4200, () => doClick());
+ schedule(4400, () => setPhase("confirm"));
- after(5000, () => {
+ // 확인 버튼으로 이동
+ schedule(5000, () => {
const p = getPos(".utm-confirm__ok");
if (p) moveTo(p.x, p.y);
});
- after(5800, () => {
- click();
- after(200, () => setPhase("done"));
- });
+ // 확인 클릭 → done
+ schedule(5800, () => doClick());
+ schedule(6000, () => setPhase("done"));
- after(7500, () => loop());
+ // 반복
+ schedule(7500, () => run());
}
- moveTo(180, 160);
- after(500, () => loop());
+ run();
return () => {
- timers.forEach(clearTimeout);
- timers = [];
+ stopped = true;
+ ids.forEach(clearTimeout);
cursor.classList.remove("utm-cursor--click");
- setPhase("list"); // ← cleanup에서만 호출
};
}, [inView]);
+
return (
@@ -349,7 +334,6 @@ function MainUtm() {
- {/* 커서 */}