`<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()`을 사용해야 한다.
참고
'Frontend > Today I Learned' 카테고리의 다른 글
[Next.js] 버튼 클릭 시 query param 추가하기 (0) | 2024.10.24 |
---|---|
[React, TS] Input 이미지 파일 미리보기 (0) | 2024.10.14 |
[기타] supabase storage 'bucket not found' 에러 (0) | 2024.10.12 |
[Next.js] 쉬운 성능 최적화 방법 소개 (0) | 2024.10.11 |
[Next.js] Middleware로 잘못된 경로 접근 시 redirect하기 (2) | 2024.10.03 |