반응형

📘 『소플의 처음 만난 리액트』를 읽고 정리한 글입니다.
폼
- 폼은 사용자로부터 입력을 받기 위해 사용한다.
- HTML의 폼은 엘리먼트 내부에 각각의 상태가 존재한다.
- 반면 리액트의 폼은 컴포넌트 내부에서 상태를 통해 데이터를 관리한다.
<form>
<label>
이름 :
<input type="text" name="name"/>
</label>
<button type="submit">제출</button>
</form>
- 상기 코드는 HTML의 폼이다.
- 리액트에서도 작동은 하지만 자바스크립트 코드를 통해 사용자가 입력한 값에 접근하기는 불편한 구조이다.
- 이를 해소하기 위해선 제어 컴포넌트를 숙지해야 한다.
제어 컴포넌트
- 리액트의 통제를 받으며, 사용자 입력에 대한 접근 및 제어를 처리하는 입력 폼 엘리먼트이다.
- 위의 HTML 폼을 리액트의 제어 컴포넌트로 만드는 것이다.
function NameForm(props) {
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
}
const handleSubmit = (event) => {
alert('입력한 이름 : ' + value);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
이름 :
<input type="text" value={value} onChange={handleChange}/>
</label>
<button type="submit">제출</button>
</form>
);
}
- 상기 코드처럼 제어 컴포넌트를 사용하면 입력 값이 리액트 컴포넌트 상태를 통해 관리된다.
- 즉 입력값을 원하는 대로 조종할 수 있게 된다.
- 예를 들어 사용자가 입력한 모든 알파벳을 대문자로 변경시켜 관리하고 싶다면 다음 코드와 같이 작성하면 된다.
const handleChange = (event) => {
setValue(event.target.value.toUpperCase());
}
textarea
function RequestForm(props) {
const [value, setValue] = useState('요청사항을 입력하세요.');
const handleChange = (event) => {
setValue(event.target.value);
}
const handleSubmit = (event) => {
alert('입력한 요청사항 : ' + value);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
요청사항 :
<textarea value={value} onChange={handleChnage}/>
</label>
<button type="submit">제출</button>
</form>
)
}
- 상기 코드는 textarea에 입력할 때마다 state의 값이 바뀌고, 바뀔 때마다 textarea의 value의 값이 바뀐 채로 재렌더링된다.
- 여기에서는 value 선언 시 초깃값을 넣어주었기 때문에 처음 렌더링 될 때부터 textarea에 텍스트가 나타난다.
select
function FruitSelect(props) {
const [value, setValue] = useState('grape');
const handleChange = (event) => {
setValue(event.target.value);
}
const handleSubmit = (even) => {
alert('선택한 과일 : ');
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
과일을 선택하세요 :
<select value={value} onChange={handleChange}>
<option value="apple">사과</option>
<option value="banana">바나나</option>
<option value="grape">포도</option>
<option value="watermelon">수박</option>
</label>
</form>
);
}
- select 태그에서 특정 값을 선택하려면 value 속성을 사용하면 된다.
- 다중 선택이 가능하도록 하려면 multiple={true}을 select 태그에 속성으로 추가하면 된다.
input file
<input type="file"/>
- 입력값이 읽기 전용이므로 리액트에서는 비제어 컴포넌트가 된다.
여러 개의 입력 다루기
- 하나의 컴포넌트에서 여러 개의 입력을 다루려면 여러 개의 state를 선언하여 각각의 입력에 대해 사용하면 된다.
- 예제 코드는 생략한다.
input null value
- 제어 컴포넌트에 value 속성을 정해진 값으로 넣으면 코드를 수정하지 않는 한 입력값을 바꿀 수 없다.
- 만약 value 값은 넣되 자유롭게 입력할 수 있게 만들고 싶다면 값에 undefined 또는 null을 넣어주면 된다.
실습
// ...
const [email, setEmail] = useState('');
const [name, setName] = useState('');
const [memberList, setMemberList] = useState([]);
const [page, setPage] = useState(1);
const pageSize = 10;
const startIndex = (page - 1) * pageSize;
const currentPageData = memberList.slice(startIndex, startIndex + pageSize);
// ...
return (
<Box>
<Box display="flex" gap={2} sx={{ marginBottom:"15px" }}>
<TextField
label="이메일"
variant="outlined"
size="small"
sx={{ flex: 1 }}
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<TextField
label="이름"
variant="outlined"
size="small"
sx={{ flex: 1 }}
value={name}
onChange={(e) => setName(e.target.value)}
/>
</Box>
<Box>
<TableContainer component={Paper}>
<Table sx={{ tableLayout: "fixed", width: "100%" }}>
<TableHead>
<TableRow>
<TableCell sx={{ width: "20%" }}>이메일</TableCell>
<TableCell sx={{ width: "20%" }}>이름</TableCell>
<TableCell sx={{ width: "30%" }}>소개</TableCell>
<TableCell sx={{ width: "10%" }}>처리</TableCell>
</TableRow>
</TableHead>
<TableBody>
{currentPageData.map((member) => (
<TableRow key={member.id} sx={{ height: "30px" }}>
<TableCell sx={{ paddingBottom: "5px", paddingTop: "5px" }}>
{member.email}
</TableCell>
<TableCell sx={{ paddingBottom: "5px", paddingTop: "5px" }}>
{member.name}
</TableCell>
<TableCell sx={{ paddingBottom: "5px", paddingTop: "5px" }}>
{member.description}
</TableCell>
<TableCell sx={{ paddingBottom: "5px", paddingTop: "5px" }}>
{member.status === null ? (
<Button
variant="contained"
color="primary"
onClick={() => createInvitation(member)}
>
초대
</Button>
) : (
member.status === 'PENDING' ? (
<Button
variant="contained"
color="error"
onClick={() => deleteInvitation(member)}
>
취소
</Button>
) : (
member.status === 'ACCEPTED' ? '가입완료' : '거절'
)
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{memberList && (
<Pagination
count={Math.ceil(memberList.length / pageSize)}
page={page}
color="primary"
onChange={handlePageChange}
sx={{ height: "50px", display: "flex", justifyContent: "center" }}
/>
)}
</TableContainer>
</Box>
</Box>
);
// ...
- 책의 내용과는 상관 없는 작성자 본인의 토이 프로젝트 코드이다.

- 입력 폼에 문자를 입력할 때마다 상태를 갱신한다.
- 참고로 상태가 갱신될 때마다 DB를 조회하는 실시간 조회 방식으로 작성되어 있다.
GIF 파일로 만들어 봤는데 화질구지네..
이미지 출처
[React] Component 를 사용하는 기본적인 방법
컴포넌트는 React의 핵심 개념 중 하나이며, 이는 사용자 인터페이스(UI)를 구축하는 기반이다. 컴포넌트를 만들어보자. 우선 여기까지는 순수 JS의 선언 및 정의에 대한 구문이다. 리액트는 컴포
velog.io
소플 - soaple.io
소플
www.soaple.io
'개발 > 리액트' 카테고리의 다른 글
리액트 핵심만 훑어보자 #14 합성과 상속 (0) | 2025.04.06 |
---|---|
리액트 핵심만 훑어보자 #13 컴포넌트 간 상태 공유 (1) | 2025.03.11 |
리액트 핵심만 훑어보자 #11 리스트와 키 (1) | 2025.03.08 |
리액트 핵심만 훑어보자 #10 조건부 렌더링 (0) | 2025.03.07 |
리액트에서 API 호출 URL을 환경 변수로 설정하자 (0) | 2025.02.23 |