์๋ก์ด `useEvent` ํ ๊ฐ๋ ์ ํ๋กํ์ผ๋งํ์ฌ React ์ฑ๋ฅ์ ๋ง์คํฐํ์ธ์. ์ด๋ฒคํธ ํธ๋ค๋ฌ ํจ์จ์ฑ์ ๋ถ์ํ๊ณ , ๋ณ๋ชฉ ํ์์ ์๋ณํ๋ฉฐ, ์ปดํฌ๋ํธ์ ์๋ต์ฑ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ฐ์ธ์.
React useEvent ์ฑ๋ฅ ํ๋กํ์ผ๋ง: ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ถ์ ์ฌ์ธต ํ๊ตฌ
์น ๊ฐ๋ฐ์ ๊ธ๋ณํ๋ ์ธ๊ณ์์ ์ฑ๋ฅ์ ๋จ์ํ ๊ธฐ๋ฅ์ด ์๋๋ผ ๊ธฐ๋ณธ์ ์ธ ์๊ตฌ ์ฌํญ์ ๋๋ค. ๋ค์ํ ์ฅ์น ๊ธฐ๋ฅ๊ณผ ๋คํธ์ํฌ ์๋๋ฅผ ๊ฐ์ง ์ ์ธ๊ณ ์ฌ์ฉ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋น ๋ฅด๊ณ ์ ๋์ ์ด๋ฉฐ ๋ฐ์์ ์ด๊ธฐ๋ฅผ ๊ธฐ๋ํฉ๋๋ค. React ๊ฐ๋ฐ์์๊ฒ ์ด๋ ์ปดํฌ๋ํธ๋ฅผ ์ง์์ ์ผ๋ก ์ต์ ํํ๊ณ , ๋ค์ ๋ ๋๋ง์ ์ต์ํํ๋ฉฐ, ์ฌ์ฉ์ ์ํธ ์์ฉ์ด ์ฆ๊ฐ์ ์ผ๋ก ๋๊ปด์ง๋๋ก ํ๋ ๋ฐฉ๋ฒ์ ๋์์์ด ๋ชจ์ํ๋ค๋ ์๋ฏธ์ ๋๋ค. ๊ฐ์ฅ ์ผ๋ฐ์ ์ด์ง๋ง, ์์ด๊ธฐ ์ฌ์ด ์ฑ๋ฅ ํ๋ ์์ญ ์ค ํ๋๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๊ด๋ จ์ด ์์ต๋๋ค.
React์ ์งํ๋ ๊ฐ๋ฐ์ ์ธ์ฒด ๊ณตํ ๋ฐ ์ฑ๋ฅ์ ์ง์์ ์ผ๋ก ํด๊ฒฐํด ์์ต๋๋ค. ํ
์ ์ปดํฌ๋ํธ ์์ฑ ๋ฐฉ์์ ํ์ ํ์ง๋ง, ํนํ useCallback ๋ฐ useMemo์ ๊ฐ์ ํ
์ ์ฌ์ฉํ ๋ฉ๋ชจ์ด์ ์ด์
๊ณผ ๊ด๋ จํ์ฌ ์๋ก์ด ํจํด๊ณผ ์ ์ฌ์ ์ธ ํจ์ ์ ๋์
ํ๊ธฐ๋ ํ์ต๋๋ค. ์์กด์ฑ ๋ฐฐ์ด๊ณผ ์ค๋๋ ํด๋ก์ ์ ๋ณต์ก์ฑ์ ๋์ํ์ฌ React ํ์ ์๋ก์ด ํ
์ธ useEvent๋ฅผ ์ ์ํ์ต๋๋ค.
useEvent๋ ์์ง React์ ์์ ์ ์ธ ๋ฒ์ ์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ ์ต์ข
ํํ๊ฐ ๋ณ๊ฒฝ๋ ์ ์์ง๋ง, ์ด ๊ฐ๋
์ด ๋ํ๋ด๋ ๋ฐ๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฐ ๋ฉ๋ชจ์ด์ ์ด์
์ ๋ํด ์๊ฐํ๋ ๋ฐฉ์์ ๊ฒ์ ์ฒด์ธ์ ์
๋๋ค. ์ด ๊ธฐ์ฌ์์๋ useEvent๋ฅผ ๊ฐ์ด๋๋ก ์ฌ์ฉํ์ฌ ์ด๋ฒคํธ ํธ๋ค๋ฌ ์ฑ๋ฅ์ ๋ถ์ํ๋ ์ฌ์ธต์ ์ธ ๋ด์ฉ์ ์ ๊ณตํฉ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์
์ ํ๋กํ์ผ๋งํ๊ณ , ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ์ธํด ๋ฐ์ํ๋ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ์๋ณํ๊ณ , ๊ฐ์์ ์ผ๋ก ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๋ ์ต์ ํ ๊ธฐ์ ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
ํต์ฌ ๋ฌธ์ ์ดํด: ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ฐ ๋ฉ๋ชจ์ด์ ์ด์ ๋ถ์์ ์ฑ
useEvent๊ฐ ์ ์ํ๋ ์๋ฃจ์
์ ์ดํดํ๋ ค๋ฉด ๋จผ์ ํด๊ฒฐํ๋ ค๋ ๋ฌธ์ ๋ฅผ ์ดํดํด์ผ ํฉ๋๋ค. JavaScript์์ ํจ์๋ ์ผ๊ธ ์๋ฏผ์
๋๋ค. ์ฆ, ๋ค๋ฅธ ๊ฐ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์์ฑ, ์ ๋ฌ ๋ฐ ๋ฐํ๋ ์ ์์ต๋๋ค. React์์ ์ด๋ฌํ ์ ์ฐ์ฑ์ ๊ฐ๋ ฅํ์ง๋ง ์ฑ๋ฅ ๋น์ฉ์ด ๋ฐ์ํฉ๋๋ค.
์ ํ์ ์ธ ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ์๊ฐํด ๋ณด์ธ์. ๋ค์ ๋ ๋๋ง๋ ๋๋ง๋ค ๋ณธ๋ฌธ ๋ด๋ถ์ ์ ์๋ ํจ์๊ฐ ๋ค์ ์์ฑ๋ฉ๋๋ค. JavaScript์ ๊ด์ ์์ ๋ณด๋ฉด, ๋ ํจ์๊ฐ ์ ํํ ๋์ผํ ์ฝ๋๋ฅผ ๊ฐ์ง๊ณ ์๋๋ผ๋ ๋ฉ๋ชจ๋ฆฌ์์๋ ๋ค๋ฅธ ๊ฐ์ฒด์ ๋๋ค. ์๋ก ๋ค๋ฅธ ์์ด๋ดํฐํฐ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
ํจ์ ์์ด๋ดํฐํฐ๊ฐ ์ค์ํ ์ด์
์ด๋ฌํ ์ฌ์ฐฝ์กฐ๋ ์ด๋ฌํ ํจ์๋ฅผ ์์ ์ปดํฌ๋ํธ์ props๋ก ์ ๋ฌํ ๋, ํนํ React.memo๋ก ๋ํ๋ ์ปดํฌ๋ํธ์ props๋ก ์ ๋ฌํ ๋ ๋ฌธ์ ๊ฐ ๋ฉ๋๋ค. React.memo๋ props๊ฐ ๋ณ๊ฒฝ๋์ง ์์ ๊ฒฝ์ฐ ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง๋๋ ๊ฒ์ ๋ฐฉ์งํ๋ ๊ณ ์ฐจ ์ปดํฌ๋ํธ์
๋๋ค. ์ด์ props์ ์ props๋ฅผ ์๊ฒ ๋น๊ตํฉ๋๋ค. ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์๋ก ์์ฑ๋ ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์
๋ ์์์๊ฒ ์ ๋ฌํ๋ฉด prop ๊ฒ์ฌ๊ฐ ์คํจํ์ฌ(oldFunction !== newFunction์ด๊ธฐ ๋๋ฌธ์) ์์์ด ๋ถํ์ํ๊ฒ ๋ค์ ๋ ๋๋ง๋๋๋ก ํฉ๋๋ค.
๊ณ ์ ์ ์ธ ์๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
const MemoizedButton = React.memo(({ onClick, children }) => {
console.log(`Rendering ${children}`);
return <button onClick={onClick}>{children}</button>;
});
function Counter() {
const [count, setCount] = useState(0);
const [otherState, setOtherState] = useState(false);
// This function is re-created on EVERY render of Counter
const handleIncrement = () => {
setCount(c => c + 1);
};
return (
<div>
<p>Count: {count}</p>
<MemoizedButton onClick={handleIncrement}>
Increment Count
</MemoizedButton>
<button onClick={() => setOtherState(s => !s)}>
Toggle Other State ({String(otherState)})
</button>
</div>
);
}
์ด ์์ ์์๋ "Toggle Other State"๋ฅผ ํด๋ฆญํ ๋๋ง๋ค Counter ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง๋ฉ๋๋ค. ์ด๋ก ์ธํด handleIncrement๊ฐ ๋ค์ ์์ฑ๋ฉ๋๋ค. ์นด์ดํธ๋ฅผ ์ฆ๊ฐ์ํค๋ ๋
ผ๋ฆฌ๊ฐ ๋ณ๊ฒฝ๋์ง ์์์์๋ ๋ถ๊ตฌํ๊ณ , ์ ํจ์๊ฐ MemoizedButton์ ์ ๋ฌ๋์ด ๋ฉ๋ชจ์ด์ ์ด์
์ด ์ค๋จ๋๊ณ ๋ค์ ๋ ๋๋ง๋ฉ๋๋ค. ๋ฒํผ๊ณผ ๊ด๋ จ๋ ๋ณ๊ฒฝ ์ฌํญ์ด ์๋๋ฐ๋ ์ฝ์์ "Rendering Increment Count"๊ฐ ํ์๋ฉ๋๋ค.
useCallback ์๋ฃจ์
๊ณผ ๊ทธ ํ๊ณ
์ด ๋ฌธ์ ์ ๋ํ ์ ํต์ ์ธ ํด๊ฒฐ์ฑ
์ useCallback ํ
์
๋๋ค. ํจ์ ์์ฒด๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์
ํ์ฌ ์์กด์ฑ์ด ๋ณ๊ฒฝ๋์ง ์๋ ํ ์์ด๋ดํฐํฐ๊ฐ ์์ ์ ์ผ๋ก ์ ์ง๋๋๋ก ํฉ๋๋ค.
import { useState, useCallback } from 'react';
// ... inside Counter component
const handleIncrement = useCallback(() => {
setCount(c => c + 1);
}, []); // Empty dependency array, function is created only once
์ด๊ฒ์ ์๋ํฉ๋๋ค. ํ์ง๋ง ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ props ๋๋ state์ ์ก์ธ์คํด์ผ ํ๋ ๊ฒฝ์ฐ๋ ์ด๋ป์ต๋๊น? ์ด๋ฅผ ์์กด์ฑ ๋ฐฐ์ด์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
function UserProfile({ userId }) {
const [comment, setComment] = useState('');
const handleSubmitComment = useCallback(() => {
// This function needs access to userId and comment
postCommentAPI(userId, { text: comment });
}, [userId, comment]); // Dependencies
return <CommentBox onSubmit={handleSubmitComment} />;
}
์ฌ๊ธฐ์ ๋ณต์ก์ฑ์ด ์์ต๋๋ค. comment๊ฐ ๋ณ๊ฒฝ๋๋ ์ฆ์ useCallback์ ์ handleSubmitComment ํจ์๋ฅผ ์์ฑํฉ๋๋ค. CommentBox๊ฐ ๋ฉ๋ชจ์ด์ ์ด์
๋ ๊ฒฝ์ฐ, ์ฃผ์ ํ๋์์ ํค๋ฅผ ๋๋ฅผ ๋๋ง๋ค ๋ค์ ๋ ๋๋ง๋ฉ๋๋ค. ์ฐ๋ฆฌ๋ ํ๋์ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ๋ค๋ฅธ ๋ฌธ์ ๋ก ๋ฐ๊พธ์์ต๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก useEvent ์ ์์ด ๋ชฉํ๋ก ํ๋ ๊ณผ์ ์
๋๋ค.
useEvent ๊ฐ๋
์๊ฐ: ์์ ์ ์ธ ์์ด๋ดํฐํฐ, ์๋ก์ด ์ํ
React ํ์ด ์ ์ํ useEvent ํ
์ ํญ์ ์์ ์ ์ธ ์์ด๋ดํฐํฐ(๋ค์ ๋ ๋๋ง๋ ๋๋ง๋ค ๋ณ๊ฒฝ๋์ง ์์)๋ฅผ ๊ฐ์ง์ง๋ง ํญ์ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์ต์ ์ "์๋ก์ด" ์ํ์ props์ ์ก์ธ์คํ ์ ์๋ ํจ์๋ฅผ ์์ฑํ๋๋ก ์ค๊ณ๋์์ต๋๋ค. ํจ์์ ์์ด๋ดํฐํฐ๋ฅผ ๊ตฌํ์ผ๋ก๋ถํฐ ์ฐ์ํ๊ฒ ๋ถ๋ฆฌํฉ๋๋ค.
๊ฐ๋ ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
// This is a conceptual example. `useEvent` is not yet in stable React.
import { useEvent } from 'react';
function ChatRoom({ theme }) {
const [text, setText] = useState('');
const onSend = useEvent(() => {
// Can access the latest 'text' and 'theme' without
// needing them in a dependency array.
sendMessage(text, theme);
});
// Because `onSend` has a stable identity, MemoizedSendButton
// will not re-render just because `text` or `theme` changes.
return <MemoizedSendButton onClick={onSend} />;
}
ํต์ฌ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ๋ด๋ถ์ ์ผ๋ก ์ต์ ๋ก์ง์ ๊ฐ๋ฆฌํค๋ ์์ ์ ์ธ ํจ์ ์ฐธ์กฐ์ ๋๋ค. ์ด๊ฒ์ ๋ฉ๋ชจ์ด์ ์ด์ ๋ ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง๋๋๋ก ํ๋ ์์กด์ฑ ์ฒด์ธ์ ๋์ด ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์๋นํ ์ฑ๋ฅ ํฅ์์ ์ด๋์ด๋ ๋๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๋ํ ์ฑ๋ฅ ํ๋กํ์ผ๋ง์ด ์ค์ํ ์ด์
useEvent ๊ฐ๋
์ ์ฃผ๋ก ๋ถ์์ ํ ํจ์ ์์ด๋ดํฐํฐ๋ก ์ธํ ๋ค์ ๋ ๋๋ง์ ์ฑ๋ฅ ๋น์ฉ์ ํด๊ฒฐํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ ์ฑ๋ฅ์ ๋ ๋ค๋ฅธ, ๋ง์ฐฌ๊ฐ์ง๋ก ์ค์ํ ์ธก๋ฉด์ด ์์ต๋๋ค. ๋ฐ๋ก ํธ๋ค๋ฌ ์์ฒด์ ์คํ ์๊ฐ์
๋๋ค.
๋๋ฆฐ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๋ถํ์ํ ๋ค์ ๋ ๋๋ง๋ณด๋ค ์ฌ์ฉ์ ๊ฒฝํ์ ํจ์ฌ ๋ ํด๋ก์ธ ์ ์์ต๋๋ค. JavaScript๋ ๋ธ๋ผ์ฐ์ ์ ๋จ์ผ ๋ฉ์ธ ์ค๋ ๋์์ ์คํ๋๋ฏ๋ก, ์ค๋ ์คํ๋๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ์ด ์ค๋ ๋๋ฅผ ์ฐจ๋จํ ์ ์์ต๋๋ค. ์ด๋ก ์ธํด ๋ค์์ด ๋ฐ์ํฉ๋๋ค.
- ๋๊ธฐ๋ UI: ๋ธ๋ผ์ฐ์ ๊ฐ ์ ํ๋ ์์ ๊ทธ๋ฆด ์ ์์ผ๋ฏ๋ก ์ ๋๋ฉ์ด์ ์ด ๋ฉ์ถ๊ณ ์คํฌ๋กค์ด ๋๊น๋๋ค.
- ์๋ตํ์ง ์๋ ์ปจํธ๋กค: ํด๋ฆญ, ํค ๋๋ฆ ๋ฐ ๊ธฐํ ์ฌ์ฉ์ ์ ๋ ฅ์ด ๋๊ธฐ์ด์ ์ถ๊ฐ๋์ด ํธ๋ค๋ฌ๊ฐ ์๋ฃ๋ ๋๊น์ง ์ฒ๋ฆฌ๋์ง ์์ผ๋ฏ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฉ์ถ ๊ฒ์ฒ๋ผ ๋๊ปด์ง๋๋ค.
- ์๋ชป๋ ์ธ์๋ ์ฑ๋ฅ: ์์ ์ด ๊ฒฐ๊ตญ ์๋ฃ๋๋๋ผ๋ ์ด๊ธฐ ์ง์ฐ ๋ฐ ํผ๋๋ฐฑ ๋ถ์กฑ์ผ๋ก ์ธํด ์ค๋ง์ค๋ฌ์ด ์ฌ์ฉ์ ๊ฒฝํ์ด ๋ฐ์ํฉ๋๋ค.
์ด๊ฒ์ด ๋ฐ๋ก ํ๋กํ์ผ๋ง์ด ์ ๋ฌธ ๊ฐ๋ฐ์์๊ฒ ์ ํ์ ์ธ ๋จ๊ณ๊ฐ ์๋๋ผ ๊ฐ๋ฐ ์๋ช ์ฃผ๊ธฐ์ ์ค์ํ ๋ถ๋ถ์ธ ์ด์ ์ ๋๋ค. ์ฐ๋ฆฌ๋ ์ฑ๋ฅ์ ๋ํ ์ถ์ธก์์ ๋ฒ์ด๋ ์ด๋ฅผ ์ ํํ๊ฒ ์ธก์ ํด์ผ ํฉ๋๋ค.
๊ฑฐ๋์ ๋๊ตฌ: React์์ ์ด๋ฒคํธ ํธ๋ค๋ฌ ํ๋กํ์ผ๋ง
๋ค์ ๋ ๋๋ง๊ณผ ์คํ ์๊ฐ์ ๋ชจ๋ ๋ถ์ํ๊ธฐ ์ํด ๋ธ๋ผ์ฐ์ ์ ๊ฐ๋ฐ์ ๋๊ตฌ์์ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋ ๋ ๊ฐ์ง ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
1. React ํ๋กํ์ผ๋ฌ(React DevTools์์)
React ํ๋กํ์ผ๋ฌ๋ ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง๋๋ ์ด์ ์ ์๊ธฐ๋ฅผ ์๋ณํ๊ธฐ ์ํ ๋๊ตฌ์ ๋๋ค. ๋ ๋๋ง ํ๋ก์ธ์ค๋ฅผ ์๊ฐํํ์ฌ ์ด๋ค ์ปดํฌ๋ํธ๊ฐ ์ ๋ฐ์ดํธ๋์๊ณ ์ผ๋ง๋ ๊ฑธ๋ ธ๋์ง ๋ณด์ฌ์ค๋๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ:
- React DevTools๊ฐ ์ค์น๋ ๋ธ๋ผ์ฐ์ ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฝ๋๋ค.
- "Profiler" ํญ์ผ๋ก ์ด๋ํฉ๋๋ค.
- ๋ นํ ๋ฒํผ(ํ๋์ ์)์ ํด๋ฆญํฉ๋๋ค.
- ์ฑ์์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ํธ๋ฆฌ๊ฑฐํ๋ ์์ ์ ์ํํฉ๋๋ค(์: ๋ฒํผ ํด๋ฆญ).
- ๋ นํ๋ฅผ ์ค์งํฉ๋๋ค.
์ปดํฌ๋ํธ์ ํ์ผ ์ฐจํธ๊ฐ ํ์๋ฉ๋๋ค. ๋ค์ ๋ ๋๋ง๋ ์ปดํฌ๋ํธ๋ฅผ ํด๋ฆญํ๋ฉด ์ค๋ฅธ์ชฝ์ ์๋ ํจ๋์ ์ ๋ค์ ๋ ๋๋ง๋์๋์ง ํ์๋ฉ๋๋ค. prop ๋ณ๊ฒฝ์ผ๋ก ์ธํ ๊ฒ์ด๋ผ๋ฉด, ์ด๋ค prop์ด ๋ณ๊ฒฝ๋์๋์ง ํ์ธํ ์ ์์ต๋๋ค. ์ด๋ฒคํธ ํธ๋ค๋ฌ prop์ด ๋ชจ๋ ๋ถ๋ชจ ๋ ๋๋ง์์ ๋ณ๊ฒฝ๋๋ ๊ฒฝ์ฐ, ์ด ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์ฆ์ ๋ช ํํด์ง๋๋ค.
2. ๋ธ๋ผ์ฐ์ ์ Performance ํญ(์: Chrome DevTools์์)
React ํ๋กํ์ผ๋ฌ๋ React ๊ด๋ จ ๋ฌธ์ ์ ์ ์ฉํ์ง๋ง, ๋ธ๋ผ์ฐ์ ์ Performance ํญ์ ์์ JavaScript ์คํ ์๊ฐ์ ์ธก์ ํ๋ ์ต๊ณ ์ ๋๊ตฌ์ ๋๋ค. ์คํฌ๋ฆฝํธ ์คํ์์ ๋ ๋๋ง ๋ฐ ํ์ธํ ๊น์ง ๋ฉ์ธ ์ค๋ ๋์์ ๋ฐ์ํ๋ ๋ชจ๋ ๊ฒ์ ๋ณด์ฌ์ค๋๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ ์คํ์ ํ๋กํ์ผ๋งํ๋ ๋ฐฉ๋ฒ:
- ๋ธ๋ผ์ฐ์ ์ DevTools๋ฅผ ์ด๊ณ "Performance" ํญ์ผ๋ก ์ด๋ํฉ๋๋ค.
- ๋ นํ ๋ฒํผ์ ํด๋ฆญํฉ๋๋ค.
- ์ฑ์์ ์์ ์ ์ํํฉ๋๋ค(์: ๋ฌด๊ฑฐ์ด ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ์๋ ๋ฒํผ ํด๋ฆญ).
- ๋ นํ๋ฅผ ์ค์งํฉ๋๋ค.
- ํ์ผ ์ฐจํธ๋ฅผ ๋ถ์ํฉ๋๋ค. "Task"๋ผ๋ ๊ธด ๋ง๋๋ฅผ ์ฐพ์ต๋๋ค. ์ด ์์ ๋ด์์ ์ด๋ฒคํธ ๋ฆฌ์ค๋(์: "Event: click")์ ํธ๋ฆฌ๊ฑฐ๋ ํจ์ ํธ์ถ ์คํ์ด ํ์๋ฉ๋๋ค. ์คํ์์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ฐพ์ ์คํํ๋ ๋ฐ ์ ํํ ๋ช ๋ฐ๋ฆฌ์ด๊ฐ ๊ฑธ๋ ธ๋์ง ํ์ธํฉ๋๋ค. 50ms๋ณด๋ค ๊ธด ๋ชจ๋ ์์ ์ ์ฌ์ฉ์ ์ธ์ง ๊ฐ๋ฅ์ฑ์ด ์๋ ๋๊น์ ์ ์ฌ์ ์์ธ์ ๋๋ค.
์ค์ฉ์ ์ธ ํ๋กํ์ผ๋ง ์๋๋ฆฌ์ค: ๋จ๊ณ๋ณ ๋ถ์
์ด๋ฌํ ๋๊ตฌ๊ฐ ์๋ํ๋ ๊ฒ์ ๋ณด๊ธฐ ์ํด ์๋๋ฆฌ์ค๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ๊ฐ ํ์ ์์ ๋ฒํผ์ด ์๋ ๋ฐ์ดํฐ ํ ์ด๋ธ์ด ์๋ ๋ณต์กํ ๋์๋ณด๋๋ฅผ ์์ํด ๋ณด์ธ์.
์ปดํฌ๋ํธ ์ค์
"after" ์ฌ๋ก์ ๋ํด useEvent์ ๋์์ ์๋ฎฌ๋ ์ด์
ํ๋ ์ฌ์ฉ์ ์ง์ ํ
์ด ํ์ํฉ๋๋ค. ์ด๋ ์ฝ๋ฐฑ์ ์ต์ ๋ฒ์ ์ ์ ์ฅํ๊ธฐ ์ํด ref๋ฅผ ํ์ฉํ๋ ๋๋ฆฌ ์ฌ์ฉ๋๋ ํจํด์
๋๋ค.
import { useLayoutEffect, useRef, useCallback } from 'react';
// A custom hook to simulate the `useEvent` proposal
function useEventCallback(fn) {
const ref = useRef(null);
useLayoutEffect(() => {
ref.current = fn;
});
return useCallback((...args) => {
return ref.current(...args);
}, []);
}
์ด์ ์ ํ๋ฆฌ์ผ์ด์ ์ปดํฌ๋ํธ์ ๋๋ค.
// A memoized child component
const ActionButton = React.memo(({ onAction, label }) => {
console.log(`Rendering button: ${label}`);
return <button onClick={onAction}>{label}</button>;
});
// The parent component
function Dashboard() {
const [searchTerm, setSearchTerm] = useState('');
const [items] = useState([...Array(100).keys()]); // 100 items
// **Scenario 1: The problematic inline function**
const handleAction = (id) => {
// Imagine this is a complex, slow function
console.log(`Action for item ${id} with search: \"${searchTerm}\"`);
let sum = 0;
for (let i = 0; i < 10000000; i++) { // A deliberately slow operation
sum += Math.sqrt(i);
}
console.log('Action complete');
};
// **Scenario 2: The optimized `useEventCallback` function**
/*
const handleAction = useEventCallback((id) => {
console.log(`Action for item ${id} with search: \"${searchTerm}\"`);
let sum = 0;
for (let i = 0; i < 10000000; i++) {
sum += Math.sqrt(i);
}
console.log('Action complete');
});
*/
return (
<div>
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<div>
{items.map(id => (
<ActionButton
key={id}
// We pass a new function instance here on every render!
onAction={() => handleAction(id)}
label={`Action ${id}`}
/>
))}
</div>
</div>
);
}
๋ถ์ 1: ๋ค์ ๋ ๋๋ง ํ๋กํ์ผ๋ง
- ์ธ๋ผ์ธ ํจ์๋ก ์คํ:
onAction={() => handleAction(id)}. - React DevTools๋ก ํ๋กํ์ผ๋ง: ํ๋กํ์ผ๋ฌ๋ฅผ ์์ํ๊ณ , ๊ฒ์ ์ ๋ ฅ์ ๋จ์ผ ๋ฌธ์๋ฅผ ์ ๋ ฅํ๊ณ , ํ๋กํ์ผ๋ง์ ์ค์งํฉ๋๋ค.
- ๊ด์ฐฐ:
Dashboard์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋์๊ณ , ๊ฒฐ์ ์ ์ผ๋ก ๋ชจ๋ 100๊ฐ์ActionButton์ปดํฌ๋ํธ๋ ๋ค์ ๋ ๋๋ง๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ํ๋กํ์ผ๋ฌ๋onActionprop์ด ๋ณ๊ฒฝ๋์๊ธฐ ๋๋ฌธ์ด๋ผ๊ณ ๋งํฉ๋๋ค. ์ด๊ฒ์ ์์ฒญ๋ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ๋๋ค. - ์ด์
useEventCallback๋ฒ์ ์ผ๋ก ์ ํํฉ๋๋ค:handleAction์ ์ต์ ํ๋ ๋ฒ์ ์ ์ฃผ์ ํด์ ํ๊ณ prop์onAction={handleAction}์ผ๋ก ๋ณ๊ฒฝํฉ๋๋ค. ID๋ฅผ ์ ๋ฌํ๊ธฐ ์ํด ์์ ๋ํผ ์ปดํฌ๋ํธ๋ ์ปค๋ง์ ์์ฑํ์ฌ ์กฐ์ ํด์ผ ํ์ง๋ง, ์ด ๊ฐ๋ ์ ์ํด ์ฌ์ฉ์ ์ง์ ํ ์ ์ฌ์ฉํ์ฌ ์์ ์ฑ์ ํ์ํฉ๋๋ค. ํต์ฌ์ ์ ๋ฌ๋ ์ฐธ์กฐ๊ฐ ์์ ์ ์ด๋ผ๋ ๊ฒ์ ๋๋ค. - React DevTools๋ก ๋ค์ ํ๋กํ์ผ๋ง: ๋์ผํ ์์ ์ ์ํํฉ๋๋ค.
- ๊ด์ฐฐ:
Dashboard๊ฐ ๋ ๋๋ง๋์์ง๋งActionButton์ปดํฌ๋ํธ๊ฐ ์ ํ ๋ค์ ๋ ๋๋ง๋์ง ์์ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.handleAction์ด ์ด์ ์์ ์ ์ธ ์์ด๋ดํฐํฐ๋ฅผ ๊ฐ๊ธฐ ๋๋ฌธ์ ํด๋น prop์ด ๋ณ๊ฒฝ๋์ง ์์์ต๋๋ค. ์ฐ๋ฆฌ๋ ๋ค์ ๋ ๋๋ง ๋ฌธ์ ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ํด๊ฒฐํ์ต๋๋ค.
๋ถ์ 2: ํธ๋ค๋ฌ ์คํ ์๊ฐ ํ๋กํ์ผ๋ง
์ด์ handleAction ํจ์ ์์ฒด์ ์๋ ์ ํ์ ์ง์คํด ๋ณด๊ฒ ์ต๋๋ค. ๋น์ผ for ๋ฃจํ๋ ๋ฌด๊ฑฐ์ด ๋๊ธฐ ์์
์ ์๋ฎฌ๋ ์ด์
ํฉ๋๋ค.
- ์ต์ ํ๋
useEventCallback์ฝ๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. - Browser Performance ํญ์ผ๋ก ํ๋กํ์ผ๋ง: ๋ นํ๋ฅผ ์์ํ๊ณ , "Action" ๋ฒํผ ์ค ํ๋๋ฅผ ํด๋ฆญํ๊ณ , "Action complete" ๋ก๊ทธ๋ฅผ ๊ธฐ๋ค๋ฆฐ ํ ๋ นํ๋ฅผ ์ค์งํฉ๋๋ค.
- ๊ด์ฐฐ: ํ์ผ ์ฐจํธ์์ ๋งค์ฐ ๊ธด "Task"๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ํ๋ํ๋ฉด ํด๋ฆญ ์ด๋ฒคํธ ๋ค์์ ์ต๋ช
ํจ์ ํธ์ถ,
handleActionํจ์๊ฐ ์๋นํ ์๊ฐ(์๋ฐฑ ๋ฐ๋ฆฌ์ด)์ ์ฐจ์งํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ์ด ์๊ฐ ๋์ ์ ์ฒด UI๊ฐ ๊ณ ์ ๋์์ต๋๋ค. ๋ค๋ฅธ ๊ฒ์ ํด๋ฆญํ๊ฑฐ๋ ํ์ด์ง๋ฅผ ์คํฌ๋กคํ ์ ์์์ต๋๋ค. ์ด๊ฒ์ ๋ฉ์ธ ์ค๋ ๋ ์ฐจ๋จ ์์ ์ ๋๋ค.
ํธ๋ค๋ฌ ์คํ ์ต์ ํ
๋ณ๋ชฉ ํ์์ ์๋ณํ๋ ๊ฒ์ด ์ ๋ฐ์ ์น๋ฆฌ์ ๋๋ค. ์ด์ ์ด๋ป๊ฒ ๊ณ ์น ๊น์? ์ ๋ต์ ์์ ์ ํน์ฑ์ ๋ฐ๋ผ ๋ค๋ฆ ๋๋ค.
- ๋๋ฐ์ด์ฑ/์ค๋กํ๋ง: ํด๋ฆญ์๋ ์ ์ฉ๋์ง ์์ง๋ง ๋ง์ฐ์ค ์ด๋ ๋๋ ์ฐฝ ํฌ๊ธฐ ์กฐ์ ๊ณผ ๊ฐ์ ๋น๋ฒํ ์ด๋ฒคํธ์ ํ์์ ์ ๋๋ค.
- ๋ด๋ถ ๊ณ์ฐ ๋ฉ๋ชจ์ด์ ์ด์
: ๋๋ฆฐ ๋ถ๋ถ์ด ์
๋ ฅ ๊ธฐ๋ฐ์ ์์ํ ๊ณ์ฐ์ธ ๊ฒฝ์ฐ, ์ปดํฌ๋ํธ ๋ด์์
useMemo๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ์บ์ํ ์ ์์ต๋๋ค. - ์์ ์ ์น ์์ปค๋ก ์ด๋: ์ด๋ ๋ฌด๊ฑฐ์ด ๋น UI ๊ด๋ จ ๊ณ์ฐ์ ์ด์์ ์ธ ์๋ฃจ์ ์ ๋๋ค. ์น ์์ปค๋ ๋ณ๋์ ์ค๋ ๋์์ ์คํ๋๋ฏ๋ก ๋ฉ์ธ UI ์ค๋ ๋๋ฅผ ์ฐจ๋จํ์ง ์์ต๋๋ค. ํ์ํ ๋ฐ์ดํฐ๋ฅผ ์์ปค์ ๊ฒ์ํ ์ ์์ผ๋ฉฐ, ์๋ฃ๋๋ฉด ๊ฒฐ๊ณผ์ ํจ๊ป ๋ฉ์์ง๋ฅผ ๋ค์ ๊ฒ์ํฉ๋๋ค.
- ์์
๋ถํ : ์น ์์ปค๊ฐ ๊ณผ๋ํ ๊ฒฝ์ฐ,
setTimeout(..., 0)์ ์ฌ์ฉํ์ฌ ๊ธด ์์ ์ ๋ ์์ ์ฒญํฌ๋ก ๋๋ ์ ์์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ฒญํฌ ์ฌ์ด์ ๋ธ๋ผ์ฐ์ ๋ก ์ ์ด๊ฐ ๋ฐํ๋์ด ๋ค๋ฅธ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ณ UI๋ฅผ ๋ฐ์์ ์ผ๋ก ์ ์งํ ์ ์์ต๋๋ค.
๊ณ ์ฑ๋ฅ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ํ ๋ชจ๋ฒ ์ฌ๋ก
๋ถ์์ ๊ธฐ๋ฐ์ผ๋ก ์ ์ธ๊ณ ๊ฐ๋ฐ์๋ฅผ ์ํ ๋ชจ๋ฒ ์ฌ๋ก ์งํฉ์ ์ถ์ถํ ์ ์์ต๋๋ค.
- ํจ์ ์์ ์ฑ ์ฐ์ ์์ ์ง์ : ๋ฉ๋ชจ์ด์ ์ด์
๋ ์ปดํฌ๋ํธ์ ์ ๋ฌ๋ ๋ชจ๋ ํจ์์ ๋ํด ์์ ์ ์ธ ์์ด๋ดํฐํฐ๊ฐ ์๋์ง ํ์ธํฉ๋๋ค.
useCallback์ ์ ์คํ๊ฒ ์ฌ์ฉํ๊ฑฐ๋ ๊ณง ์ถ์๋useEvent์ ๋์์ ๋ชจ๋ฐฉํ๋useEventCallback์ฌ์ฉ์ ์ง์ ํ ๊ณผ ๊ฐ์ ํจํด์ ์ฑํํฉ๋๋ค. - Props์์ ์ธ๋ผ์ธ ํจ์ ๋ฐฉ์ง: ๋ฉ๋ชจ์ด์ ์ด์
๋ ์์์๊ฒ ์ ๋ฌํ๋ ์ปดํฌ๋ํธ์ JSX์์
onClick={() => doSomething()}์ ์ฌ์ฉํ์ง ๋ง์ญ์์ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ชจ๋ ๋ ๋๋ง์์ ์ ํจ์๊ฐ ๋ณด์ฅ๋ฉ๋๋ค. - ํธ๋ค๋ฌ๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ์ ์ง: ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๊ฐ๋ฒผ์ด ์ฝ๋๋ค์ดํฐ์ฌ์ผ ํฉ๋๋ค. ์์ ์ ์ด๋ฒคํธ๋ฅผ ์บก์ฒํ๊ณ ๋ฌด๊ฑฐ์ด ์์ ์ ๋ค๋ฅธ ๊ณณ์ผ๋ก ์์ํ๋ ๊ฒ์ ๋๋ค. ๋ณต์กํ ๋ฐ์ดํฐ ๋ณํ ๋๋ ์ฐจ๋จ API ํธ์ถ์ ํธ๋ค๋ฌ ๋ด์์ ์ง์ ์คํํ์ง ๋ง์ญ์์ค.
- ํ๋กํ์ผ๋งํ๊ณ ๊ฐ์ ํ์ง ๋ง์ญ์์ค: ์กฐ๊ธฐ ์ต์ ํ๋ ๋ง์ ๋ฌธ์ ์ ๊ทผ๋ณธ ์์ธ์ ๋๋ค. React ํ๋กํ์ผ๋ฌ ๋ฐ Browser Performance ํญ์ ์ฌ์ฉํ์ฌ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ๊ธฐ ์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ค์ ๋ณ๋ชฉ ํ์์ ์ฐพ์ผ์ญ์์ค.
- ์ด๋ฒคํธ ๋ฃจํ ์ดํด: ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๋๊ธฐ์ ์ด๊ณ ์ค๋ ์คํ๋๋ ๋ชจ๋ ์ฝ๋๋ ์ฌ์ฉ์ ๋ธ๋ผ์ฐ์ ํญ์ ๊ณ ์ ํ๋ค๋ ๊ฒ์ ๋ด๋ฉดํํฉ๋๋ค. ํญ์ ๋น๋๊ธฐ์ ์ผ๋ก ๋๋ ๋ฉ์ธ ์ค๋ ๋ ๋ฐ์์ ์์ ์ ์ํํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํ์ญ์์ค.
๊ฒฐ๋ก : React์์ ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๋ฏธ๋
์ฑ๋ฅ ๋ถ์์ ์ถ์์ ์ธ ๊ฒ(์ปดํฌ๋ํธ ๋ค์ ๋ ๋๋ง)์์ ๊ตฌ์ฒด์ ์ธ ๊ฒ(๋ฐ๋ฆฌ์ด ์คํ ์๊ฐ)์ผ๋ก์ ์ฌ์ ์
๋๋ค. useEvent ์ ์์ ๊ธฐ๋ณธ ์์น์ ์ด ์ฌ์ ์ ์ฒซ ๋ฒ์งธ ๋ถ๋ถ์ ๋ํ ๊ฐ๋ ฅํ ์ ์ ์ ๋ชจ๋ธ์ ์ ๊ณตํฉ๋๋ค. ๋ฉ๋ชจ์ด์ ์ด์
์ ๋จ์ํํ๊ณ ๋ณด๋ค ํ๋ ฅ์ ์ธ ์ปดํฌ๋ํธ ์ํคํ
์ฒ๋ฅผ ๊ตฌ์ถํ๋ ๊ฒ์
๋๋ค. ํจ์ ์์ด๋ดํฐํฐ๊ฐ ์์ ์ ์ธ์ง ํ์ธํ์ฌ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ดด๋กญํ๋ ๋ถํ์ํ ๋ค์ ๋ ๋๋ง์ ๊ฑฐ๋ํ ํด๋์ค๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
๊ทธ๋ฌ๋ ์ง์ ํ ์ฑ๋ฅ ๋ง์คํฐ๋ฆฌ๋ ์ฌ์ฉ์๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์ํธ ์์ฉํ ๋ ์คํ๋๋ ๋ฐ๋ก ๊ทธ ์ฝ๋์์ ๋ ๊น์ด ์ดํด๋ณด๋ ๊ฒ์ ์๊ตฌํฉ๋๋ค. ๋ธ๋ผ์ฐ์ ์ ์ฑ๋ฅ ํ๋กํ์ผ๋ฌ์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ํด๋ถํ๊ณ , ๋ฉ์ธ ์ค๋ ๋์ ๋ฏธ์น๋ ์ํฅ์ ์ธก์ ํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ด๋ฅผ ์ต์ ํํ๊ธฐ ์ํ ๊ฒฐ์ ์ ๋ด๋ฆด ์ ์์ต๋๋ค.
React๊ฐ ๊ณ์ ๋ฐ์ ํจ์ ๋ฐ๋ผ ๊ฐ๋ฐ์๊ฐ ๋ ๋์, ๋ ๋น ๋ฅธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ์ ์๋๋ก ์ง์ํ๋ ๋ฐ ์ด์ ์ ๋ง์ถ๊ณ ์์ต๋๋ค. ์ค๋๋ ์ด๋ฌํ ํ๋กํ์ผ๋ง ๊ธฐ์ ์ ์ดํดํ๊ณ ์ ์ฉํจ์ผ๋ก์จ ํ์ฌ์ ๋ฒ๊ทธ๋ฅผ ์์ ํ๋ ๊ฒ๋ฟ๋ง ์๋๋ผ, ์ฑ๋ฅ์ด ๋ฐ์ด๋๊ณ ์๋ต์ฑ์ด ๋ฐ์ด๋ ์ฌ์ฉ์ ์ธํฐํ์ด์ค๊ฐ ์์ธ๊ฐ ์๋ ํ์ค์ด ๋๋ ๋ฏธ๋๋ฅผ ์ค๋นํ๋ ๊ฒ์ ๋๋ค.