Frontend/Today I Learned

[Next.js] Link 태그에 버튼을 넣었을 때 페이지 이동 막기

joycie416 2024. 10. 13. 00:10

`<Link>` 내부에 많은 것들을 할 수 있지만 그 안에 버튼을 넣어 클릭 이벤트를 넣었을 때는 어떻게 할까?

 

페이지 이동을 하지 않고 버튼 클릭 이벤트만 실행하고 싶을 때 나는 먼저 `e.stopPropagation()`을 생각했다. `<div>`에 `onClick`을 넣고 하위 `<button>`에도 클릭 이벤트가 있을 때 `e.stopPropagation()`을 사용했기 때문이다. 그런데 이렇게 했더니 이벤트가 너무 중구 난방으로 발생했다.

 

카드를 `<Link>`로 만들고 내부에 아래와 같이 `<button>`을 넣었다.

<Link href={`/todos/${todo.id}`} className="w-[200px] border">
  <p>{todo.id.slice(0,4)}</p>
  <p>{todo.contents}</p>
  <button
    className="border mr-4"
      onClick={(e) => {
        e.stopPropagation()
        updateTodo(todo);
      }}
  >
     {todo.isDone ? "취소" : "완료"}
  </button>
  <button className="border"
    onClick={(e) => {
      e.stopPropagation()
      deleteTodo(todo.id);
    }}>삭제</button>
</Link>

 

그랬더니 언제는 취소/완료가 변경되지만 언제는 카드 상세 페이지로 이동했다.

 

조금 생각해보면 `e.stopPropagation()`이 아니라 `e.preventDefault()`를 사용해야 함을 알 수 있다. 기본적으로 `<Link>`는 페이지 이동을 해야한다. 즉 페이지 이동이 default라는 것이다. 따라서 `e.preventDefault()`를 사용해 `<Link>`의 기본 기능을 제거하고 클릭 이벤트를 넣어야 한다. 즉 다음과 같이 수정하면 된다.

<Link href={`/todos/${todo.id}`} className="w-[200px] border">
  <p>{todo.id.slice(0,4)}</p>
  <p>{todo.contents}</p>
  <button
    className="border mr-4"
      onClick={(e) => {
        e.preventDefault()
        updateTodo(todo);
      }}
  >
     {todo.isDone ? "취소" : "완료"}
  </button>
  <button className="border"
    onClick={(e) => {
      e.preventDefault()
      deleteTodo(todo.id);
    }}>삭제</button>
</Link>

 

여기서 `e.stopPropagation()`과 `e.preventDefault()`의 차이점을 한 번 더 짚고 가자면, `e.stopPropagation()`은 이벤트가 캡처링/버블링 단계에서 더 이상 전파되지 않도록 하는 것이고, 기본 동작은 실행된다. 따라서 기본 동작을 막으려면 `e.preventDefault()`을 사용해야 한다.

 

참고

 

Event.stopPropagation() - Web API | MDN

Event 인터페이스의 stopPropagation() 메서드는 현재 이벤트가 캡처링/버블링 단계에서 더 이상 전파되지 않도록 방지합니다. 전파를 방지해도 이벤트의 기본 동작은 실행되므로, stopPropagation()이 링

developer.mozilla.org