5. Input 상태 관리하기
0. Input 태그의 상태 관리
<Input />
태그는 기본적으로 텍스트를 입력하는 공간을 제공합니다. 이번 장에서는 지난 장에서 언급한 useState() 함수를 사용하여, <Input />
태그가 하나일 때와 두 개 이상일 때 각각 상태를 어떻게 관리하는지에 대해 알아보겠습니다.
단순할 것 같지만 이번 장을 통해 아래 여러 가지에 대해 알 수 있습니다.
- 이벤트 객체
- onChange 이벤트
- onClick 이벤트
<input />
태그의 value 속성 지정에 따른 상태 변화
1. 단일 Input 상태 관리
입력창에 입력되는 텍스트가 그대로 브라우저에 출력되고, 버튼을 눌러 입력한 텍스트를 초기화하는 예시입니다.
InputSample.js
import React, {useState} from 'react';
function InputSample() {
const [text, setText] = useState('');
const onChange = (e) => {
setText(e.target.value)
}
const onReset = () => {
setText('');
}
return (
<div>
<input onChange={onChange} value={text} placeholder="텍스트를 입력하세요!"/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b> {text}
</div>
</div>
);
}
export default InputSample;
App.js
import React from 'react';
import InputSample from './InputSample';
function App() {
return (
<InputSample />
);
}
export default App;
위 파일을 2개를 작성하고 yarn start
하면 이런 컴포넌트가 브라우저에 등장합니다. 입력칸에 텍스트를 입력하면 입력하는 그대로 값:
이라고 된 부분에 나타나고, 초기화
버튼을 누르면 입력칸과 값:
의 텍스트가 초기화됩니다.
App.js
는 컴포넌트를 삽입하는 부분이니 크게 볼 것이 없습니다. InputSample.js
를 보면서 중요한 점들을 짚어 보겠습니다.
먼저, 상태를 정의하는 부분부터 보겠습니다.
const [text, setText] = useState('');
text
라는 상태를 정했습니다. 초기값은 공백(''
)으로 초기화되었네요. 이것이 <Input />
태그에 적힐 텍스트의 상태입니다.
그 다음은 InputSample
컴포넌트가 렌더링할 내용을 살펴봅시다.
return (
<div>
<Input onChange={onChange} value={text} placeholder="텍스트를 입력하세요!"/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b> {text}
</div>
</div>
);
<Input />
태그에서는 onChange
, value
, placeholder
속성을 지정했습니다.
onChange
는 Form event의 한 종류입니다. Form element의 일종인 <Input />
의 입력 내용에 변화가 생길 때마다 onChange 이벤트가 발생합니다. 즉, 위의 onChange={onChange}
는 onChange 이벤트가 일어날 때마다 {}
안의 함수가 발동한다는 뜻입니다. value
속성은 Form element에 표시할 내용을 의미합니다. 여기서는 text
로 설정되어 있습니다. text
가 입력되는대로 입력칸에 텍스트가 보일 것입니다. placeholder
는 아무것도 입력되지 않은 입력칸에서 보일 기본 텍스트를 의미합니다.
다음으로 <Button />
태그를 보면, onClick
속성이 지정되어 있습니다. 이는 Mouse Event로서, 마우스로 하는 행동들과 관련 있는 이벤트입니다. onClick은 마우스 클릭 이벤트를 의미합니다. 마우스 클릭이 발생할 때 {}
안의 함수가 동작합니다.
마지막으로 <div>
태그 안의 내용은 text
상태의 값을 그대로 보여주겠다는 의미입니다.
렌더되는 요소들을 봤으니, 그 안에 있던 이벤트가 발생할 때 동작하던 두 가지 함수를 살펴보겠습니다.
const onChange = (e) => {
setText(e.target.value)
//console.log(e.target)
//console.log(e.target.value)
}
const onReset = () => {
setText('');
}
onChange 함수는 <Input/>
에서 onChange
이벤트가 발생할 때 동작하는 함수입니다. e
라는 파라미터를 받으며 text
상태를 e.target.value
로 업데이트합니다. 여기서 받는 파라미터는 e
는 합성 이벤트 객체 라고 합니다(이벤트 객체는 여러 가지 속성을 가지는데 이것이 궁금하신 분들은 공식 문서를 확인해 보세요.). 어떤 이벤트가 발생했을 때 그 내용이 이벤트 객체에 담기며, 이를 파라미터로서 활용할 수 있습니다. e.target
에는 '이벤트가 발생한 타겟 DOM'에 대한 정보가 있습니다. 즉, 여기서는 <Input />
DOM 요소에 키보드로 무언가를 입력할 때마다 변화가 발생하므로, e.target
은 <Input />
이 됩니다. e.target.value
는 '변화가 발생하는 DOM 요소가 가지고 있는 값'을 의미합니다. 여기서는 <Input />
에 입력되고 있는 input 요소이겠죠. 궁금하신 분들은 console.log
주석을 풀고 개발자 도구를 확인해 보세요/>
onReset 함수는 파라미터를 받지 않으며 text
상태를 초기화해버리는 역할을 합니다.
컴포넌트 설명은 다 끝났습니다. 이제 이 코드를 실제로 실행하여 테스트해 보시기 바랍니다. 아래와 스크린샷과 같이 입력 내용을 따라 실시간으로 아래에 텍스트가 써지고, '초기화' 버튼을 통해 입력칸과 그 아래 텍스트가 모두 지워지면 잘 되는 것입니다!
2. 여러 Input 상태 관리하기
이번엔 입력창이 여러 개일 때의 텍스트 상태를 관리해 봅시다.
InputSample2.js
import React, { useState } from 'react'
function InputSample2() {
const [inputs, setInputs] = useState({
name: '',
nickname: '',
})
const {name, nickname} = inputs;
const onChange = (e) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value,
})
}
const onReset = () => {
setInputs({
name: "",
nickname: "",
})
}
return (
<div>
<input
name="name"
placeholder="이름"
onChange={onChange}
value={name}
/>
<input
name="nickname"
placeholder="닉네임"
onChange={onChange}
value={nickname}
/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b> {name} ({nickname})
</div>
</div>
)
}
export default InputSample2;
App.js
import InputSample2 from './InputSample2';
function App() {
return (
<InputSample2 />
);
}
export default App;
<InputSample2 />
에서 상태를 정의하는 부분만 봐도 알 수 있듯이, 하나의 인풋창만을 관리할 때와 많이 달라졌습니다. 차근차근히 살펴보겠습니다.
const [inputs, setInputs] = useState({
name: '',
nickname: '',
})
const {name, nickname} = inputs;
이번에는 inputs
라는 상태를 정의합니다. inputs
는 name
, nickname
이라는 프로퍼티(property)를 갖는 객체입니다. 객체 형태로 상태를 관리하는 것은 여러 가지의 상태를 한 번에 관리할 때 용이하며 코드를 더 간결하게 만들어 줍니다. 생각해 보세요. inputs
객체가 없으면 name
과 nickname
각각에 대해 Setter 함수를 따로 만들어야 합니다.
그리고 const {name, nickname} = inputs;
에서는 비구조화 할당 을 통해 input
객체 안에 있는 name
과 nickname
데이터를 각각 꺼내 변수에 할당하고 있습니다. 이로써, 각 프로퍼티를 활용해야 할 때, input.name
또는 inputs.nickname
과 같이 길게 쓰지 않아도 됩니다.
return (
<div>
<input
name="name"
placeholder="이름"
onChange={onChange}
value={name}
/>
<input
name="nickname"
placeholder="닉네임"
onChange={onChange}
value={nickname}
/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b> {name} ({nickname})
</div>
</div>
)
그 다음엔 화면에 렌더링되는 요소들을 봅시다. 2개의 <Input />
과 1개의 버튼, 그리고 입력값을 그대로 보여주는 <div>
가 있습니다.
<Input />
에는 name
, placeholder
, onChange
, value
의 4가지 속성이 있습니다. 뒤의 세 가지는 위에서도 봤지만, name
는 왜 있는 것일까요? name
속성은 해당 요소에 이름을 부여하는 것인데, 이는 Form event인 onChange가 발생할 때 그 이벤트가 발생한 요소를 참조하기 위함입니다. 즉, onChange={onChange}
에서 {}
안의 onChange 함수가 작동할 때, 두 가지 <Input />
태그 중 어떤 것에 변화가 일어나고 있는지는 name을 통해 알 수 있는 것이죠.
<div>
가 name
과 nickname
두 가지 상태를 참조한다는 것 이외에 나머지 속성은 특이할 것이 없습니다.
이제 이벤트 발생 시 작동하는 함수를 정의하는 부분을 봅시다.
const onChange = (e) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value,
})
}
const onReset = () => {
setInputs({
name: "",
nickname: "",
})
}
onChange 함수는 이벤트 객체 e를 받되, 비구조화 할당을 통해 변수 name
에는 e.target.name
을, 변수 value
에는 e.target.value
를 할당하고 있습니다. e.target.name
은 onChange 이벤트가 발생하고 있는 요소의 이름을 가리킵니다. 아까 <Input />
에서 name=
속성에 'name', 'nickname'을 지정했지요. 따라서 둘 중 어떤 입력창에 텍스트를 적느냐에 따라 e.target.name
은 'name', 'nickname' 둘 중 하나가됩니다.
그리고 setInputs
Setter 함수를 보면 ...
이 나옵니다. 이를 spread 연산자라고 부르며, 어떤 기존의 데이터를 복사하여 새로운 데이터를 만드는 기능을 합니다(이에 대해서는 velopert님께서 설명을 잘 해주셨습니다. 여기를 클릭해서 보러 가기). setInputs({...inputs, [name]: value})
의 내용을 해석하자면, Input
상태의 프로퍼티를 그대로 복사하여 만들어진 새 객체의 name
프로퍼티의 value를 value(=e.target.value)
로 업데이트 하겠다는 의미입니다. 이는 풀어쓰자면 아래 코드와 같은 의미입니다.
const nextInputs = {
...inputs,
}
nextInputs[name] = value;
setInputs(nextInputs)
inputs
상태를 복사하여 nextInputs라는 새 상태를 만들고, 그 중 name
프로퍼티의 값을 value
로 업데이트한 다음, inputs
상태를 nextInput
로 업데이트하는 것입니다. 객체 형태의 상태를 업데이트 하는 흐름이 이해 되시나요?
- spread 연산자로 기존 객체(A)를 복사한 새 객체 생성(A')
- 새 객체(A')를 업데이트
- 기존 객체(A)를 새 객체(A')로 업데이트(
setInputs(nextInputs)
)
왜 객체를 바로 업데이트 하지 않고, 새 객체를 만들어야 할까요? 이는 리액트 상태의 중요한 개념인 불변성 을 지켜야 하기 때문입니다. inpus[name] = value
와 같이 객체 형태의 state를 곧바로 수정할 경우, setter 함수를 사용하지 않으므로 state 업데이트가 감지되지 않아 리렌더링이 일어나지 않습니다. 또한 코드 최적화를 위해서라도 불변성을 꼭 유지해 주어야 합니다(이에 대해서도 역시 velopert님께서 알기 쉽게 정리해 주셨습니다... 여기를 클릭하여 설명 보러 가기)
이제 두 개의 입력창의 텍스트를 입력하는대로 브라우저에 잘 출력되는지 봅시다. 초기화 버튼도 잘 동작하나 확인해 보세요.
Author And Source
이 문제에 관하여(5. Input 상태 관리하기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@clock509/5.-Input-상태-관리하기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)