들어가면서
React에서 useState를 사용할 때, 타입을 어떻게 줘야 가장 안정적이고 깔끔할까?
처음 TS를 쓰기 시작하면 헷갈리는 부분 중 하나인데, 이번 포스팅에서는 다양한 상황에 맞는 useState 타입 선언 패턴을 정리해보겠다.
✅ 1. 기본 타입 (number, string, boolean)
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>('홍길동');
const [isLoading, setIsLoading] = useState<boolean>(false);
- useState에 초깃값만 넣으면 자동 추론되기도 하지만, 명시하는 게 더 안전할 때도 있음 (초깃값이 null일 때 등)
✅ 2. 유니언 타입 (string 또는 null)
const [selected, setSelected] = useState<string | null>(null);
- 흔히 선택값이 없을 수도 있는 경우에 사용
- 예: 선택한 드롭다운 항목, 로그인한 사용자 정보 등
✅ 3. 객체 타입
interface User {
id: number;
name: string;
}
const [user, setUser] = useState<User>({ id: 1, name: '홍길동' });
- 객체는 되도록 interface나 type으로 명확히 정의
- 특히 값이 복잡해질수록 타입 분리해두는 게 유지보수에 유리
✅ 4. 배열 타입
const [items, setItems] = useState<string[]>([]);
const [users, setUsers] = useState<User[]>([]);
- 빈 배열은 TS가 never[]로 추론할 수 있어 타입을 명시적으로 지정해주는 게 좋음
✅ 5. 제네릭 객체 예시 (Form 상태 관리)
interface FormState {
username: string;
email: string;
age?: number;
}
const [form, setForm] = useState<FormState>({
username: '',
email: '',
});
- 실무에서 흔히 쓰이는 패턴
- age처럼 optional 필드를 잘 정의하면 편리함
✅ 6. 리터럴 타입
type Tab = 'home' | 'profile' | 'settings';
const [activeTab, setActiveTab] = useState<Tab>('home');
- setActiveTab('test') 같은 의도하지 않은 값 방지 가능
- UI 상태 관리에서 매우 유용
✅ 7. useState 초기값이 null인 경우
const [data, setData] = useState<User | null>(null);
- 서버에서 데이터를 비동기로 가져오는 구조일 때 자주 등장
- 타입 단언 (null as User | null)은 되도록 피하고, 명확히 타입 선언해주기
🛠️ 보너스: 복잡한 상태 관리를 위한 패턴
type Step = 'intro' | 'form' | 'result';
type Status = 'idle' | 'loading' | 'success' | 'error';
interface ProcessState {
step: Step;
status: Status;
}
const [process, setProcess] = useState<ProcessState>({
step: 'intro',
status: 'idle',
});
- UI 흐름을 enum-like 타입으로 모델링하면 디버깅과 협업이 쉬워짐
- 실무에선 enum을 직접 쓰는 경우도 많음 (단, 문자열 literal이 더 직관적인 경우도 있음)
📌 정리표
상황 | 타입 선언 예시 |
숫자 상태 | useState<number>(0) |
null 허용 | useState<string | null>(null) |
객체 상태 | useState<User>({...}) |
배열 상태 | useState<string[]>([]) |
리터럴 제한 | useState<'on' | 'off'>('on') |
비동기 fetch 대상 | useState<DataType | null>(null) |
💡 마무리하며
- 초기값이 확정적이면 TS가 자동 추론하므로 타입 생략해도 무방
- 하지만 null, 빈 배열, 비동기 상태 등은 직접 명시하는 게 안전
- useState<>()는 상태값보다 상태의 목적에 따라 설계하는 것이 중요
'TypeScript' 카테고리의 다른 글
🔗 API 통신 타입, 이렇게 정의하면 실무에서 편해진다 (React + TypeScript + Axios) (0) | 2025.06.17 |
---|---|
🧩 Props에 타입 주는 법 완전 정리 (React + TypeScript) (0) | 2025.06.16 |