⚛️
EPISODE 06
Props 타입 · useState<T> · 이벤트 타입
React + TypeScript
React 컴포넌트에 TypeScript 적용하기 — Props 인터페이스, useState<T>, 이벤트 타입(ChangeEvent/FormEvent/MouseEvent), API 응답 타입 지정까지.
ReactTypeScriptuseStateevents
소요 시간
⏱ 45분
난이도
📊 중급
선수 조건
🎯 ts-05
결과물
타입이 적용된 컴포넌트와 폼 핸들러
이 강의에서 배우는 것
- 1create-next-app 또는 Vite로 React+TS 프로젝트를 만든다
- 2interface Props 로 컴포넌트 props 타입을 정의한다
- 3useState<T> 로 상태 타입을 명시한다
- 4ChangeEvent / FormEvent / MouseEvent 의 제네릭 인자를 안다
- 5fetch 응답을 타입으로 보호한다
1. 프로젝트 생성
bash
# Next.js
npx create-next-app@latest my-app --typescript
# React (Vite)
npm create vite@latest my-app -- --template react-ts
cd my-app && npm install && npm run dev2. 컴포넌트 Props 타입
tsx
interface ButtonProps {
label: string;
onClick: () => void;
color?: 'primary' | 'secondary' | 'danger';
disabled?: boolean;
}
// 일반 함수 (권장)
function Button({ label, onClick, color = 'primary', disabled }: ButtonProps) {
return (
<button onClick={onClick} disabled={disabled} className={color}>
{label}
</button>
);
}
// React.FC 방식
const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
return <button onClick={onClick}>{label}</button>;
};3. useState<T>
tsx
import { useState } from 'react';
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>('');
const [isVisible, setIsVisible] = useState<boolean>(false);
interface User { name: string; email: string; }
const [user, setUser] = useState<User | null>(null);
interface Todo { id: number; text: string; done: boolean; }
const [todos, setTodos] = useState<Todo[]>([]);4. 이벤트 타입
tsx
// input 변경
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
// textarea / select 도 동일 패턴
const handleTextArea = (e: React.ChangeEvent<HTMLTextAreaElement>) => {};
const handleSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {};
// 폼 제출
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
// 버튼 클릭
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {};5. API 응답 타입
tsx
interface Post {
id: number; title: string; body: string; userId: number;
}
function PostList() {
const [posts, setPosts] = useState<Post[]>([]);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then((res) => res.json())
.then((data: Post[]) => {
setPosts(data);
setLoading(false);
});
}, []);
if (loading) return <p>로딩 중...</p>;
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}