๐ŸŒŸ ๋ชจ๋‹ฌ: ํ”„๋กœํ•„ ์ˆ˜์ • ๋ชจ๋‹ฌ ์ฐฝ

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

๐ŸŒŸ Twinkle (React, Firebase)

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

ํ”„๋กœํ•„ ์ˆ˜์ •์„ ์œ„ํ•ด ๋ชจ๋‹ฌ์„ ๋งŒ๋“ค์–ด ๋ณด๊ณ  ์‹ถ์–ด์„œ ๊ตฌ๊ธ€๋ง ํ•˜๋˜ ์ค‘ ์•„์ฃผ ์ƒ์„ธํ•˜๊ฒŒ ๋ชจ๋‹ฌ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ๋Š” ๋ธ”๋กœ๊ทธ๋ฅผ ๋ฐœ๊ฒฌํ–ˆ๋‹ค!

์••๋„์  ๊ฐ์‚ฌ :)๐Ÿ’œ

How to create a Modal Component in React from basic to advanced? | Thi Tran > Go To Read ๐Ÿ“š

์œ„ ์‚ฌ์ดํŠธ์—์„œ๋Š” ๋ชจ๋‹ฌ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์ดˆ์ ์ด๊ณ  ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ด ์ฃผ๊ณ  ์žˆ๋Š”๋ฐ, ๋‚˜๋Š” ๋‚ด ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ ์•„๋ž˜ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ์žˆ์–ด์„œ ๋„์›€์„ ๋ฐ›์•˜๋‹ค.

  1. ๋ชจ๋‹ฌ ๋ ˆ์ด์•„์›ƒ ์žก๊ธฐ
  2. ์Šคํƒ€์ผ ์ถ”๊ฐ€ํ•˜๊ธฐ
  3. ๋ชจ๋‹ฌ open/close ํ•ธ๋“ค๋ง
  4. onClose ์ด๋ฒคํŠธ
  5. ๋ชจ๋‹ฌ ์ฐฝ ์™ธ๋ถ€ ํด๋ฆญ ์‹œ ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ
  6. esc ํ‚ค๋‹ค์šด ์ด๋ฒคํŠธ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ
  7. ๋ชจ๋‹ฌ์— ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€ํ•˜๊ธฐ

1. ๋ชจ๋‹ฌ ๋ ˆ์ด์•„์›ƒ ์žก๊ธฐ


๋ ˆ์ด์•„์›ƒ ์žก๊ธฐ

  • Overlay: ๋ชจ๋‹ฌ ์ปจํ…Œ์ด๋„ˆ
  • Header: ๋ชจ๋‹ฌ ์ฐฝ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ, ๋ชจ๋‹ฌ ์ œ๋ชฉ, ๋ณ€๊ฒฝ ๋‚ด์šฉ ์ €์žฅ ๋ฒ„ํŠผ
  • Body: ํ—ค๋” ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ, ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ, ์œ ์ € ์ด๋ฆ„ ๋ฐ ์ž๊ธฐ์†Œ๊ฐœ ๋ณ€๊ฒฝ

๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

Modal ํด๋”๋ฅผ ์ƒ์„ฑํ•˜์—ฌ EditProfileModal.js ํŒŒ์ผ์„ ๋งŒ๋“ ๋‹ค.

import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCamera, faTimes } from "@fortawesome/free-solid-svg-icons";

const EditProfileModal = () => {
  return (
    <div className="modal">
      <div className="modal-content">
        <div className="modal-header">
          <div className="modal-header__box">
            <div className="modal-x-btn">
              <button className="btn--min btn--circle">
                <FontAwesomeIcon icon={faTimes} />
              </button>
            </div>
            <h4 className="modal-title">ํ”„๋กœํ•„ ์ˆ˜์ •</h4>
            <button className="btn btn--blue btn--border-zero">์ €์žฅ</button>
          </div>
        </div>
        <div className="modal-body">
          <div className="profile__user__header">
            ํ—ค๋” ์ด๋ฏธ์ง€ 598*200
            <div className="btn--edit--container">
              <div
                aria-label="ํ—ค๋” ์‚ฌ์ง„ ์ถ”๊ฐ€ํ•˜๊ธฐ"
                className="btn--edit--container--btn"
              >
                <button className="btn--change">
                  <FontAwesomeIcon icon={faCamera} />
                </button>
              </div>
              <div
                aria-label="ํ—ค๋” ์‚ฌ์ง„ ์‚ญ์ œํ•˜๊ธฐ"
                className="btn--edit--container--btn"
              >
                <button className="btn--delete">
                  <FontAwesomeIcon icon={faTimes} />
                </button>
              </div>
            </div>
          </div>
          <div className="profile__user__info">
            <div className="profile__user__userImg">
              <div className="profile__user__userImg__file userImg--sm img--edit--container">
                <div
                  aria-label="ํ”„๋กœํ•„ ์‚ฌ์ง„ ์ถ”๊ฐ€ํ•˜๊ธฐ"
                  className="img--edit--container--btn"
                >
                  <button className="btn--change">
                    <FontAwesomeIcon icon={faCamera} />
                  </button>
                </div>
              </div>
            </div>
          </div>
          <div className="modal-edit">
            <form className="edit-form">
              <label htmlFor="user-name"> ์ด๋ฆ„ </label>
              <input
                id="user-name"
                aria-describedby="user-name"
                name="user-name"
                type="text"
                placeholder="์ด๋ฆ„"
                className="btn btn--skyblue"
              />
              <label htmlFor="self-introduction"> ์ž๊ธฐ ์†Œ๊ฐœ </label>
              <input
                id="self-introduction"
                aria-describedby="self-introduction"
                name="self-introduction"
                type="text"
                placeholder="์ž๊ธฐ ์†Œ๊ฐœ"
                className="btn btn--skyblue"
              />
            </form>
          </div>
        </div>
      </div>
    </div>
  );
};

export default EditProfileModal;

2. ์Šคํƒ€์ผ ์ถ”๊ฐ€ํ•˜๊ธฐ


๋ฐ˜๋ณต๋˜๋Š” ์Šคํƒ€์ผ์ธ ๊ฒฝ์šฐ ๊ธฐ์กด์— ์ž‘์„ฑํ–ˆ๋˜ ํด๋ž˜์Šค๋ช…์„ ์ถ”๊ฐ€ํ•ด ์คฌ๋‹ค.
์ผ๋‹จ styles.scss ์‹œํŠธ์— ๋ชจ๋‘ ์ ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋กœ์ ํŠธ ๋‹ค ์ž‘์„ฑํ•˜๊ณ  ๋‚˜์„œ ํด๋ฆฌ๋‹ ์ฝ”๋“œํ•˜์ž ^3^~

/*๐Ÿ’ฌ  EditProfileModal*/
.modal {
  z-index: 99;
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  opacity: 0;
  
  .modal-content {
    width: 600px;
    height: 70%;
    background-color: white;
    border-radius: 20px;
    
    .modal-header {
      padding: 0 16px;
      display: flex;
      flex-direction: row;
      align-items: center;
      height: 58px;
      width: 100%;
      
      .modal-header__box {
        width: 100%;
        display: flex;
        flex-direction: row;
        justify-content: center;
        align-items: center;
        font-size: 20px;
        
        .modal-x-btn {
          min-width: 56px;
          min-height: 32px;
          display: flex;
          flex-direction: column;
          flex-basis: auto;
          flex-shrink: 0;
          align-self: stretch;
          justify-content: center;
          align-items: flex-start;
          button {
            font-size: 20px;
          }
        }
        
        .modal-title {
          font-weight: 800;
          display: flex;
          flex-grow: 1;
          align-items: stretch;
        }
        
        button {
          font-size: 15px;
        }
      }
    }
    
    
    .modal-body {
    
      .profile__user__header {
        display: flex;
        justify-content: center;
        align-items: center;
        position: relative;
        
        .btn--edit--container {
          position: absolute;
          display: flex;
          flex-direction: row;
          
          .btn--edit--container--btn {
            border-radius: 50px;
            width: 45px;
            height: 45px;
            background-color: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-content: center;
            box-sizing: border-box;
            font-size: 20px;
            button {
              color: white;
            }
          }
          
          .btn--edit--container--btn:last-child {
            margin-left: 20px;
          }
        }
      }


      .profile__user__info {
        .userImg-sm {
          width: 200px;
          height: 200px;
          background-color: black;
        }
        
        .profile__user__userImg {
          position: relative;
          
          .img--edit--container {
            position: absolute;
            display: flex;
            justify-content: center;
            align-items: center;
            
            .img--edit--container--btn {
              border-radius: 50px;
              width: 45px;
              height: 45px;
              background-color: rgba(0, 0, 0, 0.5);
              display: flex;
              justify-content: center;
              align-content: center;
              box-sizing: border-box;
              font-size: 20px;
             
              button {
                color: white;
              }
            }
          }
        }
      }
      
      
      .modal-edit {
        margin-top: 50px;
        
        .edit-form {
          display: flex;
          flex-direction: column;
          padding: 16px;
          
          label {
            padding-left: 15px;
          }
          
          input {
            font-size: 13px;
            font-weight: 500;
            width: 300px;
            margin-bottom: 15px;
            border-radius: 5px;
            cursor: auto;
          }
          
          input:focus {
            background-color: #ebf5fe;
            transition: 0.3s;
          }
        }
      }
    }
  }
}

3. ๋ชจ๋‹ฌ open/close ํ•ธ๋“ค๋ง


  1. /Modal/EditProfileModal.js์— ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ชจ๋‹ฌ์„ ์—ด๊ณ  ๋‹ซ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ์ž.
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCamera, faTimes } from "@fortawesome/free-solid-svg-icons";

const EditProfileModal = (isModalOpen) => {
  if (!isModalOpen){
    return null
  }
  
  return (
    <div className="modal">
      //์ƒ๋žต

๋ชจ๋‹ฌ ์ฐฝ์ด ์—ด๋ฆฌ์ง€ ์•Š์€ ๊ฒฝ์šฐ, ์ฆ‰ isModalOpen์˜ ๊ฐ’์ด false์ธ ๊ฒฝ์šฐ null์„ ๋ฆฌํ„ดํ•˜์—ฌ ๋ชจ๋‹ฌ ๋ ˆ์ด์•„์›ƒ์ด ๋œจ์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.

  1. Profile.js์—์„œ isModalOpen์˜ value ๊ด€๋ฆฌํ•˜๊ธฐ
//๋ชจ๋‹ฌ ์ฐฝ ์—ด๊ธฐ
<EditProfileModal isModalOpen={true} />

//๋ชจ๋‹ฌ ์ฐฝ ๋‹ซ๊ธฐ
<EditProfileModal isModalOpen={false} />
const Profile = ({ refreshUser, userObj }) => {
//์ƒ๋žต

  const [isModalOpen, setIsModalOpen] = useState(false);

  const handleEditModalOpen = () => {
    setIsModalOpen((prev) => !prev);
  };


return(
  <button
    className="btn btn--grey"
    onClick={handleEditModalOpen}
    >
    ํ”„๋กœํ•„ ์ˆ˜์ •
  </button>
  <EditProfileModal
    userObj={userObj}
    isModalOpen={isModalOpen}
    />
);

4. ๋ชจ๋‹ฌ ์ฐฝ ๋‹ซ๊ธฐ ์ด๋ฒคํŠธ


  1. /Modal/EditProfileModal.js์— onClick์ด๋ฒคํŠธ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ๋ฐ”์ธ๋”ฉํ•˜๊ธฐ

const EditProfileModal = ({ isModalOpen, onClose }) => {
  return(
  //์ƒ๋žต
  <div className="modal-x-btn">
    <button className="btn--min btn--circle" onClick={onClose}>
      <FontAwesomeIcon icon={faTimes} />
    </button>
  </div>
  //์ƒ๋žต
  );
};
  1. isModalOpen์˜ value ๊ด€๋ฆฌ๋ฅผ Profile.js์—์„œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— onClose๋„ ์—ฌ๊ธฐ์—์„œ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
const Profile = ({ refreshUser, userObj }) => {
//์ƒ๋žต
  
  const [isModalOpen, setIsModalOpen] = useState(false);

  const handleEditModalOpen = () => {
    setIsModalOpen((prev) => !prev);
  };

  const handleEditModalClose = () => {
    setIsModalOpen((prev) => !prev);
  };
  
  return(
  <button
    className="btn btn--grey"
    onClick={handleEditModalOpen}
    >
    ํ”„๋กœํ•„ ์ˆ˜์ •
  </button>
  <EditProfileModal
    userObj={userObj}
    isModalOpen={isModalOpen}
    onClose={handleEditModalClose}
    />
  );
};

5. ๋ชจ๋‹ฌ ์ฐฝ ์™ธ๋ถ€ ํด๋ฆญ ์‹œ ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ


์ผ๋ฐ˜์ ์œผ๋กœ ๋ชจ๋‹ฌ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์™ธ๋ถ€ ํด๋ฆญ(๋‹ซ์œผ๋ ค๋ฉด ์˜ค๋ฒ„๋ ˆ์ด ํด๋ฆญ)์„ ํ†ตํ•ด ๋‹ซ์„ ์ˆ˜ ์žˆ๋‹ค. ๋™์ž‘์„ ๊ตฌํ˜„ํ•ด ๋ณด์ž.
/Modal/EditProfileModal.js์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

const EditProfileModal = ({ userObj, isModalOpen, onClose }) => {
  //์ด๋ฒคํŠธ ์ „๋‹ฌ ๋ง‰๊ธฐ: modal ์ „์ฒด ํ™”๋ฉด ํด๋ฆฌ ์‹œ onClose ์ด๋ฒคํŠธ ๋ฐœ์ƒํ•˜๋‚˜ modal-content ์˜์—ญ ํด๋ฆญ์‹œ ์ž‘๋™๋˜์ง€ ์•Š๋„๋ก ์ด๋ฒคํŠธ ์ „๋‹ฌ ๋ง‰์Œ
  const handleStopPropagation = (event) => {
    event.stopPropagation();
  };
  
  return (
    <div className="modal" onClick={onClose}>
      <div className="modal-content" onClick={handleStopPropagation}>
        <div className="modal-header">
  //์ƒ๋žต
 );
};
          
  • ์˜ค๋ฒ„๋ ˆ์ด๋œ ๋ถ€๋ถ„์„ ํด๋ฆญํ–ˆ์„ ๋•Œ onClick ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜์—ฌ onClose๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

  • ์™ธ๋ถ€ ํด๋ฆญ ์‹œ์—๋งŒ ๋‹ซ์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— modal-content ๋‚ด๋ถ€ ํด๋ฆญ ์‹œ์—๋Š” ์ด ์ด๋ฒคํŠธ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๋„๋ก ์ด๋ฒคํŠธ ์ „๋‹ฌ ์ค‘์ง€ ๋ฉ”์„œ๋“œ event.stopPropagation()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

6. esc ํ‚ค๋‹ค์šด ์ด๋ฒคํŠธ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ


esc ํ‚ค๋‹ค์šด ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋‹ฌ ์ฐฝ์„ ๋‹ซ์•„๋ณด์ž.
/Modal/EditProfileModal.js์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

const EditProfileModal = ({ userObj, isModalOpen, onClose }) => {
  
  const handleStopPropagation = (event) => {
    event.stopPropagation();
  };

  const onEscapeKeyDown = (event) => {
    if ((event.charCode || event.keyCode) === 27) {
      onClose();
    }
  };

  useEffect(() => {
    document.body.addEventListener("keydown", onEscapeKeyDown);
    return function cleanup() {
      document.body.addEventListener("keydown", onEscapeKeyDown);
    };
  }, []);
  
  //์ƒ๋žต
  • Escape ํ‚ค(์–ด๋–ค ํ‚ค ์ฝ”๋“œ๊ฐ€ 27์ธ์ง€)๊ฐ€ ๋ˆŒ๋ ธ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ํ‚ค ๋‹ค์šด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ •์˜ํ•œ๋‹ค.

  • useEffect๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‚ค๋‹ค์šด ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

7. ๋ชจ๋‹ฌ์— ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€ํ•˜๊ธฐ


CSS ์ „ํ™˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋‹ฌ์„ ์• ๋‹ˆ๋ฉ”์ด์…˜ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
์˜ค๋ฒ„๋ ˆ์ด์— ๋Œ€ํ•ด ํŽ˜์ด๋“œ ์ธ/ํŽ˜์ด๋“œ ์•„์›ƒ์„ ์ ์šฉํ•ด ๋ณด์ž.

  • Fade in/out: opacity๋ฅผ 0 ์—์„œ 1๋กœ ์ „ํ™˜(transition)ํ•˜๊ธฐ
  1. ๊ธฐ์กด์— /Modal/EditProfileModal.js์—์„œ ์ž‘์„ฑํ–ˆ๋˜
if(!isOpenModal){
  return null
}

๋ถ€๋ถ„์„ ์‚ญ์ œํ•˜๊ณ  ๋Œ€์‹  className์ด "modal"์ธ ์ฒซ ์ฒซ ๋ฒˆ์งธ div ์š”์†Œ์— ์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ถ”๊ฐ€ํ•ด ์ค€๋‹ค.

  return (
    <div className={`modal ${isModalOpen ? "show" : ""}`} onClick={onClose}>

CSS์—์„œ show/hide๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ ‡๊ฒŒ ๋ณ€๊ฒฝํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.
isModalOpen์ด true์ธ ๊ฒฝ์šฐ "show" ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ์‹์ด๋‹ค.

  1. css ์ž‘์—…์„ ํ•ด์ฃผ์ž.
.modal {
  //์ƒ๋žต
  opacity: 0;
  transition: all 0.15s ease-in-out;
  pointer-events: none;
  
.modal.show {
  opacity: 1;
  pointer-events: visible;
}
  • opacity: 0;
    ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋‹ฌ์„ ํŽ˜์ด๋“œ ์•„์›ƒํ•œ๋‹ค.

  • transition: all 0.15s ease-in-out;
    ๋ถˆํˆฌ๋ช…๋„์— ๋Œ€ํ•œ ํŠธ๋žœ์ง€์…˜ ์ ์šฉ

  • pointer-events: none;
    display: none;์„ ํ•˜๋ฉด ์š”์†Œ๋Š” ๋ฐ˜๋“œ์‹œ ์ˆจ๊ฒจ์ง„๋‹ค.
    display: none;์ฒ˜๋Ÿผ ์š”์†Œ๋ฅผ 100% ์ˆจ๊ธฐ๋ฉด์„œ ํŽ˜์ด๋“œ ์ธ/์•„์›ƒ์„ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ถˆํˆฌ๋ช…๋„(opacity)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ .modal์ด ๋ชจ๋“  ํ™”๋ฉด ๋ณด๋‹ค ์ œ์ผ ์œ„์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์—(z-index: 99;) ์š”์†Œ๊ฐ€ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ• ์ง€๋ผ๋„ ์•„๋ž˜ ํ™”๋ฉด์— ์žˆ๋Š” ์š”์†Œ๋“ค์„ ํด๋ฆญํ•  ์ˆ˜ ์—†๊ฒŒ๋œ๋‹ค. ์ฆ‰, ํ”„๋กœํ•„ ์ˆ˜์ • ๋ฒ„ํŠผ์„ ํด๋ฆญํ•  ์ˆ˜ ์—†๋‹ค.
    pointer-events: none;๋Š” .modal ์•„๋ž˜์— ์žˆ๋Š” ๋‹ค๋ฅธ ์š”์†Œ๋ฅผ ํด๋ฆญํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.

  • .modal.show
    .show ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ opacity: 1๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ชจ๋‹ฌ ์ฐฝ์ด ๋ณด์ด๊ฒŒ ๋งŒ๋“ค๊ณ , pointer-events: visible;๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ชจ๋‹ฌ ๋‚ด๋ถ€๋ฅผ ํด๋ฆญํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿฅฐ๐Ÿ”ฅ

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

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