๐Ÿ“˜ [Recat] React Refs(์ฐธ์กฐ) & Portals(ํฌํƒˆ) ํ™œ์šฉ

๐Ÿ“˜ [Recat] React Refs(์ฐธ์กฐ) & Portals(ํฌํƒˆ) ํ™œ์šฉ

[Recat] React Refs(์ฐธ์กฐ) & Portals(ํฌํƒˆ) ํ™œ์šฉ

Refs(์ฐธ์กฐ)๋กœ HTML ์š”์†Œ ์—ฐ๊ฒฐ ๋ฐ ์ ‘๊ทผ

useRef(์ฐธ์กฐ)๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

  • ref๋Š” HTML ์š”์†Œ๋‚˜ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ฆฌ์•กํŠธ์˜ ํŠน๋ณ„ํ•œ ๊ฐ’ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
  • state์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ref๋Š” ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์–ด๋„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌ๋ Œ๋”๋งํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • useRef()๋ฅผ ํ†ตํ•ด ์ฐธ์กฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๊ฐ์ฒด๋Š” .current ์†์„ฑ์„ ๊ฐ€์ง.
//refs ์ƒ์„ฑ
const playerName = useRef();

// jsx์š”์†Œ์— ์—ฐ๊ฒฐ
<input ref={playerName} />

// ์ฐธ์กฐ๊ฐ’ ์‚ฌ์šฉ current.value๋ฅผ ํ†ตํ•ด ์—ฐ๊ฒฐ๋œ input ์š”์†Œ์˜ ํ˜„์žฌ ๊ฐ’์— ์ ‘๊ทผ ๊ฐ€๋Šฅ
const handleClick = () => {
  const enteredValue = playerName.current.value;
  setEnteredPlayerName(enteredValue);
};

useRef์™€ useState์˜ ์ฐจ์ด์ 

ํ•ญ๋ชฉstateref
์šฉ๋„์ปดํฌ๋„ŒํŠธ UI์— ์ง์ ‘ ์˜ํ–ฅ์„ ์ฃผ๋Š” ๊ฐ’DOM ์ ‘๊ทผ ๋˜๋Š” UI์™€ ๋ฌด๊ด€ํ•œ ๋‚ด๋ถ€ ๊ฐ’ ์ €์žฅ
๊ฐ’ ๋ณ€๊ฒฝ ์‹œ์ปดํฌ๋„ŒํŠธ ์žฌ๋ Œ๋”๋ง ๋ฐœ์ƒ์ปดํฌ๋„ŒํŠธ ์žฌ๋ Œ๋”๋ง ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ
์ ‘๊ทผ ์‹œ์ ํ•ญ์ƒ ์ตœ์‹ ๊ฐ’ ๋ณด์žฅ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์งํ›„์—๋Š” ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์•„ undefined์ผ ์ˆ˜ ์žˆ์Œ
์‚ฌ์šฉ ์œ„์น˜JSX ๋‚ด ์ƒํƒœ ๊ด€๋ฆฌ์— ์ฃผ๋กœ ์‚ฌ์šฉDOM ์š”์†Œ ์ฐธ์กฐ, ์ž„์‹œ ๊ฐ’ ์ €์žฅ ๋“ฑ์— ์‚ฌ์šฉ

ref๋ฅผ ์ž˜๋ชป ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ

const playerName = useRef();
console.log(playerName.current.value); // โŒ ์—๋Ÿฌ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
  • ์ปดํฌ๋„ŒํŠธ ์ตœ์ดˆ ๋ Œ๋”๋ง ์‹œ์ ์—๋Š” ref ์—ฐ๊ฒฐ์ด ๋˜์ง€ ์•Š์•„ playerName.current๋Š” undefined
  • ๋”ฐ๋ผ์„œ playerName.current.value ์ ‘๊ทผ ์‹œ ์—๋Ÿฌ ๋ฐœ์ƒ

๋ฌธ์ œ ๊ฐœ์š”

๊ฐ๊ฐ์˜ ํƒ€์ด๋จธ๋ฅผ ์‹œ์ž‘ํ–ˆ๋‹ค๊ฐ€ ์ค‘์ง€ํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  setTimeout()์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋กœ "์กŒ์Šต๋‹ˆ๋‹ค" ๊ฐ™์€ ๋ฉ”์‹œ์ง€๊ฐ€ ์—ฌ์ „ํžˆ ์ถœ๋ ฅ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•จ. ์ด๋Š” ํƒ€์ด๋จธ๋ฅผ ์ €์žฅํ•œ ๋ณ€์ˆ˜(timer) ๊ฐ€ ๊ฐ ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค๋งˆ๋‹ค ๊ณ ์œ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ.

let timer; // ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ์™ธ๋ถ€์— ์œ„์น˜
  • ๋ฌธ์ œ: ์ด ๋ณ€์ˆ˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค ๊ฐ„์— ๊ณต์œ ๋จ
  • A ์ธ์Šคํ„ด์Šค์—์„œ ํƒ€์ด๋จธ๋ฅผ ์„ค์ •ํ•˜๊ณ  B ์ธ์Šคํ„ด์Šค์—์„œ ๋‹ค์‹œ ์„ค์ •ํ•˜๋ฉด, ๊ธฐ์กด A์˜ ํƒ€์ด๋จธ ID๊ฐ€ ๋ฎ์–ด์“ฐ๊ธฐ๋จ
  • ์ค‘์ง€ ์š”์ฒญ ์‹œ, ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ํƒ€์ด๋จธ ID๋ฅผ ์ค‘์ง€ํ•˜๊ฒŒ ๋˜์–ด "์กŒ์Šต๋‹ˆ๋‹ค" ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚จ
const timer = useRef();
  • ๊ฐ ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ ref๋ฅผ ๊ฐ€์ง
  • useRef๋Š” HTML ์š”์†Œ ์ ‘๊ทผ๋ฟ ์•„๋‹ˆ๋ผ ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ๋ถ€์ ์ ˆํ•œ ๊ฐ’ ์ €์žฅ์—๋„ ์œ ์šฉ
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ์‹คํ–‰๋˜์–ด๋„ ref ๊ฐ’์€ ์œ ์ง€๋˜๋ฉฐ, ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค๋งˆ๋‹ค ๊ณ ์œ 
  • ์ƒํƒœ์ฒ˜๋Ÿผ UI ๊ฐฑ์‹ ์„ ์œ ๋„ํ•˜์ง€ ์•Š๊ณ , ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ๋‹จ๊ธฐ ์ €์žฅ์†Œ ์—ญํ• ์„ ํ•จ
  • ํƒ€์ด๋จธ ID, ์ด์ „ ๊ฐ’, ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ธ์Šคํ„ด์Šค ๋“ฑ์„ ๋ณด์กดํ•  ๋•Œ ์ตœ์ ์˜ ์„ ํƒ

์ปค์Šคํ…€ ์ปดํฌ๋„ŒํŠธ๋กœ Refs(์ฐธ์กฐ) ์ „๋‹ฌ

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ(TimerChallenge)์—์„œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ(ResultModal)์˜ <dialog> DOM ์š”์†Œ์— ์ ‘๊ทผํ•˜์—ฌ showModal() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์‹ถ์€ ์ƒํ™ฉ

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ (TimerChallenge)์—์„œ ref ์ƒ์„ฑ

const dialog = useRef(); // dialog DOM ์ฐธ์กฐ์šฉ ref ์ƒ์„ฑ

์ž์‹ ์ปดํฌ๋„ŒํŠธ (ResultModal)์— ref ์ „๋‹ฌ

<ResultModal ref={dialog} />

์ž์‹ ์ปดํฌ๋„ŒํŠธ (ResultModal)์—์„œ ref ์ถ”์ถœ

function ResultModal({ ... }, ref) {
  return <dialog ref={ref}>...</dialog>;
}

โœ… ์ฃผ์˜: ์ด ๋ฐฉ์‹์€ ์ตœ์‹  React์—์„œ๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค (React 18 ์ด์ƒ, ํŠนํžˆ React 19๋ถ€ํ„ฐ ๋” ๊ณต์‹ํ™”๋จ)

React ๊ตฌ๋ฒ„์ „ (17 ์ดํ•˜)

React๋Š” ์ผ๋ฐ˜์ ์ธ props๋กœ๋Š” ref๋ฅผ ์ง์ ‘ ์ „๋‹ฌ๋ฐ›์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

forwardRef() ์‚ฌ์šฉ๋ฒ• (React ๋ฒ„์ „์— ์ƒ๊ด€์—†์ด ์ž‘๋™)

forwardRef()๋กœ ์ž์‹ ์ปดํฌ๋„ŒํŠธ ๊ฐ์‹ธ๊ธฐ forwardRef๋Š” ref๋ฅผ ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•จ

import React, { forwardRef } from "react";

const ResultModal = forwardRef(function ResultModal(props) {
  return <dialog ref={ref}>...</dialog>;
},ref);

export default ResultModal;

ํฌํ„ธ(Portal)์ด๋ž€?

ํฌํ„ธ์€ JSX๋กœ ์ž‘์„ฑ๋œ UI ์š”์†Œ๋ฅผ ํ˜„์žฌ ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ ์™ธ๋ถ€์˜ DOM ๋…ธ๋“œ์— ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” React์˜ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

์™œ ํฌํ„ธ์„ ์‚ฌ์šฉํ•˜๋‚˜์š”?

๊ธฐ์กด ๊ตฌ์กฐ์—์„œ๋Š” ๋ชจ๋‹ฌ(dialog) ๊ฐ™์€ UI ์š”์†Œ๊ฐ€ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋ Œ๋”๋ง๋˜์–ด, DOM ํŠธ๋ฆฌ์—์„œ ๋‹ค๋ฅธ ์š”์†Œ๋“ค๊ณผ ์ค‘์ฒฉ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ 

  • ์‹œ๊ฐ์ ์œผ๋กœ๋Š” ๊ดœ์ฐฎ์•„ ๋ณด์—ฌ๋„,
  • ์‹ค์ œ๋กœ๋Š” HTML ๊ตฌ์กฐ๊ฐ€ ์—‰์ผœ์„œ ์ ‘๊ทผ์„ฑ ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
  • CSS ์ถฉ๋Œ์ด๋‚˜ z-index ๋ฌธ์ œ, ๋ ˆ์ด์•„์›ƒ ๊นจ์ง ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ
  • ๊นŠ์ด ์ค‘์ฒฉ๋œ ์š”์†Œ ์•ˆ์˜ ๋ชจ๋‹ฌ์€ ์˜๋„๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ โ†’ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ชจ๋‹ฌ ๊ฐ™์€ UI ์š”์†Œ๋ฅผ body ๋ฐ”๋กœ ๋ฐ‘ ๋“ฑ ์ตœ์ƒ๋‹จ์— ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋„๋ก ํฌํ„ธ์„ ์‚ฌ์šฉํ•จ

ํฌํ„ธ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•

index.html์— ๋ชจ๋‹ฌ์šฉ DOM ๋…ธ๋“œ ์ถ”๊ฐ€

<!-- public/index.html -->
<body>
  <div id="root"></div>
  <div id="modal"></div> <!-- ํฌํ„ธ ๋ Œ๋”๋ง ์œ„์น˜ -->
</body>

React์—์„œ ํฌํ„ธ ๊ธฐ๋Šฅ import

import { createPortal } from 'react-dom';

JSX๋ฅผ ํฌํ„ธ๋กœ ์ด๋™

return createPortal(
  <dialog>๋ชจ๋‹ฌ ๋‚ด์šฉ</dialog>,
  document.getElementById('modal')  // ๋ชจ๋‹ฌ div๋กœ ์ด๋™
);
  • createPortal(JSX, DOM ์š”์†Œ)
  • ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜: ๋ Œ๋”๋งํ•  UI
  • ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜: ๋ Œ๋”๋ง ์œ„์น˜
  • ์ฆ‰ ํ™”๋ฉด์—์„œ๋Š” ๊ทธ๋Œ€๋กœ, ์ฝ”๋“œ์ƒ ๊ตฌ์กฐ๋Š” ๊ฐœ์„ ๋จ

๐Ÿ“‘ Reference



Pagination


ยฉ 2025. All rights reserved.

Powered by Hydejack v9.2.1