useRef
1. useRef란?
- React의 상태(useState)와는 다르게 리렌더링 없이 값을 저장할 수 있는 Hook.
- DOM 요소에 직접 접근하거나, 이전 값을 저장할 때 사용됨.
- 값이 변경되어도 컴포넌트가 리렌더링되지 않음.
2. useRef 기본 문법
import { useRef } from "react";
const Example = () => {
const inputRef = useRef(null); // 초기값 설정
const focusInput = () => {
inputRef.current.focus(); // input에 포커스 적용
};
return (
<div>
<input ref={inputRef} type="text" placeholder="클릭하면 포커스" />
<button onClick={focusInput}>포커스</button>
</div>
);
};
export default Example;
useRef를 사용하면 input 요소에 직접 접근하여 포커스를 적용할 수 있음.
리렌더링이 발생하지 않음 → useState를 사용했다면 버튼을 누를 때마다 렌더링이 발생함.
3. useRef의 주요 활용 사례
1) DOM 요소 직접 접근 (input, button 등)
import { useRef } from "react";
const Example = () => {
const buttonRef = useRef(null);
return (
<button ref={buttonRef} onClick={() => buttonRef.current.style.background = "red"}>
버튼 색 변경
</button>
);
};
export default Example;
buttonRef.current.style.background = "red"를 통해 버튼 색상 변경 가능.
useRef는 상태를 변경하지 않으므로 리렌더링 없이 DOM 조작 가능.
2) 이전 값 저장 (prevValue 관리)
import { useState, useRef, useEffect } from "react";
const Example = () => {
const [count, setCount] = useState(0);
const prevCount = useRef(count);
useEffect(() => {
prevCount.current = count; // 이전 count 값을 저장
}, [count]);
return (
<div>
<h1>현재 값: {count}</h1>
<h2>이전 값: {prevCount.current}</h2>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
};
export default Example;
useRef를 사용하여 이전 값을 저장할 수 있음.
useEffect를 활용하여 prevCount.current를 업데이트.
이전 값(prevCount.current)은 컴포넌트가 리렌더링되더라도 유지됨.
3) setTimeout, setInterval을 useRef로 관리
import { useRef, useState } from "react";
const Timer = () => {
const [count, setCount] = useState(0);
const intervalRef = useRef(null);
const startTimer = () => {
intervalRef.current = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval(intervalRef.current);
};
return (
<div>
<h1>{count}</h1>
<button onClick={startTimer}>시작</button>
<button onClick={stopTimer}>정지</button>
</div>
);
};
export default Timer;
intervalRef.current에 setInterval 값을 저장하여 언제든지 중지 가능.
리렌더링이 발생해도 setInterval ID가 유지됨.
4) useRef vs useState
렌더링을 방지하려면 useRef 사용
UI 업데이트가 필요하면 useState 사용
4. useRef 사용 시 주의할 점
1) useRef 값을 변경해도 리렌더링되지 않음
const Example = () => {
const countRef = useRef(0);
return (
<div>
<h1>{countRef.current}</h1> {/* 화면에 보이는 값은 업데이트되지 않음 */}
<button onClick={() => countRef.current += 1}>증가</button>
</div>
);
};
countRef.current 값이 변경되어도 화면이 업데이트되지 않음.
화면 업데이트가 필요하면 useState를 사용해야 함.
2) ref는 null로 초기화된 상태일 수 있음
const Example = () => {
const inputRef = useRef(null);
const handleClick = () => {
console.log(inputRef.current); // 초기에는 null일 수도 있음
};
return <button onClick={handleClick}>콘솔 확인</button>;
};
초기에는 inputRef.current가 null일 수도 있으므로, 항상 체크해야 함.
useImperativeHandle
1. useImperativeHandle란?
- React에서 ref를 사용할 때, 부모 컴포넌트가 자식 컴포넌트의 특정 메서드나 속성에만 접근할 수 있도록 제한하는 Hook.
- forwardRef와 함께 사용해야 함.
- DOM 요소뿐만 아니라, 컴포넌트의 특정 기능을 부모가 직접 호출할 수 있도록 함.
2. useImperativeHandle 기본 문법
import { useImperativeHandle, useRef, forwardRef } from "react";
// 자식 컴포넌트
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
showAlert: () => {
alert("자식 컴포넌트에서 실행됨!");
},
}));
return <h1>자식 컴포넌트</h1>;
});
// 부모 컴포넌트
const Parent = () => {
const childRef = useRef();
return (
<>
<Child ref={childRef} />
<button onClick={() => childRef.current.showAlert()}>실행</button>
</>
);
};
export default Parent;
useImperativeHandle을 사용하면, 부모가 ref를 통해 자식의 특정 함수만 호출할 수 있음.
forwardRef를 사용해야 ref를 자식 컴포넌트로 전달할 수 있음.
부모는 childRef.current.showAlert()을 호출하여 자식의 메서드를 실행 가능.
3. useImperativeHandle이 필요한 이유
1) 부모가 자식의 특정 기능만 실행하도록 제한
- 일반적으로 ref는 DOM 요소에 직접 접근할 때 사용됨.
- 하지만 useImperativeHandle을 사용하면 자식 컴포넌트의 특정 메서드만 부모가 호출할 수 있도록 제한할 수 있음.
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
showAlert: () => {
alert("자식 내부에서 실행됨");
},
}));
// 부모가 직접 접근하지 못하도록 내부 상태를 관리
const privateFunction = () => {
console.log("부모는 이 함수에 접근할 수 없음");
};
return <h1>자식 컴포넌트</h1>;
});
부모는 showAlert()만 실행할 수 있고, privateFunction()에는 접근할 수 없음.
2) ref를 이용한 포커스 제어
- 부모가 자식 컴포넌트의 input에 포커스를 강제 적용하고 싶을 때.
import { useImperativeHandle, forwardRef, useRef } from "react";
const InputField = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
}));
return <input ref={inputRef} type="text" />;
});
const Parent = () => {
const inputRef = useRef();
return (
<div>
<InputField ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>포커스</button>
</div>
);
};
export default Parent;
부모의 ref를 통해 focus()를 호출하면, 자식의 input 요소에 포커스를 적용할 수 있음.
3) Modal을 외부에서 열고 닫기
- useImperativeHandle을 사용하면 부모에서 ref를 통해 모달을 제어할 수 있음.
import { useImperativeHandle, forwardRef, useState, useRef } from "react";
const Modal = forwardRef((props, ref) => {
const [visible, setVisible] = useState(false);
useImperativeHandle(ref, () => ({
open: () => setVisible(true),
close: () => setVisible(false),
}));
if (!visible) return null;
return (
<div className="modal">
<h2>모달 창</h2>
<button onClick={() => setVisible(false)}>닫기</button>
</div>
);
});
const App = () => {
const modalRef = useRef();
return (
<div>
<button onClick={() => modalRef.current.open()}>모달 열기</button>
<Modal ref={modalRef} />
</div>
);
};
export default App;
부모에서 modalRef.current.open()을 호출하면 모달이 열리고, modalRef.current.close()로 닫을 수 있음.
useState로 상태를 관리하면서, 부모가 특정 메서드를 통해 상태를 변경할 수 있도록 제어.
4. useImperativeHandle과 useRef 차이점
| useRef | DOM 요소 직접 접근 | 부모가 접근 가능한 범위: 모든 DOM 속성 (input.value, div.style 등) |
| useImperativeHandle | 부모가 자식의 특정 메서드만 실행 | 부모가 접근 가능한 범위: useImperativeHandle에서 정의한 메서드만 접근 가능 |
즉, useImperativeHandle을 사용하면 부모가 접근할 수 있는 기능을 제한할 수 있음!
보안성 및 코드의 명확성을 높이는 데 유용.
5. useImperativeHandle은 언제 사용해야 할까?
부모가 자식의 특정 기능만 실행할 수 있도록 제한하고 싶을 때
부모가 자식의 ref를 통해 특정 동작(포커스, 모달 열기 등)을 수행해야 할 때
보안성을 강화하여 부모가 불필요한 내부 상태에 접근하지 못하도록 하고 싶을 때
🚨 주의! useImperativeHandle을 남용하면 부모가 자식의 내부 동작을 과도하게 제어하게 될 수 있음.
🚨 컴포넌트 간 의존성이 강해지므로 꼭 필요한 경우에만 사용해야 함!
'JavaScript > React' 카테고리의 다른 글
| Jest와 React Test Library(RTL) (0) | 2025.03.25 |
|---|---|
| React 고급 Hook (0) | 2025.03.21 |
| useEffect, useLayoutEffect, useInsertionEffect (0) | 2025.03.21 |
| React Router DOM (0) | 2025.03.21 |
| React 기본 개념 (0) | 2025.03.21 |