728x90
728x90
폼 데이터 처리하기 (React, React Router)
들어가며
- 리액트(React)에서 폼(@<form>@) 데이터를 처리하는 방법을 정리해본다.
폼(Form, @<form>@)
개념
- HTML에서 사용자 입력 데이터를 수집하고 서버로 전송하기 위한 요소
- 로그인, 회원가입, 검색 등 사용자 입력이 필요한 기능을 구현할 때 사용된다.
<form action="/submit" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<button type="submit">Submit</button>
</form>
특징
- @<form>@ 요소에서 @method@ 속성을 지정하지 않을 경우, 기본적으로 @GET@ 요청이 수행된다.
- 이때, @<form>@ 요소 안의 @<input>@요소의 값을 URL의 쿼리 문자열에 포함하여 서버로 전송한다.
- 속성의 값을 @POST@로 지정할 경우, 데이터를 본문에 포함하여 서버로 전송한다.
- @<form>@ 요소의 @action@ 속성의 값에 폼을 제출할 때 데이터를 보낼 서버의 URL을 넣는다.
- 생략할 경우, 현재 페이지의 URL로 전송된다.
- @<form>@의 내부 요소로 @<input>@, @<button>@, @<label>@을 넣을 수 있다.
- @<input>@ 요소의 @type@ 속성에 따라 다양한 입력 형태를 설정할 수 있다. (@text@, @password@, @email@, @checkbox@, @radio@, @file@ 등)
- 또한 @<input>@ 요소에 @name@ 속성은 필수로 넣어줘야 하는데, 이 속성은 서버로 전송될 때 각 필드의 이름이 된다.
- @<button>@ 요소의 @type@을 @'submit'@으로 지정할 경우(@<button type="submit"></button>@), 버튼 클릭 시 폼 제출 효과가 발생된다.
- @<label>@ 요소는 폼 필드에 대한 설명을 제공하고, @for@ 속성(리액트에서는 @htmlFor@ 속성)을 사용하여 특정 @<input>@과 연결시킬 수 있다.
기본 동작
- 사용자가 @Submit@ 버튼을 클릭하면 폼 데이터가 서버로 전송된다.
- @action@ 속성에 지정된 URL로 폼 데이터가 전송되며, @method@에 따라 전송 방식이 결정된다.
- 서버는 해당 데이터를 처리하고, 처리 결과를 브라우저로 반환한다.
이벤트 처리하기
- @<form>@은 기본적으로 제출 시 페이지를 새로고침한다.
- 하지만 아래와 같이 자바스크립트를 이용하여 이러한 기본 동작을 막을 수 있다.
const handleSubmit = (event) => {
event.preventDefault(); // 기본 동작 중단시키기
console.log("Form submitted!");
};
<form onSubmit={handleSubmit}>
<input type="text" name="name" />
<button type="submit">Submit</button>
</form>
폼 처리하기
(1) 리액트에서 폼 처리하기
- 리액트에서는 @<form>@ 요소를 사용할 때, 주로 제어 컴포넌트 또는 비제어 컴포넌트 방식으로 폼 데이터를 처리한다.
import { Form } from 'react-router-dom';
<Form action="/submit" method="POST">
<label htmlFor="name">Name:</label>
<input type="text" id="name" name="name" required>
<label htmlFor="email">Email:</label>
<input type="email" id="email" name="email" required>
<button type="submit">Submit</button>
</Form>
① 제어 컴포넌트(Controlled Component)
- 리액트 상태(State)가 폼 입력값을 완전히 관리한다.
- 입력 필드의 값이 항상 컴포넌트의 상태에 의해 제어되고, 상태가 업데이트될 때마다 입력 필드의 값도 업데이트 된다.
- 입력값이 컴포넌트의 상태(@useState@)로 관리되며, 입력이 발생할 때마다 @onChange@ 이벤트를 통해 상태가 업데이트된다.
- 입력 필드의 값(@value@)은 상태에 의해 제어되므로, 상태가 변경되면 자동으로 화면에 반영된다.
- 컴포넌트에서 폼 데이터를 완전히 제어할 수 있어 입력 값의 검증 및 변경 로직을 쉽게 처리할 수 있다.
- 모든 입력 필드에 대해 상태를 정의해야 하므로 폼 필드가 많을 경우 코드가 길어질 수 있다.
- 입력 필드의 값이 항상 컴포넌트의 상태에 의해 제어되고, 상태가 업데이트될 때마다 입력 필드의 값도 업데이트 된다.
예제 코드
import { useState } from 'react';
function ControlledForm() {
// 상태로 입력값 관리하기
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (event) => {
event.preventDefault();
console.log('Name:', name);
console.log('Email:', email);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
value={name} // 업데이트한 값 표시
onChange={(e) => setName(e.target.value)} // 상태 업데이트
/>
</label>
<br />
<label>
Email:
<input
type="email"
value={email} // 업데이트한 값 표시
onChange={(e) => setEmail(e.target.value)} // 상태 업데이트
/>
</label>
<br />
<button type="submit">Submit</button>
</form>
);
}
export default ControlledForm;
② 비제어 컴포넌트(Uncontrolled Component)
- DOM 자체가 입력값을 관리한다.
- 리액트의 상태와 상관없이 폼 입력의 현재 값을 직접 참조할 수 있다.
- 이 방식에서는 @ref@를 사용하여 DOM 요소에 직접 접근한다.
- 코드가 간결하며, 상태를 정의하지 않아도 되므로 입력 필드가 많아도 복잡하지 않다.
- 리액트에서 DOM을 직접 제어하는 방식이 아니므로 입력 값에 대한 즉각적인 제어나 검증이 어렵다.
- 따라서 간단한 폼 처리에 적합하다.
예제 코드
import { useRef } from 'react';
function UncontrolledForm() {
// ref로 입력값 관리하기
const nameInputRef = useRef(null);
const emailInputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
// ref를 통해 DOM에서 값 가져오기
console.log('Name:', nameInputRef.current.value);
console.log('Email:', emailInputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" ref={nameInputRef} /> // 직접 참조
</label>
<br />
<label>
Email:
<input type="email" ref={emailInputRef} /> // 직접 참조
</label>
<br />
<button type="submit">Submit</button>
</form>
);
}
export default UncontrolledForm;
(2) 리액트 라우터(React Router)를 이용하여 폼 처리하기
- 리액트 라우터(React Router)에서는 @<form>@ 요소 대신 리액트 라우터의 @Form@ 컴포넌트를 사용하여 폼 제출을 처리한다.
- @Form@ 컴포넌트는 전통적인 HTML 폼과 유사하게 동작하지만, 리액트 라우터가 폼 제출을 자동으로 처리하고, 폼 데이터를 액션(Action) 함수로 전달한다.
방법
- 예제 코드를 이용하여 이해해보자.
파일 구조
src/
├── App.jsx
├── pages/
│ └── Newsletter.jsx
└── main.jsx
/src/App.jsx
- 라우터에 @action@ 속성을 넣어주고, 폼 제출 시 실행 될 @action@을 지정해준다.
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Newsletter, { action as newsletterAction } from './pages/Newsletter';
const router = createBrowserRouter([
{
path: "/newsletter",
element: <Newsletter />,
action: newsletterAction, // 폼 제출 시 실행할 action 지정
},
]);
function App() {
return (
<RouterProvider router={router} />
);
}
export default App;
/src/pages/Newsletter.jsx
- 리액트 라우터의 @Form@ 컴포넌트는 전통적인 HTML 폼과 동일하게 동작하지만, 폼을 제출하면 설정된 @action@ 함수가 실행된다.
- @action@ 함수는 폼이 제출될 때 호출된다.
- @request.formData()@를 통해 폼 데이터를 가져올 수 있다. 이 데이터를 @Object.fromEntries()@를 이용하여 객체(Object)로 변환하여 처리할 수 있다.
import { Form, redirect } from 'react-router-dom';
export const action = async ({ request }) => {
const formData = await request.formData(); // 폼 데이터 가져오기
const data = Object.fromEntries(formData); // 데이터를 객체로 변환
console.log(data); // 예: { name: "John", email: "john@example.com" }
// 폼 데이터를 처리하고, 필요한 경우 서버에 요청을 보낸다.
const response = await fetch("https://api.example.com", { method: 'POST', body: formData });
return redirect('/');
};
function Newsletter() {
return (
<div>
<h1>Subscribe to our Newsletter</h1>
<Form method="post"> {/* React Router의 Form 컴포넌트 */}
<label>
Name:
<input type="text" name="name" required />
</label>
<br />
<label>
Email:
<input type="email" name="email" required />
</label>
<br />
<button type="submit">Subscribe</button>
</Form>
</div>
);
}
export default Newsletter;
HTML5의 required 속성을 사용하여 폼 입력값이 비어 있을 경우 폼 제출을 막을 수 있다.
참고
Object.fromEntries()
- @Object.fromEntries()@는 ES2019에서 도입된 자바스크립트 메서드로, 키-값 쌍의 배열 또는 이터러블(Iterable) 객체를 객체(Object)로 변환해준다.
- @Map@, @FormData@, @URLSearchParams@ 같은 이터러블 객체를 다룰 때 유용하다.
- 중복된 키를 허용하지 않으며, 마지막 키의 값만 유지된다.
- 예) @[['a', 1], ['a', 2]]@와 같이 같은 키가 여러 번 나타나면, 마지막 값인 @2@가 유지된다.
- 비어 있는 배열이나 이터러블을 사용하면 빈 객체가 반환된다.
사용 예제
// 1️⃣ 배열을 객체로 변환하기
const entries = [['name', 'John'], ['age', 30], ['city', 'Zurich']];
const obj = Object.fromEntries(entries);
console.log(obj);
// { name: 'John', age: 30, city: 'Zurich' }
// 2️⃣ Map 객체를 객체로 변환하기
const map = new Map([['name', 'Jane'], ['age', 25]]);
const obj = Object.fromEntries(map);
console.log(obj);
// { name: 'Jane', age: 25 }
// 3️⃣ FormData 객체를 사용하여 폼 데이터를 객체로 변환하기
const formData = new FormData();
formData.append('username', 'JohnDoe');
formData.append('email', 'johndoe@example.com');
const obj = Object.fromEntries(formData);
console.log(obj);
// { username: 'JohnDoe', email: 'johndoe@example.com' }
// 4️⃣ URLSearchParams를 객체로 변환하기
const params = new URLSearchParams('name=John&age=30');
const obj = Object.fromEntries(params);
console.log(obj);
// { name: 'John', age: '30' }
// ☑️ 중복된 키를 허용하지 않는다.
const entries = [['a', 1], ['a', 2]];
const obj = Object.fromEntries(entries);
console.log(obj);
// { a: 2 }
// ☑️ 비어 있는 배열이나 이터러블을 사용하면 긴 객체가 반환된다.
const obj = Object.fromEntries([]);
console.log(obj);
// {}
참고 사이트
728x90
728x90
'Programming > React' 카테고리의 다른 글
[React.js] index.js로 컴포넌트(Component), 페이지(Page) 관리하기 (0) | 2024.10.01 |
---|---|
[React.js] Thunk API (Redux Toolkit) (1) | 2024.09.28 |
[React.js] 리액트 라우터(React Router) (0) | 2024.09.26 |
[React.js] 라우팅 관련 기능들 정리 (React Router) : useNavigate, useNavigation, redirect, useLocation, useParams, useHistory, Navigate (1) | 2024.09.26 |
[React.js] 무한 스크롤(Infinite Scroll), 스켈레톤(Skeleton) 효과 적용하기 (with React Query) (0) | 2024.09.23 |
[React.js] .env 파일 만들고 사용하기 (환경 변수 관리) (0) | 2024.09.23 |
[React.js] React Query Devtools (0) | 2024.09.22 |
[React.js] 코드 분할(Code Splitting) : useTransition 훅, Suspense 컴포넌트, lazy 함수 (0) | 2024.09.20 |