Land of Joe

Link와 useNavigate의 활용도 차이 (Link태그 내 이벤트 존재 시 이벤트 작동 안 함 문제 해결을 위한 useNavigate 활용) 본문

🌐 Web/⚛️ React

Link와 useNavigate의 활용도 차이 (Link태그 내 이벤트 존재 시 이벤트 작동 안 함 문제 해결을 위한 useNavigate 활용)

Arendt 2024. 4. 18. 10:28

[ 내가 원하는 작동 방식 ]

회색 알람 아이템을 하나 누르면 그 아이템의 type에 해당하는 url로 이동한다. 

그러나 알람 아이템 안의 DELETE 버튼을 누르면 url 이동이 아닌, deleteAlarmEvent 이벤트가 작동해야한다!

 

import React from "react";

import styled from "styled-components";
import { Span } from "../style/TextStyle";
import { Div, Article } from "../style/LayoutStyle";

import ImgTextBtnItem from "./ImgTextBtnItem";

import DeleteIcon from "../img/deleteIcon.svg";

import { useCookies } from "react-cookie";
import { Link } from "react-router-dom";

import TimeStampUtil from "../util/TimeStampUtil";

const NotificationListItem = (props) => {
  const { idx, user_idx, type, post_idx, game_idx, created_at, post_title, game_title } =
    props.data;
  const [cookies] = useCookies(["token"]);

  // (일반사용자) 알림 삭제하기 DELETE
  const deleteAlarmEvent = async (event) => {
    event.stopPropagation(); // 이벤트 캡쳐링 방지 (url 이동 막기 위함)

    const response = await fetch(`${process.env.REACT_APP_API_KEY}/account/notification/${idx}`, {
      method: "DELETE",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${cookies.token}`,
      },
    });

    if (response.status === 200) {
      window.location.reload();
    }
    if (response.status === 400) {
      return alert("유효하지 않은 요청입니다.");
    }
    if (response.status === 401) {
      return alert("권한이 없는 사용자입니다.");
    }
    if (response.status === 500) {
      console.log("서버 내부 에러입니다.");
    }
  };

  // 알림 내용과 URL 설정
  let content, url;
  if (type === 1) {
    content = `사용자의 "${post_title}" 게시글에 새로운 댓글이 달렸습니다. 어서 확인해보세요~!`;
    url = `/game/${game_idx}/post/${post_idx}`;
  }
  if (type === 2) {
    content = `사용자가 작성했던 "${game_title}" 위키가 다른 사용자에 의해 수정되었습니다. 어떻게 바뀌었나 궁금하지 않으세요?`;
    url = `/game/${game_idx}`;
  }
  if (type === 3) {
    content = `요청하신 "${game_title}" 게임 생성이 거절되었습니다..ㅠㅠ `;
    url = `./`;
  }

  return (
    <Div $flex="v_start_start" $width="100%" $margin="70px 0 0 0">
      {/* 알람 시각 */}
      <Span $margin="0 0 10px 0">{TimeStampUtil(created_at)}</Span>

      {/* 알람 내용, 알람 클릭 시 해당 url로 이동 */}
      <MoveLink to={`${url}`}>
        <BorderStyleArticle
          $flex="h_between_center"
          $width="100%"
          $backgroundColor="lightGray"
          $height="100px"
          $padding="0 3%"
        >
          <Div>
            <Span $fontWeight="bold">{content}</Span>
          </Div>

          <ImgTextBtnItem
            img={DeleteIcon}
            text="DELETE"
            color="major"
            backgroundColor="default"
            onClick={(event) => deleteAlarmEvent(event, idx)}
          />
        </BorderStyleArticle>
      </MoveLink>
    </Div>
  );
};

export default NotificationListItem;

 

나는 <MoveLink to={`${url}`}></MoveLink>를 통해 알람 아이템을 감싸서

아이템의 어디를 클릭하든 해당 경로로 이동하도록 구현했었다. 

그리고 이 Link 기능이 <ImgTextBtnItem /> 내 onClick 이벤트인 deleteAlarmEvent에 영향을 주지 않도록

이벤트 캡쳐링 방지를 위해 event.stopPropagation()을 적용했었다.

 

그러나 원하는 대로 되지 않았다.

 

 

[ 문제 상황 ]

DELETE 버튼을 눌러도 url 이동이 우선적으로 작동한다.

 

 

[ 해결 과정 ]

1. 백엔드 쪽 데이터베이스 내역을 실시간으로 확인하고, 

또 url 이동이 이뤄졌다가 다시 알람 페이지로 돌아와 남아있는 알람 내역 확인을 통해

알람 삭제 API는 정상적으로 작동하고 있다는 사실을 파악했다.

 

2. 처음엔 이벤트 버블링/캡쳐링을 의심했다.

사실 이벤트 캡쳐링이라는 게 있는지도 몰랐다. '하나의 영역만 클릭했는데 다른 영역까지 이벤트 핸들러가 작동하는 것'이라는 정도로 '이벤트 버블링' 키워드만 알고있던 정도라 검색을 해보았고, 그 둘의 차이와 해결방법 등에 대해 알아볼 수 있었다. 

그러한 이유로 deleteAlarmEvent 내에 event.stopPropagation()라는 이벤트 캡쳐링 방지 함수를 넣어보기도 했다. 

* 이벤트 버블링: 이벤트가 타겟에서 시작해서 DOM트리를 타고 Window 객체까지 전파되는 현상(기본적으로 항상 발생)
* 이벤트 캡쳐링: 이벤트가 Window 객체에서 출발해서 DOM트리를 타고 타겟까지 전파되는 현상
* event.preventDefault(): 브라우저 기본 동작 취소
* event.stopPropation(): 그 다음 요소로의 전파 방지
event.stopImmediatePropation()도 있음
 

[JavaScript] 이벤트 캡쳐링, 이벤트 버블링 개념, 방지하는 방법

HTML 요소에서 이벤트가 발생하면 해당 요소를 포함한 모든 조상 요소에 이벤트를 전달한다. 왜 전달하는지 알아보고 이벤트가 전파되는 과정을 설명한다.

velog.io

 

그러나 원하는 대로 작동은 하지 않았고........

가만히 앉아 생각하다보니... React Router의 Link는 이벤트가 아니잖아!!!!!!!!!

 

 

 

 

[ 해결 방법 ]

기존에 회색 알람 아이템(<BorderStyleArticle/>) 전체를 <Link>로 감싸서 원하는 url로 이동시켰던 것과 다르게, 

알람 아이템에 onClick 이벤트를 통해 useNavigate로 원하는 url로 이동하도록 하였다.

 

return (
    <Div $flex="v_start_start" $width="100%" $margin="70px 0 0 0">
      {/* 알람 시각 */}
      <Span $margin="0 0 10px 0">{TimeStampUtil(created_at)}</Span>

      {/* 알람 내용, 알람 클릭 시 해당 url로 이동 */}
      <BorderStyleArticle
        $flex="h_between_center"
        $width="100%"
        $backgroundColor="lightGray"
        $height="100px"
        $padding="0 3%"
        onClick={(e) => moveURLEvent(e)}
      >
        <Div>
          <Span $fontWeight="bold">{content}</Span>
        </Div>

        <ImgTextBtnItem
          img={DeleteIcon}
          text="DELETE"
          color="major"
          backgroundColor="default"
          onClick={(event) => deleteAlarmEvent(event, idx)}
        />
      </BorderStyleArticle>
    </Div>
  );

 

이렇게 하니 DELETE 버튼(deleteAlarmEvent)는 버튼대로, 페이지 이동은 페이지 이동대로

너무너무 잘 작동하는 것으로 해결 완~!~!