React를 공부하다가 다음 문제를 마주쳤습니다.
Q) event listener는 등록되면 반드시 해제되어야 합니다.
클래스형 컴포넌트에서는 컴포넌트가 화면에서 사라질 때(unmount 될 때)
event listener를 해제합니다. (componentWillUnmount에서요!)
그럼 라이프사이클 메소드를 사용할 수 없는 함수형 컴포넌트에서는
event listener를 해제할 때 어떻게 해야 할까요?
이게 문제입니다만...
문제가 뭘 묻는 건지 이해하지 못하겠습니다.
모르는 용어 투성이에요.
차라리 고대 그리스어를 해독하는 게 빠르겠네요.
아쉽게도 전 개발 공부를 하고 있기에
언어학자의 꿈은 접고, 구글링을 하러 가보겠습니다.

먼저, 질문에 등장한 용어들의 정의는 이렇습니다.
event listener: 이벤트 리스너란 이벤트가 발생했을 때
그 처리를 담당하는 함수를 가리키며,
이벤트 핸들러(event handler)라고도 합니다.
지정된 타입의 이벤트가 특정 요소에서 발생하면,
웹 브라우저는 그 요소에 등록된 이벤트 리스너를 실행시킵니다.
무슨 말인지 아직도 잘 모르겠지만,
이벤트라는 것이 발생하면 처리가 되어야 하고,
그 처리를 하는 것이 이벤트 리스너 또는 핸들러이며,
이벤트는 타입을 지정할 수 있고,
이벤트가 발생하는 장소는 특정 요소이며,
이벤트 리스너는 그 특정 요소에 등록되고,
이벤트 리스너를 실행시키는 것은 웹 브라우저라는 거네요.
계속 이벤트, 이벤트, 하는데
여기서 말하는 이벤트란 대체 무엇일까요?
이벤트: 웹페이지에서 마우스를 클릭했을 때, 키를 입력했을 때,
특정 요소에 포커스가 이동되었을 때와 같은 어떤 사건
이벤트란 웹페이지에 접속한 사용자가 취할 수 있는
행동을 말하는 거네요!
사용자가 취할 수 있는 행동은 마우스 클릭하기, 키보드 치기,
스크롤링하기 등 다양한 종류가 있기에,
비슷한 종류의 행동끼리 묶어 비슷한 반응을 보일 필요가 있겠죠.
즉, 이벤트에는 타입이 지정됩니다.
어떤 이벤트가 발생했을 때,
이벤트 리스너는 어떤 반응을 보여야 할지 결정합니다.
이때 이벤트 리스너가 반응을 보일지, 안 보일지
명령을 내리는 것은 웹 브라우저입니다.
이벤트는 웹페이지 상 특정 요소,
예를 들어 "로그인 하기" 같은 어떤 버튼에서 발생할 수 있습니다.
그리고 이벤트 리스너는 그 버튼에 등록됩니다.
그런데 이벤트 리스너는 등록되고 난 뒤
나중에 반드시 해제되어야 합니다.
왜 그럴까요?
이벤트 리스너는 컴퓨터 프로그램이 필요하지 않은 메모리를
계속 점유하는 메모리 누수 원인이 될 수 있기 때문입니다.
따라서, 해당 이벤트 리스너가 더 이상 필요하지 않다면
그 이벤트 리스너는 반드시 삭제(해제)해줘야 합니다.
마찬가지로, 특정 페이지에서만 사용하는 이벤트 리스너라면
해당 페이지를 떠날 때 이벤트 리스너를 삭제해줍니다.
이벤트 리스너를 삭제하는 방법은 컴포넌트의 종류에 따라 다릅니다.
컴포넌트(Component)란 React로 작성된 앱을 구성하는 최소한의 단위를 말합니다.
컴포넌트는 크게 클래스형과 함수형으로 나눌 수 있습니다.
클래스형 컴포넌트는 컴포넌트 내 변경 가능한 데이터 저장소인
상태 값(State)을 가질 수 있고,
컴포넌트가 브라우저 상에 나타나고, 업데이트되고, 사라질 때,
또는 컴포넌트에서 에러가 났을 때 호출되는 메소드인
생명 주기 메소드(LifeCycle Method)*를 작성할 수 있습니다.
그러나 함수형 컴포넌트는 이 모든 일을 할 수 없습니다.
즉, 둘의 차이점은 상태 값과 생명 주기 메소드를 가질 수 있느냐, 없느냐입니다.
클래스형 컴포넌트와 함수형 컴포넌트의 차이를 예시를 통해 확인해 보겠습니다.
다음과 같이 작성된 클래스 컴포넌트가 있다고 해봅시다.
import React from "react";
import Text from "./Text";
class App extends React.Component{
constructor(props){
super(props);
this.state = {};
this.circle = React.createRef(null);
}
// 여기에 이벤트 함수 작성
componentDidMount(){
console.log(this.circle);
// 여기에 이벤트 리스너 등록
}
componentWillUnmount() {
// 여기에 이벤트 리스너 해제
}
render() {
return (
<div>
<Text/>
<div ref={this.circle}></div>
</div>
);
}
}
export default App;
다음과 같은 이벤트 함수를 만듭니다.
hoverEvent = (e) => {
console.log(e.target); // 콘솔로 이 이벤트가 누구에게서 일어났는지 확인
console.log(this.circle.current); // ref랑 같은 녀석인지 확인
this.circle.current.style.background = "yellow";
}
DOM 요소가 있어야 이벤트 발생을 지켜볼 수 있으므로
componentDidMount()에 다음 이벤트 리스너를 넣어줍니다.
componentDidMount(){
console.log(this.circle); // 리액트 요소가 잘 잡혔나 확인
this.circle.current.addEventListener("mouseover", this.hoverEvent);
// 마우스를 올렸을 때, 이벤트가 일어나는 지 확인
}
이제 이벤트 리스너를 제거하는 함수를 작성해줍니다.
componentWillUnmount() {
this.circle.current.removeEventListener("mouseover", this.hoverEvent);
}
완성된 클래스 컴포넌트입니다.
import React from "react";
import Text from "./Text";
class App extends React.Component{
constructor(props){
super(props);
this.state = {};
this.circle = React.createRef(null);
}
hoverEvent = (e) => {
console.log(e.target); // 콘솔로 이 이벤트가 누구에게서 일어났는지 확인
console.log(this.circle.current); // ref랑 같은 녀석인지 확인
this.circle.current.style.background = "yellow";
}
componentDidMount(){
console.log(this.circle); // 리액트 요소가 잘 잡혔나 확인
this.circle.current.addEventListener("mouseover", this.hoverEvent);
// 마우스를 올렸을 때, 이벤트가 일어나는 지 확인
}
componentWillUnmount() {
this.circle.current.removeEventListener("mouseover", this.hoverEvent);
}
render() {
return (
<div>
<Text/>
<div ref={this.circle}></div>
</div>
);
}
}
export default App;
클래스형 컴포넌트와 달리,
함수형 컴포넌트는 생명 주기 메소드를 사용할 수 없기 때문에
이벤트 리스너를 해제하려면 다른 방법을 사용해야 합니다.
함수형 컴포넌트에서는 componentDidMount() 역할을 하는
useEffect() 훅을 사용하여 이벤트 리스너를 위치시킵니다.
useEffect()는 라이플 사이클 함수 중 componentDidMount, componentDidUpdate,
그리고 componentWillUnmount를 합쳐둔 거라 생각하면 쉽게 이해할 수 있습니다.
이벤트 함수를 만듭니다.
const hoverEvent = (e) => {
// 이벤트가 누구에게서 일어나는지 확인
console.log(e.target);
// ref와 같은 요소에서 발생했는지 확인
console.log(text.current);
text.current.style.background = 'yellow';
}
이벤트 리스너를 등록합니다.
React.useEffect(()=> {
// rendering 때 실행는 부분
// componentDidMount, componentDidUpdate일 때 동작 부분
text.current.addEventLisener('mouseover', hoverEvent);
return () => {
// clean up부분
// componentWillUnmount 동작부분
// do something
};
},[text]); // 두번째인자 []! 디펜던시 array는 여기 넣어준 값이 변하면
//첫번째 인자인 콜백함수를 실행합니다.
이벤트 리스너를 삭제합니다.
React.useEffect(()=>{
text.current.addEventListener('mouseover', hoverEvnet);
return () => {
text.current.removeEventListener('mouseover', hoverEvent);
}
}
즉, 클래스형 컴포넌트에서는 컴포넌트가 화면에서 사라질 때(unmount 될 때)
componentWillUnmount에서 이벤트 리스너를 해제하지만,
함수형 컴포넌트에서는 컴포넌트가 사라질 때
useEffect의 return 구문에서 이벤트 리스너를 제거해 줍니다.
출처 및 참고자료
http://www.tcpschool.com/javascript/js_event_eventListenerRegister
'프로그래밍 > React' 카테고리의 다른 글
React에서 반드시 알아야 하는 것들! (0) | 2022.07.30 |
---|---|
리액트 훅(Hooks) 종류 및 특징 (0) | 2022.07.29 |
라우팅이 뭘까? (0) | 2022.07.29 |
리덕스란? 새로고침할 때도 리덕스 안의 데이터를 유지하려면? (0) | 2022.07.29 |
DOM이 그래서 정확히 뭘까? (0) | 2022.07.28 |