๐ŸŒŸ ๋ชจ๋‹ฌ: useRef()๋กœ DOM ์ง€์ •ํ•˜์—ฌ ๋ชจ๋‹ฌ ์ฐฝ ๋‹ซ๊ธฐ

summereuna๐Ÿฅยท2022๋…„ 9์›” 7์ผ

๐ŸŒŸ Twinkle (React, Firebase)

๋ชฉ๋ก ๋ณด๊ธฐ
35/42

์ผ์ „์— ๋ธŒ๋ผ์šฐ์ € ํฌ๊ธฐ๋ฅผ ์ค„์—ฌ๋„ ํ—ค๋”๋ฅผ ๋”ฐ๋ผ๋‹ค๋‹ˆ๋Š” ๋ชจ๋‹ฌ์„ ๊ตฌํ˜„ํ–ˆ์—ˆ๋Š”๋ฐ,
์ž˜ ๋˜๋‹ค๊ฐ€ ์–ด๋А์ƒˆ ๋ณด๋‹ˆ ์œ„์น˜๊ฐ€ ์•ˆ ๋งž๋Š”๊ฒŒ ์•„๋‹Œ๊ฐ€...
์•„๋ฌด๋ฆฌ position์„ ๋ณ€๊ฒฝํ•ด ๊ฐ€๋ฉฐ ์‚ฝ์งˆ์„ ํ•ด๋„ ์•ˆ๋˜๊ธธ๋ž˜ ๋ชจ๋‹ฌ ํด๋กœ์ง• ๋ฐฉ์‹์„ ๋ฐ”๊ฟจ๋‹ค.

๊ธฐ์กด ๋ฐฉ์‹


๊ธฐ์กด์—๋Š” LogOutModal ์ปดํฌ๋„ŒํŠธ์—์„œ ์ปจํ…์ธ  ๋ฐ•์Šค์ธ modal-logout-content-1์˜ ์ƒ์œ„ ์š”์†Œ, ์ฆ‰ ๋ฐ•์Šค ๋ฐ”๊นฅ์„ ํด๋ฆญํ•  ๊ฒฝ์šฐ ๋ชจ๋‹ฌ์ด ํด๋กœ์ง•๋˜๊ฒŒ ํ–ˆ์—ˆ๋‹ค.

๐Ÿ“ Navigation.js

import { useState } from "react";
import LogOutModal from "./Modal/LogOutModal";

const Navigation = ({ userObj }) => {
  
  //Log Out Modal
  const [isOpenLogOutModal, setIsOpenLogOutModal] = useState(false);

  const handleOpenLogOutModal = () => {
    setIsOpenLogOutModal((prev) => !prev);
  };

  const handleCloseLogOutModal = () => {
    setIsOpenLogOutModal(false);
  }
  
  return (
    
    //์ƒ๋žต
    
    <LogOutModal
      userObj={userObj}
      onClose={handleCloseLogOutModal}
      show={isOpenLogOutModal}
      />
    
    //์ƒ๋žต
    

๐Ÿ“ LogOutModal.js

const LogOutModal = ({ show, userObj, onClose }) => {
  
  //์ƒ๋žต
  
  return (
    <div className={`modal-layout ${show ? "show" : ""}`} onClick={onClose}>
      <div className="modal-layout-inside">
        <div className="modal-logout">
          <div
            className="modal-logout-content-1"
            onClick={(e) => e.stopPropagation()}
          >
            //์ƒ๋žต

๐Ÿ“ scss

.modal-layout {
  z-index: 999;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  opacity: 0;
  transition: all 0.15s ease-in-out;
  pointer-events: none;
  
  .modal-layout-inside {
    position: fixed;
    left: 0;
    right: 0;
    top: 68%;
    display: flex;
    
    .modal-logout {
      position: sticky;
      left: -13px;
      
      .modal-logout-content-1 {
        width: 300px;
        
        //์ƒ๋žต
      

๐Ÿฅบ ๋กœ๊ทธ์•„์›ƒ ๋ชจ๋‹ฌ์„ ๋‹ค๋ฃฐ ๋•Œ ์•„๋ž˜ ๋‘ ๊ฐ€์ง€๋ฅผ ๋™์‹œ์— ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค.

  1. ๋ชจ๋‹ฌ์ด ํ—ค๋”์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ ์œ„์— ๋”ฑ ๋‹ฌ๋ผ ๋ถ™์–ด ์žˆ์„ ๊ฒƒ.

  2. ๋ชจ๋‹ฌ ์ปจํ…์ธ  ๋ฐ•์Šค ์™ธ๋ถ€ ํด๋ฆญ ์‹œ ๋ชจ๋‹ฌ ์ฐฝ์ด ๋‹ซํž ๊ฒƒ.


์ž˜ ๋˜๋˜๊ฒŒ ์™œ ๊ฐ‘์ž๊ธฐ ์•ˆ๋˜๋Š” ๊ฑธ๊นŒ, ์‹ค์€ ์›๋ž˜๋ถ€ํ„ฐ ์ฝ”๋“œ๊ฐ€ ์ด์ƒํ–ˆ๋Š”๋ฐ ์ด์ƒํ•˜๊ฒŒ ์ž˜ ์ž‘๋™ํ–ˆ๋˜๊ฑด ์•„๋‹๊นŒ ์‹ถ๊ธฐ๋„ ํ•˜๋‹ค...๐Ÿฅฒ
์•ˆํƒ€๊น๊ฒŒ๋„ ์ „์— ์ž˜๋˜๋˜ ์ด ์ฝ”๋“œ๋กœ๋Š” 1๋ฒˆ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋˜์–ด๋ฒ„๋ ธ๋‹ค.

position์„ ๋ฐ”๊พธ๋ฉฐ ๊ณ„์†์ ์ธ ์‚ฝ์งˆ์„ ํ–ˆ์ง€๋งŒ ๊ณ„์†์ ์ธ ์‹คํŒจ์— ์ง„์ ˆ๋จธ๋ฆฌ๊ฐ€ ๋‚  ์ฆˆ์Œ... ๋ชจ๋‹ฌ ํด๋กœ์ง• ๋ฐฉ์‹์„ ๋ฐ”๊พธ๊ธฐ๋กœ ํ–ˆ๋‹ค.


ํด๋กœ์ง• ๋ฐฉ์‹ ๋ฐ”๊ฟ”์„œ ๋ฌธ์ œ ํ•ด๊ฒฐ


๐Ÿ“ Navigation.js

import { useEffect, useRef, useState } from "react";
import LogOutModal from "./Modal/LogOutModal";

const Navigation = ({ userObj }) => {
  
  const outsideOfLogOutModal = useRef();
  
  //Log Out Modal
  const [isOpenLogOutModal, setIsOpenLogOutModal] = useState(false);

  const handleOpenLogOutModal = () => {
    setIsOpenLogOutModal((prev) => !prev);
  };

 const handleCloseLogOutModal = ({ target }) => {
    if (isOpenLogOutModal && !outsideOfLogOutModal.current.contains(target))
      setIsOpenLogOutModal(false);
  };
  
  useEffect(() => {
    window.addEventListener("click", handleCloseLogOutModal);
    return () => {
      window.removeEventListener("click", handleCloseLogOutModal);
    };
  });
  
  return (
    
    //์ƒ๋žต
    
    <div ref={outsideOfLogOutModal}>
      <LogOutModal userObj={userObj} show={isOpenLogOutModal} />
    </div>
    
    //์ƒ๋žต
    
  • const outsideOfLogOutModal = useRef();
    useRef()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด DOM์—์„œ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ์š”์†Œ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ์— ํ•ด๋‹นํ•˜๋Š” ์˜์—ญ์„ ํฌํ•จํ•œ ์š”์†Œ๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด outsideOfLogOutModal๋ผ๊ณ  ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ์ฃผ๊ณ  useRef()๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์ž.

  • window.addEventListener("click", handleCloseLogOutModal);
    ๋ธŒ๋ผ์šฐ์ €์— ์—ด๋ ค์žˆ๋Š” window ๊ฐ์ฒด, ์ฆ‰ ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์„ ํด๋ฆญํ•˜๋ฉด handleCloseLogOutModal ์ด๋ฒคํŠธ๊ฐ€ ์‹คํ–‰์ด ๋œ๋‹ค.

  • ๋ชจ๋‹ฌ์„ ์—ด์—ˆ์„ ๋•Œ์™€ ๋‹ซ์•˜์„ ๋•Œ์˜ outsideOfLogOutModal.current ์ฐจ์ด๋ฅผ ์•Œ์•„๋ณด๊ธฐ ์œ„ํ•ด ์ฝ˜์†”์— ์ฐ์–ด ๋ดค๋‹ค.
    ๋ชจ๋‹ฌ์ด ๋ณด์ด๊ณ  ์•ˆ๋ณด์ด๊ณ ์˜ ์ฐจ์ด(.show)์ผ ๋ฟ, ํด๋ฆญ์ด๋ฒคํŠธ๊ฐ€ ์‹คํ–‰๋  ๋•Œ useRef()๋กœ ๊ฐ€๋ฆฌํ‚ค๋Š” DOM์š”์†Œ๋Š” ๋˜‘๊ฐ™์ด ๋ชจ๋‹ฌ์ปดํฌ๋„ŒํŠธ ๋ถ€๋ถ„์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
  • if (isOpenLogOutModal && !outsideOfLogOutModal.current.contains(target))
    isOpenLogOutModal์˜ state๊ฐ€ true, ์ฆ‰, ๋กœ๊ทธ์•„์›ƒ ๋ชจ๋‹ฌ์ด ์—ด๋ ธ์„ ๋•Œ,
    ๊ทธ๋ฆฌ๊ณ  ๋™์‹œ์— outsideOfLogOutModal.current.contains(target)์ด false์ผ ๋•Œ, ์ฆ‰, ํด๋ฆญํ•œ ํƒ€๊ฒŸ์ด ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์†ํ•œ ๋ถ€๋ถ„์ด ์•„๋‹Œ ๋ชจ๋‹ฌ์˜ ์™ธ๋ถ€๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ๋ชจ๋‹ฌ์„ ํด๋กœ์ง•ํ•œ๋‹ค.

    ๐ŸŒŸ contains ๋ฉ”์„œ๋“œ๋Š” ์ด๋ฒคํŠธ์˜ ํƒ€๊ฒŸ์ด ref์— ์†ํ•ด ์žˆ๋Š”์ง€ ์ฆ‰, ์ž๋…€์ธ์ง€ ์•Œ๋ ค ์ฃผ๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
    ๋”ฐ๋ผ์„œ LogOutModal ์ปดํฌ๋„ŒํŠธ ์ƒ๋‹จ์— div๋ฅผ ํ•˜๋‚˜ ์ƒˆ๋กœ ๋งŒ๋“ค๊ณ , ๊ทธ div์— ref๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ์ ์šฉ๋  ์˜์—ญ์„ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿ“ LogOutModal.js

const LogOutModal = ({ show, userObj }) => {
  
  //์ƒ๋žต

  return (
    <div className={`modal-layout ${show ? "show" : ""}`}>
      <div className="modal-logout">
        <div className="modal-logout-content-1">

๊ธฐ์กด์— ์žˆ๋˜ onClose ํ”„๋กœํผํ‹ฐ๋Š” ์ œ๊ฑฐํ–ˆ๋‹ค.

๐Ÿ“ scss

.modal-layout {
  z-index: 999;
  position: absolute;
  left: 0;
  top: 68vh;
  display: flex;
  opacity: 0;
  transition: all 0.15s ease-in-out;
  pointer-events: none;
  
  .modal-logout {
    position: fixed;
    top: 68vh;
    
    .modal-logout-content-1 {
      width: 300px;
      
      //์ƒ๋žต
  • ์ƒ์œ„ ์š”์†Œ์ธ header๊ฐ€ position: relative์ธ๊ฑด ๋™์ผํ•˜๋‹ค.

  • ๋จผ์ € ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ ์ตœ์ƒ๋‹จ div์ธ modal-layout์— position: absolute๋ฅผ ์ฃผ์–ด ๋ถ€๋ชจ์ธ ํ—ค๋”์— ๋ถ™์ธ๋‹ค.

  • ๊ทธ๋Ÿฌ๊ณ ๋‚˜์„œ ์ปจํ…์ธ  ๋ฐ•์Šค ๋ฐ”๋กœ ์ƒ๋‹จ div์ธ modal-logout์— position: fixed๋ฅผ ์ค˜์„œ ํ—ค๋” ์˜์—ญ์˜ ๋ทฐํฌํŠธ์— ๊ณ ์ •์‹œํ‚จ๋‹ค.

    ๐ŸŒŸ ์ „์ฒด์˜ ๋ฐ”๊นฅ ๋ถ€๋ถ„์„ ํด๋ฆญํ–ˆ์„ ๋•Œ๋งŒ ๋ชจ๋‹ฌ์ด ๋‹ซํžˆ๊ธฐ ๋•Œ๋ฌธ์— ์ปจํ…์ธ  ๋ฐ•์Šค ์ƒ๋‹จ div๋“ค์—๋Š” height ๊ฐ’์„ ์ฃผ์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค. ๊ธฐ์กด์— ํ–ˆ๋˜ ๋ฐฉ์‹๊ณผ ์™„์ „ ๋ฐ˜๋Œ€๋‹ค.


ํœด ์–ด์จŒ๋“  ๋ฌธ์ œ ํ•ด๊ฒฐ ๐Ÿฅฒ


๐Ÿฅบ ํœด ์ด์ œ ๋‘˜ ๋‹ค ๋œ๋‹ค.

  1. ๋ชจ๋‹ฌ์ด ํ—ค๋”์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ ์œ„์— ๋”ฑ ๋‹ฌ๋ผ ๋ถ™์–ด ์žˆ์„ ๊ฒƒ.

  2. ๋ชจ๋‹ฌ ์ปจํ…์ธ  ๋ฐ•์Šค ์™ธ๋ถ€ ํด๋ฆญ ์‹œ ๋ชจ๋‹ฌ ์ฐฝ์ด ๋‹ซํž ๊ฒƒ.

์ž์ž˜์ž์ž˜ํ•œ ์˜ค๋ฅ˜๊ฐ€ ์ฐธ ๋งŽ๋‹ค.
๊ณผ์—ฐ ๋‚˜๋Š” ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ž˜ ๋งˆ๋ฌด๋ฆฌ ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ธ๊ฐ€...!

profile
Always have hope๐Ÿ€ & constant passion๐Ÿ”ฅ

0๊ฐœ์˜ ๋Œ“๊ธ€