티스토리 뷰
- 사용자가 웹 브라우저에서 DOM 요소들과 상호 작용하는 것을 이벤트(event)라고 한다.
- 예를 들어 버튼에 마우스 커서를 올렸을 때는 onmouseover 이벤트를 실행하고, 클릭했을 때는 onclick 이벤트를 실행한다. Form 요소는 값이 바뀔 때 onchange 이벤트를 실행한다.
리액트의 이벤트 시스템
- 리액트의 이벤트 시스템은 웹 브라우저의 HTML 이벤트와 인터페이스가 동일하기 때문에 사용법이 꽤 비슷하다.
- 다음 코드를 살펴 보자.
import { useState } from 'react'; const Say = () => { const [message, setMessage] = useState(''); const onClickEnter = () => setMessage('안녕하세요!'); const onClickLeave = () => setMessage('안녕히 가세요!'); const [color, setColor] = useState('black'); return( <div> <button onClick={onClickEnter}>입장</button> <button onClick={onClickLeave}>퇴장</button> <h1 style={{ color }}>{message}</h1> <button style= {{ color: 'red' }} onClick={() => setColor('red')}>빨간색</button> <button style= {{ color: 'green' }} onClick={() => setColor('green')}>초록색</button> <button style= {{ color: 'blue' }} onClick={() => setColor('blue')}>파란색</button> </div> ); }; export default Say; |
이벤트를 사용할 때 주의 사항
1. 이벤트 이름은 카멜 표기법으로 작성한다.
- 예를 들어 HTML의 onclick은 리액트에서는 onClick으로 작성해야 한다.
- 또 onkeyup은 onKeyUp으로 작성한다.
2. 이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태의 값을 전달한다.
- HTML에서 이벤트를 설정할 때는 큰따옴표 안에 실행할 코드를 넣었지만, 리액트에서는 함수 형태의 객체를 전달한다.
- 앞서 버튼 예제에서도 화살표 함수 문법으로 함수를 만들어 전달했다. 이렇게 바로 만들어서 바로 전달해도 되고, 렌더링 부분 외부에 미리 만들어서 전달해도 된다.
<button style= {{ color: 'green' }} onClick={() => setColor('green')}>초록색</button>
3. DOM 요소에만 이벤트를 설정할 수 있다.
- 즉 div, button, input, form, span 등의 DOM 요소에는 이벤트를 설정할 수 있지만, 우리가 직접 만든 컴포넌트에는 이벤트를 자체적으로 설정할 수 없다.
- 예를 들어 MyComponent에 onClick 값을 설정한다면 MyComponent를 클릭할 때 doSomething 함수를 실행하는 것이 아니라, 그냥 이름이 onClick인 props를 MyComponent에게 전달해 줄 뿐이다.
<MyComponent onClick = {doSomething} />
- 따라서 컴포넌트에 자체적으로 이벤트를 설정할 수는 없다. 하지만 전달받은 props를 컴포넌트 내부의 DOM 이벤트로 설정할 수는 있다.
<div onClick = {this.props.onClick}>
{ /* (...) */ }
</div>
이벤트 종류
- Clipboard
- Touch
- Composition
- UI
- Keyboard
- Wheel
- Focus
- Media
- Form
- Image
- Mouse
- Animation
- Selection
- Transition
https://reactjs.org/docs/events.html
SyntheticEvent – React
A JavaScript library for building user interfaces
legacy.reactjs.org
이벤트 핸들링 연습하기
1. 컴포넌트 생성
- src/EventPractice.js 파일을 생성한다.
- 만든 후에 컴포넌트 초기 코드를 작성한다.
- 먼저 클래스형 컴포넌트로 작성하여 기능을 구현한다.
* EventPractice.js
import { Component} from 'react'; class EventPractice extends Component{ render(){ return( <div> <h1>이벤트 연습</h1> </div> ); } } export default EventPractice; |
2. App.js에서 EventPractice 렌더링
* App.js
import EventPractice from './EventPractice'; const App = () => { return ( <EventPractice /> ); } export default App; |
3. onChange 이벤트 설정
- EventPractice 컴포넌트에 input 요소를 렌더링하는 코드와 해당 요소에 onChange 이벤트를 설정하는 코드를 작성한다.
- 다음 코드를 EventPractice 컴포넌트의 render 메서드에 작성한다.
* EventPractice.js
import { Component} from 'react'; class EventPractice extends Component{ render(){ returnn( <div> <h1>이벤트 연습</h1> <input type="text" name="message" placeholder="아무거나 입력해 보세요" onChange={ (e) => { console.log(e); } } /> </div> ); }; } export default EventPractice; |
- 코드를 저장하고, 웹 브라우저에서 크롬 개발자 도구를 열어 인풋에 아무 것이나 입력해 보자.
- 이벤트 객체가 콘솔에 나타났다.
* EventPractice.js의 onChange 설정 부분 다시 보기
onChange={ (e) => { console.log(e); } } |
- 여기서 콘솔에 기록되는 e 객체는 SyntheticEvent로 웹 브라우저의 네이티브 이벤트를 감싸는 객체다.
- 네이티브 이벤트와 인터페이스가 같으므로 순수 자바스크립트에서 HTML 이벤트를 다룰 때와 똑같이 사용하면 된다.
- SyntheicEvent는 네이티브 이벤트와 달리 이벤트가 끝나고 나면 이벤트가 초기화되므로 정보를 참조할 수 없다.
- 예를 들어 0.5초 뒤에 e 객체를 참조하면 e 객체 내부의 모든 값이 비워지게 된다.
- 만약 비동기적으로 이벤트 객체를 참조할 일이 있다면 e.persist() 함수를 호출해 주어야 한다.
- 에를 들어 onChange 이벤트가 발생할 때, 앞으로 변할 인풋 값인 e.target.value를 콘솔에 기록해 보자.
- 코드를 다음과 같이 수정해 보자.
* EventPractice.js의 onChange 코드 수정
onChange={ (e) => { console.log(e.target.value); } } |
- 값이 바뀔 때마다 바뀌는 값을 콘솔에 기록한다.
state에 input 값 담기
- 이번에는 state에 input 값을 담아 보자.
- 생성자 메서드인 constructor에서 state 초기값을 설정하고, 이벤트 핸들링 함수 내부에서 this.setState 메서드를 호출하여 state를 업데이트해 본다.
- 그 다음에는 input의 value 값을 state에 있는 값으로 설정한다.
* EventPractice.js
import { Component} from 'react'; class EventPractice extends Component{ state = { message: '' } render(){ return( <div> <h1>이벤트 연습</h1> <input type="text" name="message" placeholder="아무거나 입력해 보세요" value={this.state.message} onChange={ (e) => { this.setState({ message: e.target.value }); } } /> </div> ); }; } export default EventPractice; |
- 코드를 저장하고, 인풋에 아무것이나 입력해 보자. 오류를 발생시키지 않고 제대로 입력할 수 있다면 state에 텍스트를 잘 담은 것이다.
버튼을 누를 때 comment 값을 공백으로 설정
- 입력한 값이 state에 잘 들어갔는지, 인풋에서 그 값을 제대로 반영하는지 검증해 보자.
- input 요소 코드 아래쪽에 button을 하나 만들고, 클릭 이벤트가 발생하면 현재 comment 값을 메시지 박스로 띄운 후 comment 값을 공백으로 설정하자.
* EventPractice.js
import { Component} from 'react'; class EventPractice extends Component{ state = { message: '' } render(){ return( <div> <h1>이벤트 연습</h1> <input type="text" name="message" placeholder="아무거나 입력해 보세요" value={this.state.message} onChange={ (e) => { this.setState({ message: e.target.value }); } } /> <button onClick={() => { alert(this.state.message); this.setState({ message: '' }); }} >확인</button> </div> ); }; } export default EventPractice; |
- alert를 사용하여 현재 message 값을 화면에 표시하게 했다.
임의 메서드 만들기
- "이벤트에 실행할 JS 코드를 전달하는 것이 아니라, 함수 형태의 값을 전달한다"에 따라, 이벤트를 처리할 때 렌더링을 하는 동시에 함수를 만들어서 전달해 주었다.
- 이 방법 대신 함수를 미리 준비하여 전달하는 방법도 있다. 성능상 차이는 없지만, 가독성은 훨씬 높다.
(하지만 상황에 따라 렌더링 메서드 내부에서 함수를 만드는 것이 더 편할 때도 있다)
- 앞서 onChange와 onClick에 전달한 함수를 따로 빼내서 컴포넌트 임의 메서드를 만들어 보자.
기본 방식
* EventPractice.js
import { Component} from 'react'; class EventPractice extends Component{ state = { message: '' } constructor(props){ super(props); this.handleChange = this.handleChange.bind(this); this.handleClick = this.handleClick.bind(this); } handleChange(e){ this.setState({ message: e.target.value }); } handleClick(){ alert(this.state.message); this.setState({ message: '' }); } render(){ return( <div> <h1>이벤트 연습</h1> <input type="text" name="message" placeholder="아무거나 입력해 보세요" value={this.state.message} onChange={this.handleChange} /> <button onClick={this.handleClick} >확인</button> </div> ); }; } export default EventPractice; |
- 함수가 호출될 때 this는 호출부에 따라 결정되므로, 클래스의 임의 메서드가 특정 HTML 요소의 이벤트로 등록되는 과정에서 메서드와 this의 관계가 끊어져 버린다.
- 이 때문에 임의 메서드가 이벤트로 등록되어도 this를 컴포넌트 자신으로 제대로 가리키기 위해서는 메서드를 this와 바인딩(binding)하는 작업이 필요하다.
- 만약 바인딩하지 않는 경우라면 this가 undefined를 가리키게 된다.
- 현재 constructor 함수에서 함수를 바인딩하는 작업이 이루어지고 있다.
Property Initializer Syntax를 사용한 메서드 작성
- 메서드 바인딩은 생성자 메서드에서 하는 것이 정석이다.
- 하지만 이 방식이 불편하다고 느낄 수 있다. 새 메서드를 만들 때 마다 constructor를 수정해야 하기 때문이다.
- 이 작업을 좀 더 간단하게 하는 방법이 있으며, 바로 바벨의 transform-class-properties 문법을 사용하여 화살표 함수 형태로 메서드를 정의하는 것이다.
- 이 문법을 사용하면 코드가 어떻게 변하는지 한번 보자.
* EventPractice.js
import {Component} from 'react'; class EventPractice extends Component{ state = { message: '' } handleChange = (e) => { this.setState({ message: e.target.value }); } handleClick = (e) => { alert(this.state.message); this.setState({ message: '' }); } render(){ return( <div> <h1>이벤트 연습</h1> <input type="text" name="message" placeholder="아무거나 입력하세요." value={this.state.message} onChange={this.handleChange }/> <button onClick={this.handleClick}>확인</button> </div> ); } } export default EventPractice; |
- 훨씬 깔끔해졌다.
input 여러 개 다루기
- 우리는 input 값을 state에 넣는 방법을 배웠다. 하지만 input이 여러 개일 때는 어떻게 작업해야 하는가?
- event 객체를 활용하며, e.target.name 값을 사용하면 된다.
- onChange 이벤트 핸들러에서 e.target.name은 해당 인풋의 name을 가리킨다. 지금은 message 일 것이다.
- 이 값을 사용하여 state를 설정하면 쉽게 해결할 수 있다. 코드를 한번 살펴 보자.
- 다음 코드에서는 render 함수에서 name 값이 username인 input을 렌더링해 주었고, state 쪽에도 username이라는 값을 추가해 주었다.
- 그리고 handleChange도 조금 변경했다.
* EventPractice.js
import {Component} from 'react'; class EventPractice extends Component{ state = { username: '', message: '' } handleChange = (e) => { this.setState({ [e.target.name]: e.target.value }); } handleClick = () => { alert(this.state.username + ': '+ this.state.message); this.setState({ username: '', message: '' }); } render(){ return( <div> <h1>이벤트 연습</h1> <input type="text" name="username" placeholder="사용자명" value={this.state.username} onChange={this.handleChange }/> <input type="text" name="message" placeholder="아무거나 입력하세요." value={this.state.message} onChange={this.handleChange }/> <button onClick={this.handleClick}>확인</button> </div> ); } } export default EventPractice; |

- 위에서는 다음 코드가 핵심이다.
* EventPractice.js
handleChange = (e) => { this.setState({ [e.target.name]: e.target.value }); } |
- 객체 안에서 key를 [ ]로 감싸면 그 안에 넣은 레퍼런스가 가리키는 실제 값이 key 값으로 사용된다.
- 예를 들어 다음과 같은 객체를 만들면
const name = 'variantKey';
const object = {
[name] : 'value'
};
- 결과는 다음과 같다.
{
'varianKey' : 'value'
}
onKeyPress 이벤트 핸들링
- 이번에는 키를 눌렀을 때 발생하는 KeyPress 이벤트를 처리하는 방법을 알아보자.
=> 현재 onKeyPress는 deprecated 되었다. 따라서 onKeyDown으로 바꿔 사용한다.
- comment 인풋에서 [Enter]를 눌렀을 때 handleClick 메서드를 호출하도록 코드를 작성한다.
* EventPractice.js
import {Component} from 'react'; class EventPractice extends Component{ state = { username: '', message: '' } handleChange = (e) => { this.setState({ [e.target.name]: e.target.value }); } handleClick = () => { alert(this.state.username + ': '+ this.state.message); this.setState({ username: '', message: '' }); } handleKeyDown = (e) => { if(e.key === 'Enter'){ this.handleClick(); } } render(){ return( <div> <h1>이벤트 연습</h1> <input type="text" name="username" placeholder="사용자명" value={this.state.username} onChange={this.handleChange }/> <input type="text" name="message" placeholder="아무거나 입력하세요." value={this.state.message} onChange={this.handleChange } onKeyDown={this.handleKeyDown}/> <button onClick={this.handleClick}>확인</button> </div> ); } } export default EventPractice; |
- 두 번째 텍스트 인풋에서 텍스트를 입력하고 [Enter]를 눌러 보자. handleCLick 메서드가 실행되었는가?
함수 컴포넌트로 구현해 보기
- 앞 페이지에서 작성한 것을 함수 컴포넌트로도 똑같이 구현할 수 있다. 기존 EventPractice 컴포넌트를 모두 지우고 다음과 같이 작성해 본다.
* EventPractice.js
import { useState } from 'react'; const EventPractice = () => { const [username, setUsername] = useState(''); const [message, setMessage ] = useState(''); const onChangeUsername = e => setUsername(e.target.value); const onChangeMessage = e => setMessage(e.target.value); const onClick = () => { alert(username + ': ' + message); setUsername(''); setMessage(''); }; const onKeyDown = e => { if(e.key === 'Enter'){ onClick(); } }; return( <div> <h1>이벤트 연습</h1> <input type="text" name="username" placeholder="사용자명" value={username} onChange={onChangeUsername} /> <input type="text" name="message" placeholder="아무거나 입력해 보세요" value={message} onChange={onChangeMessage} onKeyDown={onKeyDown} /> <button onClick={onClick}>확인</button> </div> ); } export default EventPractice; |
- 기능이 이전과 같이 잘 작동하는가?
- 위 코드에서는 e.target.name을 활용하지 않고 onChange 관련 함수 두 개를 따로 만들어 주었다.
- input이 두 개밖에 없다면 이런 코드도 나쁘지는 않으나 input의 개수가 많아질 것 같으면 e.target.name을 활용하는 것이 더 좋을 수도 있다.
- 이번에는 useState를 통해 사용하는 상태에 문자열이 아닌 객체를 넣어 본다. 코드를 다음과 같이 수정해 본다.
* EventPractice.js
import { useState } from 'react'; const EventPractice = () => { const [form, setForm] = useState({ username: '', message: '' }); const { username, message } = form; const onChange = e => { const nextForm = { ...form, // 기존의 form 내용을 이 자리에 복사한 뒤 [e.target.name]: e.target.value // 원하는 값을 덮어 씌우기 }; setForm(nextForm); } const onClick = () => { alert(username + ': ' + message); setForm({ username: '', message: '' }); }; const onKeyDown = e => { if(e.key === 'Enter'){ onClick(); } }; return( <div> <h1>이벤트 연습</h1> <input type="text" name="username" placeholder="사용자명" value={username} onChange={onChange} /> <input type="text" name="message" placeholder="아무거나 입력해 보세요" value={message} onChange={onChange} onKeyDown={onKeyDown} /> <button onClick={onClick}>확인</button> </div> ); } export default EventPractice; |
- 코드를 저장하고 기능이 잘 작동하는지 확인해 보자.
- e.target.name 값을 활용하려면, 위와 같이 useState를 쓸 때 input 값들이 들어 있는 form 객체를 사용해 주면 된다.
'Javascript > React' 카테고리의 다른 글
[React] 리액트 state (0) | 2025.03.24 |
---|---|
[React] 리액트 props (0) | 2025.03.13 |
[React] 스타일링 (0) | 2025.03.06 |
[React] undefined 렌더링하지 않기 (0) | 2025.03.06 |
[React] 조건부 연산자 (0) | 2025.03.06 |
- Total
- Today
- Yesterday
- 서블릿
- 리액트
- CSS 속성
- Network
- 제이쿼리
- 스프링
- HTML
- react
- 내장객체
- JSP
- javaserverpage
- Session
- 네트워크
- script element
- 미들웨어
- Servlet
- a 태그
- html css
- nodejs
- Redux
- Java Server Page
- Binding
- 서브넷팅
- Spring
- el
- CSS
- 세션
- httpServletRequest
- Spring MVC
- FMT
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |