๐Ÿ•’

9. useImperativeHandle

ย 
ย 

9.1 useImperativeHandle์ด๋ž€?

useImperativeHandle์€ ํ”ํžˆ ์‚ฌ์šฉ๋˜์ง€๋Š” ์•Š์ง€๋งŒ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ๋˜๋Š” Hook์ž…๋‹ˆ๋‹ค. useImperativeHandle์€ ref๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์— ๋…ธ์ถœ๋˜๋Š” ์ธ์Šคํ„ด์Šค ๊ฐ’์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด Hook์€ ํ•ญ์ƒ ref์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ณ  ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋Š” React.forwardRef์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ฒŒ๋” ๊ธฐ์žฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
ย 

9.1.1 ๊ธฐ๋ณธ ๊ตฌ์กฐ

ย 
useImperativeHandle(ref, createHandle, [deps])
ย 
useImperativeHandle์€ ์œ„์™€ ๊ฐ™์ด ์„ธ ๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ถ€์—ฌํ•  ref์ž…๋‹ˆ๋‹ค. ๋ณดํ†ต forwardRef( ) ํ•จ์ˆ˜๋กœ ๋ถ€๋ชจ์—๊ฒŒ์„œ ๋ฐ›์•„์˜จ ref๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ ์ธ์ž๋Š” createHandle๋กœ ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ฐ์ฒด์—๋Š” ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ธ ๋ฒˆ์งธ ์ธ์ž๋Š” ์˜์กด์„ฑ ๋ฐฐ์—ด๋กœ ํ•„์ˆ˜๊ฐ€ ์•„๋‹Œ ์„ ํƒ์  ์ธ์ž์ด๋ฉฐ Hook์ด ์žฌ์ •์˜๋˜์–ด์•ผ ํ•˜๋Š” ์กฐ๊ฑด๋“ค์„ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.
ย 

9.1.2 ์‚ฌ์šฉ์‹œ ์ฃผ์˜์ 

useImperativeHandle์€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฃผ์š”ํ•œ ์ƒํƒœ๋‚˜ ๋กœ์ง์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ์— ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Hook์ž…๋‹ˆ๋‹ค.
๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ref๋ฅผ ์‚ฌ์šฉํ•œ ๋ช…๋ นํ˜• ์ฝ”๋“œ๋Š” ํ”ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย 
ํ•˜์ง€๋งŒ ๊ณต์‹ ๋ฌธ์„œ์— ๊ธฐ์žฌ๋˜์–ด ์žˆ๋“ฏ์ด '๋ฌด์—‡'์„ ํ•  ๊ฒƒ์ธ๊ฐ€? ์— ์ดˆ์ ์„ ๋งž์ถ˜ ์„ ์–ธํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ธ React๋Š” ์„ ์–ธ์ ์œผ๋กœ ํ•ด๊ฒฐ๋  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ (state๋‚˜ prop์„ ์‚ฌ์šฉํ•ด์„œ ํ•ด๊ฒฐ๋  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ)์—์„œ๋Š” ๋ฌด์—‡์„ '์–ด๋–ป๊ฒŒ' ํ•  ๊ฒƒ์ธ๊ฐ€?์— ์ดˆ์ ์„ ๋งž์ถ˜ ๋ช…๋ นํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ธ ref๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ useImperativeHandle Hook์€ ํ•„์š”ํ•œ ๊ณณ์—์„œ๋งŒ ์„ ๋ณ„์ ์œผ๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
ย 

9.2 ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

์ด์ œ useImperativeHandle์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ useImperativeHandle์€ React.forwardRef์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋จผ์ € React.forwardRef์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
ย 

9.2.1 React.forwardRef

React.forwardRef๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ref๋ฅผ ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ DOM ๋…ธ๋“œ์— ์ง์ ‘ ์ ‘๊ทผํ•ด์•ผ ํ•  ๋•Œ ํŠนํžˆ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. React ์ปดํฌ๋„ŒํŠธ์—์„œย ref๋ฅผ prop์œผ๋กœ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” React.forwardRef๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
ย 
๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ React.forwardRef๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆผ 9-1๊ณผ ๊ฐ™์ด ๋ฒ„ํŠผ์„ ํด๋ฆญํ•  ๋•Œ MyInput์ด๋ผ๋Š” Input ์ปดํฌ๋„ŒํŠธ์— ํฌ์ปค์Šค๋ฅผ ์ฃผ๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.
ย 
๊ทธ๋ฆผ 9-1๊ทธ๋ฆผ 9-1
๊ทธ๋ฆผ 9-1
ย 
๋จผ์ € ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์ธ App ์ปดํฌ๋„ŒํŠธ์— useRef๋ฅผ ์ด์šฉํ•ด์„œ ref๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ๊ฒƒ์„ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด MyInput์˜ ref ์†์„ฑ์œผ๋กœ ๋„ฃ์–ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
ย 
import React, { useRef } from 'react'; import MyInput from './MyInput'; function App() { const inputRef = useRef(); const focus = () => { inputRef.current.focus(); }; return ( <div style={{ margin: '20px 10px' }}> <MyInput ref={inputRef} /> <button style={{ marginLeft: '10px' }} onClick={focus}>์ธํ’‹ ํฌ์ปค์Šค</button> </div> ); }; export default App;
App.js
ย 
ref๋ฅผ ์ „๋‹ฌ๋ฐ›์„ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ forwardRef() ํ•จ์ˆ˜๋กœ ๊ฐ์‹ธ์ฃผ๋ฉด ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ฐ–๊ฒŒ ๋˜๋Š”๋ฐ, ์—ฌ๊ธฐ์— ๋ถ€๋ชจ์—๊ฒŒ์„œ ์ „๋‹ฌ๋ฐ›์€ ref๊ฐ€ ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค. ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋Š” ์ „๋‹ฌ๋ฐ›์€ ref๋ฅผ input ํƒœ๊ทธ์˜ ref ์†์„ฑ์œผ๋กœ ๋„ฃ์–ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด์ œ ref๋Š” input์˜ DOM ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒ ๋˜์–ด ๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ๋งˆ๋‹ค focus() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
ย 
import { forwardRef } from 'react'; function MyInput(props, ref) { return <input ref={ref} />; }; export default forwardRef(MyInput);
MyInput.jsx
ย 
์œ„์™€ ๊ฐ™์ด React.forwardRef๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด HTML ์—˜๋ฆฌ๋จผํŠธ์— ref๋ฅผ ์ „๋‹ฌํ•˜๋“ฏ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ref๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ forwardRef() ํ•จ์ˆ˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์ ์œผ๋กœ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’์€ ์ตœํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋ฉฐ, ๊ทธ๋ณด๋‹ค ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
ย 

9.2.2 useImperativeHandle ์‚ฌ์šฉํ•˜๊ธฐ

์ด์ œ useImperativeHandle์„ React.forwardRef์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. useImperativeHandle์„ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” React.forwardRef๋กœ ๊ฐ์‹ธ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ๋ถ€ํ„ฐ React.forwardRef์˜ ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋ฐ›์€ ref๋ฅผ useImperativeHandle์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž ๊ฐ’์œผ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
ย 
์œ„์˜ ์˜ˆ์ œ์™€ ๋น„์Šทํ•˜๊ฒŒ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•  ๋•Œ MyInput์ด๋ผ๋Š” Input ์ปดํฌ๋„ŒํŠธ์— ํฌ์ปค์Šค๋ฅผ ์ฃผ๋ฉด์„œ ๋™์‹œ์— input ํƒœ๊ทธ์˜ value๋ฅผ ํฌ์ปค์Šค ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!๋ผ๊ณ  ๋ณ€๊ฒฝํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆผ 9-2๋Š” ์˜ˆ์ œ ์ฝ”๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค.
ย 
๊ทธ๋ฆผ 9-2๊ทธ๋ฆผ 9-2
๊ทธ๋ฆผ 9-2
ย 
์ž์‹ ์ปดํฌ๋„ŒํŠธ์ธ MyInput๋ฅผ ๋ณด๋ฉด, forwardRef() ํ•จ์ˆ˜๋กœ ๊ฐ์‹ธ์—ฌ ์žˆ๊ณ  ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์ธ ref๊ฐ€ useImperativeHandle์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋“ค์–ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ย 
import { forwardRef, useImperativeHandle, useRef } from 'react'; function MyInput(props, ref) { const inputRef = useRef(); useImperativeHandle(ref, () => ({ customFocus: () => { inputRef.current.focus(); inputRef.current.value = 'ํฌ์ปค์Šค ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!'; }, })); return <input ref={inputRef} />; } export default forwardRef(MyInput);
MyInput.jsx
ย 
useImperativeHandle์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž์—๋Š” customFocus๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ–ˆ๊ณ , ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ณณ์—์„œ inputRef.current์— customFocus๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ย 
import React, { useRef } from 'react'; import MyInput from './MyInput'; function App() { const inputRef = useRef(); return ( <div style={{ margin: '20px 10px' }}> <MyInput ref={inputRef} /> <button style={{ marginLeft: '10px' }} onClick={() => inputRef.current.customFocus()} > ์ธํ’‹ ํฌ์ปค์Šค </button> </div> ); } export default App;
App.js
ย 
๊ธฐ์กด์˜ input ์—˜๋ฆฌ๋จผํŠธ์—๋Š” customFocus๋ผ๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์—†์ง€๋งŒ,ย useImperativeHandle์—์„œ ์ •์˜ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ฒ˜๋Ÿผ useImperativeHandle Hook์„ ์ด์šฉํ•˜๋ฉด, ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
ย 

9.3 ์‹ค์šฉ ์˜ˆ์ œ

9.3.1 useImperativeHandle์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์‹ค์šฉ ์˜ˆ์ œ

useImperativeHandle์˜ ์‹ค์šฉ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ๊ทธ๋ฆผ 9-3์€ ์‹ค์šฉ ์˜ˆ์ œ์˜ ์™„์„ฑ๋ณธ์ž…๋‹ˆ๋‹ค. ์‹ค์šฉ ์˜ˆ์ œ๋Š” ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด CSS ๋ฐ•์Šค ์ƒ‰์ƒ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค. useImperativeHandle Hook์„ ์‚ฌ์šฉํ•œ ์˜ˆ์ œ์™€ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์˜ˆ์ œ๋ฅผ ๋น„๊ตํ•˜์—ฌ Hook์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ๋‹ค๋ค„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
ย 
๊ทธ๋ฆผ 9-3๊ทธ๋ฆผ 9-3
๊ทธ๋ฆผ 9-3
ย 
์šฐ์„ ์€ useImperativeHandle Hook์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
ย 
import { useState } from "react"; import CustomModal from "./CustomModal"; function App() { const [open, setOpen] = useState(false); const [isTomato, setIsTomdato] = useState(false); const [isPink, setIsPink] = useState(false); const [isSky, setIsSky] = useState(false); return ( <> <button onClick={() => setOpen(true)}>Open</button> <button onClick={() => { setIsPink(true); }} > Pink Btn </button> <button onClick={() => { setIsSky(true); }} > Skyblue Btn </button> <button onClick={() => { setIsTomdato(true); }} > Tomato Btn </button> <CustomModal isOpen={open} onClose={() => { setOpen(false); setIsPink(false); setIsSky(false); setIsTomdato(false); }} isPink={isPink} isSky={isSky} isTomato={isTomato} /> </> ); } export default App;
App.js
ย 
์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ DOM ๋…ธ๋“œ์— ์ง์ ‘ ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ˆ, ์ƒํƒœ ๊ฐ’์„ ์ƒ์„ฑํ•˜์—ฌ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— props ๊ฐ’์œผ๋กœ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค. ๊ฐ div ํƒœ๊ทธ์˜ ์ƒ‰์ƒ์„ ๊ฒฐ์ •ํ•˜๊ธฐ ์œ„ํ•œ ์„ธ ๊ฐœ์˜ ์ƒํƒœ ๊ฐ’, ๋ชจ๋‹ฌ์„ ์—ฌ๋‹ซ์„ ํ•˜๋‚˜์˜ ์ƒํƒœ ๊ฐ’์„ ์ƒ์„ฑํ•˜์—ฌ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
ย 
import React, { useRef } from "react"; import { useEffect } from "react"; const CustomModal = ({ isOpen, onClose, isPink, isSky, isTomato }) => { const closeRef = useRef(); const pinkRef = useRef(); const skyblueRef = useRef(); const tomatoRef = useRef(); useEffect(() => { if (isPink) { pinkRef.current.style = "width:50px; height:50px; background:pink;"; } if (isSky) { skyblueRef.current.style = "width:50px; height:50px; background:skyblue;"; } if (isTomato) { tomatoRef.current.style = "width:50px; height:50px; background:tomato;"; } }, [isPink, isSky, isTomato]); if (!isOpen) return null; return ( <div> <br /> <button ref={closeRef} onClick={onClose}> &times; </button> <h1>Color Change</h1> <div> <div ref={pinkRef}>color1</div> <div ref={skyblueRef}>color2</div> <div ref={tomatoRef}>color3</div> </div> </div> ); }; export default CustomModal;
CustomModal.jsx
ย 
CustomModal ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ๊ฐ div ํƒœ๊ทธ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ Ref๋ฅผ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค. props๋กœ ๋„˜๊ฒจ๋ฐ›์€ ๊ฐ ์ƒํƒœ ๊ฐ’์ด true๋กœ ๋ฐ”๋€” ๊ฒฝ์šฐ, Ref๋ฅผ ํ†ตํ•ด ํ•ด๋‹น DOM ๋…ธ๋“œ์— ์ ‘๊ทผํ•˜์—ฌ ์Šคํƒ€์ผ์„ ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค.
ย 
์œ„ ์ฝ”๋“œ๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ useState๋ฅผ ๋‚ด๋ ค์ฃผ์–ด ์ž์‹ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ํŠน์ • ํƒœ๊ทธ์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‹ค์Œ์— ๋‚˜์˜ฌ ์˜ˆ์ œ์ฒ˜๋Ÿผ useImperativeHandle์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ๋ถ€๋ชจ์—์„œ ์ง์ ‘ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ํƒœ๊ทธ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ย 

9.3.2 useImperativeHandle์„ ์‚ฌ์šฉํ•œ ์‹ค์šฉ ์˜ˆ์ œ

useImperativeHandle Hook์„ ์‚ฌ์šฉํ•˜์—ฌ ์•ž์„œ ๋งŒ๋“  ์‹ค์šฉ ์˜ˆ์ œ์™€ ๊ฐ™์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
ย 
import { useState, useRef } from "react"; import CustomModal from "./CustomModal"; function App() { const [open, setOpen] = useState(false); const modalRef = useRef(); return ( <> <button onClick={() => setOpen(true)}>Open</button> <button onClick={() => { //์ž‘์—…๋‚ด์šฉ }} > Pink Btn </button> <CustomModal ref={modalRef} open={open} onClose={() => setOpen(false)} /> </> ); } export default App;
App.js
ย 
๋จผ์ € App.js์—์„œ Open ๋ฒ„ํŠผ๊ณผ Pink Btn ๋ฒ„ํŠผ์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. Open ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด Color Change ๋ชจ๋‹ฌ์ด ๋‚˜ํƒ€๋‚˜๊ณ  Pink Btn ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด โ€˜color1โ€™ ํ…์ŠคํŠธ์˜ ๋ฐฐ๊ฒฝ์ƒ‰์ด ๋ถ„ํ™์ƒ‰์œผ๋กœ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๋ชจ๋‹ฌ์— ํ•ด๋‹นํ•˜๋Š” CustomModal ์ปดํฌ๋„ŒํŠธ์—์„œ useImperativeHandle Hook์„ ํ™œ์šฉํ•ด ๊ตฌํ˜„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
ย 
useImperativeHandle Hook์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด CustomModal ์ปดํฌ๋„ŒํŠธ์— modalRef๋ฅผ prop์œผ๋กœ ์ „๋‹ฌํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ๊ทธ๋ฆผ 9-4๋Š” Open ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ๋ชจ๋‹ฌ์„ ๋„์šด ์ƒํƒœ์ž…๋‹ˆ๋‹ค.
ย 
๊ทธ๋ฆผ 9-4๊ทธ๋ฆผ 9-4
๊ทธ๋ฆผ 9-4
ย 
import React from "react"; import { useRef } from "react"; function CustomModal({ open, onClose }, ref) { const closeRef = useRef(); const pinkRef = useRef(); if (!open) return null; return ( <div> <br /> <button ref={closeRef} onClick={onClose}> &times; </button> <h1>Color Change</h1> <div> <div ref={pinkRef}>color1</div> </div> </div> ); } export default React.forwardRef(CustomModal);
CustomModal.jsx
ย 
CustomModal ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. useRef Hook์„ ํ†ตํ•ด closeRef์™€ pinkRef๋ผ๋Š” ref ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋‘ ๊ฐ€์ง€ ref ๊ฐ์ฒด๋ฅผ ๊ฐ๊ฐ ๋ชจ๋‹ฌ์˜ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ๊ณผ โ€˜color1โ€™ ํ…์ŠคํŠธ์˜ ref ๊ฐ’์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋‹ซ๊ธฐ ๋ฒ„ํŠผ๊ณผ โ€˜color1โ€™ ํ…์ŠคํŠธ์˜ DOM ๋…ธ๋“œ๋ฅผ ๊ฐ€๋ฆฌ์ผœ ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•œ ๋ชฉ์ ์ž…๋‹ˆ๋‹ค.
ย 
useImperativeHandle(ref, () => { return { closeBtn: closeRef.current, pinkBtn: pinkRef.current, }; });
CustomModal.jsx
ย 
์ดํ›„ useImperativeHandle Hook์˜ ์ธ์ž๋กœ App.js์—์„œ ์ „๋‹ฌํ•ด์˜จ prop์ธ ref๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ ์ฝœ๋ฐฑํ•จ์ˆ˜๋กœ closeBtn๊ณผ pinkBtn์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์ธ App.js์—์„œ๋„ ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. closeBtn์— closeRef.current์„ ํ• ๋‹นํ•˜์—ฌ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ DOM ๋…ธ๋“œ๋ฅผ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ณ , pinkBtn์—๋Š” pinkRef.current ์„ ๋„ฃ์–ด โ€˜color1โ€™ ํ…์ŠคํŠธ์˜ div ํƒœ๊ทธ๋ฅผ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด App.js์—์„œ closeBtn๊ณผ pinkBtn์„ ํ™œ์šฉํ•˜์—ฌ ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ ๊ธฐ๋Šฅ๊ณผ ๋ฐฐ๊ฒฝ์ƒ‰ ๋ณ€๊ฒฝ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
ย 
import React from "react"; import { useRef, useImperativeHandle } from "react"; function CustomModal({ open, onClose }, ref) { const closeRef = useRef(); const pinkRef = useRef(); useImperativeHandle(ref, () => { return { closeBtn: closeRef.current, pinkBtn: pinkRef.current, }; }); if (!open) return null; return ( <div> <br /> <button ref={closeRef} onClick={onClose}> &times; </button> <h1>Color Change</h1> <div> <div ref={pinkRef}>color1</div> </div> </div> ); } export default React.forwardRef(CustomModal);
CustomModal.jsx
ย 
์œ„ ์ฝ”๋“œ๋Š” useImperativeHandle Hook์„ ํฌํ•จํ•œ CustomModal ์ปดํฌ๋„ŒํŠธ์˜ ์ „์ฒด ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ref๋ฅผ ์ „๋‹ฌ๋ฐ›๋Š” ์ž์‹ ์ปดํฌ๋„ŒํŠธ์ธ CustomModal์—์„œย forwardRef() ํ•จ์ˆ˜๋กœ ๊ฐ์‹ผ ํ›„ exportํ•ด์•ผ useImperativeHandle Hook์˜ ์ธ์ž๋กœ ref๋ฅผ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งˆ์ง€๋ง‰ ์ฝ”๋“œ์— export default React.forwardRef(CustomModal)๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.
ย 
<button onClick={() => { modalRef.current.pinkBtn.style = "width:50px; height:50px; background:pink;"; }} > Pink Btn </button>;
App.js
ย 
๋งˆ์ง€๋ง‰์œผ๋กœ App.js๋กœ ๋Œ์•„์™€ Pink Btn ๋ฒ„ํŠผ onClick ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์— modalRef.current.pinkBtn.style์„ ๋„ฃ์–ด css ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•ด์ค๋‹ˆ๋‹ค. width:50px; height:50px; background:pink;์˜ ๊ฐ’์„ ์„ค์ •ํ•ด ๋„ˆ๋น„, ๋†’์ด์™€ ๋ฐฐ๊ฒฝ์ƒ‰์„ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ๊ทธ๋ฆผ 9-5๋Š” Pink Btn์„ ํด๋ฆญํ•˜์—ฌ โ€˜color1โ€™ ํ…์ŠคํŠธ์˜ ์Šคํƒ€์ผ์„ ๋ฐ”๊พผ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค.
ย 
๊ทธ๋ฆผ 9-5๊ทธ๋ฆผ 9-5
๊ทธ๋ฆผ 9-5
ย 
์œ„์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ชจ๋‹ฌ์˜ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ, Skyblue Btn๊ณผ Tomato Btn์„ ๋งŒ๋“ค์–ด ์›๋ž˜ ๋งŒ๋“ค๊ณ ์ž ํ–ˆ๋˜ ๊ทธ๋ฆผ 9-1๊ณผ ๊ฐ™์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์™„์„ฑํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ์ตœ์ข… ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
ย 
import { useState, useRef } from "react"; import CustomModal from "./CustomModal"; function App() { const [open, setOpen] = useState(false); const modalRef = useRef(); return ( <> <button onClick={() => setOpen(true)}>Open</button> <button onClick={() => { modalRef.current.pinkBtn.style = "width:50px; height:50px; background:pink;"; }} > Pink Btn </button> <button onClick={() => { modalRef.current.skyblueRefBtn.style = "width:50px; height:50px; background:skyblue;"; }} > Skyblue Btn </button> <button onClick={() => { modalRef.current.tomatoBtn.style = "width:50px; height:50px; background:tomato;"; }} > Tomato Btn </button> <CustomModal ref={modalRef} open={open} onClose={() => setOpen(false)} /> </> ); } export default App;
App.js
ย 
import React from "react"; import { useRef, useImperativeHandle } from "react"; function CustomModal({ open, onClose }, ref) { const closeRef = useRef(); const pinkRef = useRef(); const skyblueRef = useRef(); const tomatoRef = useRef(); useImperativeHandle(ref, () => { return { closeBtn: closeRef.current, pinkBtn: pinkRef.current, skyblueRefBtn: skyblueRef.current, tomatoBtn: tomatoRef.current, }; }); if (!open) return null; return ( <div> <br /> <button ref={closeRef} onClick={onClose}> &times; </button> <h1>Color Change</h1> <div> <div ref={pinkRef}>color1</div> <div ref={skyblueRef}>color2</div> <div ref={tomatoRef}>color3</div> </div> </div> ); } export default React.forwardRef(CustomModal);
CustomModal.jsx
ย 
ย