스터디 관리 프로젝트 - 이벤트 버블링과 캡쳐링??

Seuling·2023년 3월 8일
0

스터디관리프로젝트

목록 보기
10/10

모달 작업중, background를 클릭하면 closeModal 이 작동하여 모달을 닫아주는 동작이 되도록 하였다.
하지만, 원래 생각과는 다르게, 모달내의 클릭이 되어도 모달이 닫혀버리는 현상이 발생하였다.

코드를 먼저 보자면?

import React, { ChangeEvent, FormEvent, useRef, useState } from "react";
import Button from "./Button";
import Text from "./Text";
import * as Icon from "react-feather";
import { updateTodo } from "../../services/api/todo";

type Variant = "code" | "certification";
interface Props {
  variant?: Variant;
  closeModal: () => void;
  onSubmit: any;
}

interface UpdateParams {
  key: "authenticationMethod" | "authenticationContent";
  value: string;
}

export default function Modal({ variant, closeModal, onSubmit }: Props) {
  const inputRef = useRef(null);

  const [newDone, setNewDone] = useState<TodoParam>({
    complitedAt: new Date(),
    authenticationMethod: "",
    authenticationContent: "",
  });

  const [isShowImage, SetIsShowImage] = useState(false);

  const update = (params: UpdateParams[]) => {
    const _newDone = { ...newDone };
    params.forEach((p) => {
      _newDone[p.key] = p.value;
    });
    setNewDone(_newDone);
  };

  return (
      <div className="background" onClick={closeModal}>
        <div className="modal-container" >
          {variant === "code" ? (
            <div className="variant-code">
              <Text type="title">입장코드를 입력해주세요</Text>
              <input placeholder="e.g. 1234567" />
            </div>
          ) : (
            <form className="variant-certification">
              <Text type="title">공부내용 인증</Text>
              <Text type="title" size="sm">
                블로그 인증
              </Text>
              <input
                onChange={(e) => {
                  update([
                    {
                      key: "authenticationContent",
                      value: e.currentTarget.value,
                    },
                    {
                      key: "authenticationMethod",
                      value: "link",
                    },
                  ]);
                }}
                placeholder="블로그 링크를 입력해주세요"
              />
              <Text type="title" size="sm">
                스크린샷 인증
              </Text>
              {isShowImage ? (
                <img
                  src={newDone.authenticationContent}
                  alt={newDone.authenticationContent}
                />
              ) : (
                <>
                  <div
                    className="input-file-box"
                    onClick={() => {
                      (inputRef.current as any).click();
                    }}
                  >
                    <Icon.UploadCloud size={18} color="#828fa3" />
                    <Text size="md" color="gray" style={{ marginLeft: "5px" }}>
                      파일 업로드
                    </Text>
                  </div>
                  <input
                    className="hidden"
                    ref={inputRef}
                    type="file"
                    style={{ visibility: "hidden" }}
                    name={"fileName"}
                    onChange={(e) => {
                      e.target.files &&
                        update([
                          { key: "authenticationMethod", value: "image" },
                          {
                            key: "authenticationContent",
                            value: e.target.files[0].name,
                          },
                        ]);

                      SetIsShowImage(true);
                    }}
                  />
                </>
              )}
            </form>
          )}
          <div className="button-wrapper">
            <Button onClick={closeModal} size="sm" variant="outlined">
              취소
            </Button>
            <Button onClick={(e: React.MouseEvent<HTMLButtonElement>) => onSubmit(e, newDone)} size="sm">
              확인
            </Button>
          </div>
        </div>
      </div>
  );
}

이련 형태인데, 여기서 하위 modal-container에 e.stopPropagation()을 해줌으로써 이벤트 전파를 막아서 정상적으로 동작하도록 구현하였다.

그렇다면, 여기서 드는 궁금점이 생겼다.
이벤트 버블링은 뭐지?

이벤트 버블링(Event Bubbling)은 HTML 요소들이 중첩된 구조로 이루어져 있을 때, 자식 요소에서 발생한 이벤트가 부모 요소를 거쳐 상위 요소까지 이벤트가 전파되는 현상을 말합니다.

이벤트 캡쳐링은 뭐지?

이벤트 캡처링(event capturing)은 HTML 요소에서 이벤트가 발생했을 때, 해당 요소의 부모 요소에서부터 시작하여 이벤트가 발생한 요소까지 이벤트를 전파하는 방식입니다.
이벤트 캡처링은 다음과 같은 과정으로 이루어집니다.

이벤트가 동작하는 순서는?

JavaScript에서 이벤트가 발생하는 순서는 다음과 같습니다.
1. 이벤트 캡처링 단계: 이벤트가 발생한 요소의 최상위 부모 요소부터 이벤트를 캡처링하며 이벤트의 전파가 시작됩니다.
2. 대상 요소에서 이벤트 발생: 이벤트가 발생한 요소에서 이벤트 핸들러가 실행됩니다.
3. 이벤트 버블링 단계: 이벤트가 발생한 요소에서부터 최상위 부모 요소까지 이벤트를 버블링하며 이벤트의 전파가 끝납니다.
이벤트 캡처링은 addEventListener 메서드의 세 번째 인자로 true 값을 전달하여 활성화할 수 있습니다. 이벤트 버블링은 기본적으로 활성화되어 있습니다. 이벤트 핸들러는 캡처링 단계에서 등록된 핸들러부터 실행되고, 이벤트 버블링 단계에서 등록된 핸들러는 이벤트 발생 요소에서부터 실행됩니다.
이벤트 캡처링과 이벤트 버블링은 모두 DOM 이벤트 모델에서 지원되는 방식으로, 이벤트 전파 방식을 선택하여 이벤트를 처리할 수 있습니다. 기본적으로는 이벤트 버블링이 적용되며, 이벤트 캡처링을 사용하려면 addEventListener 메서드의 세 번째 인자로 true를 전달하면 됩니다.

모든 이벤트에 대해서는 이벤트 캡처링이 발생하고, 그 이후 버블링이 발생한다고한다. 기본적으로 캡처링의 동작은 비활성화 되어있고, 버블링은 활성화되어있기에 우리가 코드에서 직면하는 문제의 대부분은 버블링 이슈인것같다.
때문에, e.stopPropagation()을 통해 버블링을 막아주어 쉽게 구현할 수 있다.

하지만, 내가 궁금한점은 내 보드를 보자면,

<div className="background" onClick={closeModal}>
        <div className="modal-container" onClick={(e) => e.stopPropagation()}>
        </div>
</div>

부모요소의 background에 onClick의 closeModal을 해주었고, 하위 요소의 modal-container에 onClick이 전파되는 형태이면, 이건 이벤트 캡쳐링이 발생한것아닌가?

캡쳐링은 기본적으로 동작하지않는다하였는데??

그래서 chatGPT에 물어보았다!

그래도 이해가 안된다 버블링은 위로 올라가는거고 캡쳐링은 아래로내려가는것아닌가.....? 😵‍💫

내가 잘못알았던 개념이 있다.
버블링은 내가 직접 이벤트를 넣어준 경우에 발생하는줄 알았는데, 그것이 아니였더라!!!

버블링이란 ??
한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작합니다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작합니다.

그렇다! background의 onClick 이벤트가 modal-container에도 할당되어 modal-container부터 이벤트가 시작되어 버블링이 일어난것이다. 때문에 버블링을 막아주는 e.stopPropagation으로 정상동작하게 된것이다!

profile
프론트엔드 개발자 항상 뭘 하고있는 슬링

0개의 댓글