๋จ๋ฐฉํฅ WebSocket์ ์๋ฐฉํฅ ์์ฒญ/์๋ต์ผ๋ก โ UUID์ Map์ผ๋ก ๋์ ์์ฒญ ๋งค์นญํ๊ธฐ
๋ก์ปฌ ์๋ฒ๋ฅผ ๊ณต๊ฐ 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