๐๏ธ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋์: ๋ฉ๋ชจ๋ฆฌ ๋์์ ์ฑ๋ฅ ์ ํ์ ์ฃผ๋ฒ
Summary
๋ ์ด์ ํ์ ์๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํ์ง ์์ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์์ ์ฑ๋ฅ ์ ํ๋ฅผ ์ผ๊ธฐํฉ๋๋ค. addEventListener
์ removeEventListener
๋ฅผ ํญ์ ์์ผ๋ก ์ฌ์ฉํ๊ณ , ํนํ React์์๋ useEffect
์ ํด๋ฆฐ์
ํจ์๋ฅผ ํ์ฉํ์ฌ ๋ฆฌ์ค๋๋ฅผ ์ฒด๊ณ์ ์ผ๋ก ๊ด๋ฆฌํด์ผ ํฉ๋๋ค.
Why Wrong?
์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ ํ ๋ช ์์ ์ผ๋ก ์ ๊ฑฐํ์ง ์์ผ๋ฉด, ํด๋น ๋ฆฌ์ค๋(๋ฐ ๊ด๋ จ ํด๋ก์ ๋ฒ์)๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ๊ณ์ ๋จ์์์ด ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ์ ๋ฐํฉ๋๋ค. ํนํ Single Page Application(SPA)์์ ์ปดํฌ๋ํธ๊ฐ ์์ฃผ ๋ง์ดํธ๋๊ณ ์ธ๋ง์ดํธ๋ ๋, ์ด์ ์ปดํฌ๋ํธ ์ธ์คํด์ค์ ์ฐ๊ฒฐ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ์ถ์ ๋์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ ์ ํ์ ์๊ธฐ์น ์์ ๋์์ ์ด๋ํฉ๋๋ค. ์ด๋ ๋๋ฒ๊น ์ ์ด๋ ต๊ฒ ๋ง๋ค๊ณ , ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ํ์ํค๋ ์ฃผ์ ์์ธ์ด ๋ฉ๋๋ค.
How to Fix?
์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ ๋๋ ํญ์ ํด๋นํ๋ removeEventListener
ํธ์ถ์ ์์ผ๋ก ๊ตฌ์ฑํ์ฌ, ๋ ์ด์ ํ์ ์์ ๋ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํด์ผ ํฉ๋๋ค. React์ ๊ฐ์ ํ๋ ์์ํฌ์์๋ useEffect
ํ
์ ํด๋ฆฐ์
(cleanup) ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธ๋๊ฑฐ๋ ์์กด์ฑ์ด ๋ณ๊ฒฝ๋ ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์๋์ผ๋ก ์ ๊ฑฐํ๋๋ก ํฉ๋๋ค. ๋๋ AbortController
๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ผ๊ด์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์ ๊ฑฐํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์
๋๋ค. ์ด๋ก์จ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฐฉ์งํ๊ณ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ฆฌ์์ค ํจ์จ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
Before Code (Bad)
import React, { useEffect, useState } from 'react';
function ResizableBox() {
const [width, setWidth] = useState(window.innerWidth);
// โ ๋ฌธ์ : ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธ๋ ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ์ ๊ฑฐ๋์ง ์์ต๋๋ค.
// ์ด ์ปดํฌ๋ํธ ์ธ์คํด์ค๊ฐ DOM์์ ์ฌ๋ผ์ ธ๋, window resize ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ ๊ณ์ ๋จ์์์ต๋๋ค.
useEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
console.log('Window resized!');
};
window.addEventListener('resize', handleResize);
// return () => window.removeEventListener('resize', handleResize); // ์ด ํด๋ฆฐ์
๋ก์ง์ด ๋๋ฝ๋จ
}, []); // ๋น ์์กด์ฑ ๋ฐฐ์ด๋ก ์ปดํฌ๋ํธ ๋ง์ดํธ ์ ํ ๋ฒ๋ง ์คํ๋์ง๋ง, ์ธ๋ง์ดํธ ์ ํด๋ฆฐ์
์ด ์์
return (
<div style={{ border: '1px solid black', padding: '20px', margin: '20px' }}>
<h1>ํ์ฌ ์ฐฝ ๋๋น: {width}px</h1>
<p>์ฐฝ ํฌ๊ธฐ๋ฅผ ์กฐ์ ํด๋ณด์ธ์.</p>
</div>
);
}
export default ResizableBox;
// ์ด ์ปดํฌ๋ํธ๋ฅผ ์ฌ๋ฌ ๋ฒ ๋ง์ดํธํ๊ณ ์ธ๋ง์ดํธํ๋ฉด,
// `handleResize` ํจ์๊ฐ ๊ณ์ํด์ ๋ฉ๋ชจ๋ฆฌ์ ๋จ์์๊ณ ,
// window resize ์ด๋ฒคํธ ๋ฐ์ ์ ์ด์ ์ ๋ง์ดํธ๋์๋ ๋ชจ๋ ์ปดํฌ๋ํธ์ handleResize ํจ์๊ฐ ํธ์ถ๋์ด
// ๋ถํ์ํ ์ฐ์ฐ๊ณผ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๋ฅผ ์ด๋ํฉ๋๋ค.
After Code (Good)
import React, { useEffect, useState } from 'react';
function ResizableBox() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
console.log('Window resized!');
};
// โ
์ฌ๋ฐ๋ฅธ ํด๊ฒฐ์ฑ
: ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ๊ณ , ์ปดํฌ๋ํธ ์ธ๋ง์ดํธ ์ ์ ๊ฑฐํฉ๋๋ค.
window.addEventListener('resize', handleResize);
// useEffect์ ํด๋ฆฐ์
ํจ์๋ ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธ๋๊ฑฐ๋
// ๋ค์ ๋ ๋๋ง ์ ์์กด์ฑ ๋ฐฐ์ด์ด ๋ณ๊ฒฝ๋๊ธฐ ์ ์ ํธ์ถ๋ฉ๋๋ค.
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // ๋น ์์กด์ฑ ๋ฐฐ์ด๋ก ์ปดํฌ๋ํธ ๋ง์ดํธ ์ ํ ๋ฒ๋ง ์คํ ๋ฐ ์ธ๋ง์ดํธ ์ ํด๋ฆฐ์
๋ณด์ฅ
return (
<div style={{ border: '1px solid black', padding: '20px', margin: '20px' }}>
<h1>ํ์ฌ ์ฐฝ ๋๋น: {width}px</h1>
<p>์ฐฝ ํฌ๊ธฐ๋ฅผ ์กฐ์ ํด๋ณด์ธ์.</p>
</div>
);
}
export default ResizableBox;
// ์ด์ ์ด ์ปดํฌ๋ํธ๋ฅผ ๋ง์ดํธ/์ธ๋ง์ดํธํด๋
// ๋ฉ๋ชจ๋ฆฌ ๋์ ์์ด ๊น๋ํ๊ฒ ๊ด๋ฆฌ๋๋ฉฐ, ๋ถํ์ํ ํจ์ ํธ์ถ๋ ๋ฐฉ์ง๋ฉ๋๋ค.