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의 쿼리 문자열에 포함하여 서버로 전송한다.
    • GET 요청이 수행되는 모습. <form> 안의 요소가 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);
// {}

 

참고 사이트

 

<form> - HTML: Hypertext Markup Language | MDN

HTML <form> 요소는 정보를 제출하기 위한 대화형 컨트롤을 포함하는 문서 구획을 나타냅니다.

developer.mozilla.org

 

What are Controlled and Uncontrolled Components in React.js?

In React.js, managing form inputs and user interactions is a crucial part of building dynamic web applications. Two key concepts that developers need to understand are controlled and uncontrolled components. These concepts define how form data is ha...

www.freecodecamp.org

 

<input> – React

The library for web and native user interfaces

react.dev

 

Form | React Router

 

reactrouter.com

 

Object.fromEntries() - JavaScript | MDN

Object.fromEntries() 메서드는 키값 쌍의 목록을 객체로 바꿉니다.

developer.mozilla.org

728x90
728x90

폼 데이터 처리하기 (React, React Router)들어가며폼(Form, <form>)개념특징기본 동작이벤트 처리하기폼 처리하기(1) 리액트에서 폼 처리하기① 제어 컴포넌트(Controlled Component)② 비제어 컴포넌트(Uncontrolled Component)(2) 리액트 라우터(React Router)를 이용하여 폼 처리하기방법참고Object.fromEntries()참고 사이트