본문 바로가기

리액트

✅ [20편] 리액트 타입스크립트 적용 완벽 가이드: 기본 문법부터 실무 패턴, 컴포넌트 타입 정의까지

✅ 리액트 타입스크립트 적용 완벽 가이드: 기본 문법부터 실무 패턴, 컴포넌트 타입 정의까지


프런트엔드 개발에서 타입스크립트(TypeScript)는 사실상 표준 기술로 자리 잡았습니다.
리액트 역시 타입스크립트와 결합하면 생산성과 안정성이 크게 향상되기 때문에 많은 개발팀이 필수적으로 도입하고 있습니다.
타입스크립트를 적용한 리액트 프로젝트는 다음과 같은 엄청난 이점을 제공합니다.
타입 안전성 향상
런타임 오류 감소
IDE 자동 완성 + 코드 인텔리전스 강화
컴포넌트 props 구조 명확화
협업 및 유지보수 개선
이번 글에서는 리액트에 타입스크립트를 적용하는 방법부터, props 타입 정의, 이벤트 타입, API 타입 관리, 실무 적용 전략까지 단계별로 정리해 드립니다.


🟦 1. 리액트 프로젝트에 타입스크립트 적용하기


리액트는 타입스크립트와 매우 자연스럽게 통합됩니다.
가장 일반적인 적용 방식은 CRA(Create React App) 또는 Vite로 프로젝트를 생성하는 것입니다.
🔷 1) CRA + TypeScript 설정
npx create-react-app my-app --template typescript
이 명령을 실행하면 다음과 같은 기본 구조가 자동 생성됩니다.
src/
├ App.tsx
├ index.tsx
├ react-app-env.d.ts
이 구조는 타입스크립트를 사용하는 데 필요한 환경 설정이 모두 갖춰져 있어 바로 개발을 시작할 수 있습니다.
🔷 2) Vite + TypeScript 설정
최근에는 Vite가 CRA를 거의 대체할 정도로 빠르게 성장하고 있습니다.
npm create vite@latest my-app -- --template react-ts
Vite는 번들링 속도와 개발 경험이 훨씬 좋아 대다수의 실무 프로젝트에서 선호됩니다.


🟦 2. .tsx 파일 구조와 타입 선언 기본 원리


타입스크립트에서는 JSX 문법을 사용하기 위해 컴포넌트 파일 확장자가 .tsx여야 합니다.
✔ 기본 컴포넌트 예제
function App() {
return <h1>Hello TypeScript</h1>;
}
타입을 명시하지 않아도 기본적으로 타입 추론이 이루어지기 때문에 개발 초기 진입 장벽이 매우 낮습니다.


🟦 3. Props 타입 정의하기 (가장 중요한 부분)


리액트에서 타입스크립트를 사용할 때 가장 많이 접하고 가장 중요한 부분은 Props에 대한 타입 정의입니다.
🔷 3-1. 인터페이스를 사용한 Props 정의
interface ButtonProps {
label: string;
onClick?: () => void;
}
function Button({ label, onClick }: ButtonProps) {
return <button onClick={onClick}>{label}</button>;
}
✔ 설명
label: string (필수)
onClick: 함수 타입이지만 optional(선택)
이렇게 타입을 정의하면 IDE가 타입 안내를 제공하고,
잘못된 타입을 넘기면 컴파일 단계에서 오류를 바로 확인할 수 있습니다.
🔷 3-2. 타입(type)으로 정의하기
type UserCardProps = {
name: string;
age?: number;
};
interface와 type은 거의 동일하게 사용할 수 있습니다.
실무에서는 props 구조가 복잡하지 않다면 type도 많이 사용합니다.


🟦 4. children 타입 정의


children은 리액트 컴포넌트에서 매우 자주 사용되는 props입니다.
✔ 기본 children 타입
interface Props {
children: React.ReactNode;
}
예시
const Layout = ({ children }: Props) => {
return <main>{children}</main>;
};
ReactNode는 텍스트, 숫자, 컴포넌트 등 모든 렌더링 가능한 요소를 의미합니다.


🟦 5. 이벤트 타입 정의


리액트에서 이벤트 타입을 잘 정의하면 IDE 자동완성이 매우 좋아지며, 잘못된 이벤트 핸들러 사용을 방지할 수 있습니다.
🔷 5-1. InputChange 이벤트
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
🔷 5-2. Submit 이벤트
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
🔷 5-3. 클릭 이벤트
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log("clicked");
};
이러한 정교한 이벤트 타입은 타입스크립트 환경의 큰 장점이며, 실수 방지에 아주 효과적입니다.


🟦 6. useState에 타입 선언하기


타입스크립트는 useState에서도 명확한 타입 선언을 지원합니다.
✔ 단순 타입
const [count, setCount] = useState<number>(0);
✔ 객체 타입
interface User {
name: string;
age: number;
}
const [user, setUser] = useState<User | null>(null);
여기서 User | null처럼 유니언 타입을 설정해주는 것이 실무에서 매우 중요합니다.


🟦 7. API 응답 타입 관리하기


실무에서는 API 데이터 타입을 잘 관리하는 것이 유지보수성과 오류 방지 측면에서 필수적입니다.
🔷 7-1. 타입 선언
interface Product {
id: number;
title: string;
price: number;
}
🔷 7-2. API 호출에 적용
const fetchProducts = async (): Promise<Product[]> => {
const res = await fetch("/api/products");
return res.json();
};
이렇게 타입을 명시해두면, API 응답 데이터 구조를 명확히 이해할 수 있고 오타나 실수를 방지할 수 있습니다.


🟦 8. 커스텀 훅에서 타입 정의하기


커스텀 훅에서도 타입을 넣으면 훨씬 안정적인 구조가 됩니다.
예시: useFetch 커스텀 훅
export function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
useEffect(() => {
const load = async () => {
const res = await fetch(url);
const json = await res.json();
setData(json);
};
load();
}, [url]);
return data;
}
사용 시:
const products = useFetch<Product[]>("/api/products");
이렇게 제네릭을 활용하면 훅의 재사용성이 엄청나게 높아집니다.


🟦 9. 컴포넌트 타입 정의 실무 패턴


실무에서 자주 사용하는 패턴 몇 가지를 살펴보겠습니다.
🔷 9-1. React.FC 사용하는 방식
const Header: React.FC<Props> = ({ title }) => {
return <h1>{title}</h1>;
};
장점
children 기본 적용
가독성 높음
단점
일부 타입 추론이 과도함
최근에는 권장되지 않는 경향 있음

🔷 9-2. React.FC 대신 직접 타입 지정 (요즘 가장 많이 쓰는 방식)
type Props = {
title: string;
};
const Header = ({ title }: Props) => {
return <h1>{title}</h1>;
};
이 방식이 현재 가장 깔끔하고 명확하다는 이유로 널리 사용됩니다.


🟦 10. 타입스크립트 적용 시 주의해야 할 점


타입스크립트를 도입할 때 주의하면 좋은 부분들도 있습니다.
✔ 1) 너무 복잡한 타입을 만들지 말 것
필요 이상의 복잡한 타입은 오히려 유지보수를 어렵게 만듭니다.
✔ 2) any 남용 금지
any는 타입스크립트의 장점을 무력화함.
✔ 3) 유니언 타입 적극 활용
API 응답처럼 nullable 상태가 많을 때 매우 유용.
✔ 4) 컴포넌트 props 타입과 API 타입을 분리
하나의 타입에 여러 역할을 부여하면 관리가 어려워짐.
✔ 5) 에러 메시지는 친절하게
타입스크립트 오류는 문법이 생소해보이지만, 대부분 원인을 쉽게 파악할 수 있음.
🟦 결론: 리액트 + 타입스크립트는 “현대 프런트엔드 개발의 기본 조합”
리액트에 타입스크립트를 적용하면 개발 안정성이 극적으로 향상되고, 규모가 커질수록 그 가치는 더 커집니다.
특히 props 타입 정의, 이벤트 타입, API 타입, 커스텀 훅 제네릭 등의 개념은 실무에서 아주 자주 사용되므로 반드시 익혀야 하는 필수 기술입니다.
초기에는 조금 어렵게 느껴질 수 있지만, 한번 익숙해지면
✔ 오류 감소
✔ 자동완성 강화
✔ 유지보수성 향상
✔ 협업 구조 향상
✔ 코드 품질 개선
이라는 큰 장점을 누릴 수 있습니다.