์ด๋ฒ ์ฅ์์๋ React์์ DOM์ ์ง์ ์ ๊ทผํด์ผ ํ ๋ ์ฌ์ฉํ๋ ๋๊ตฌ์ธ Ref์ ๋ํด ๋ค๋ฃฌ๋ค.
React์ ํฐ ์ฅ์ ์ค ํ๋๋ ์ค์ DOM์ ์ง์ ๋ค๋ฃจ๋ ๋ณต์กํ ๊ณผ์ ์ ๊ฐ๋ฐ์๊ฐ ์ ๊ฒฝ ์ฐ์ง ์์๋ ๋๋๋ก ์ถ์ํํด ์ค๋ค๋ ์ ์ด๋ค. getElementById๋ก DOM ์์๋ฅผ ์ฐพ๊ณ , ํด๋์ค๋ฅผ ์๋์ผ๋ก ์ถ๊ฐํ๊ฑฐ๋, ๋ธ๋ผ์ฐ์ ๊ฐ์ ์ฐจ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ์ผ ์์ด, ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๋ ๊ฒ๋ง์ผ๋ก UI๋ฅผ ๊ตฌ์ถํ ์ ์๋ค. ํ์ง๋ง ํฌ์ปค์ค๋ฅผ ์ฃผ๊ฑฐ๋, ์์๋ฅผ ์คํฌ๋กคํ๊ฑฐ๋, ์ฌ์ด์ฆ๋ฅผ ๊ณ์ฐํ๋ ๋ฑ์ ํน์ ์์ ์์๋ ์ค์ DOM์ ์ง์ ์ ๊ทผํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ๋๋ฐ, ์ด๋ ํ์ํ ๋๊ตฌ๊ฐ ๋ฐ๋ก Ref๋ค.
Ref vs State
๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด Ref๋ ๊ฐ์ ์ ์ฅํ์ง๋ง, ๊ทธ ๊ฐ์ด ๋ฐ๋๋ค๊ณ ํด์ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง ๋์ง ์๋๋ค. ๋ฐ๋ฉด state๋ ๊ฐ์ด ๋ฐ๋๋ฉด ๋ฆฌ๋ ๋๋ง์ ํธ๋ฆฌ๊ฑฐํ๋ค. ๊ทธ๋์ ํ๋ฉด์ ๋ฌด์ธ๊ฐ ๋ณด์ฌ์ฃผ๊ฑฐ๋, ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ก ๊ฐ์ ์ ๋ฌํด์ผ ํ๋ค๋ฉด state๋ฅผ ์จ์ผ ํ๊ณ , ๋จ์ํ ๋ด๋ถ์์ ์ ์ฅ๋ง ํ ๊ฑฐ๋ผ๋ฉด Ref๊ฐ ์ ํฉํ๋ค.
const Form = () => {
const ref = useRef();
const onChange = (e) => {
ref.current = e.target.value;
};
const submit = () => {
console.log(ref.current);
};
return (
<>
<input type="text" onChange={onChange} />
<button onClick={submit}>submit</button>
</>
);
};
์ ์์ ๋ Ref๋ฅผ ํตํด ๊ฐ์ ์ ์ฅํ๊ณ ์ ์ถ ์์ ์ ์ฌ์ฉํ๋๋ฐ, ๋ง์ฝ ํ๋ฉด์ ๊ฐ์ ๋ ๋๋งํด์ผ ํ๋ค๋ฉด Ref๋ง์ผ๋ก๋ ์ ๋๊ณ State๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
const numberOfLetters = ref.current?.length ?? 0;
๋ง์ฝ ์์ ๊ฐ์ด ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ฉด Ref๋ ๊ฐ์ด ๋ณํด๋ React๊ฐ ๋ค์ ๋ ๋๋ง ํ์ง ์๊ธฐ ๋๋ฌธ์ numberOfLetters ๊ฐ์ ํญ์ 0์ผ๋ก ๋ณด์ธ๋ค.
๋น์ทํ ๋งฅ๋ฝ์ผ๋ก Ref ๊ฐ์ props๋ก ํ์ ์ปดํฌ๋ํธ์ ์ ๋ฌํ๋ ๊ฒ๋ ๋์ํ์ง ์๋๋ค.
๋ ํ๋ ์ค์ํ ์ ์ Ref๋ ๋๊ธฐ์ ์ผ๋ก ๊ฐ์ด ๋ฐ์๋๊ณ , state๋ ๋น๋๊ธฐ์ ์ผ๋ก ๋์ํ๋ค๋ ์ ์ด๋ค.
const onChange = (e) => {
console.log("state before:", value);
setValue(e.target.value);
console.log("state after:", value); // ์ด์ ๊ฐ
console.log("ref before:", ref.current);
ref.current = e.target.value;
console.log("ref after:", ref.current); // ๋ฐ๋ก ๋ฐ์๋จ
};
return (
<div style={{ padding: 20 }}>
<input type="text" onChange={onChange} placeholder="ํ์ดํ ํด๋ณด์ธ์" />
</div>
);
crus
๋ผ๊ณ ์
๋ ฅ๋ ์ํ์์ h
๋ฅผ ์
๋ ฅํ๋ฉด, Ref๋ crush
๋ก ๋ฐ๋ก ๋ฐ์๋์ง๋ง, State๋ ์ฌ์ ํ crus
์ธ ์ํ๋ก ์ฝ์์ ์ฐํ๋ค.
ref.current๋ ๋๊ธฐ์ ์ผ๋ก ๋ฐ๋ก ์
๋ฐ์ดํธ๋๊ณ , useState๋ ๋ค์ ๋ ๋๋ง ์ ๊น์ง ๊ฐ์ด ์ ์ง๋๊ธฐ ๋๋ฌธ์ด๋ค.
Ref๋ฅผ ์์ ์ปดํฌ๋ํธ์ ์ ๋ฌํ๊ธฐ (forwardRef)
Ref๋ฅผ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์ ๋ฌํ๋ ค๋ฉด props์ฒ๋ผ ๋๊ธฐ๋ฉด ์ ๋๊ณ forwardRef๋ฅผ ์ฌ์ฉํ๋ฉด ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ์ input DOM์ ์ง์ ์ ๊ทผํ ์ ์๋ค.
const InputField = forwardRef((props, ref) => {
return <input ref={ref} />;
});
const Form = () => {
const inputRef = useRef(null);
return <InputField ref={inputRef} />;
};
imperative API ๊ตฌ์ฑํ๊ธฐ (useImperativeHandle)
์๋ฅผ ๋ค์ด, ์๋ฌ ์ํฉ์์ input์ ํ๋ค์ด ์ฌ์ฉ์์๊ฒ ์๊ฐ์ ์ผ๋ก ์๋ฆฌ๋ ๊ธฐ๋ฅ์ ๊ตฌํํด์ผ ํ๋ค๊ณ ํด๋ณด์. ์ด๋ฐ ๋์์ ๋จ์ํ ์ํ๋ง์ผ๋ก๋ ์ฒ๋ฆฌํ๊ธฐ ์ด๋ ค์ฐ๋ฉฐ, ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์์ ์ปดํฌ๋ํธ์ ๋ฉ์๋๋ฅผ ์ง์ ํธ์ถํด์ผ ํ๋๋ฐ, ์ด๋ ํ์ํ ๊ฒ์ด ๋ฐ๋ก imperative API๋ค.
useImperativeHandle ์ imperative API๋ฅผ ๊ตฌ์ฑํ ์ ์๊ฒ ํด์ฃผ๋ React ํ ์ธ๋ฐ, ์์ ์ปดํฌ๋ํธ๋ ํด๋น ํ ์ ํตํด ์ธ๋ถ์ ํ์ํ ๋ฉ์๋๋ง ์ ํ์ ์ผ๋ก ๋ ธ์ถํ ์ ์๊ณ , ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ ํด๋น ๋ฉ์๋๋ฅผ ์ง์ ํธ์ถํ ์ ์๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ด๋ถ์ DOM ๊ตฌ์กฐ๋ ๊ตฌํ ์ธ๋ถ์ฌํญ์ ๊ฐ์ถ ์ฑ, ํ์ํ ๋์๋ง ์ธ๋ถ์ ์์ ํ๊ฒ ์ ๊ณตํ ์ ์๋ค๋ ์ ์์ ์๋ฏธ๊ฐ ์๋ค.
const InputField = ({ apiRef }) => {
const inputRef = useRef(null);
const [shouldShake, setShouldShake] = useState(false);
useImperativeHandle(apiRef, () => ({
focus: () => inputRef.current.focus(),
shake: () => setShouldShake(true),
}), []);
return (
<input
ref={inputRef}
className={shouldShake ? 'shake-animation' : ''}
onAnimationEnd={() => setShouldShake(false)}
/>
);
};
๋ถ๋ชจ ์ปดํฌ๋ํธ์์๋ ์๋์ ๊ฐ์ด ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
const Form = () => {
const inputRef = useRef(null);
const [name, setName] = useState('');
const onSubmitClick = () => {
if (!name) {
inputRef.current.focus();
inputRef.current.shake();
} else {
// submit logic
}
};
return (
<>
<InputField apiRef={inputRef} onChange={setName} />
<button onClick={onSubmitClick}>Submit the form!</button>
</>
);
};
๊ทผ๋ฐ ๊ผญ useImperativeHandle์ ์จ์ผ ํ๋ ๊ฑด ์๋๋ค. useEffect ์์์ ref์ ์ง์ ํจ์ ๊ฐ์ฒด๋ฅผ ํ ๋นํด๋ ๋๊ฐ์ด ์๋ํ๋ค.
const InputField = ({ apiRef }) => {
const inputRef = useRef(null);
useEffect(() => {
apiRef.current = {
focus: () => inputRef.current.focus(),
shake: () => {/* setState ๋ฑ */},
};
}, [apiRef]);
return <input ref={inputRef} />;
};
๋ค๋ง useImperativeHandle์ ์ฌ์ฉํ๋ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ๋ค.
- ๋ช ํํ ์๋ ํํ: ์ด ์ปดํฌ๋ํธ๊ฐ ์ธ๋ถ์ ํน์ ๋ช ๋ นํ API๋ฅผ ์ ๊ณตํ๋ค๋ ๊ฒ์ ์ฝ๋๋ง ๋ด๋ ์ ์ ์๋ค.
- ๋ถํ์ํ ์ฌํ ๋น ๋ฐฉ์ง: useEffect ๋ฐฉ์์ ์์กด์ฑ ๋ฐฐ์ด์ ๋ฐ๋ผ API ๊ฐ์ฒด๊ฐ ๋ฆฌ๋ ๋๋ง๋ง๋ค ์ฌ์์ฑ๋ ์ ์๋๋ฐ, useImperativeHandle์ ์ด๋ฐ ๋ถํ์ํ ํ ๋น์ ๋ฐฉ์งํ๋ ๋ฐ ๋ ์ ์ ํ๋ค.
๋จ์ํ ๋์ํ๊ฒ ๋ง๋๋ ๊ฒ๋ง ๋ชฉ์ ์ด๋ผ๋ฉด useEffect๋ก๋ ์ถฉ๋ถํ์ง๋ง, ์๋๋ฅผ ๋ช ํํ ํ๊ณ ์ปดํฌ๋ํธ์ ์บก์ํ๋ฅผ ์ ์งํ๊ณ ์ ํ๋ค๋ฉด useImperativeHandle์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์์ ํ๊ณ ๊ถ์ฅ๋๋ ๋ฐฉ์์ด๋ผ๊ณ ํ๋ค.
๋๋ ์
๋๋ ์คํฌ๋กค ์์น๋ฅผ ๊ณ์ฐํ๊ฑฐ๋ ์ ๋์ ์ธ width ๊ฐ์ ์ฒ๋ฆฌํ ๋ ref๋ฅผ ์ฌ์ฉํ์๋๋ฐ, ์ด๋ฒ ์ฅ์ ์ฝ์ผ๋ฉฐ ๋ค์ ํ๋ฒ ref์ ์ญํ ๊ณผ ์ ์ ํ ์ฌ์ฉ๋ฐฉ๋ฒ์ ๋ช ํํ๊ฒ ์ ๋ฆฌํ ์ ์์ด ์ข์๋ค.
์ ๋ฆฌํ์๋ฉด
- ๋ ๋๋ง์ ์ํฅ์ ์ฃผ๋ ๊ฐ์ด๋ผ๋ฉด ๋ฌด์กฐ๊ฑด state๋ฅผ ์ฐ์.
- ์ธ๋ถ์ ๋ ธ์ถ๋๋ฉด ์ ๋๋ ๋ด๋ถ ๋์์ useImperativeHandle๋ก ๊ฐ์ถ ์ ์๋ค.
- ๋จ์ํ ์ ์ฅ๋ง ํ๊ฑฐ๋ ํน์ ์์ ์๋ง ๋์์ ํธ๋ฆฌ๊ฑฐํ๋ ๊ฐ์ ref๊ฐ ์ ํฉํ๋ค.
๊ทธ๋ฆฌ๊ณ , ๊ณต์ ๋ฌธ์์์๋ ref ์ฌ์ฉ ์ ๋ค์๊ณผ ๊ฐ์ ์ฃผ์์ฌํญ์ ๋ช ํํ ๋ช ์ํ๊ณ ์๋ค.
ref๋ฅผ ๊ณผ๋ํ๊ฒ ์ฌ์ฉํ์ง ๋ง์ธ์. ref๋ props๋ก ํํํ ์ ์๋ ํ์์ ์ธ ํ๋์๋ง ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด ํน์ ๋ ธ๋๋ก ์คํฌ๋กคํ๊ธฐ, ๋ ธ๋์ ์ด์ ๋ง์ถ๊ธฐ, ์ ๋๋ฉ์ด์ ์ด๋ฐํ๊ธฐ, ํ ์คํธ ์ ํํ๊ธฐ ๋ฑ์ด ์์ต๋๋ค.
prop์ผ๋ก ํํํ ์ ์๋ ๊ฒ์ ref๋ฅผ ์ฌ์ฉํ์ง ๋ง์ธ์. ์๋ฅผ ๋ค์ด Modal ์ปดํฌ๋ํธ์์ { open, close }์ ๊ฐ์ imperative handle์ ๋ ธ์ถํ๋ ๋์ <Modal isOpen={isOpen} />๊ณผ ๊ฐ์ isOpen prop์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข์ต๋๋ค. Effects๋ฅผ ์ฌ์ฉํ๋ฉด prop์ ํตํด ๋ช ๋ นํ ๋์(imperative behavior)์ ๋ ธ์ถํ ์ ์์ต๋๋ค.
'๐ Books > ๐ Advanced React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Ch8] React Context and Performance (0) | 2025.04.06 |
---|---|
[Ch7] Higher-order components in modern world (0) | 2025.02.23 |
[ch6] Diffing, Reconciliation (0) | 2025.02.16 |
[Ch5] Memoization with useMemo, useCallback and React.memo (1) | 2025.02.09 |
[Ch3 - Ch4] Elements as Props์ Render Props (0) | 2025.02.02 |