[TIL]20. 버튼에 이벤트가 있는데 왜 다른 이벤트가 먹음?

강지수·2025년 2월 10일
0
post-thumbnail

문제 발생

분명 버튼 onClick 이벤트 메서드에 내가 만든 함수를 넣어줬는데 이 이벤트는 동작 안하고 그 상위요소인 Link의 이벤트가 동작하여 페이지가 이동하는 문제가 발생

문제 발생 코드

Link를 사용하여 코드를 구현했다.

const PokemonCard = ({ card, cardMethod, text }) => {
  return (
    <Link to={`/dex/${card.id}`}>
      <Card>
        <img src={card.img_url} alt={card.korean_name} />
        <h4>{card.korean_name}</h4>
        <p>No.{card.id}</p>
        <button onClick={() => cardMethod(card)}>{text}</button>
      </Card>
    </Link>
  );
};

하지만 이와같이 코드를 구현하면 button의 onClick을 클릭해서 cardMethod가 실행되지 않고 Link가 실행되면서 페이지가 이동되었다.

해결방법 - e.preventDefault() 적용

const PokemonCard = ({ card, cardMethod, text }) => {
  return (
    <Link to={`/dex/${card.id}`}>
      <Card>
        <img src={card.img_url} alt={card.korean_name} />
        <h4>{card.korean_name}</h4>
        <p>No.{card.id}</p>
        <button
          onClick={(e) => {
            e.preventDefault();
            return cardMethod(card);
          }}
        >
          {text}
        </button>
      </Card>
    </Link>
  );
};

Link는 기본동작인 a태그를 수행하므로 기본동작을 막아주는 e.preventDefault()를 사용하여 기본동작(페이지 이동)을 방지했다.

쿼리스트링을 사용한 후 문제발생

이 후 코드를 수정하는 과정에서 Link대신에 useNavigate()를 활용하여 쿼리스트링 방식으로 변경을 했다.

쿼리스트링을 사용 후 코드

const handleDetail = () => {
  navigate(`/detail?pid=${card.id}`);
};

return (
  <Card onClick={handleDetail}>
    <img src={card.img_url} alt={card.korean_name} />
    <h4>{card.korean_name}</h4>
    <p>No.{card.id}</p>
    {data.some((item) => item.id === card.id) ? (
      <button
        onClick={(e) => {
          e.preventDefault();
          return removeCard(card);
        }}
      >
        삭제
      </button>
    ) : (
      <button
        onClick={(e) => {
          e.preventDefault();
          return addCard(card);
        }}
      >
        추가
      </button>
    )}
  </Card>
);

위 코드에서 또 버튼 이벤트가 동작하지 않았다. 이유를 찾아보니 e.preventDefault는 링크 이동, 제출 품 등 기본 동작을 막아줄 뿐 이벤트 버블링은 막아주지 않았다. 그래서 이벤트가 Card 의 onClick까지 전달되어 handleDetail()이 실행되었다.

버블링 : 이벤트가 상위 요소로 전파되는 단계

해결방법 - e.stopPropagation() 적용

이 문제를 해결하려면 e.stopPropagation을 사용하여 이벤트 버블링을 막아야 한다.

const handleDetail = () => {
  navigate(`/detail?pid=${card.id}`);
};

return (
  <Card onClick={handleDetail}>
    <img src={card.img_url} alt={card.korean_name} />
    <h4>{card.korean_name}</h4>
    <p>No.{card.id}</p>
    {data.some((item) => item.id === card.id) ? (
      <button
        onClick={(e) => {
          e.stopPropagation(); // 이벤트 전파 중지 (상위 요소의 이벤트 실행 방지)
          return removeCard(card);
        }}
      >
        삭제
      </button>
    ) : (
      <button
        onClick={(e) => {
          e.stopPropagation();
          return addCard(card);
        }}
      >
        추가
      </button>
    )}
  </Card>
);

e.preventDefault 를 e.stopPropagation으로 바꿔주면 원하는대로 이벤트가 실행되는 것을 볼 수 있다.

결론

기본 동작을 이해하고 적절한 매서드를 사용하자 너무 남발하지 말고.
e.preventDefault() → 기본 동작(링크 이동, 폼 제출 등)을 방지할 때 사용
e.stopPropagation() → 이벤트 전파를 막아 상위 요소의 이벤트 실행을 방지할 때 사용

이런것도 성능 최적화와 관련된 얘기가 된다니 뭔가 사소한 부분이라고 생각했던 곳에서 최적화를 할 수 있다는 점이 신기했다. 이렇게 지식이 늘었다.

profile
프론트엔드 잘하고 싶다

0개의 댓글

관련 채용 정보