컴포넌트 이벤트

#React

이벤트 개념

자바스크립트에서 이벤트(event)는 사용자와의 상호작용에 따라 발생하는 일련의 사건을 의미합니다. 예를 들어, 마우스 버튼을 클릭하거나 키보드로 글자를 입력하는 등의 동작들은 모두 이벤트로 간주합니다. 리액트에서도 이러한 이벤트를 활용해 사용자와 상호작용하는 기능을 구현할 수 있습니다.

<JSXElement 이벤트_속성='이벤트_핸들러'/>

JSX 엘리먼트를 사용하면 html dom 엘리먼트(ex. button)처럼 이벤트를 사용할 수 있다. 대표적인 예는 버튼에 이벤트 속성과 이벤트 핸들러를 연결하여 버튼을 클릭할 때 함수를 호출하는 것이 있다.

순수 html의 경우 이벤트 속성은 모두 소문자로 작성하고, 이벤트 핸들러는 js 코드가 적힌 문자열로 전달한다. JSX의 경우 이벤트 속성을 카멜 케이스로 작성하고, 이벤트 핸들러는 중괄호({})에 넣어 함수를 전달한다. 순수 html에서는 문자열로 이벤트 핸들러를 전달하여 휴먼 에러가 발생할 확률이 있었지만, JSX의 경우 함수를 전달하여 안정성을 높였다(IDE 인텔리센스 가능).

이벤트 속성을 사용할 때 주의해야할 점은, dom에서 기본으로 제공되지 않는 이벤트 속성은 리액트에서 알 수 없어 무시된다는 것이다. 커스텀 이벤트 속성은 컴포넌트를 따로 빼서 props로 전달받아 사용할 수 있다.

이벤트 핸들러 작성 방법

JSX에서 이벤트 핸들러를 전달하는 방식은 두 가지가 있는데, 인라인 함수를 넣는 것과 함수 참조를 넣는 방법이 있다.

인라인 핸들러

인라인 함수를 넣는 방식은 말 그대로 중괄호에 인라인 함수를 전달하여 사용한다.

export default function App(){
	return(
		<button onClick={ () => { console.log('clicked'); } }>클릭</button>
	);
}

함수 참조

함수 참조를 넣는 방식은 중괄호에 변수에 저장한 함수를 전달하여 사용한다.

export default function App(){
	const eventHandler = () => {
		console.log('clicked');
	}
	return(
		<button onClick={ eventHandler }>클릭</button>
	);
}

함수 참조 방식을 사용할 때 중괄호 안에 괄호를 넣어 함수를 호출(ex. onClick={ eventHandler() })해 버리면 컴포넌트 렌더링 시 함수가 즉시 호출된다(버튼을 클릭하기 전에 함수가 실행됨). 함수 참조 방식을 사용할 때는 괄호를 넣어서는 안되므로, 매개변수를 넣어 함수를 호출해야할 경우 인라인 함수로 한 번 랩핑해서 사용해야한다.

export default function App(){
	const eventHandler = (str: string) => {
		console.log('clicked' + str);
	}
	return(
		<button onClick={ () => { eventHandler('hello'); } }>클릭</button>

	);
}

이벤트 객체

리액트에서는 이벤트가 발생하면 해당 이벤트 핸들러 함수가 호출되고, 이벤트 객체(event dobjext)가 매개변수로 전달됩니다. … 리액트는 웹 브라우저의 기본 이벤트를 감싸서 만든 자체 이벤트 객체인 합성 이벤트(synthetic event)를 사용합니다. 이는 웹 브라우저마다 이벤트 객체의 동작 방식이 조금씩 달라 이를 일관 되게 처리하기 위해서입니다. 이를 ‘크로스 브라우저 호환성(cross-browser compatibility)을 확보한다’고 표현합니다.

이벤트 핸들러가 호출될 경우, 리액트는 이벤트 핸들러의 매개변수로 이벤트 객체를 전달한다. 이 때의 이벤트 객체는 리액트의 합성 이벤트 객체로, 순수 html의 이벤트 객체와는 다르다. 합성 이벤트 객체는 크로스브라우징을 위해 리액트에서 고안한 객체로, 브라우저 간 차이가 있어도 하나의 이벤트 객체로 이벤트 사용이 가능하다. 이벤트 객체를 출력하는 예시는 다음과 같다.

export default function App(){
	return(
		<button onClick={ (e) => { console.log(e); } }>클릭</button>
	);
}

매개변수와 이벤트 객체를 함께 사용해야하는 경우, 반드시 인라인 함수 방식을 사용해야한다.

export default function App(){
	const eventHandler = (e: React.MouseEvent<HTMLButtonElement>, str: string) => {
		console.log(e);
		console.log('clicked' + str);
	}
	return(
		<button onClick={ (e) => { eventHandler(e, 'hello'); } }>클릭</button>

	);
}

이벤트 매개변수 이름은 관례적으로 e나 event로 사용한다. 이벤트 매개변수의 타입(ex. React.MouseEvent<HTMLButtonElement>)은 IDE와 ts의 타입추론 기능을 사용하여 확인하면 편하다.

이벤트 전파

리액트 엘리먼트들의 이벤트는 전파될 수 있는데, 이벤트가 전파되면 해당 엘리먼트에 같은 종류의 이벤트가 있는 경우 해당 이벤트 핸들러가 호출된다. 이벤트는 전파되는 방향에 따라 캡처링버블링으로 나뉜다.

캡처링

캡처링은 사용자가 의도해야 발생되는데, 부모에서 자식 방향으로 전파된다. 캡처링을 사용하려면 이벤트 속성 뒤에 Capture 단어를 붙이면 된다.

export default function App() {
	return(
		<div onClickCapture={ () => { console.log('Parent'); } }
			<button onClick={ () => { console.log('Child'); } }>클릭</ button>
		</div>
	);
}

위 예시의 경우 버튼을 클릭하면 아래와 같이 출력된다.

Parent
Child

버블링

버블링은 사용자가 의도하지 않아도 기본적으로 발생되는 이벤트 전파로, 자식에서 부모 방향으로 전파된다.

export default function App() {
	return(
		<div onClick={ () => { console.log('Parent'); } }
			<button onClick={ () => { console.log('Child'); } }>클릭</ button>
		</div>
	);
}

위 예시의 경우 버튼을 클릭하면 아래와 같이 출력된다.

Child
Parent

버블링은 기본으로 일어나므로 캡처링이 일어날 때도 버블링은 일어난다. 하지만 위 캡처링 코드에서 Parent 출력은 최초 한 번 밖에 일어나지 않았는데, 그 이유는 자식인 button이 이벤트 타겟이 되어 onClick 이벤트가 발생하여 버블링이 일어났을 때, onClickCapture은 반응하지 않기 때문이다.

  1. 캡처링 div: onClickCapture -> 이벤트 발생, 캡처링 시작해 타겟에서 자식으로 하강 ↓ button: onClick -> onClickCapture는 onClick을 같은 종류의 이벤트로 취급하므로 onClick 이벤트 발생, 버블링 시작
  1. 버블링 div: onClickCapture -> onClick이 없으므로 이벤트 발생하지 않음 ↑ button: **onClick ** -> 버블링 시작해 타겟에서 부모로 상승

이벤트 전파 막기

기본적으로 발생하는 버블링이 일어나지 않게 막기 위해서는 e.stopPropagtion(); 을 호출하면 된다.

export default function App() {
	return(
		<div onClick={ () => { console.log('Parent'); } }
			<button onClick={ (e) => { e.stopPropagtion(); console.log('Child'); } }>클릭</ button>
		</div>
	);
}

위 예시의 경우 버튼을 클릭하면 아래와 같이 출력된다.

Child

이외에도 html 엘리먼트는 기본적인 이벤트 동작들이 있는 경우가 있는데, 예를 들어 form 태그의 onSubmit을 호출할 때는 페이지의 모든 내용을 새로고침해버린다. 이런 기본적인 이벤트 동작들을 막으려면 e.preventDefault()를 사용한다.