useRef 사용하기 (Modal UI, 이벤트버블링)

Romuru·2023년 1월 16일
0

FE tech

목록 보기
2/3
post-thumbnail

모든 코드는 직접 작성했으며, github에서 확인하실 수 있습니다.

useRef?

크게 두가지의 사용이유가 있는데,

  1. 리렌더링을 유발하지 않는 변수로서의 사용

  2. 특정 DOM 선택을 하는 용도로 사용

이번 포스트에서는 2번인 특정 DOM을 선택하는 용도로 사용을 해볼 것이다.


const box = useRef(null)
 // useRef의 인자는 초기 value를 설정해줌.


const onDivClick = ()=>{ console.log(box)}
//버튼을 클릭하면 box를 console에 표시 해주는 함수.


 <div id="niceDiv" ref={box} onClick={onDivClick}>123</div> 
// DOM을 선택하는 방법은 ref속성에 useRef를 선언하면서 작성한 변수를 넣어준다. 

   

클릭시 console 출력 결과

성공적으로 "niceDiv" id를 가진 div 요소가 선택 되었다.


익숙해지기 위해 Modal을 만들어 보면서 useRef를 사용해봤다.

Modal은 웹사이트를 사용하는 도중에

자세한 안내가 담긴 UI, 회원가입이나 주문서 같이 상세한 입력이 필요하지만

별도의 페이지 이동없이 입력폼을 제공하기위해 사용한다.

그냥 큰 팝업창이라고 생각하면 편하다.

(구글 Modal webpage 검색)


요구사항

  1. 페이지 진행시에 입력을 하기위해 폼(Modal)을 호출할 버튼이 MainPage안에 존재해야 함

  2. Modal에는 사용자의 이메일을 입력받는 input 태그와 제출버튼이 있어야 함

  3. Modal이 활성화된 상태에서는 입력 form을 제외한 다른 화면은 어두운 색상의 투명도를 넣어 기존 MainPage를 볼 수 있어야 하며, 그 부분을 클릭하면 Modal창이 닫혀야함

  4. 제출 버튼을 누르면 사용자의 입력 값이 MainPage에 보이고, 다시 제출된다면 최근값을 표시한다.

Modal.js


export default function Modal() {
  const modal = useRef(null); 
  //const modal을 선언하여  value의 기본값이 null인 ueRef를 할당한다. 

  const [onModal, setOnModal] = useState(false);
  // 모달의 on/off 상태를 가질 useState
  const [email, setEmail] = useState();
  // onChange 이벤트 발생시마다 현재 값을 저장할 useState
  const [result, setResult] = useState();
  // 유저가 입력을 마무리 하고, 저장버튼을 누르면 본문에 표시할 정보를 담을 useState
  
  return (
    <>
      {onModal ? (
        <div
          className="ModalContainer"
          ref={modal}
          onClick={() => {
            setOnModal(false);
          }}
        >
          <div className="Modal">
            <div className="ModalTextContainer">
              <h1 className="ModalText">게시글 저장하기!</h1>
              <span>게시글을 저장하기 위해 이메일을 입력해주세요.</span>
            </div>
            <hr />
            <form>
              <input
                className="ModalInput"
                placeholder="E-mail"
                onChange={(e) => setEmail(e.currentTarget.value)}
              />

              <button
                className="ModalButton"
                onClick={(e) => {
                  e.preventDefault();
                  setResult(email);
                  setOnModal(false);
                }}
              >
                이메일에 저장하기
              </button>
            </form>
          </div>
        </div>
      ) : null}

      <div className="MainContainer">
        //본문 코드 
        <div className="ImageContainer">
          <img src={mokoko1} alt="mokoko1" />
          <img src={mokoko2} alt="mokoko2" />
        </div>
        <p className="Text">
         // 본문
        </p>
        <div className="ButtonContainer">
          <button
            className="Button"
            onClick={() => {
              setOnModal(true);
            }}
          >
            저장하기!
          </button>
        </div>
        {result ? <span>{result}로 저장했어요!</span> : null}
        // 처음 입력값을 받기 전까지는 null 리턴
      </div>

    </>
  );
}

삼항연산자로 Modal의 렌더링 여부를 결정하고, 받은 입력 값을 본문에 표시 해준다.

어려운 로직이 없어 따로 설명은 더 안하겠다.


css파일

"ModalContainer" class를 가진 div에 화면의 100%를 채우도록 하고,

position: absolute 속성을 주고, background-color: 속성에 투명도가 포함된

색상코드(rgba)를 지정하여 본문 전부를 덮지만 Modal의 자식태그를 제외한

나머지 부분은 투명도가 있는 회색으로 보이게 css를 작성했다.

모달 off/on 상태



구현은 완료 근데?

상위 목차에 있는 예시 코드만 실행을 한다면

Modal 요소 어느 곳을 눌러도 Modal UI가 종료 되는 치명적인 버그가 발생한다.


이벤트 버블링(Event Bubbling)

쉽게 말해 부모 div안에 자식 button이 존재하고

각각 click시 console에 "div event", "button event"라고 출력을 하는 코드를 작성하게 된다면,

자식 요소인 button만 정확히 클릭을 하더라도, 부모 요소인 div의 click event까지 발생 한다는 것이다.

더 자세한 내용은 링크를 참조하면 된다.

모던 JavaScript 튜토리얼 - 버블링과 캡처링


if문으로 버블링 관리하기

자식요소를 클릭한 뒤에 부모의 요소까지 이벤트가 발생하는 것은 필연적이다.

그래서, useRef를 통해 최상단에 있는 부모 ModalContaier에서 발생한 Event인지 확인 해야한다.


// ModalContainer 코드중 일부

...
		<div
          className="ModalContainer"
          ref={modal}
          onClick={(e) => {
 
            if (e.target === modal.current) {
            // onClick Evnet가 발생한 요소와, useRef로 지정한 요소가 일치할 때만 Modal이 off.
              setOnModal(false);
              console.log(modal);
           
            }
          }}
        >
...
  

Bubbling 이슈가 해결된 코드는 상단과 깃허브에서 확인할 수 있다.


Vanila JS에서는?

Vanila JS는 아래의 예시처럼 하나씩 요소를 찾아 변수에 할당하고 EventListener를 붙혀줘야한다.

 const modal = document.querySelector(".modal")
 
 modal.addEventListener("click", clickEventFn);

React가 아닌 JS로 구현 해놓은 예시도 있는데

자세한 코드는 JS 깃허브에서 확인할 수 있다.



참고 자료

useRef 로 특정 DOM 선택하기 - 벨로퍼트와 함께하는 모던 리액트

profile
늘 새로운 호기심을 찾고, 기술적 한계에 도전하고, 하늘색이 잘 어울리는 사람입니다.

0개의 댓글