๐ŸŒฑ <groot/>
์ฝ”๋“œ ์ˆ˜์ • ์—†์ด ์›๊ฒฉ ๋ธŒ๋ผ์šฐ์ €์˜ ์ฝ˜์†” ๋กœ๊ทธ ๊ฐ€์ ธ์˜ค๊ธฐ โ€” HTML ์ฃผ์ž… + console ์˜ค๋ฒ„๋ผ์ด๋“œ | Groot's Tech Blog
๐ŸŒWeb

์ฝ”๋“œ ์ˆ˜์ • ์—†์ด ์›๊ฒฉ ๋ธŒ๋ผ์šฐ์ €์˜ ์ฝ˜์†” ๋กœ๊ทธ ๊ฐ€์ ธ์˜ค๊ธฐ โ€” HTML ์ฃผ์ž… + console ์˜ค๋ฒ„๋ผ์ด๋“œ

2026.07.02ยท2๋ถ„ ์ฝ๊ธฐ

ํ„ฐ๋„๋กœ ๋กœ์ปฌ ์„œ๋ฒ„๋ฅผ ํฐ์— ๋„์› ์„ ๋•Œ, ์ •์ž‘ ๋‹ต๋‹ตํ•œ ๊ฑด "ํฐ์—์„œ ๋ญ๊ฐ€ ์ž˜๋ชป๋๋Š”์ง€ ์ฝ˜์†”์„ ๋ณผ ์ˆ˜ ์—†๋‹ค" ๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. ํฐ ๋ธŒ๋ผ์šฐ์ €์˜ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ์—ฌ๋Š” ๊ฑด ๋ฒˆ๊ฑฐ๋กญ๋‹ค. ๊ทธ๋ž˜์„œ ๋„ฃ์€ ๊ธฐ๋Šฅ์ด ์›๊ฒฉ ์ฝ˜์†” ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง โ€” ํ„ฐ๋„์„ ํ†ตํ•ด ์ ‘์†ํ•œ ๋ธŒ๋ผ์šฐ์ €์˜ console.log๊ฐ€ VSCode ์‚ฌ์ด๋“œ๋ฐ”์— ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋œฌ๋‹ค.

์—ฌ๊ธฐ์„œ ์ง€ํ‚จ ์›์น™ ํ•˜๋‚˜: ์‚ฌ์šฉ์ž(๊ฐœ๋ฐœ์ž)์˜ ์ฝ”๋“œ๋Š” ํ•œ ์ค„๋„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.

์•„์ด๋””์–ด: ์‘๋‹ต HTML์— ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ชฐ๋ž˜ ๋ผ์›Œ๋„ฃ๊ธฐ

ํ„ฐ๋„์€ ์–ด์ฐจํ”ผ ๋ชจ๋“  ์‘๋‹ต์ด ๋กœ์ปฌ ํด๋ผ์ด์–ธํŠธ๋ฅผ ๊ฑฐ์ณ ๋‚˜๊ฐ„๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ HTML ์‘๋‹ต์ผ ๋•Œ, </head> ์•ž์— ์ž‘์€ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ฃผ์ž…ํ•˜๋ฉด ๋œ๋‹ค. ๊ฐœ๋ฐœ์ž ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ํ„ฐ๋„์ด ๋„ฃ๋Š” ๊ฒƒ์ด๋ผ, ์‚ฌ์šฉ์ž๋Š” ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ํ•ด๋„ ๋œ๋‹ค.

if (response.status === 200 && ct.includes("text/html")) {
  const script = `
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<script data-tunnel-script="${Date.now()}">
  /* ...์•„๋ž˜ ๋‚ด์šฉ... */
</script>`;
  responseBody = responseBody.replace("</head>", script + "</head>");
}

HTML ํŒŒ์„œ๋ฅผ ๋Œ๋ฆฌ์ง€ ์•Š๊ณ  </head> ๋ฌธ์ž์—ด๋งŒ ์ฐพ์•„ ๋ฐ”๊พผ๋‹ค. ๋น ๋ฅด๊ณ  ์ถฉ๋ถ„ํ–ˆ๋‹ค. data-tunnel-script์— ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ฐ•๊ณ  ์บ์‹œ ๋ฐฉ์ง€ ๋ฉ”ํƒ€๋ฅผ ํ•จ๊ป˜ ๋„ฃ์–ด, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์˜› ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์บ์‹ฑํ•˜๋Š” ๊ฒƒ๋„ ๋ง‰์•˜๋‹ค.

ํ•ต์‹ฌ: console์„ ๊ฐ์‹ธ๋˜, ์›๋ณธ์€ ๋ณด์กดํ•œ๋‹ค

์ฃผ์ž…๋œ ์Šคํฌ๋ฆฝํŠธ๋Š” console.log ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€๋กœ์ฑ„์„œ, ์›๋ž˜ ๋™์ž‘์€ ๊ทธ๋Œ€๋กœ ํ•˜๊ณ  ์ถ”๊ฐ€๋กœ ์„œ๋ฒ„์— ์ „์†กํ•œ๋‹ค. IIFE(์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜)๋กœ ๊ฐ์‹ธ ์›๋ณธ ์ฐธ์กฐ๋ฅผ ํด๋กœ์ €์— ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€ํ–ˆ๋‹ค.

(function () {
  var tunnelId = getCookie("tunnelId"); // ์ฟ ํ‚ค์—์„œ ํ„ฐ๋„ ID
  if (!tunnelId) return;

  var original = { log: console.log, warn: console.warn,
                   error: console.error, info: console.info };

  function sendLog(level, args) {
    var message = Array.from(args).map(function (a) {
      try { return typeof a === "object" ? JSON.stringify(a) : String(a); }
      catch { return String(a); }
    }).join(" ");

    fetch(tunnelServerUrl + "/log", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ tunnelId: tunnelId, level: level, message: message }),
    }).catch(function () {}); // ์‹คํŒจํ•ด๋„ ์•ฑ์— ์˜ํ–ฅ ์—†๊ฒŒ ์กฐ์šฉํžˆ ๋ฌด์‹œ
  }

  console.log = function () { original.log.apply(console, arguments); sendLog("log", arguments); };
  console.warn = function () { original.warn.apply(console, arguments); sendLog("warn", arguments); };
  console.error = function () { original.error.apply(console, arguments); sendLog("error", arguments); };
  console.info = function () { original.info.apply(console, arguments); sendLog("info", arguments); };
})();

ํฌ์ธํŠธ๊ฐ€ ๋ช‡ ๊ฐœ ์žˆ๋‹ค.

  • ์›๋ณธ์„ ๋จผ์ € ํ˜ธ์ถœํ•œ๋‹ค. ๊ทธ๋ž˜์•ผ ์‹ค์ œ ์ฝ˜์†”์—๋„ ์ •์ƒ์ ์œผ๋กœ ์ฐํžŒ๋‹ค.
  • ๊ฐ์ฒด ์ธ์ž๋Š” JSON.stringify๋กœ ๋ฌธ์ž์—ดํ™”ํ•˜๋˜, ์ˆœํ™˜ ์ฐธ์กฐ ๋“ฑ์œผ๋กœ ์‹คํŒจํ•˜๋ฉด String()์œผ๋กœ ํด๋ฐฑ.
  • ๋กœ๊ทธ ์ „์†ก(fetch)์ด ์‹คํŒจํ•ด๋„ .catch(()=>{})๋กœ ์‚ผํ‚จ๋‹ค. ๋กœ๊น… ๊ธฐ๋Šฅ์ด ์•ฑ ๋™์ž‘์„ ๋ฐฉํ•ดํ•˜๋ฉด ์•ˆ ๋˜๋‹ˆ๊นŒ.

๋กœ๊ทธ๊ฐ€ VSCode๊นŒ์ง€ ๊ฐ€๋Š” ๊ธธ

์ „์†ก๋œ ๋กœ๊ทธ๋Š” ์ด๋ ‡๊ฒŒ ํ˜๋Ÿฌ ์‚ฌ์ด๋“œ๋ฐ”์— ๋œฌ๋‹ค.

๋ธŒ๋ผ์šฐ์ € console.log
  โ†’ fetch POST /log (ํ„ฐ๋„ IDยท๋ ˆ๋ฒจยท๋ฉ”์‹œ์ง€)
  โ†’ ๊ณต๊ฐœ ์„œ๋ฒ„: ํ•ด๋‹น ํ„ฐ๋„์˜ WebSocket์œผ๋กœ push
  โ†’ ๋กœ์ปฌ ํด๋ผ์ด์–ธํŠธ โ†’ VSCode ํ™•์žฅ
  โ†’ Webview ์ฝ˜์†” ํŒจ๋„์— ์‹ค์‹œ๊ฐ„ ๋ Œ๋”

Webview ์ชฝ์€ ๋ ˆ๋ฒจ(LOG/WARN/ERROR) ํ•„ํ„ฐ์™€ ๊ฒ€์ƒ‰, ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์œ„๋กœ ์Šคํฌ๋กคํ•˜๋ฉด ์ž๋™ ์Šคํฌ๋กค์„ ๋„๊ณ , ๋งจ ์•„๋ž˜๋กœ ์˜ค๋ฉด ๋‹ค์‹œ ์ผœ๋Š” ์ง€๋Šฅํ˜• ์˜คํ† ์Šคํฌ๋กค๋„ ๋„ฃ์—ˆ๋‹ค. ๋กœ๊ทธ ๋ทฐ์–ด์—์„œ "์ƒˆ ๋กœ๊ทธ ๋•Œ๋ฌธ์— ๋ณด๋˜ ์œ„์น˜๊ฐ€ ํŠ€๋Š”" ํ”ํ•œ ์งœ์ฆ์„ ์—†์•ค ๋””ํ…Œ์ผ์ด๋‹ค.

๋ฐฐ์šด ๊ฒƒ

  • ๋ชฝํ‚ค ํŒจ์นญ(์›๋ณธ ํ•จ์ˆ˜ ๊ฐ์‹ธ๊ธฐ) ์€ ๊ฐ•๋ ฅํ•˜๋‹ค. ๋‹จ, "์›๋ณธ์„ ๋ฐ˜๋“œ์‹œ ๋จผ์ € ํ˜ธ์ถœ"ํ•˜๊ณ  "๋ถ€๊ฐ€ ๊ธฐ๋Šฅ ์‹คํŒจ๊ฐ€ ์›๋ณธ์„ ๋ง์น˜์ง€ ์•Š๊ฒŒ" ํ•˜๋Š” ๋‘ ์›์น™์„ ์ง€์ผœ์•ผ ์•ˆ์ „ํ•˜๋‹ค.

  • ์‚ฌ์šฉ์ž ์ฝ”๋“œ๋ฅผ ์•ˆ ๊ฑด๋“œ๋ฆฌ๊ณ  ๊ธฐ๋Šฅ์„ ์–น๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์‘๋‹ต HTML ์ฃผ์ž…์€ ์•„์ฃผ ์‹ค์šฉ์ ์ด๋‹ค. (๊ฐœ๋ฐœ ๋„๊ตฌ๋“ค์ด ํ”ํžˆ ์“ฐ๋Š” ์ˆ˜๋ฒ•์ด๊ธฐ๋„ ํ•˜๋‹ค.)

  • ๋กœ๊น…ยท๊ณ„์ธก ๊ฐ™์€ ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์€ ์ ˆ๋Œ€ ๋ณธ์ฒด๋ฅผ ๋ฐฉํ•ดํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. ์‹คํŒจ๋Š” ์กฐ์šฉํžˆ ์‚ผํ‚ค๋Š” ๊ฒŒ ์˜ณ๋‹ค.

  • ๊ด€๋ จ ํ”„๋กœ์ ํŠธ: Custom Tunnel

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ
ยฉ 2026 ยท Groot's Tech Blog. Built with Gatsby ๐ŸŒฑ