메시지 디테일 페이지를 만들고 난 이후에 문제가 생겼다. 메시지를 수정할 때 생기는 구역을 눌렀을 때 원치 않는 클릭 이벤트가 발생하는 것이다. 메시지 수정이 거의 불가능할 정도의 큰 버그여서 해결해보기로 했다.
메세지의 내용이 들어가 있는 부분을 누르면 디테일 페이지로 넘어가게 구현됀다. 하지만 그 부분을 래핑하고 있는 Link
컴포넌트가 <a>
태그를 기반으로 만들어져 있기 때문에 원치 않는 곳에서 클릭 이벤트가 발생하는 것이었다.
문제의 코드는 다음과 같다.
[Message.js 문제코드]
// 생략
<Link className="message-body" to={"/detail/"+message._id}>
{isUpdate ?
<div>
<div className="box-update" aria-label="update">
<textarea className="cotent-box"
rows="5"
placeholder="내용을 입력해주세요."
onChange={changeContentHandler}
>
</textarea>
</div>
<div className="update-button-wrapper">
<button className="btn-primary mr-2" onClick={deactivateUpdate}>취소</button>
<button className="btn-primary" onClick={clickUpdateHandler}>수정</button>
</div>
</div>
:
<p>
{content}
</p>
}
</Link> {/* Message Body */}
// 생략
원래 원하는 이벤트 발생 조건은 위의 박스가 클릭 됐을 때이다. 하지만 위처럼 update 구역까지 <Link>
컴포넌트로 감싸다 보니 아래의 사진 속 박스에서도 클릭이 돼버린다.
문제는 간단하게 풀린다.
[해결 코드]
<Link className="message-body" to={"/detail/"+message._id}>
{isUpdate ?
{/************ 해결 방법 ***************/}
<div onClick={(e)=>e.preventDefault();}>
<div className="box-update" aria-label="update">
<textarea className="cotent-box"
rows="5"
placeholder="내용을 입력해주세요."
onChange={changeContentHandler}
>
</textarea>
</div>
<div className="update-button-wrapper">
<button className="btn-primary mr-2" onClick={deactivateUpdate}>취소</button>
<button className="btn-primary" onClick={clickUpdateHandler}>수정</button>
</div>
</div>
:
<p>
{content}
</p>
}
</Link> {/* Message Body */}
업데이트 구역에 해당하는 가장 상위 엘리먼트의 클릭 이벤트 핸들러에서 e.preventDefault();
명령을 실행해주는 것이다. 이 명령은 기본적으로 브라우저에서 설정되어 있는 이벤트의 발생을 막아버리는 것이다. react-router 라이브러리의 <Link>
컴포넌트가 <a>
태그를 기반으로 하기 때문에 위 명령으로 이 문제를 해결할 수 있었다.
그리고 가장 상위 엘리먼트에 이벤트 핸들러를 할당한 이유는, 자식 엘리먼트에게서 이벤트가 전파되어오기 때문이다. 클릭된 자식 엘리먼트에서 이벤트가 전파되어 <Link>
까지 전달이 되면 비로소 해당 이벤트가 발생된다. 하지만 전달되기 바로 이전에 이벤트 발생을 막아버리기 때문에 더 이상 문제가 없게 된다. 만약 내가 <textarea>
에 e.preventDefault()
명령을 수행했다면 메시지 타이핑은 가능하지만 최종적으로 전송 버튼을 누를 수가 없었을 것이다.
e.preventDefault()
의 쓰임새
저도 이벤트 버블링을 흥미롭게 본 경험이 있어서 코드를 찬찬히 살펴봤습니다.
최상위 div에서 e.preventDefault()하는건 버그 해결만을 위해서 행동을 너무 제약하는 것 같다는 생각이 드는데 문제가 발생하는 textarea 태그에서 onClick 이벤트의 콜백을 정의해서
event.stop.propagation()
을 호출하는게 낫지 않을까요? 상위로 이벤트가 전달되는 현상이 문제이므로, 상위에서 해결을 하는 것 보다, 해당 버그에 책임이 있는 태그에서 상위로 전달자체가 되지 않게 설계하는게 좋을 것 같다는 개인적인 생각이 듭니다.왜냐하면 textArea밖의 div를 사용자가 클릭할 경우, 자연스럽게 입력창이 없어지는게 사용자 경험에서 직관적이라 생각하기 때문입니다. 물론 이건 프론트엔드 디자인 취향 차이이긴 하겠지만...
또 다른 이유는 추후 div에 onclick 이벤트를 새롭게 정의해야 할 경우 textarea까지 다시 손봐야하니까 버그의 발생 가능성이 높아지지 않을까 하는 생각입니다. textarea 컴포넌트가 상위 div에 의존적이지 않게되어 하나로서 완전히 기능하는 컴포넌트화 한다면 재사용성이 높아지는 효과도 장점인 것 같아요.