[React] ๋ฆฌ์กํธ useRef ํ์ฉ ์ผ์ด์ค ๋ชจ์
useRef ํจ์๋ current ์์ฑ์ ๊ฐ์ง ref ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. ref.current ๊ฐ์ HTMLElement์ ํ ๋นํด์ ํด๋น ์์์ focusํ๊ฑฐ๋, ์๋ฆฌ๋จผํธ ํฌ๊ธฐ ๋ฑ์ ํ์ธํ ์ ์๋ค. ref ๊ฐ์ฒด๋ ์๋ ํน์ง์ ๊ฐ๋๋ค.
- ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง ๋ผ๋
ref.current๊ฐ์ ๊ทธ๋๋ก ์ ์งํ๋ค. ref.current๊ฐ์ด ๋ณ๊ฒฝ๋ผ๋ ์ปดํฌ๋ํธ๋ ๋ค์ ๋ ๋๋งํ์ง ์๋๋คHTMLElement๋ฟ๋ง ์๋๋ผ ์ซ์ / ๋ฌธ์์ด / ๋ฐฐ์ด ๋ฑ ๊ฐ์ ํ ๋นํ ์ ์๋ค.
Case 1 — ๋ฆฌ๋ ๋๋ง์ด ํ์ ์๋ ๊ฐ์ ๊ด๋ฆฌํ ๋ โญ๏ธ
useRef๋.currentํ๋กํผํฐ๋ก ์ ๋ฌ๋ ์ธ์(initialValue)๋ก ์ด๊ธฐํ๋ ๋ณ๊ฒฝ ๊ฐ๋ฅํref๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค. ๋ฐํ๋ ๊ฐ์ฒด๋ ์ปดํฌ๋ํธ์ ์ ์์ ์ฃผ๊ธฐ๋ฅผ ํตํด ์ ์ง๋ ๊ฒ์ ๋๋ค.
— React ๊ณต์ ๋ฌธ์
๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด useRef ํจ์๊ฐ ๋ฐํํ ref ๊ฐ์ฒด๋ ์ปดํฌ๋ํธ ์์ ์ฃผ๊ธฐ๋ฅผ ํตํด ์ ์ง๋๋ค๊ณ ๋งํ๋ค. ์ฌ๊ธฐ์ ๋งํ๋ ์์ ์ฃผ๊ธฐ๋ DOM์ ๋ง์ดํธ~์ธ๋ง์ดํธ๊น์ง ๊ณผ์ ์ด๋ค. ์ปดํฌ๋ํธ๋ ๋ณ๊ฒฝ๋ props๋ฅผ ๋ฐ๊ฑฐ๋, ์์ ์ state๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ค์ ๋ ๋๋งํ๋๋ฐ, ์ด๋ ์ปดํฌ๋ํธ๊ฐ ๋ค์ ์คํ๋๋ฉด์ const a = 1 ๊ฐ์ ๋ณ์๋ ์ฌ์ ์ธ/์ฌํ ๋น ๋๋ค.
const a = 1; // ๋ ๋๋งํ ๋๋ง๋ค ๊ฐ ์ด๊ธฐํ
const [state, setState] = useState(1); // ๊ฐ ์ ์ง, ๊ฐ ๋ณ๊ฒฝ ์ ๋ฆฌ๋ ๋๋ง
const ref = useRef(1); // ๊ฐ ์ ์ง, ๊ฐ ๋ณ๊ฒฝ๋ผ๋ ๋ฆฌ๋ ๋๋ง ์ํจ
์๋ 2๊ฐ ๋ฒํผ์ด ์๋ค. ์๋จ ๋ฒํผ์ ํด๋ฆญํ ๋๋ง๋ค useState๋ก ์์ฑํ state ๊ฐ์ 1์ฉ ๋ํ๊ณ , ํ๋จ ๋ฒํผ๋ ref.current ๊ฐ์ 1์ฉ ๋ํ๋ค.

์๋จ ๋ฒํผ์ ํด๋ฆญํด์ state ๊ฐ์ด ๋ณํ๋ฉด ๋ฆฌ๋ ๋๋ง ๋ผ์ ํ๋ฉด์ ๋ฐ๋ก ์
๋ฐ์ดํธ ๋๋ค. ๋ฐ๋ฉด ํ๋จ ๋ฒํผ์ ํด๋ฆญํด์ ref.current ๊ฐ์ด ๋ณํด๋ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ง ์์ผ๋ฏ๋ก ํ๋ฉด์๋ ์
๋ฐ์ดํธ๋์ง ์๋๋ค. ๋์ ๋ณํ ๊ฐ์ ๊ณ์ ์ ์งํ๊ณ ์๋ค.
ํ๋จ ๋ฒํผ์ 3๋ฒ ๋๋ฅด๋ฉด ref.current๊ฐ +3 ๋ผ์ ๋ด๋ถ์ ์ผ๋ก 4 ๊ฐ์ ๊ฐ๊ณ (ํ๋ฉด์ ์
๋ฐ์ดํธ๋์ง ์์), ๋ค์ ์๋จ ๋ฒํผ์ ๋๋ฌ state ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ฌ ๊ทธ์ ์์ผ ๋ณ๊ฒฝ๋ ref.current ๊ฐ์ ๋ณผ ์ ์๋ค.
import React, { useRef, useState } from "react";
const initialState = 1;
const buttonStyle =
"w-52 bg-blue-600 text-white p-2 rounded leading-none flex items-center justify-between active:bg-violet-500 focus:ring";
const spanStyle =
"font-bold bg-white p-1 rounded text-blue-600 text-xs ml-2 select-none";
const UseRef = function () {
const [state, setState] = useState(initialState); // ๊ฐ ์ ์ง, ๊ฐ ๋ณ๊ฒฝ ์ ๋ฆฌ๋ ๋๋ง
const ref = useRef(initialState); // ๊ฐ ์ ์ง, ๊ฐ ๋ณ๊ฒฝ๋ผ๋ ๋ฆฌ๋ ๋๋ง ์ํจ
return (
<div className="h-full flex flex-col justify-center items-center gap-2 text-lg text-neutral-700">
<button
className={buttonStyle}
type="button"
onClick={() => setState((prev) => prev + 1)}
>
STATE + 1 <span className={spanStyle}>{state}</span>
</button>
<button
className={buttonStyle}
type="button"
onClick={() => {
ref.current += 1;
console.log("ref.current: ", ref.current);
}}
>
REF.CURRENT + 1 <span className={spanStyle}>{ref.current}</span>
</button>
</div>
);
};
export default UseRef;
์ด ๊ฐ์ ref ๊ฐ์ฒด ํน์ฑ์ ์ด์ฉํด ์ปดํฌ๋ํธ ๋ด๋ถ์์ ๋ณ๊ฒฝ๋ ๊ฐ์ ๊ด๋ฆฌํด์ผ ํ์ง๋ง, ํ๋ฉด์ ์
๋ฐ์ดํธ(๋ฆฌ๋ ๋๋ง) ํ ํ์ ์๋ ๊ฐ์ ๊ด๋ฆฌํ ๋ ์ ์ฉํ๊ฒ ์ฐ์ธ๋ค.

Case 2 — callbackRef (DOM Node ํฌ๊ธฐ ๊ตฌํ๊ธฐ) โญ๏ธ
useRef๋ ๋ด์ฉ์ด ๋ณ๊ฒฝ๋ ๋ ๊ทธ๊ฒ์ ์๋ ค์ฃผ์ง ์๋๋ค๋ ๊ฒ์ ์ ๋ ํ์ธ์..currentํ๋กํผํฐ๊ฐ ๋ณ๊ฒฝ๋ผ๋ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ง ์์ต๋๋ค. ๋ง์ฝ React๊ฐ DOM ๋ ธ๋์ ref๋ฅผ attachํ๊ฑฐ๋ detach ํ ๋ ์ด๋ค ์ฝ๋๋ฅผ ์คํํ๊ณ ์ถ๋ค๋ฉด ์ฝ๋ฐฑref๋ฅผ ์ฌ์ฉํ์ธ์.
— React ๊ณต์ ๋ฌธ์(์ ๋ฌธ๋งฅ ์กฐ๊ธ ์์ )
ref.current ๊ฐ์ ๋ณ๊ฒฝํด๋ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ง ์๋๋ค. ์ฆ, DOM ๋
ธ๋์ ref ์์ฑ์ ref.current ๊ฐ์ ํ ๋นํ๊ฑฐ๋(attach) ํ ๋น์ ํด์ ํด๋(detach) ์ปดํฌ๋ํธ๋ ์ด๋ฅผ ์ธ์งํ์ง ๋ชปํ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค.
์๋ ์ฝ๋์์ wrapperRef.current ๊ฐ์ด null์์ HTMLDivElement๋ก ๋ณ๊ฒฝ๋ผ๋ ์ปดํฌ๋ํธ๋ ์ด๋ฅผ ์ธ์งํ์ง ๋ชปํ๋ค. ๋๋ฌธ์ console.log(wrapperRef.current)๋ฅผ ์ฐ์ด๋ด๋ null๋ก ๋์จ๋ค.
import React, { useRef } from "react";
const App = function () {
const wrapperRef = useRef(null);
console.log(wrapperRef.current); // null
return <div ref={wrapperRef}></div>;
};
ref ๊ฐ์ฒด๊ฐ DOM Node์ attach ํน์ detach๋ ๋ ํน์ ์์
์ ์ํํ๊ณ ์ถ๋ค๋ฉด callback ref ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ๋๋ค. DOM Node์ ref ์์ฑ์ ref ๊ฐ์ฒด๊ฐ ์๋ ํจ์๋ฅผ ๋๊ธฐ๋ ๋ฐฉ์์ด๋ค. DOM Node์ ๊ฐ๋ก/์ธ๋ก ๊ฐ์ ์์์ผ ํ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
๐ก node.offsetWidth, node.offsetHeight ๊ฐ์ getBoundingClientRect ๋ฉ์๋๋ก ์กฐํํ width, height ๊ฐ๊ณผ ๋์ผํ๋ค. ๋จ, offsetWidth, offsetHeight๋ ์์์ ์ ์ ์๋ก ๋ฐ์ฌ๋ฆผํ ๊ฐ์ ๋ฐํํ๋ค. (์ฐธ๊ณ ๋งํฌ)
import React, { useCallback, useState } from "react";
import CatImg from "../../assets/cat.png";
const CallbackRef = function () {
const [size, setSize] = useState({ height: 0, width: 0 });
const [loading, setLoading] = useState(true);
const callbackRef = useCallback(
(node) => {
if (node && !loading) {
const { width, height } = node.getBoundingClientRect();
setSize({ width, height });
}
},
[loading],
);
return (
<div className="h-full flex flex-col justify-center items-center gap-8">
<p className="text-lg text-neutral-700">
HEIGHT : {size.height} / WIDTH : {size.width}
</p>
<div ref={callbackRef}>
<img
className="max-w-[13rem]"
src={CatImg} // src={require(../assets/cat.png)}
alt="cat"
onLoad={() => setLoading(false)}
/>
</div>
</div>
);
};
export default CallbackRef;
// useCallback์ ์ฌ์ฉํ ์์
const callbackRef = useCallback((node) => {
if (node) {
/* ... */
}
}, []);
return (
<div ref={callbackRef}>
<img />
</div>
);
// useState๋ฅผ ์ฌ์ฉํ ์์
const [callbackRef, setCallbackRef] = useState(null);
useEffect(() => {
if (callbackRef) {
/* ... */
}
}, [callbackRef]);
return (
<div ref={callbackRef}>
<img />
</div>
);

Case 3 — forwardRef
props๋ฅผ ์ ๋ฌํ๋ฏ ์์ ์ปดํฌ๋ํธ์๊ฒ ref๋ฅผ ์ ๋ฌํ ์ ์๋ค. ๋ค๋ง ref๋ props ๊ฐ์ฒด์ ํ๋กํผํฐ๋ก ๋ค์ด๊ฐ์ง ์๊ณ , ref๋ฅผ ๋๊ฒจ ๋ฐ๋ ํ์ ์ปดํฌ๋ํธ๋ฅผ forwardRef ํจ์๋ก ๊ฐ์ธ์ฃผ๋ ์์
์ด ํ์ํ๋ค. ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ์ ref ์ํ๊ฐ์ ๊ด๋ฆฌํด์ผํ ๋ ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ค.
import React from "react";
import { RandomCornerStyle, RandomWidthHeight } from "../../utils/webUtils";
const randomAxis = RandomWidthHeight(300, 120);
const randomCorner = RandomCornerStyle();
const Circle = React.forwardRef((props, ref) => {
return (
<div
className={`bg-indigo-500 ${randomCorner}`}
style={{
width: randomAxis.width,
height: randomAxis.height,
}}
ref={ref}
/>
);
});
Circle.displayName = "Circle"; // ESLint: react/display-name ์๋ฌ ๋์
const ForwardRef = function () {
const circleRef = React.useRef(null);
const [circleSize, setCircleSize] = React.useState({ width: 0, height: 0 });
React.useEffect(() => {
const { width, height } = circleRef.current.getBoundingClientRect();
setCircleSize({ width, height });
}, []);
return (
<div className="h-full flex flex-col justify-center items-center gap-8">
<p className="text-lg text-neutral-700">
WIDTH : {circleSize.width} / HEIGHT : {circleSize.height}
</p>
<Circle ref={circleRef} />
</div>
);
};
export default ForwardRef;
// ์์ ์ปดํฌ๋ํธ
const randomAxis = RandomWidthHeight(300, 120); // parameter: max, min
const Circle = React.forwardRef((props, ref) => {
return (
<div
style={{
width: randomAxis.width,
height: randomAxis.height,
}}
ref={ref}
/>
);
});
Circle.displayName = "Circle"; // ESLint: react/display-name ์๋ฌ ๋์
// ๋ถ๋ชจ ์ปดํฌ๋ํธ
const App = function () {
const circleRef = React.useRef(null);
// ...
return <Circle ref={circleRef} />;
};
์ ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก <Circle /> ์ปดํฌ๋ํธ๋ ๋ ๋๋งํ ๋๋ง๋ค ๋๋คํ width, height ๊ฐ์ ๊ฐ๋๋ค๊ณ ๊ฐ์ ํด๋ณธ๋ค. ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ ์์ ์ปดํฌ๋ํธ์ ๋๋คํ width, height ๊ฐ์ ์์์ผ ํ๋ ์ํฉ์ด๋ค.

- ๋ถ๋ชจ ์ปดํฌ๋ํธ์์
ref๋ฅผ ๋๊ธธ ์์ ์ ์์ง ์์ ์ปดํฌ๋ํธ์width,height๊ฐ์ ๋ชจ๋ฅด๋ ์ํ๋ค. - ์์ ์ปดํฌ๋ํธ๊ฐ ๋๋คํ
width,height๋ฅผ ๊ฐ์ง๊ณ , ์ ๋ฌ๋ฐ์ref๋ฅผ attachํด์ ๋ ๋๋งํ๋ฉด, - ๋ง์ดํธ๋ฅผ ์๋ฃํ์ผ๋ฏ๋ก ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์๋
useEffect๊ฐ ์คํ๋๋ค.useEffect๊ฐ ์คํ๋ ์์ ์ ์์ ์ปดํฌ๋ํธ๋ก ๋๊ฒผ๋ref๊ฐ์ฒด(circleRef)๋ฅผ ํตํด,width,height๊ฐ์ ์ฝ์ ์ ์๋ค.
// ๋ถ๋ชจ ์ปดํฌ๋ํธ
const [circleSize, setCircleSize] = React.useState({ width: 0, height: 0 });
const circleRef = React.useRef(null); // null | HTMLDivElement
React.useEffect(() => {
const { width, height } = circleRef.current.getBoundingClientRect();
setCircleSize({ width, height });
}, []);
๋ ํผ๋ฐ์ค
React useRef์ ๋ค์ํ ํ์ฉ ๋ฐฉ๋ฒ(mutable object, callback ref์ forwardRef) - ์ดํ๋ ๋ธ๋ก๊ทธ
React useRef์ ๋ค์ํ ํ์ฉ ๋ฐฉ๋ฒ(mutable object, callback ref์ forwardRef) ๋ฆฌ์กํธ์์ render() ๋ฉ์๋์ ์ํด ๋ง๋ค์ด์ง๋ DOM์ ์ ๊ทผํ๋ ๋ฐฉ์ ์ผ๋ก ref ๋ฅผ ์ ๊ณตํ๋ค. ์๋ฅผ ๋ค์ด ๋ฐฐ์ก์ง ์ ๋ณด๋ฅผ ์ ๋ ฅ ๋ฐ์์ผ ํ
leehwarang.github.io
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ ํ ๋ง ์ปฌ๋ฌ ๋ณ๊ฒฝ ๋ฐฉ๋ฒ (0) | 2024.05.03 |
|---|---|
| [HTML/CSS] ์ ์ฉํ HTML ํ๊ทธ ๋ชจ์ (0) | 2024.05.03 |
| [HTML/CSS] CSS ์ธ์ ํ๋ ์๋ฆฌ๋จผํธ๋ค์ border ๊ฒน์นจ ๋ฌธ์ ํด๊ฒฐ (0) | 2024.05.03 |
| [HTML/CSS] ์ด๋ฉ์ผ ํ ํ๋ฆฟ ์์ฑ๋ฒ (feat. ํ ์ด๋ธ ์ฝ๋ฉ) (0) | 2024.05.03 |
| [React] ๋ฆฌ์กํธ ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ(์ ๋ก๋) ๊ตฌํ / File API (0) | 2024.05.02 |
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ ํ ๋ง ์ปฌ๋ฌ ๋ณ๊ฒฝ ๋ฐฉ๋ฒ
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ ํ ๋ง ์ปฌ๋ฌ ๋ณ๊ฒฝ ๋ฐฉ๋ฒ
2024.05.03 -
[HTML/CSS] ์ ์ฉํ HTML ํ๊ทธ ๋ชจ์
[HTML/CSS] ์ ์ฉํ HTML ํ๊ทธ ๋ชจ์
2024.05.03 -
[HTML/CSS] CSS ์ธ์ ํ๋ ์๋ฆฌ๋จผํธ๋ค์ border ๊ฒน์นจ ๋ฌธ์ ํด๊ฒฐ
[HTML/CSS] CSS ์ธ์ ํ๋ ์๋ฆฌ๋จผํธ๋ค์ border ๊ฒน์นจ ๋ฌธ์ ํด๊ฒฐ
2024.05.03 -
[HTML/CSS] ์ด๋ฉ์ผ ํ ํ๋ฆฟ ์์ฑ๋ฒ (feat. ํ ์ด๋ธ ์ฝ๋ฉ)
[HTML/CSS] ์ด๋ฉ์ผ ํ ํ๋ฆฟ ์์ฑ๋ฒ (feat. ํ ์ด๋ธ ์ฝ๋ฉ)
2024.05.03