React 게시글은 대부분 인프런의 '한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지' 강의를 기반으로 내용을 정리했습니다.
React에서 사용자 입력 처리하기
새로운 프로젝트를 만들자.
npx create-reace-app simplediary
불필요한 파일은 삭제한다.
삭제한 파일과 관련된 코드들을 지운다.
App.js
import './App.css';
function App() {
return (
<div className="App">
<h2>일기장</h2>
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Terminal
npm start
프로젝트가 에러 없이 잘 구동되는지 확인한다.
오늘 만들 다이어리
다이어리의 에디터 부분을 만들기 위해 새로운 파일을 생성한다.
const DiaryEditor = () => {
return <div className='DiaryEditor'></div>
}
export default DiaryEditor;
모듈 생성 후 화면에 랜더 할 수 있도록 App.js에 코드를 추가한다.
import './App.css';
import DiaryEditor from './DiaryEditor'; // 추가
function App() {
return (
<div className="App">
<h2>일기장</h2>
<DiaryEditor/> <!--추가-->
</div>
);
}
export default App;
이제 다이어리에 작성자를 입력할 수 있도록 input 태그를 추가한다.
DiaryEditor.js
const DiaryEditor = () => {
return (
<div className='DiaryEditor'>
<h2>오늘의 일기</h2>
<div>
<input />
</div>
</div>
);
};
export default DiaryEditor;
input 태그를 핸들링하기 위한 코드를 작성하자.
input 태그는 작성자를 나타내며 상태를 가질 수 있다.
그래서 저번에 배웠던 State를 사용해 input을 핸들링해보자.
import {useState} from "react"; // 추가
const DiaryEditor = () => {
const [author,setAuthor] = useState(""); // 추가
return (
<div className='DiaryEditor'>
<h2>오늘의 일기</h2>
<div>
<input value={author} /> // 추가
</div>
</div>
);
};
export default DiaryEditor;
useState를 사용하기 위해 react를 import 시켜준다.
그리고 모듈 내에 State를 사용하기 위해 변수와 함수 그리고 초깃값을 지정한다. 현재 초기값은 빈 문자열이다.
지금 이 코드에서 화면 input에 값을 아무리 입력해도 빈 문자열을 그대로 유지하고 있는데 그 이유는 input 태그의 value가 author로 설정되어 있는데 author의 값은 상태변화 함수인 setAuthor를 사용하지 않았기 때문이다. input 태그에서 author값을 변경하고 싶다면 아래 코드처럼 작성하면 된다.
import {useState} from "react";
const DiaryEditor = () => {
const [author,setAuthor] = useState("");
return (
<div className='DiaryEditor'>
<h2>오늘의 일기</h2>
<div>
<input value={author}
onChange={(e)=>{
console.log(e);
console.log(e.target.value);
setAuthor(e.target.value);
}}
/>
</div>
</div>
);
};
export default DiaryEditor;
onChange는 Event 중 하나이다. 이벤트는 사용자가 어떤 행동을 했을 때 호출된다. onChange는 input 값에 상태가 변경됐을때(입력 됐을때) 발생한다. onChange 이벤트에 등록되는 콜백 함수에서는 e를 매개변수로 받는다. 콜백 함수 내에서 콘솔로 e를 찍어봤을 때 아래처럼 객체가 나온다. 이 객체 안의 프로퍼티 중 target.value 값을 이용해 현재 값을 받아올 수 있다.
이제 일기 내용을 작성할 textarea 부분을 구현해보자.
import {useState} from "react";
const DiaryEditor = () => {
const [author,setAuthor] = useState("");
const [content,setContent] = useState("");
return (
<div className='DiaryEditor'>
<h2>오늘의 일기</h2>
<div>
<input value={author}
onChange={(e)=>{
setAuthor(e.target.value);
}}
/>
</div>
<div>
<textarea valeu={content}
onChange={(e)=>{
setContent(e.target.value);
}}
/>
</div>
</div>
);
};
export default DiaryEditor;
textarea도 input와 동일한 방식으로 처리할 수 있다. 그래서 input과 똑같이 State 처리를 해주면 된다. input의 State와 textarea의 State와 동일하게 동작하며 코드도 똑같다. 이렇게 비슷하게 동작하는 State는 하나로 합칠 수도 있다.
import {useState} from "react";
const DiaryEditor = () => {
const [state, setState] = useState({
author:"",
content:"",
}
);
// const [author,setAuthor] = useState("");
// const [content,setContent] = useState("");
return (
<div className='DiaryEditor'>
<h2>오늘의 일기</h2>
<div>
<input value={state.author}
onChange={(e)=>{
setState({
author:e.target.value,
content:state.content,
});
}}
/>
</div>
<div>
<textarea valeu={state.content}
onChange={(e)=>{
setState({
author:state.author,
conten:e.target.value,
});
}}
/>
</div>
</div>
);
};
export default DiaryEditor;
input의 경우 author의 값은 변화되어야하고, content의 값은 원래 상태 값을 그대로 유지해야 한다. 그래서 state.content를 적어주면 된다. 그런데 state에 두 개의 상태 말고 10개의 상태가 존재하면 어떨까? 값이 변경되는 부분 말고는 다 현재의 상태를 유지시키기 위해 state.속성 이런 식으로 적었을 것이다. 이럴 땐 스프레드 연산자를 사용하면 불필요한 부분을 짧게 줄일 수 있다.
setState({
...state,
author:e.target.value,
});
스프레드 연산자는 객체 안에 있는 속성들을 모두 펼쳐주는 연산자로 setState함수 안에서 사용하면 '속성 : state.속성' 상태로 모든 속성을 펼쳐줄 것이다. 그래서 변경될 속성만 따로 선언 후 코드를 작성하면 된다.
이번에는 중복되는 onChange 이벤트를 하나로 합쳐보자.
import {useState} from "react";
const DiaryEditor = () => {
const [state, setState] = useState({
author:"",
content:"",
}
);
const handleChangeState = (e) => {
console.log(e.target.name);
console.log(e.target.value);
setState({
...state,
[e.target.name]: e.target.value,
});
}
return (
<div className='DiaryEditor'>
<h2>오늘의 일기</h2>
<div>
<input name="author" value={state.author}
onChange={handleChangeState}
/>
</div>
<div>
<textarea name="content" valeu={state.content}
onChange={handleChangeState}
/>
</div>
</div>
);
};
export default DiaryEditor;
마지막으로 나의 감정을 숫자로 표현할 수 있도록 셀렉트 박스와 저장할 수 있는 버튼을 만들자.
import {useState} from "react";
const DiaryEditor = () => {
const [state, setState] = useState({
author:"",
content:"",
emotion:1,
}
);
// const [author,setAuthor] = useState("");
// const [content,setContent] = useState("");
const handleChangeState = (e) => {
console.log(e.target.name);
console.log(e.target.value);
setState({
...state,
[e.target.name]: e.target.value,
});
};
const handleSubmit =(e) => {
console.log(state);
alert("저장 성공");
}
return (
<div className='DiaryEditor'>
<h2>오늘의 일기</h2>
<div>
<input name="author" value={state.author}
onChange={handleChangeState}
/>
</div>
<div>
<textarea name="content" valeu={state.content}
onChange={handleChangeState}
/>
</div>
<div>
<span>오늘의 감정 점수 : </span>
<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;
그리고 CSS를 적용해준다.
App.css
.DiaryEditor{
border: 1px solid gray;
text-align: center;
padding : 20px;
}
.DiaryEditor input, textarea {
margin-bottom: 20px;
width : 500px;
padding: 10px;
}
.DiaryEditor textarea {
height : 150px;
}
.DiaryEditor select {
width: 300px;
padding: 10px;
margin-bottom : 20px;
}
.DiaryEditor button {
width: 500px;
padding : 10px;
cursor: pointer;
}