๐ŸŒฑ <groot/>
๋‹จ๋ฐฉํ–ฅ WebSocket์„ ์–‘๋ฐฉํ–ฅ ์š”์ฒญ/์‘๋‹ต์œผ๋กœ โ€” UUID์™€ Map์œผ๋กœ ๋™์‹œ ์š”์ฒญ ๋งค์นญํ•˜๊ธฐ | Groot's Tech Blog
๐ŸŒWeb

๋‹จ๋ฐฉํ–ฅ WebSocket์„ ์–‘๋ฐฉํ–ฅ ์š”์ฒญ/์‘๋‹ต์œผ๋กœ โ€” UUID์™€ Map์œผ๋กœ ๋™์‹œ ์š”์ฒญ ๋งค์นญํ•˜๊ธฐ

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

๋กœ์ปฌ ์„œ๋ฒ„๋ฅผ ๊ณต๊ฐœ URL๋กœ ์—ฌ๋Š” ํ„ฐ๋„์˜ ์‹ฌ์žฅ์€ "๋ฐ”๊นฅ์—์„œ ์˜จ HTTP ์š”์ฒญ์„, WebSocket ํ•˜๋‚˜๋กœ ๋กœ์ปฌ๊นŒ์ง€ ๋‚˜๋ฅด๊ณ  ๋‹ค์‹œ ๋Œ๋ ค๋ฐ›๋Š”" ๋ถ€๋ถ„์ด๋‹ค. ์—ฌ๊ธฐ์—” ๊ทผ๋ณธ์ ์ธ ๋ฏธ์Šค๋งค์น˜๊ฐ€ ์žˆ๋‹ค.

  • HTTP๋Š” ์š”์ฒญ ํ•˜๋‚˜์— ์‘๋‹ต ํ•˜๋‚˜๊ฐ€ ๋”ฑ ์ง์ง€์–ด์ง„๋‹ค. (request โ†” response)
  • WebSocket์€ ๊ทธ๋ƒฅ ๋ฉ”์‹œ์ง€๊ฐ€ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ํ๋ฅด๋Š” ์ŠคํŠธ๋ฆผ์ด๋‹ค. ์ง ๊ฐœ๋…์ด ์—†๋‹ค.

๊ฒŒ๋‹ค๊ฐ€ ๋ฐฉ๋ฌธ์ž๊ฐ€ ์—ฌ๋Ÿฌ ๋ช…์ด๋ฉด, ํ˜น์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํŽ˜์ด์ง€ ํ•˜๋‚˜ ๊ทธ๋ฆฌ๋ ค๊ณ  CSSยทJSยท ์ด๋ฏธ์ง€๋ฅผ ๋™์‹œ์— ์ˆ˜์‹ญ ๊ฐœ ์š”์ฒญํ•˜๋ฉด, ๊ทธ ์š”์ฒญ๋“ค์ด ์ „๋ถ€ ๊ฐ™์€ WebSocket ํ•˜๋‚˜๋กœ ์„ž์—ฌ ํ๋ฅธ๋‹ค. ๋Œ์•„์˜จ ์‘๋‹ต์ด ์–ด๋А ์š”์ฒญ์˜ ๊ฒƒ์ธ์ง€ ์–ด๋–ป๊ฒŒ ์•Œ๊นŒ?

ํ•ด๊ฒฐ: ์š”์ฒญ๋งˆ๋‹ค UUID๋ฅผ ๋ถ™์ด๊ณ , Map์œผ๋กœ ๋Œ€๊ธฐ์‹œํ‚จ๋‹ค

๋‹ต์€ ๋‹จ์ˆœํ•˜๊ณ  ๊ฐ•๋ ฅํ–ˆ๋‹ค. ์š”์ฒญ๋งˆ๋‹ค ๊ณ ์œ  ID(UUID)๋ฅผ ๋ฐœ๊ธ‰ํ•˜๊ณ , ์‘๋‹ต์ด ๊ทธ ID๋ฅผ ๋‹ฌ๊ณ  ๋Œ์•„์˜ค๊ฒŒ ํ•˜๋Š” ๊ฒƒ. ์„œ๋ฒ„๋Š” "์•„์ง ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์š”์ฒญ๋“ค"์„ Map์— ๋‹ด์•„๋‘”๋‹ค.

// ๊ณต๊ฐœ ์„œ๋ฒ„: ์™ธ๋ถ€ HTTP ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด
const requestId = uuidv4();

// ์‘๋‹ต ๊ฐ์ฒด(res)๋ฅผ ID๋กœ ๋Œ€๊ธฐ ๋ชฉ๋ก์— ์ €์žฅ
pendingRequests.set(requestId, { req, res });

// WebSocket์œผ๋กœ ๋กœ์ปฌ ํด๋ผ์ด์–ธํŠธ์— ์š”์ฒญ์„ ๋„˜๊ธด๋‹ค (ID๋ฅผ ํ•จ๊ป˜)
ws.send(JSON.stringify({
  type: "request",
  requestId,           // โ† ์ด ID๊ฐ€ ์ง์„ ๋งž์ถ”๋Š” ์—ด์‡ 
  method: req.method,
  url: fullPath,
  headers: req.headers,
  body,
}));

๋กœ์ปฌ ํด๋ผ์ด์–ธํŠธ๋Š” localhost์— ์š”์ฒญ์„ ์‹ค์ œ๋กœ ๋˜์ง€๊ณ , ์‘๋‹ต์„ ๊ฐ™์€ ID๋ฅผ ๋‹ฌ์•„ ๋Œ๋ ค๋ณด๋‚ธ๋‹ค. ์„œ๋ฒ„๋Š” ID๋กœ ๋Œ€๊ธฐ ์ค‘์ด๋˜ res๋ฅผ ์ฐพ์•„ ์‘๋‹ต์„ ํ˜๋ ค๋ณด๋‚ธ๋‹ค.

// ์‘๋‹ต์ด WebSocket์œผ๋กœ ๋Œ์•„์˜ค๋ฉด
if (data.type === "response") {
  const { requestId, statusCode, headers, body, isBase64 } = data;
  const pending = pendingRequests.get(requestId); // โ† ID๋กœ ์ง ์ฐพ๊ธฐ
  if (pending) {
    pending.res.writeHead(statusCode, headers);
    pending.res.end(isBase64 ? Buffer.from(body, "base64") : body);
    pendingRequests.delete(requestId); // ์™„๋ฃŒ๋์œผ๋‹ˆ ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐ
  }
}

Map ๋•๋ถ„์— ์ˆ˜์‹ญ ๊ฐœ์˜ ์š”์ฒญ์ด ๋™์‹œ์— ์ง„ํ–‰ ์ค‘์ด์–ด๋„, ๊ฐ์ž ์ œ ์‘๋‹ต์„ ์ •ํ™•ํžˆ ์ฐพ์•„๊ฐ„๋‹ค. ์‘๋‹ต์ด ๋„์ฐฉํ•˜๋Š” ์ˆœ์„œ๊ฐ€ ๋’ค์ฃฝ๋ฐ•์ฃฝ์ด์–ด๋„ ์ƒ๊ด€์—†๋‹ค. ID๊ฐ€ ์ง์„ ๋ณด์žฅํ•˜๋‹ˆ๊นŒ. ์ด๊ฑด ์‚ฌ์‹ค ngrok ๊ฐ™์€ ํ”„๋กœ๋•์…˜ ํ„ฐ๋„๋ง ๋„๊ตฌ๋„ ์“ฐ๋Š” ๊ธฐ๋ณธ ํŒจํ„ด์ด๋‹ค. "์ƒ๊ด€ ID(correlation id)"๋กœ ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆผ ์œ„์— ์š”์ฒญ/์‘๋‹ต ์˜๋ฏธ๋ฅผ ์–น๋Š” ๊ฒƒ.

์žŠ์œผ๋ฉด ์ƒˆ๋Š” ๊ฒƒ๋“ค: ํƒ€์ž„์•„์›ƒ๊ณผ ์—ฐ๊ฒฐ ์ข…๋ฃŒ

์—ฌ๊ธฐ์„œ ๋๋‚ด๋ฉด ์œ„ํ—˜ํ•˜๋‹ค. pendingRequests์— ๋„ฃ๊ธฐ๋งŒ ํ•˜๊ณ  ์•ˆ ์ง€์šฐ๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ƒŒ๋‹ค. ๋กœ์ปฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์˜์˜ ์‘๋‹ต์„ ์•ˆ ์ค„ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ๊นŒ. ๊ทธ๋ž˜์„œ ๋‘ ๊ฐ€์ง€ ์ •๋ฆฌ ์žฅ์น˜๋ฅผ ๋’€๋‹ค.

1. ํƒ€์ž„์•„์›ƒ โ€” 30์ดˆ ์•ˆ์— ์‘๋‹ต์ด ์—†์œผ๋ฉด 504๋กœ ๋Š๊ณ  ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐํ•œ๋‹ค.

const timeout = setTimeout(() => {
  if (pendingRequests.has(requestId)) {
    pendingRequests.delete(requestId);
    if (!res.headersSent) res.status(504).send("Gateway Timeout");
  }
}, 30000);

2. ๋ฐฉ๋ฌธ์ž๊ฐ€ ๋จผ์ € ๋– ๋‚œ ๊ฒฝ์šฐ โ€” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์—ฐ๊ฒฐ์„ ๋Š์œผ๋ฉด(res์˜ close), ๊ทธ ๋Œ€๊ธฐ ํ•ญ๋ชฉ๋„ ์ง€์šด๋‹ค.

res.on("close", () => {
  if (pendingRequests.has(requestId)) pendingRequests.delete(requestId);
});

์ „์†ก ์‹คํŒจ(ws.send ์˜ˆ์™ธ)๋‚˜ ์š”์ฒญ ์ŠคํŠธ๋ฆผ ์—๋Ÿฌ๋„ ๊ฐ๊ฐ 502๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋ชฉ๋ก์—์„œ ์ง€์šด๋‹ค. "๋“ค์–ด์˜จ ๊ฑด ๋ฐ˜๋“œ์‹œ ์ง€์šด๋‹ค" ๋ฅผ ๋ชจ๋“  ๊ฒฝ๋กœ์—์„œ ์ง€ํ‚ค๋Š” ๊ฒŒ ํ•ต์‹ฌ์ด์—ˆ๋‹ค.

๋ฐฐ์šด ๊ฒƒ

  • ํ”„๋กœํ† ์ฝœ์˜ ์˜๋ฏธ(์š”์ฒญโ†”์‘๋‹ต)๊ฐ€ ์•ˆ ๋งž์„ ๋• ์ƒ๊ด€ ID๋กœ ์–น์œผ๋ฉด ๋œ๋‹ค. ์ด๊ฑด ๋ฉ”์‹œ์ง€ ํ, RPC, WebSocket ๋“ฑ ์–ด๋””์„œ๋‚˜ ๋ฐ˜๋ณต๋˜๋Š” ํŒจํ„ด์ด๋‹ค.

  • ๋น„๋™๊ธฐ๋กœ "๋Œ€๊ธฐ ์ค‘์ธ ๊ฒƒ"์„ ๋‹ด์•„๋‘๋Š” ์ž๋ฃŒ๊ตฌ์กฐ(Map)๋Š”, ๋ชจ๋“  ์ข…๋ฃŒ ๊ฒฝ๋กœ์—์„œ ๋ฐ˜๋“œ์‹œ ๋น„์›Œ์•ผ ํ•œ๋‹ค. ์„ฑ๊ณตยทํƒ€์ž„์•„์›ƒยท์—ฐ๊ฒฐ๋Š๊น€ยท์—๋Ÿฌ โ€” ํ•˜๋‚˜๋ผ๋„ ๋น ์ง€๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋‹ค.

  • ๋‹จ๋ฐฉํ–ฅ ์ŠคํŠธ๋ฆผ ์œ„์— ์–‘๋ฐฉํ–ฅ ์˜๋ฏธ๋ฅผ ์„ธ์šฐ๋Š” ๊ฑด, ์ƒ๊ฐ๋ณด๋‹ค ์ ์€ ์ฝ”๋“œ๋กœ ๋˜์ง€๋งŒ ์—ฃ์ง€ ์ผ€์ด์Šค ์ฒ˜๋ฆฌ๊ฐ€ 8ํ• ์ด์—ˆ๋‹ค.

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

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