이벤트 핸들링

nasagong·2023년 2월 7일
0

React

목록 보기
5/15
post-thumbnail

📚 들어가며

기본적인 이벤트 핸들링에 대한 내용이다. state를 다루는 방법과 value를 관리하는 법에 집중해봤다.

리액트에서 이벤트를 사용하는 법

HTML에서의 이벤트는 아래처럼 실행할 코드를 따옴표 안에 직접 작성하는 식이었다.

<button onclick="funcName()">btn</button>

리액트는 객체형태로 함수를 전달한다. 인라인으로 함수를 작성해도 되고, 따로 작성해둔 함수를 전달해도 된다.

const App = () =>{
    function foo(){
        alert('test');
    }
    return(
      <>
        <button onClick={foo}>BTN</button>
        <button onClick={()=>alert('test2')}>btn</button>
      </>
    );
}
export default App;

위처럼 중괄호로 감싸 함수를 전달하면 이벤트 함수를 호출할 수 있다. 또 하나 차이가 있다면 이벤트 이름을 카멜표기법으로 쓴다는 것. 가령 onclickonClick으로 표기한다. 리액트에서는 다양한 이벤트들을 지원하지만 이번 장에서는 자주 쓰는 간단한 이벤트들만 실습해보자.

onChange 이벤트 핸들링

class EventPractice extends Component{
    render(){
        return(
            <div>
                <h1>이벤트 연습</h1>
                <input 
                type = "text"
                name = "message"
                placeholder = "아무거나 입력해 보세요"
                onChange = {
                    (e)=>{
                        console.log(e);
                    }
                }
                />
            </div>
        );
    }
}
export default EventPractice;

onChange 이벤트는 값의 변화를 감지한다. 위 코드는 input 값의 변화에 따라 이벤트 객체를 반환한다.

onChange에 넘겨진 함수가 받는 파라미터인 e는 이벤트 객체를 의미하는데(여기서는 input), 그 자체로는 참조 불가능한 형태의 객체이기 때문에 입력값의 변화를 비동기적으로 참조하고 싶다면 아래와 같은 형태로 작성하면 된다.

 onChange = {(e)=>{console.log(e.target.value);}}

input값이 변화할 때마다 console에 기록된다. persisit함수라던가 더 큰 개념들은 나중에 알게 될테니 일단은 사용법 정도만 알아두는 걸로..

state에 값 담기

input에 입력된 값을 state에 저장하기 위해선 어떡해야 될까? 코드로 보자.

class EventPractice extends Component{
    state = {
        message : ''
    }
    render(){
        const {message} = this.state;
        return(
            <div>
                <h1>이벤트 연습</h1>
                <input 
                type = "text"
                name = "message"
                placeholder = "Enter something"
                onChange = {
                    (e)=>{
                        this.setState({message : e.target.value});
                    }
                }
                />
                <h1>{message}</h1>
            </div>
        );
    }
}

setState를 사용해 state의 message값을 이벤트 객체의 값으로 갱신해주고 있다. (역시나 객체 형태로 넣어야 한다)
렌더링해보면 입력값이 바로바로 반영되는 모습을 확인할 수 있다.

버튼을 사용해 onClick 핸들링하기

앞서 작성한 코드를 조금 발전시켜서, input 박스 옆에 버튼을 만들고 그 버튼을 누르면 input값이 초기화되도록 만들어보자.

render(){
        const {message} = this.state;
        const clear = () =>{
            this.setState({message:''})
        }
        return(
            <div>
                <h1>이벤트 연습</h1>
                <input 
                type = "text"
                name = "message"
                value = {this.state.message} // for clear
                placeholder = "Enter something"
                onChange = {
                    (e)=>{
                        this.setState({message : e.target.value});
                    }
                }
                />
                <button onClick = {clear}>clear!</button>
                <h1>{message}</h1>
            </div>
        );
    }

input태그 내부가 조금 수정됐다. state의 message를 value에 할당해주고 있는데, message값이 공백이 됐을 때 input에서도 이를 반영하게 만들기 위해 추가된 부분이다.

onClick 이벤트 자체는매우 간단하게 추가됐다. 정말 단순하게 클릭했을 때 clear함수를 호출해 state값을 공백으로 만들어 주면 될 뿐이다. 렌더링 결과는 너무 뻔하기에 생략하도록 하자.

임의 메서드 만들기

사실 클리어 버튼을 만들 때 이미 임의 메서드를 사용했다. clear함수를 미리 준비한 후 전달한 것이 그 예다. 다만 클래스형 컴포넌트에서 임의 메서드를 만들기 위해선 주의해야 될 것들이 있다.

constructor(props){
   super(props);
   this.methodName = this.methodName.bind(this);
}

기본적으로는 생성자 메소드에서 명시적으로 this(컴포넌트)를 바인딩 해준 후에 사용할 수 있다. 함수의 this는 호출되는 인스턴스에 따라 달라지기 때문에, HTML요소 내에서 사용되는 메서드는 컴포넌트와의 연결이 끊기게 된다. 따라서 임의로 컴포넌트에 바인드를 해주지 않으면 state를 읽어올 수 없다.

하지만 바벨의 transform-class-properties문법을 사용하면 더 간단하게 작성이 가능하다. 당연히 난 이게 뭔지 모른다.. 아무튼 화살표 함수를 통해 메서드를 작성하면 생성자 메서드에서 따로 바인딩을 해주지 않아도 된다는 장점이 있다는 것만 알아두자. clear메서드를 그렇게 작성했기에 따로 예시 코드를 작성하진 않겠다.

input 여러 개 다루기

input을 여러개 사용한다면 어떻게 관리해야 할까? username을 입력하는 input을 하나 더 만들어보자.

class EventPractice extends Component {
  state = {
    message: '',
    username: '',
  };
  render() {
    const { message, username } = this.state;
    const clear = () => {
      this.setState({ message: '', username: '' });
    };
    const handleChange = (e) => {
      this.setState({ [e.target.name]: e.target.value });
    };
    return (
      <div>
        <h1>이벤트 연습</h1>
        <input
          type="text"
          name="message"
          value={this.state.message}
          placeholder="Enter something"
          onChange={handleChange}
        />
        <input
          type="text"
          name="username"
          value={this.state.username}
          placeholder="Username"
          onChange={handleChange}
        />
        <button onClick={clear}>clear!</button>
        <h1>
          {message}<br/>{username}
        </h1>
      </div>
    );
  }
}

태그 작성 자체는 달라질 부분이 없다. 유심히 봐야 할 부분은 여러개의 state를 관리하는 방법이다. 나는 아래와 같은 방식을 선택했다.

const handleChange = (e) => {
      this.setState({ [e.target.name]: e.target.value });
    };

이벤트 객체의 이름을 state의 key값과 동일하게 설정해두면 위 코드처럼 유동적으로 적절한 값을 매핑할 수 있다. 일일히 함수를 따로 작성하는 방법도 있겠지만 이렇게 하는 편이 더 간단하다.

keyPress 이벤트 핸들링

웹사이트를 이용하다 보면 굳이 버튼을 누르지 않고 input에 커서가 켜져있는 상태에서 Enter키를 누르면 버튼의 동작을 수행하게 만드는 경우를 종종 볼 수 있다. 그러한 기능을 리액트로 구현해보자.

class EventPractice extends Component {
  state = {
    message: '',
    username: '',
  };
  clear = () => {
    this.setState({message: '', username: '' });
  };
  handleChange = (e) => {
    this.setState({ [e.target.name]: e.target.value });
  };
  handleKeyPress = (e) =>{
      if(e.key === 'Enter'){
          this.clear();
      }
  }
  render() {
    const { message, username } = this.state;
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="message"
          value={this.state.message}
          placeholder="Enter something"
          onChange={this.handleChange}
        />
        <input
          type="text"
          name="username"
          value={this.state.username}
          placeholder="Username"
          onChange={this.handleChange}
          onKeyPress={this.handleKeyPress}
        />
        <button onClick={this.clear}>clear!</button>
        <h1>
          {message}
          <br />
          {username}
        </h1>
      </div>
    );
  }
}

event객체의 key값을 판단한 후 clear함수를 호출했다. 역시나 어려운 부분은 없다. 여태 메서드들을 render 함수 안에서 선언해놔서 이번엔 밖으로 빼놨다.

함수형 컴포넌트로 작성해보기

useState와 위에서 사용한 대괄호를 통한 state값 매핑으로 함수형 컴포넌트를 작성해보자.

import {useState} from 'react';

const EventPractice = () =>{
    const [form,setForm] = useState({
        username : '',
        message : ''
    });
    const {username,message} = form;
    const onChange = e =>{
        const nextForm = {
            ...form,
            [e.target.name] : e.target.value
        }
        setForm(nextForm);
    }
    const onClick = () =>{
        setForm({
            username:'',
            message:''
        });
    }
    const onKeyPress = e =>{
        if(e.key === 'Enter'){
            onClick();
        }
    }
    return(
        <div>
            <input
            type = "text"
            name = "username"
            placeholder = "username"
            value = {username}
            onChange={onChange}
            />
            <input
            type = "text"
            name = "message"
            placeholder = "message"
            value = {message}
            onChange = {onChange}
            onKeyPress = {onKeyPress}
            />
            <button onClick={onClick}>clear</button>
        </div>
    )
}
export default EventPractice;

코드 자체는 직관적으로 들어온다. 객체 형태의 state값 중 일부를 업데이트 하는 방법에 주목해보자.

const onChange = e =>{
     const nextForm = {
          ...form,
          [e.target.name] : e.target.value
      }
      setForm(nextForm);
 }
const onClick = () =>{
     setForm({
            username:'',
            message:''
        });
    }

사본을 만들어 준 후 변경값만 덮어씌우는 방식을 사용하고 있다. 반면 onClick은 아예 다시 객체를 다시 설정해주고 있는데, 이는 일부를 수정하는 것과 객체 자체를 초기화하는 것의 차이다.

profile
잘쫌해

0개의 댓글