Eat Study Love

먹고 공부하고 사랑하라

Coding With LLM/GPT랑 하고 싶은 코딩 해보기

Web 새로고침 + 색인식 매크로 짜보기(2)

eatplaylove 2025. 6. 20. 16:10

https://eglife.tistory.com/353

 

Web 새로고침 + 색인식 매크로 짜보기(1)

코딩은 기초적인 Python / C / C++ 정도만 코드를 보고 이해하고, 어느 정도 구현할 줄 안다. 근데 삿포로 여행가려고 계획을 짜던 중, 니카 요이치 위스키 증류소 무료투어가 있다는 것을 알게 되었

eglife.tistory.com

지난 시간, 매크로를 짜보면서 느낀건 

 

역시나 LLM만 있다고 코딩 Master가 될 순 없다는 것이다.

 

기본적으로 내가 원하는 Task를 Natural Language를 통해 LLM이 정확하게 이해할 수 있도록 전달이 잘 안 되는 거 같다.

 

그래서 어린아이 달래듯 했던 말 또 하고, 했던 말 또 하는 것이 반복되고 계속 비슷한 Error가 발생하는 Code를 도돌이표처럼 사용하게 되는 거 같다.

 

이 때, 관련된 코딩지식이 조금만 있어도 성공과 실패 사이 그 미묘한 차이를 Catch해서 Debugging할 수 있을 것인데, 지금은 그냥 무지성으로 모든 Process를 GPT한테 맡기다 보니까 뭐가 어디서 잘못 됐는지 모르고, 그러다 보니 그냥 GPT한테 계속 땡깡만 쓰는 꼴이다.

 

결론은, GPT한테 ~를 해줘만 할 게 아니라, 매 Process에서 왜 그런 코드가 나왔는지 설명을 덧붙이라고 해야한다. 그래야 그 과정에서 Logic Fault를 내가 잡아낼 수 있는 힌트를 얻을 수 있다.

 

각설하고, 얼른 매크로를 다시 만드는 작업을 해보자.

 

지금까지 문제는 일단 2가지,

 1. 새로고침을 하면 UI가 사라짐
 2. Target Date를 Catch 하지 못했을 때, 새로고침을 하며 계속해서 매크로동작 시도를 하지 않음  

 

또 하나 느끼는 건, 어차피 지금 당장 보이는 문제가 2가지라고 해도 디버깅 하다보면 또 다른 문제가 보인다.

 

이래서 SW개발과정엔 여러 명의 인력이 붙어야 하나보다 ㅋ 조금이라도 문제점들을 빨리 도출해서 해결시킬 수 있도록..!


 

GPT와의 호투 끝에 그래도 원하는 코드를 만들어냈다.

중간 중간 디버깅하면서 꽤나 많은 항목들을 추가했다.

1. Yoichi 예약화면이 아닌 Chrome Window 에선 해당 UI가 실행되지 않을 것
2. 복수개의 Target Date에 대해선 가능 날짜를 모두 Notify해줄 것
3. 현재 추적중인 Date가 UI에 표시되게 할 것
4. 기타 등등..

 

간단한 줄만 알았던 APP이 막상 만들다보니 생각보다 신경쓸 게 많다는 것을 알았다.

 

이래서, 항상 개발과정은 말 할 때와 직접 Implementate할 때 다른 것 같다.

 

console.log("✅ content_script.js 로딩됨");

let monitorInterval = null;
let observer = null;
let reloadTimeoutId = null;  // 🔁 새로고침 예약 ID
let lastSuccessTime = Date.now();

// 🔄 자동 재시작
chrome.storage.local.get("isTracking", ({ isTracking }) => {
  if (isTracking) {
    console.log("🔄 새로고침 후 자동 모니터링 재시작");
    window.startYoichiTracker();
    chrome.runtime.sendMessage({ action: "open_popup_ui" });
  }
});

function checkDates(targetDates) {
  const calendar = document.querySelector("#calendar");
  if (!calendar) return;

  const groups = [
    { selector: ".ui-datepicker-group-first", label: "first" },
    { selector: ".ui-datepicker-group-last", label: "second" },
  ];

  const matchedDates = [];

  for (const group of groups) {
    const monthEl = calendar.querySelector(group.selector);
    if (!monthEl) continue;

    const tds = monthEl.querySelectorAll("td");

    tds.forEach((td) => {
      const rawDate = td.textContent.trim();

      if (!/^\d+$/.test(rawDate)) return;
      if (!targetDates.includes(rawDate)) return;

      if (td.getAttribute("data-event") !== "click") return;

      const fullDate = ` ${group.label}월 ${rawDate}일`;
      matchedDates.push(fullDate);
    });
  }

  if (matchedDates.length > 0) {
    console.log("✅ 예약 가능 날짜 감지됨:", matchedDates);

    chrome.runtime.sendMessage({ type: "notify_slack", dates: matchedDates });
    window.stopYoichiTracker();
    chrome.runtime.sendMessage({ action: "set_popup_status", status: "stopped" });
    chrome.storage.local.set({ isTracking: false });
  } else {
    console.log("🔁 감지 실패 (계속 감시 중)");

    // 🔁 3초 후 reload 예약 (이미 예약된 reload가 없을 때만)
    if (!reloadTimeoutId) {
      reloadTimeoutId = setTimeout(() => {
        chrome.storage.local.set({ isTracking: true }, () => {
          console.warn("🕒 일정 시간 감지 실패 → 새로고침");
          location.reload();
        });
      }, 3000);
    }
  }
}

window.startYoichiTracker = () => {
  if (monitorInterval) return;

  chrome.storage.local.get("targetDates", ({ targetDates }) => {
    console.log("▶️ 모니터링 대상 날짜:", targetDates);

    const invalid = targetDates.some(
      (d) => !/^\d+$/.test(d) || parseInt(d) < 1 || parseInt(d) > 31
    );
    if (invalid) {
      alert("❌ 1~31 사이의 숫자만 입력해주세요.");
      chrome.runtime.sendMessage({ action: "set_popup_status", status: "stopped" });
      chrome.storage.local.set({ isTracking: false });
      return;
    }

    observer = new MutationObserver(() => {
      checkDates(targetDates);
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
    });

    monitorInterval = setInterval(() => {
      checkDates(targetDates);
    }, 1000);

    chrome.storage.local.set({ isTracking: true });
  });
};

window.stopYoichiTracker = () => {
  if (observer) {
    observer.disconnect();
    observer = null;
  }
  if (monitorInterval) {
    clearInterval(monitorInterval);
    monitorInterval = null;
  }
  if (reloadTimeoutId) {
    clearTimeout(reloadTimeoutId);  // ✅ 예정된 reload 취소
    reloadTimeoutId = null;
  }

  chrome.storage.local.set({ isTracking: false });
  console.log("⏹️ 모니터링 중단됨");
};

 

기본적인 JavaScript는 위와 같다.

JS를 아예 몰라도, 대충 Python 코딩을 해봐서 그런지 Code맥락은 이해할 수 있었다.

 

그리고 역시나, 어느 정도 내가 보정을 해줘야 코드 debugging이 빨리 됐다.

 

여기까지 하면, 대충 내가 원하는 Logic은 다 구현하긴 했다.

Chrome Extension으로 자동새로고침 + Target 날짜 Tracking 후 성공 시 프로그램 Stop + Slack 알람

 

근데 여기까지 하면, 여태 GPT랑 으쌰으쌰 하면서 구현한 코드가 아까우니, 일반인을 위한 코드를 만들어 보기로 한다.

 

기본적인 Logic은 같으나, 이번엔 Alert를 PC Sound + Tracking 성공 Pop-up 띄우는 것으로 바꾸기.

 

그러면 위 content_script가 아닌 background.js 파일을 고쳐야 한다.

 

그리고 새로 성공 Pop-up을 띄우기 위해서 popup_result 라는 것도 만들었다.

<!-- 📄 popup_result.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>예약 알림</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    h2 { color: green; }
  </style>
</head>
<body>
  <h2>🎉 예약 가능 날짜 감지!</h2>
  <p id="dateInfo">예약 Web Page를 확인해주세요~</p>

  <script>
    const params = new URLSearchParams(window.location.search);
    const dates = params.get("dates");
    document.getElementById("dateInfo").innerText = dates ? decodeURIComponent(dates) : "알 수 없음";
  </script>
</body>
</html>

 

이 경우 backgroud js파일은 아래와 같이 고치면 된다. Slack API 필요 없음!

 

chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
  console.log("📨 background.js 메시지 수신:", req);

  if (req.action === "open_popup_ui") {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      const tab = tabs[0];
      if (tab?.url?.includes("distillery.nikka.com/eng/yoichi/reservation")) {
        chrome.action.openPopup();
      }
    });
    return false;
  }

  if (req.action === "start_monitoring") {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      chrome.scripting.executeScript({
        target: { tabId: tabs[0].id },
        func: () => window.startYoichiTracker && window.startYoichiTracker()
      });
    });
    return false;
  }

  if (req.action === "stop_monitoring") {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      chrome.scripting.executeScript({
        target: { tabId: tabs[0].id },
        func: () => window.stopYoichiTracker && window.stopYoichiTracker()
      });
    });
    return false;
  }

  // 🎯 예약 날짜 감지 시 알림 (Slack 없이!)
  if (req.type === "notify_local") {
    const { dates } = req;
    const message = dates.map(d => `• ${d}`).join("\n");

    chrome.notifications.create({
      type: "basic",
      iconUrl: "icon.png",
      title: "🎉 예약 가능!",
      message: `${dates.length}개 날짜:\n${message}`,
    });

    return false;
  }
});

 

알람음 MP3 파일은 아래 사이트에서 무료로 다운받아 사용한다. 띄리링~

https://notificationsounds.com

 

Ringtones and other free notification sounds

Hundreds of original notification sounds, ringtones, message tones and more. Free.

notificationsounds.com

 

Popup version은 개인이 사용해도 요긴할 거 같아서.. Chrome web store에 배포하려 했는데,

이거 뭔;; Chrome 개발자 등록에 최초 5$를 등록하라고 한다.

 

내가 Chrome 개발을 계속 하는 사람이면 등록하겠다만 이런 허접한 코드 배포하자고 굳이 등록까진 하기 꺼려지는 짠돌이 인생..ㅎ

 

- T. B. D -