폼 제출 및 데이터 처리
Next.js에서 폼 제출을 처리하고 데이터 유효성을 검사하는 방법은 애플리케이션의 사용자 경험과 보안에 중요한 역할을 합니다.
이 절에서는 다양한 폼 처리 방법과 데이터 유효성 검사 기법에 대해 알아보겠습니다.
클라이언트 사이드 폼 처리
React의 state를 사용한 기본적인 폼 처리 방법입니다.
'use client'
import { useState } from 'react'
export default function ClientSideForm() {
const [formData, setFormData] = useState({ name: '', email: '' })
const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value })
}
const handleSubmit = (e) => {
e.preventDefault()
console.log('Form submitted:', formData)
// 여기서 API 호출 또는 다른 처리를 수행할 수 있습니다.
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
<button type="submit">Submit</button>
</form>
)
}
서버 액션을 이용한 폼 제출
Next.js의 서버 액션을 사용하면 폼 제출을 서버에서 직접 처리할 수 있습니다.
export async function submitForm(formData) {
const name = formData.get('name')
const email = formData.get('email')
// 서버 사이드 유효성 검사
if (!name || !email) {
return { error: 'Name and email are required' }
}
// 데이터베이스 저장 로직 등을 여기에 구현
return { success: true, message: 'Form submitted successfully' }
}
import { submitForm } from '../actions'
export default function ServerActionForm() {
return (
<form action={submitForm}>
<input type="text" name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<button type="submit">Submit</button>
</form>
)
}
실시간 유효성 검사 구현
사용자 경험을 개선하기 위해 실시간 유효성 검사를 구현할 수 있습니다.
'use client'
import { useState } from 'react'
export default function FormWithValidation() {
const [formData, setFormData] = useState({ name: '', email: '' })
const [errors, setErrors] = useState({})
const validateField = (name, value) => {
if (name === 'name' && value.length < 2) {
return 'Name must be at least 2 characters long'
}
if (name === 'email' && !/\S+@\S+\.\S+/.test(value)) {
return 'Invalid email address'
}
return ''
}
const handleChange = (e) => {
const { name, value } = e.target
setFormData({ ...formData, [name]: value })
setErrors({ ...errors, [name]: validateField(name, value) })
}
const handleSubmit = (e) => {
e.preventDefault()
const formErrors = Object.keys(formData).reduce((acc, key) => {
const error = validateField(key, formData[key])
if (error) acc[key] = error
return acc
}, {})
if (Object.keys(formErrors).length === 0) {
console.log('Form submitted:', formData)
// 폼 제출 로직
} else {
setErrors(formErrors)
}
}
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
{errors.name && <span className="error">{errors.name}</span>}
</div>
<div>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<button type="submit">Submit</button>
</form>
)
}
10장 인증 및 권한 관리와의 연결
8장의 폼 제출 및 데이터 처리는 10장의 인증 및 권한 관리와 밀접하게 연관됩니다.
예를 들어, 로그인 폼이나 사용자 등록 폼을 구현할 때 이 장에서 배운 기술을 적용할 수 있습니다.
또한, 폼 제출 시 사용자 인증 상태를 확인하고 권한에 따라 데이터 접근을 제어하는 등의 작업이 필요할 수 있습니다.
실습 : 복잡한 다단계 폼 구현
다음 요구사항을 만족하는 다단계 사용자 등록 폼을 구현해보세요.
- 개인 정보 입력 단계 (이름, 이메일)
- 주소 정보 입력 단계 (주소, 도시, 우편번호)
- 계정 설정 단계 (사용자명, 비밀번호)
- 각 단계마다 유효성 검사 수행
- 서버 액션을 사용하여 최종 제출 처리
구현 예시
'use client'
import { useState } from 'react'
import { submitRegistration } from './actions'
export default function MultiStepForm() {
const [step, setStep] = useState(1)
const [formData, setFormData] = useState({
name: '', email: '', address: '', city: '', zipCode: '', username: '', password: ''
})
const [errors, setErrors] = useState({})
const validateStep = (step) => {
const newErrors = {}
switch (step) {
case 1:
if (!formData.name) newErrors.name = 'Name is required'
if (!/\S+@\S+\.\S+/.test(formData.email)) newErrors.email = 'Invalid email'
break
case 2:
if (!formData.address) newErrors.address = 'Address is required'
if (!formData.city) newErrors.city = 'City is required'
if (!/^\d{5}$/.test(formData.zipCode)) newErrors.zipCode = 'Invalid zip code'
break
case 3:
if (formData.username.length < 4) newErrors.username = 'Username must be at least 4 characters'
if (formData.password.length < 8) newErrors.password = 'Password must be at least 8 characters'
break
}
return newErrors
}
const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value })
}
const handleNextStep = () => {
const stepErrors = validateStep(step)
if (Object.keys(stepErrors).length === 0) {
setStep(step + 1)
} else {
setErrors(stepErrors)
}
}
const handleSubmit = async (e) => {
e.preventDefault()
const stepErrors = validateStep(3)
if (Object.keys(stepErrors).length === 0) {
const result = await submitRegistration(formData)
if (result.success) {
alert('Registration successful!')
} else {
setErrors({ submit: result.error })
}
} else {
setErrors(stepErrors)
}
}
return (
<form onSubmit={handleSubmit}>
{step === 1 && (
<div>
<input name="name" value={formData.name} onChange={handleChange} placeholder="Name" />
{errors.name && <span>{errors.name}</span>}
<input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
{errors.email && <span>{errors.email}</span>}
</div>
)}
{step === 2 && (
<div>
<input name="address" value={formData.address} onChange={handleChange} placeholder="Address" />
{errors.address && <span>{errors.address}</span>}
<input name="city" value={formData.city} onChange={handleChange} placeholder="City" />
{errors.city && <span>{errors.city}</span>}
<input name="zipCode" value={formData.zipCode} onChange={handleChange} placeholder="Zip Code" />
{errors.zipCode && <span>{errors.zipCode}</span>}
</div>
)}
{step === 3 && (
<div>
<input name="username" value={formData.username} onChange={handleChange} placeholder="Username" />
{errors.username && <span>{errors.username}</span>}
<input name="password" type="password" value={formData.password} onChange={handleChange} placeholder="Password" />
{errors.password && <span>{errors.password}</span>}
</div>
)}
{step < 3 ? (
<button type="button" onClick={handleNextStep}>Next</button>
) : (
<button type="submit">Submit</button>
)}
{errors.submit && <div>{errors.submit}</div>}
</form>
)
}
이 실습을 통해 복잡한 다단계 폼을 구현하고, 각 단계별로 유효성 검사를 수행하며, 최종적으로 서버 액션을 통해 데이터를 제출하는 과정을 경험할 수 있습니다.
이는 실제 애플리케이션에서 자주 사용되는 패턴으로, Next.js에서의 고급 폼 처리 기술을 익히는 데 도움이 됩니다.