728x90

라이브러리 없이 state로 폼 만들기
주요 기능
- 폼 입력 상태 관리 (useState)
- 유효성 검사 + 오류 메시지
- 아이돌 선택 (클릭 기반)
- 아이돌 검색 (실시간)
- API GET / POST
- 등록 후 리스트 페이지 이동
- 간단한 애니메이션 처리
1️⃣ 아이돌 리스트 가져오기 (GET)
아이돌 목록은 API로 받아온다. (그냥 한 번에 다 가져와서 처리하는 방식)
const getIdolsData = async () => {
setLoading(true);
const result = await getIdols({ pageSize: 10000 });
const idolsList = result.list;
setIdolsData(idolsList);
};
useEffect(() => {
getIdolsData();
}, []);
2️⃣ 폼 상태 관리
const [datas, setDatas] = useState({
idolId: '',
title: '',
subtitle: '',
targetDonation: 0,
deadline: '',
});
const handleInputChange = (e) => {
const { name, value } = e.target;
if (name === 'targetDonation') {
const val = e.target.value;
setOutput(val);
setMin(e.target.min || 0);
setMax(e.target.max || 1000000);
}
setDatas((prevDatas) => ({
...prevDatas,
[name]: value,
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(datas);
};
- 모든 input은 name 기반으로 상태 관리
- 숫자 input은 따로 처리
3️⃣ 아이돌 선택 (클릭 기반)
클릭하면 idolId 저장됨
<IdolAvatar
src={idol.profilePicture}
size="medium"
onClick={() => {
setDatas((prev) => ({
...prev,
idolId: idol.id,
}));
}}
/>
✔️ 선택된 아이돌 체크 표시
선택된 애만 UI 다르게 처리한다.
{datas.idolId === idol.id ? (
<CheckedIdolAvatar src={idol.profilePicture} />
) : (
<IdolAvatar
src={idol.profilePicture}
onClick={() => {
setDatas((prev) => ({
...prev,
idolId: idol.id,
}));
}}
/>
)}
4️⃣ 아이돌 검색 (실시간)
const [keyword, setKeyword] = useState('');
const handleKeywordSearch = (e) => {
setKeyword(e.target.value);
};
useEffect(() => {
getIdolsData();
}, [keyword]);
👉 입력할 때마다 API 다시 호출
<input
placeholder="아이돌 검색"
onChange={handleKeywordSearch}
/>
5️⃣ POST 요청 (폼 제출)
const postDonationsApi = async (requestData) => {
await axiosInstance.post('/donations', requestData);
};
const handleSubmit = async (e) => {
e.preventDefault();
try {
await postDonationsApi(datas);
alert('조공 등록 완료!');
navigate('/list');
} catch (error) {
console.error(error);
}
};
6️⃣ 유효성 검사 (1차 방식 → 문제 발생)
처음엔 이렇게 했었다.
const isTitleValid = !!datas.title;
{isTitleValid || <p>제목을 입력해주세요</p>}
❌ 문제 → 처음 들어오자마자 에러 메시지 다 뜸
7️⃣ 유효성 검사 개선
👉 valid 상태를 따로 관리
const [isTitleValid, setIsTitleValid] = useState(true);
- 처음: true → 에러 안 보임
- 제출 시: false로 바꿔서 에러 표시
const handleSubmit = async (e) => {
e.preventDefault();
if (
datas.idolId &&
datas.title &&
datas.subtitle &&
datas.targetDonation &&
datas.deadline
) {
await postDonationsApi(datas);
navigate('/list');
} else {
if (!datas.idolId) setIsIdolIdValid(false);
if (!datas.title) setIsTitleValid(false);
if (!datas.subtitle) setIsSubtitleValid(false);
if (!datas.deadline) setIsDeadlineValid(false);
if (!datas.targetDonation) setIsTargetDonationValid(false);
}
};
입력 바뀌면 에러 다시 숨김
useEffect(() => {
setIsIdolIdValid(true);
setIsTitleValid(true);
setIsSubtitleValid(true);
setIsDeadlineValid(true);
setIsTargetDonationValid(true);
}, [datas]);
8️⃣ 에러 UI + 애니메이션
{!isDeadlineValid && (
<p className="text-red-600">마감일을 입력해주세요</p>
)}
className={`input ${!isDeadlineValid ? 'animate-vibration border-red-600' : ''}`}
Tailwind 커스텀 애니메이션 (에러 시 살짝 흔들림)
animation - Transitions & Animation
Utilities for animating elements with CSS animations.
tailwindcss.com
[CSS] 떨리는 효과 구현하기
별도의 라이브러리 없이 간단한 코드로 진동처럼 흔들리는 애니메이션을 구현할 수 있습니다. 코드 See the Pen Vibration Animation Box by hyukson (@hyukson) on CodePen. 코드 풀이 떨림 효과를 줄 박스 요소를
gurtn.tistory.com
keyframes: {
vibration: {
from: { transform: 'rotate(0.5deg)' },
to: { transform: 'rotate(-0.5deg)' },
},
},
animation: {
vibration: 'vibration .1s ease-in-out 3'
}
결과

9️⃣ CORS 이슈
웹 애플리케이션에서 다른 도메인으로 리소스를 요청할 때 발생할 수 있는 보안 정책
응답 헤더에 CORS 관련 설정 추가했다.
import axios from 'axios';
const teamName = '6-15';
const axiosConfig = {
baseURL: `https://fandom-k-api.vercel.app/${teamName}`,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers':
'Origin, X-Requested-With, Content-Type, Accept',
},
};
const axiosInstance = axios.create(axiosConfig);
export default axiosInstance;
- 모든 origin으로부터의 요청을 허용
- 허용되는 HTTP 메서드 설정
- 허용되는 헤더를 설정
⚠️서드파티 이슈
**Third-party cookie will be blocked. Learn more in the Issues tab.**
제3자 쿠키: 사용자가 방문한 웹사이트가 아닌 다른 웹사이트에서 설치하는 쿠키

이걸 해주면 된다는데 난 이미 되어있음 → 서드파티 쿠키 허용 사이트에 우리 사이트를 넣음

'✨FRONTEND > 📍React' 카테고리의 다른 글
| react-hook-form + yup = 회원가입/로그인(feat.쿠키) (0) | 2024.08.02 |
|---|---|
| input 공통 컴포넌트로 만들기 (feat. react-hook-form + yup) (0) | 2024.08.01 |
| 서버 상태 💞 클라이언트 상태 (0) | 2024.07.20 |
| React Query 왜 씀? (1) | 2024.07.20 |
| 리액트 왜 씀? (0) | 2024.05.27 |