728x90

React 태그 입력 기능 구현하기⌨️
구현 목표
- 프로젝트를 등록할 때, 기술 스택을 태그 형태로 관리하는 기능을 추가하고 싶음!!(like 티스토리 태그 기능, 벨로그 태그 기능)
- 엔터 시 태그 등록태그 삭제
- 태그 개수 제한
1. 상태(state) 정의
const [stacks, setStacks] = useState<string[]>([])
const [stackTag, setStackTag] = useState('')
- 최종적으로 반환될 stacks
- stackTag는 현재 사용자가 입력하고 있는 개별 태그 값
2. Enter 키 입력 시 태그 등록 (onKeyDown)
const handleTagInput = (e: ChangeEvent<HTMLInputElement>) => {
setStackTag(e.target.value)
}
const inputKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && stackTag) {
if (!stacks.includes(stackTag)) {
setStacks([...stacks, stackTag])
}
setStackTag('')
e.preventDefault()
}
}
사용자가 Enter 키를 눌렀을 때 실행
stackTag가 비어있지 않고, 이미 존재하지 않는 태그일 경우에만 추가하기!
추가 후에는 stackTag를 비워서 입력창을 초기화하고 e.preventDefault()로 Enter의 기본 동작(폼 제출)을 막아준다!
3. 태그 삭제 기능
const removeTag = (removeTagIndex: number) => {
const newStacks = stacks.filter((_, index) => index !== removeTagIndex)
setStacks(newStacks)
}
각 태그 옆에 삭제 아이콘(X 버튼)을 만들어 삭제 함수를 넣어줬다.
filter()를 사용해 클릭한 태그의 index를 제외한 나머지 태그만 남긴다!
<div className="flex flex-col gap-2">
<label className="text-xl font-semibold">⚒️ 기술 스택</label>
<input
type="text"
value={stackTag}
onChange={handleTagInput}
onKeyDown={inputKeyDown}
placeholder="태그를 입력하세요 (엔터를 누르면 태그가 적용돼요, 최대 8개)"
className="text-custom-white focus:border-custom-white border-custom-gray-200 rounded-lg border-2 bg-transparent px-4 py-3 outline-none"
/>
<div className="flex flex-wrap gap-2">
{stacks.map((stack, index) => (
<div
key={index}
className="bg-primary text-custom-white flex items-center gap-1 rounded-full px-4 py-2"
>
{stack}
<Image
src={remove}
onClick={() => removeTag(index)}
className="h-5 w-5 cursor-pointer"
alt="Remove tag"
/>
</div>
))}
</div>
{stacks.length > 8 && (
<p className="text-custom-red">
기술 스택은 최대 8개까지 입력할 수 있습니다
</p>
)}
{errors.techStack && (
<p className="text-custom-red">{errors.techStack.message}</p>
)}
</div>

구현 완!!!
🚨 zod, react-hook-form required 오류

이제 폼을 제출할 때 stacks 배열을 react-hook-form에 전달하려고 했는데, 이상한 오류가 발생했다... 바로 required 오류... 에러메세지에서 스키마에 명시한 오류가 아닌 required 만 떴다.
알고보니 react-hook-form은 기본적으로 input과 textarea에서 value를 string으로 관리하는데, stacks는 zod에서 array(z.string())로 정의되어 있었다. 이때 react-hook-form은 input에서 string 값을 기대하는데, stacks는 배열이기 때문에 타입이 맞지 않아서 required 오류가 발생한 거였뜸..!
import { z } from 'zod'
export type ProjectInput = z.infer<typeof projectSchema>
export const projectSchema = z.object({
title: z
.string()
.min(1, '제목은 필수입니다')
.max(50, '제목은 최대 50자 이하이어야 합니다'),
description: z.string().min(1, '본문은 필수입니다'),
techStack: z.array(z.string()).min(1, '기술 스택을 1개 이상 입력해주세요'),
deadline: z.string().min(1, '마감일을 선택해주세요'),
})
해결방법)) setValue로 react-hook-form에 수동으로 값 넣기
stacks 배열을 react-hook-form의 techStack 필드에 수동으로 넣어주자.
폼 제출 직전에 setValue를 사용해서 stacks 배열을 react-hook-form에 전달하면 오류가 해결된다!!
// stacks 배열이 바뀔 때마다 react-hook-form에 주입
useEffect(() => {
setValue('techStack', stacks)
}, [stacks, setValue])

해결 후 오류 메세지가 잘 뜬돠 🥳🥳🥳
'✨FRONTEND > 📍React' 카테고리의 다른 글
| 컴파운드 컴포넌트 + Headless UI 톺아보기 (1) | 2025.06.01 |
|---|---|
| React/Next.js 마크다운 적용하기 (0) | 2025.03.30 |
| Expo 알림(Notification) 구현하기 (2) | 2025.02.25 |
| Expo 앱 배포하기(웹 링크 + Expo QR 코드) (2) | 2025.02.15 |
| react native에 kakao map 연동하기(feat. 현재 위치 띄우기 + 키워드 검색) (2) | 2025.02.10 |