[HandTris] #8. WebSocket์ด ์ค๋ณต ์ฐ๊ฒฐ๋ ๊ฒ ๊ฐ์์ ๐ค - (์ ๋์์ผ๋ก ํ๋ ์จ๋ผ์ธ ์น ํ ํธ๋ฆฌ์ค ๊ฒ์ ๋ง๋ค๊ธฐ)
์จ๋ผ์ธ ๊ฒ์์ ํ์ํ ์ค์๊ฐ ์ํธ์์ฉ์ ๊ตฌํํ๊ธฐ ์ํด Web Socket์ ํ์ฉํ์๋ค. ๊ทธ์ค STOMP๋ผ๋ ๋ฉ์์ง ํ๋กํ ์ฝ์ ํตํด ์๋๋ฐฉ์ด ๊ณต๊ฒฉ์ ํ๋์ง ํน์ ๊ฒ์์ด ๋๋ฌ๋์ง ๋ฑ์ ์ํ ์ ๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ์๋ค. ์ด๋ ๊ฒ WS์ ์ฌ์ฉํ๋ฉฐ ๊ฒช์๋ ๋ฒ๊ทธ ์ค ๊ฐ์ฅ ๊ณ ์น๊ธฐ ์ด๋ ค์ ๋ ์ด์๋ socket์ด ๋ ๋ฒ ์ฐ๊ฒฐ๋๋ ๋ฏํ ํ์์ด์๋ค.
1. ์ํฉ ํ์
์ค๋ฅธ์ชฝ์์ ํ ์ค์ ๋ธ๋ญ์ ์์ฑํ๋ฉด ์ผ์ชฝ ํ๋ ์ด์ด์ ๋ ์ค์ ๊ณต๊ฒฉ์ด ๊ฐํด์ง์ ์ ์ ์๋ค. ์ด๋ ์๋ํ ๋ฐ๊ฐ ์๋์๋ค. ํ ์ค์ ๋ธ๋ญ์ ์์ฑํ๋ฉด ํ ์ค์ ๊ณต๊ฒฉ์ด ๊ฐํด์ง๋๋ก ์๋ํ์๋ค.
2. ์์ธ ํ์
์ ํ์์ ํ ๊ฒ์์ด ์ข ๋ฃ๋ ํ game restart ์ ๋ฐ์ํ์๋ค. ๋๋ถ์ด ์น์บ ์ ์์ ์ธ์ํด์ node์ edge๋ฅผ ๋ ๋๋งํ๋ ์๋๋ ํ์ ํ ๋๋ ค์ก๋ค. ์ฒซ ๋ฒ์งธ ๊ฒ์์์๋ ๋ฌธ์ ๊ฐ ์๋ค๊ฐ Play Again ๋ฒํผ์ ๋๋ฌ ๊ฒ์์ ๋ค์ ์์ํ๋ฉด ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ์ฌ์ง์ด Play Again์ ๋ค์ ๋๋ฌ ์ธ ๋ฒ์งธ ๊ฒ์์ ์์ํ๋ฉด ๊ฒ์์ด ๋ถ๊ฐ๋ฅํ ์ ๋๋ก ๋ฒ๋ฒ ๊ฑฐ๋ ธ๋ค. ์ด๋ ๊ผญ ํด๊ฒฐํด์ผ ํ๋ ๋ฌธ์ ์๋ค.
ํฌ๋กฌ ๋ธ๋ผ์ฐ์ ์์ ๊ธฐ๋ณธ์ผ๋ก ์ง์ํ๋ ํญ๋ณ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ํ์ธ ๊ธฐ๋ฅ์ด ์๋ค. ์ด๋ก ํ์ธํ์ ๋ ํ ๊ฒ์ ๋น ๋๋ต 50mb์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ฆ๊ฐ๋ฅผ ์ผ์ผ์ผฐ๋ค. ์ด๊ธฐ 200~250mb ์์ค์ผ๋ก ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฐจ์งํ๋ค๊ฐ ์ธ ๋ค ํ ์ ๋ 'play again'์ ํ๋ฉด 400~450mb ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฐจ์งํ๋ฉฐ ๋ ๋๋ง ์๋๊ฐ ๋๋ ค์ก๋ค.
3. ์์ธ
์ฒซ ๋ฒ์งธ ๊ฐ์ค์ '๋ฉ๋ชจ๋ฆฌ ๋์'์๋ค. Play Again์ ๋ฐ๋ณตํ๋ฉด์ ๊ฒ์ ์ปดํฌ๋ํธ๋ฅผ ๊ณ์ํด์ ์ฌ๋ ๋๋งํ๊ฒ ๋๋๋ฐ, ์ด ๊ณผ์ ์์ ๋ฐ๋ณต์ ์ผ๋ก ์ ์ด๋ ๋ณ์๊ฐ ์๊ฒ ๋ค๊ณ ์๊ฐํ๋ค.
ํฌ๋กฌ ๊ฐ๋ฐ์ ๋๊ตฌ์์ ํ ์ค๋ ์ท์ ์ฐ์ด ๊ฒ์์ด ์ฌ์คํ๋๋ ๊ณผ์ ์์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ฆ๊ฐํจ์ ํ์ธํ๋ค. ๋ค๋ง ์์ด๋ ์ ๋๊ฐ 40~50 mb ์์ค์ผ๋ก ์์์ผ๋ฉฐ, ์ด๋ ๊ฒ ์ฌํ๊ฒ ๋ฒ๋ฒ ๊ฑฐ๋ฆฌ๊ฒ ํ ์์ธ์ ์๋๋ผ๋ ์๊ฐ์ด ๋ค์๋ค. ๊ฒฐ์ ์ ์ผ๋ก 'garbage์ ์ํด ๊ณต๊ฒฉ์ด ๋ ๋ฒ์ฉ ์ ์ฉ๋ ํ๋ฅ ์ ์ ๋ค'๊ณ ์๊ฐํ์ฌ ๋ ๋ฒ์งธ ๊ฐ์ค์ ๊ฒ์ฆํ๋ฌ ๋์ด๊ฐ๋ค.
๋ ๋ฒ์งธ ๊ฐ์ค์ WS์ผ๋ก๋ถํฐ ๋ ๋ฒ ๊ณต๊ฒฉ ์ ํธ๋ฅผ ๋ฐ๋ ๊ฒ์ด์๋ค(์ด ๋ํ ํ๋ ธ์ผ๋ฉฐ ์ธ ๋ฒ์งธ ๊ฐ์ค๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํจ). ์ฒ์์๋ ๋ ๋๋ง์ด ๋๋ ค์ง๋ ๊ฒ ๊ณผ๋ ๋ฌด๊ดํ ๊ฐ์ค์ด๋ผ ์๊ฐํ๋ค. ๊ทธ๋ฐ๋ฐ, ๊ฒ์์ด ์ฌ์์ํ๋ ํ์๊ฐ ๋ ๋๋ง๋ค WS์ ํตํด Subscribe ํ์ฌ ๋ฐ๋ ๋ฐ์ดํฐ๊ฐ ๋ ๋ฐฐ, ์ธ ๋ฐฐ ์ฆ๊ฐํ๋ค๋ฉด ์ถฉ๋ถํ ๋ธ๋ผ์ฐ์ ์ ๋ถ๋ด์ ์ค ์ ์๊ฒ ๋ค ํ๋จํ์๋ค.
isSub๋ผ๋ Ref ๊ฐ์ฒด๋ฅผ useRef ํ ์ผ๋ก ์์ฑํ์ฌ ๊ตฌ๋ ์ฌ๋ถ๋ฅผ ํ๋จํ๊ณ , ๊ตฌ๋ ๋์ด ์์ง ์์ ๊ฒฝ์ฐ(์ฆ, isSub.current === false)์๋ง ๊ตฌ๋ ํ๋๋ก ์์ ํ์๋ค.
์ ์ด๋ฏธ์ง์ ๊ฐ์ด ์ฌ๊ตฌ๋ ๋ฐฉ์ง๋ฅผ ์ค์ ํ์์์๋ ๋ฌธ์ ๋ ํด๊ฒฐ๋์ง ์์๋ค. Subscribe๋ ํ ๋ฒํ๊ณ , Publish๋ ํ ๋ฒ ํ๋๋ฐ ๋ง์น ๋ ๊ฐ์ ๋ฉ์์ง๋ฅผ ๋ฐ์ ๊ฒ ๊ฐ์ ํ์์ด ๋ฐ์ํ๋ ์ด์ ๋ ๋ญ๊น ๊ฐ์ด ์ ์ ์กํ๋ค. ๐ซ
์ดํ ์ธ ๋ฒ์งธ ๊ฐ์ค์ ๋์ถํ๋๋ฐ ๊น์ง 3~4์ผ์ ๊ฑธ๋ ธ๋ ๊ฒ ๊ฐ๋ค..
์ธ ๋ฒ์งธ ๊ฐ์ค์ WS์ผ๋ก๋ถํฐ ํ ๋ฒ ๊ณต๊ฒฉ์ ๋ฐ์ง๋ง ๋ธ๋ผ์ฐ์ ๋ด์ ์ด๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๋ ๋ถ๋ถ์ด ์ฌ๋ฌ ๊ฐ๋ผ๋ ๊ฒ์ด๋ค. ํ๋์ ๋ช ๋ น(=๊ณต๊ฒฉ ๋ช ๋ น)์ ๋ฐ์์ ๋, (์ฝ๋ ์ด๋๊ฐ์..) ๋ ๋ถ๋ถ์์ Board๋ผ๋ ํ ๊ฐ์ฒด๋ฅผ ์กฐ์ํ ์๋ ์๋ค๋ ํ๋จ์ด ๋ค์๋ค. ์ถฉ๋ถํ ์ผ๋ฆฌ๊ฐ ์์๊ณ , ๋ ๋๋ง ์๋๊ฐ ๋๋ ค์ง๋ ์ด์ ๊ฐ ๋ ์ ์์๋ค.
useEffect(() => {
const roomCode = getRoomCode();
const connectWebSocket = async () => {
if (!wsManagerRef.current) { // WS ์ค๋ณต ์ฐ๊ฒฐ ๋ฐฉ์ง
wsManagerRef.current = new WebSocketManager();
try {
await wsManagerRef.current.connect(
"https://api.checkmatejungle.shop/ws",
);
subscribeToEntering(roomCode);
} catch (error) {
console.error("Failed to connect to WebSocket", error);
}
}
};
connectWebSocket();
}, []);
`wsManagerRef`๊ฐ ํ ๋ฒ ํ ๋น๋์๋ค๋ฉด ๋ ์ด์ ํ ๋น๋์ง ์๊ฒ ์กฐ๊ฑด์ ๊ฑธ์๋ค. ์ด๋ ๊ฒ wsManagerRef๊ฐ ๋ฐ๋ณต ์์ฑ๋๋ ๊ฒ์ ๋ง์ ๋ ๋๋ง ์๋๊ฐ ๋ฆ์ด์ง๋ ์ด์, ๊ฒ์ ์ฌ์์ ์ ๊ณต๊ฒฉ์ด ๋ ๋ฒ ์ ์ฉ๋๋ ์ด์, ๊ฒ์์ ํ ๋๋ง๋ค ๋ธ๋ผ์ฐ์ ๊ฐ ์ฐจ์งํ๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋์ด๋๋ ์ด์ ์ธ ๊ฐ๋ฅผ ๋ชจ๋ ํด๊ฒฐํ ์ ์์๋ค.
4. ํด๊ฒฐ โญ
Play Again ๋ฒํผ์ ๋๋ฅด๋ฉด์ ๋ฐ๋ณต์ ์ผ๋ก wsManagerRef์ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ํ ๋นํ๋ ๋ก์ง์ผ๋ก ์ธํ ๋ฒ๊ทธ์๋ค. ๊ทธ๋ฐ๋ฐ, ์๋ฐ์คํฌ๋ฆฝํธ์ garbage collector๋ ๋ญ ํ๋๋ผ ์ด์ ๊ฒ์์์ ์์ฑํ wsManagerRef๋ฅผ Sweepํ์ง ๋ชป ํ์๊น? ๊ทธ ์ด์ ๋ wsManagerRef์ ๋จ์ ์๋ ์ฝ๋ฐฑํจ์ ๋๋ฌธ์ด์๋ค.
try {
if (!isSub.current) {
wsManagerRef.current?.subscribe( // SUBSCRIBE ์ฝ๋ฐฑํจ์ ์กด์ฌ
`/user/queue/tetris/${roomCode}`,
(message: {
board: TetrisBoard;
isEnd: boolean;
isAddAttack: boolean;
isFlipAttack: boolean;
isDonutAttack: boolean;
}) => {
if (tetrisGameRef.current) {
if (message.isEnd) {
tetrisGameRef.current.gameEnd = true;
stopAllMusic();
playSoundEffect("/sound/winner.mp3");
setGameResult("you WIN!");
}
if (message.isAddAttack) {
tetrisGameRef.current.isAddAttacked = true;
} else if (message.isFlipAttack) {
setIsFlipping(true);
const playOppTetrisElement =
document.getElementById("tetris-container");
if (
playOppTetrisElement &&
!playOppTetrisElement.classList.contains("flipped-canvas")
) {
playOppTetrisElement.classList.add("flipped-canvas");
...
}, 100);
}, 2900);
}
} else if (message.isDonutAttack) {
tetrisGameRef.current.isDonutAttacked = true;
}
tetrisGameRef.current.drawBoard2(message.board);
}
},
);
isSub.current = true;
}
...
์ ์ฝ๋์์ ์ ์ ์๋ฏ์ด subscribe ํ ๋ฉ์์ง๋ฅผ ์ฝ๋ฐฑ ํ๋ ํจ์๊ฐ wsManagerRef ์ธ์คํด์ค๋ฅผ ์ฐธ์กฐํ๊ณ ์๋ค. ์ฆ, ๋ช ์์ ์ผ๋ก ์ธ์คํด์ค๋ฅผ ํด์ ํ์ง ์์ผ๋ฉด garbage collector๋ ์ฝ๋ฐฑ ํจ์ ์ฐธ์กฐ๊ฐ ๋จ์ ์๊ธฐ์ ์ด ์ธ์คํด์ค๋ฅผ ํด์ ์ํค์ง ์๋๋ค. ๊ฒ์์ ์ฌ์์ํ ๋๋ง๋ค ์ด ์ธ์คํด์ค์ ๊ด๋ จ ์๋ ์์๋ค์ด ์์ฌ์ ๋ฒ๊ทธ๋ฅผ ์ผ์ผ์ผฐ๋ ๊ฒ์ผ๋ก Issue๋ฅผ ๋ง๋ฌด๋ฆฌํ์๋ค.
'๋๋ง์ ๋ฌด๊ธฐ๋ฅผ ์ค๋นํ๊ธฐ' ๊ธฐ๊ฐ์ด ๋๋ ํ์๋ผ๋ ์ด ๊ธ์ ์ฐ๋ ์ด์ ๋ 'ํธ๋ํธ๋ฆฌ์ค'๋ฅผ ๊ฐ๋ฐํ๋ฉฐ ๋๊ผ๋ ์ง๋ฆฟํ๋ ๊ฐ์ ์ ์์ง ์๊ธฐ ์ํด์์ด๋ค.
์๋ ค๊ณ ๊ธฐ์์ฌ ์นจ๋์ ๋์ ๋ค๊ฐ ๋ฒ๊ทธ๊ฐ ๋จธ๋ฆฟ์์ ๋งด๋์ ๋ค์ ๊ฐ์์ค๋ก ๋์์๋ ๋ , ๊ทธ๋ ์๋ฒฝ์ ํด๊ฒฐํ ๋ฒ๊ทธ๋ ์์ง ๋ชปํ ์์คํ ๊ฒฝํ์ด ๋๋ค.
์๋ ๊ตฌํ์ด ๋ถ๊ฐ๋ฅํ๊ฐ ์ถ์ ์ ๋๋ก ์ ๋ ๊ฒ ๊ฐ์ ๊ธฐ๋ฅ์ ๊ตฌํํด ๋ธ ๋ ๋๊ผ๋ ๋ฟ๋ฏํจ์ ๊ทธ ์ด๋ค ์ฑ์ทจ๋ณด๋ค ์ปธ์๋ค. ๋ด๊ฐ ๋๋ฌด ๋๊ฒฌํ๊ฒ ๋๊ปด์ก๋ ๋ ์ด๋ค.
๊ฐ๋ฐ์๋ก ์ปค๋ฆฌ์ด๋ฅผ ์ด์ด๊ฐ๋ฉฐ ํ๋ค๊ณ , ์์ํ ๋ ๋ ๋ง์ ํ ์ง๋ง ์ด๋ ๋๊ผ๋ ๊ฐ์ ์ ๊ธฐ์ตํ๋ฉฐ ๊พธ์คํ ์์ผ๋ก ๋์๊ฐ์ผ๋ฉด ์ข๊ฒ ๋ค.
๋๋ง๋ฌด ๋