본문 바로가기
Front-End/React

[ React ] React 기본 ① 사용자 입력 처리하기

by 2CHAE._.EUN 2022. 9. 19.

[ React에서 사용자 입력 처리하기 ]

 

1. 컴포넌트가 input에 작성된 값을 직접 다룰 수 있도록 만들어야된다. → useState 사용

 

input에 들어가는 값이 실시간으로 바뀔 때 마다 상태 변화 함수를 이용해서 state에 입력 값을 저장해주면 된다. 

→ state에 input 태그의 value 속성을 이용해서 전달해주면 된다. 

 

DiaryEditor.js

import {useState} from "react";

const DiaryEditor = () => {

    const [ author, setAuthor ] = useState("");
    // input에 들어갈 내용을 관리할 author 라는 state와 
    // 그 state의 상태변화를 주도하는 함수인 setAuthor를 만들어준다. 

    return <div className="DiaryEditor">
        <h2>오늘의 일기</h2>
        <div>
            <input value={author}></input>
        </div>
    </div>;
};

export default DiaryEditor;

 

state는 상태 변화 함수가 작동하지 않는다면 그 값을 변경할 수 없다.

 

즉, input 태그에 변화가 일어났을 때마다 상태 변화 함수가 수행되도록 명시하는 코드를 작성해야 한다.

작성하지 않는다면 자동으로 상태가 변화할 일이 없어서 useState()의 초기값으로만 고정되어 수행하게 된다.

 

2. onchange prop과 콜백 함수를 사용해서 input 태그의 값을 실시간으로 반영하기

 

onchange는 이벤트이다. 이벤트는 사용자가 어떠한 행동을 하는 것을 의미한다. 사용자가 어떠한 값을 입력을
했을 때 input은 이벤트가 발생했다고 생각하게 된다. onchange 이벤트는 값이 바뀌었을 때, 수행하는 이벤트이다. 

 

즉, onchange에 등록되는 콜백 함수는 이벤트 객체인 매개변수 e를 전달받게 되고 input의 값이 바뀌었을 때 onchange prop에 전달한 콜백 함수를 수행하게 된다. 값이 바뀔 때 마다 onchange에 전달한 콜백함수가 계속해서
호출이 된다.

 

이벤트 객체 e의 target의 value 프로퍼티를 사용하면 사용자가 input 태그에 입력한 값을 콜백 함수에 불러서

사용할 수 있다.

 

import {useState} from "react";

const DiaryEditor = () => {

    const [ author, setAuthor ] = useState("");
    // input에 들어갈 내용을 관리할 author 라는 state와 
    // 그 state의 상태변화를 주도하는 함수인 setAuthor를 만들어준다. 

    return <div className="DiaryEditor">
        <h2>오늘의 일기</h2>
        <div>
            <input value={author} onChange={(e)=>{
                setAuthor(e.target.value);
            }}></input>
        </div>
    </div>;
};

export default DiaryEditor;

 

author는 변화해야하는 값이므로 상태 변화 함수인 setAuthor를 통해 값이 변화할 때 마다 그 값으로 업데이트할 수 있다.

 

 

* 이벤트 객체는 target의 value 뿐만 아니라 target의 name도 출력할 수 있다.

 

import {useState} from "react";

const DiaryEditor = () => {

    const [ author, setAuthor ] = useState("");
    // input에 들어갈 내용을 관리할 author 라는 state와 
    // 그 state의 상태변화를 주도하는 함수인 setAuthor를 만들어준다. 

    return <div className="DiaryEditor">
        <h2>오늘의 일기</h2>
        <div>
            <input name="2chaechae" value={author} onChange={(e)=>{
                console.log(e.target.value);
                console.log(e.target.name);
                setAuthor(e.target.value);
            }}></input>
        </div>
    </div>;
};

export default DiaryEditor;

 

3. 여러 줄을 입력할 수 있는 게시글 본문 만들기 → textarea 사용

 

textarea은 HTML 태그로 랜더링 해주면 여러 줄을 입력받을 수 있다. 

textarea는 input 태그와 완전 사용법이 똑같으므로 textarea에 들어가는 값이 실시간으로 바뀔 때마다

상태 변화 함수를 사용해야 한다.

 

import {useState} from "react";

const DiaryEditor = () => {
    
    const [ author, setAuthor ] = useState("");
    const [ content, setContent ] = useState("");
    

    return <div className="DiaryEditor">
        <h2>오늘의 일기</h2>
        <div>
            <input name="2chaechae" value={author} onChange={(e)=>{
                setAuthor(e.target.value);
            }}></input>
        </div>
        <div>
            <textarea value={content} onChange={(e)=>{
                setContent(e.target.value);
            }}></textarea>
        </div>
    </div>;
};

export default DiaryEditor;

 

textarea의 값은 content state가 가진 값으로 고정이 되어 있고 textarea에 값이 입력이 되어서 onchange 이벤트가

발생 한다면 콜백함수가 수행이 된다. 콜백함수의 이벤트 객체인 e 매개변수에서의 변화한 값을 새로운 content 값으로

변화시키면서 content state가 바뀌면서 value가 바뀌게 되고 실제로 화면에 입력한 데로 반영이 된다. 

 

4. input과 textarea 같이 동작이 비슷한 state들은 하나의 state로 묶어줄 수 있다.

 

import {useState} from "react";

const DiaryEditor = () => {

    const [state, setState] = useState({
        author : "",
        content : ""
    });

    return <div className="DiaryEditor">
        <h2>오늘의 일기</h2>
        <div>
            <input name="2chaechae" value={state.author} onChange={(e)=>{
                setState({
                    author : e.target.value,
                    content : state.content
                });
            }}></input>
        </div>
        <div>
            <textarea value={state.content} onChange={(e)=>{
                setState({
                    author : state.content,
                    content : e.target.value
                });
            }}></textarea>
        </div>
    </div>;
};

export default DiaryEditor;

 

input 태그나 textarea 태그에 값 변화가 발생하면 setState 콜백 함수가 실행이 된다. 

state는 author와 content 프로퍼티를 가진 객체이기 때문에 state를 변경시켜줄 때 새로운 객체를 만들어서 전달을

해줘야한다. 왜냐하면 state를 변화시킬 때는 항상 상태 변화 함수에 새로 변경 시킬 값을 인자로 전달해줘야하기 때문에

객체도 마찬가지로 객체의 값을 변경하기 위해서는 새로운 객체를 만들어서 전달을 해줘야한다.

 

하지만 input 태그의 값이 변경될 때 textarea의 값은 변경되지 않고 당시 그 값을 유지해야 하므로 원래 있던 객체의 

값을 전달해 업데이트를 하지 않아야 한다.  

 

만약 state 객체의 프로퍼티가 여러 개 있을 경우 setstate 함수에 전달해줄 새로운 객체에는 spread 연산자를 사용해서

전달해주면 된다. 

* spread 연산자 : 객체가 가지고 있는 프로퍼티들을 펼쳐주는 연산자

 

import {startTransition, useState} from "react";

const DiaryEditor = () => {

    const [state, setState] = useState({
        author : "",
        content : ""
    });
   
    return <div className="DiaryEditor">
        <h2>오늘의 일기</h2>
        <div>
            <input name="2chaechae" value={state.author} onChange={(e)=>{
                setState({
                    ...state,
                    author : e.target.value,
                });
            }}></input>
        </div>
        <div>
            <textarea value={state.content} onChange={(e)=>{
                setState({
                    ...state,
                    content : e.target.value
                });
            }}></textarea>
        </div>
    </div>;
};

export default DiaryEditor;

 

spread 연산자는 반드시 원래 있던 state를 먼저 펼쳐주고 나서 변경하고자 하는 state의 프로퍼티를 마지막에 작성해

줘야한다. 

 

spread 연산자가 마지막에 작성될 경우, 위에 작성된 프로퍼티에 들어온 새로운 값이 spread 연산자를 통해

프로퍼티에는 원래 state의 프로퍼티로 값이 또 들어가게 되어 방금 업데이트한 값이 원래의 값으로 덮어 씌어지게 되어

결론적으로는 아무것도 업데이트가 되지 않는다. 

 

5. 이벤트 핸들러를 하나로 합쳐보기

 

    const handleChangeState = (e) => {
        setState({
            ...state,
            [e.target.name] : e.target.value
        })
    }

 

author state의 값을 변경하게 되면은 e.target.name은 author이므로 author : 변경한 값으로 자동으로 값을 바꾸고 

content state의 값을 변경하게 되면은 content : 변경한 값으로 자동으로 업데이트가 되면서 상태 변화

이벤트 핸들러로 합칠 수 있다. 

* 상태 변화 함수에 전달되는 객체의 프로퍼티 키 값과 전달되는 태그의 name 속성의 값이 같아야 한다. 

 

import {useState} from "react";

const DiaryEditor = () => {

    const [state, setState] = useState({
        author : "",
        content : ""
    });

    const handleChangeState = (e) => {
        setState({
            ...state,
            [e.target.name] : e.target.value
        })
    }
    

    return <div className="DiaryEditor">
        <h2>오늘의 일기</h2>
        <div>
            <input name="author" value={state.author} onChange={handleChangeState}></input>
        </div>
        <div>
            <textarea name="content" value={state.content} onChange={handleChangeState}></textarea>
        </div>
    </div>;
};

export default DiaryEditor;

 

6. 감정 점수 표현하기

 

select 태그도 input과 똑같이 e.target.value로 새로 바뀌는 값이 전달이 된다.

 

import {useState} from "react";

const DiaryEditor = () => {

    const [state, setState] = useState({
        author : "",
        content : "",
        emotion : 1
    });

    const handleChangeState = (e) => {
        setState({
            ...state,
            [e.target.name] : e.target.value
        })
    }
    

    return <div className="DiaryEditor">
        <h2>오늘의 일기</h2>
        <div>
            <input name="author" value={state.author} onChange={handleChangeState}></input>
        </div>
        <div>
            <textarea name="content" value={state.content} onChange={handleChangeState}></textarea>
        </div>
        <div>
            <select name="emotion" value={state.emotion} onChange={handleChangeState}>
                <option value={1}>1</option>
                <option value={2}>2</option>
                <option value={3}>3</option>
                <option value={4}>4</option>
                <option value={5}>5</option>
            </select>
        </div>
    </div>;
};

export default DiaryEditor;

 

select 태그의 실시간 값을 컴포넌트가 핸들링하기 위해서 state에 emotion 프로퍼티를 생성한다.

emotion 프로퍼티는 select가 선택하는 값을 계속해서 저장을 하게 된다. select 태그에 변화가 일어난다면

onChange 이벤트가 발생하게 되어 handleChangeState가 실행된다. 그러면 e.target.name은 emotion이므로

emotion : 변경한 값으로 새로 바뀌게 되면은 state가 변화가 되면서 emotion의 변경되는 값을

select의 value로 사용하게 되면은 자동으로 값이 바뀌게 된다.

 

7. 저장 버튼 만들기

 

import {useState} from "react";

const DiaryEditor = () => {

    const [state, setState] = useState({
        author : "",
        content : "",
        emotion : 1
    });

    const handleChangeState = (e) => {
        setState({
            ...state,
            [e.target.name] : e.target.value
        })
    }
    
    const handleSubmit = () => {
        console.log(state);
        alert("저장 성공");
    }

    return <div className="DiaryEditor">
        <h2>오늘의 일기</h2>
        <div>
            <input name="author" value={state.author} onChange={handleChangeState}></input>
        </div>
        <div>
            <textarea name="content" value={state.content} onChange={handleChangeState}></textarea>
        </div>
        <div>
            <select name="emotion" value={state.emotion} onChange={handleChangeState}>
                <option value={1}>1</option>
                <option value={2}>2</option>
                <option value={3}>3</option>
                <option value={4}>4</option>
                <option value={5}>5</option>
            </select>
        </div>
        <div>
            <button onClick={handleSubmit}>일기 저장하기</button>
        </div>
    </div>;
};

export default DiaryEditor;

 

 

8. CSS 작성하기

 

.App {
  text-align: center;
}

.DiaryEditor {
  margin: 0 auto;
  border: 5px solid skyblue;
  padding: 20px;
  width: 500px;
  margin-top: 30px;
}

.DiaryEditor input, textarea {
  margin-bottom: 20px;
  width: 400px;
  padding: 10px;
}

.DiaryEditor textarea {
  height: 150px;
}

.DiaryEditor select {
  width: 400px;
  padding: 10px;
  margin-bottom: 20px;
}

.DiaryEditor button {
  width: 400px;
  padding: 10px;
  cursor: pointer;
  border: 2px solid skyblue;
  color: aliceblue;
  background-color: skyblue;
}