퍼블리셔가 TypeScript에서 처음 막히는 지점들
Article
퍼블리셔 출신 지인이 React를 배우다가 TypeScript에서 멈췄다고 연락이 왔다. 듣다 보니 내가 막혔던 지점과 정확히 같았다. 마크업 하던 사람이 TS를 만나면 막히는 곳은 대체로 정해져 있다. 그리고 대부분은 TS 전체를 몰라서가 아니라, 어디까지 알아야 하는지 몰라서 막힌다.
1. props 타입 — 여기서 절반이 멈춘다
HTML만 만지다 오면 "컴포넌트에 값을 넘긴다"는 개념 자체가 낯선데, 거기에 타입까지 붙으니 두 배로 어렵게 느껴진다. 근데 실체는 별거 없다. props 타입은 그냥 컴포넌트의 명세서다.
interface CardProps {
title: string;
date: string;
tags: string[];
}
export default function Card({ title, date, tags }: CardProps) {
return (
<article>
<h3>{title}</h3>
<time>{date}</time>
</article>
);
}
"이 컴포넌트를 쓰려면 title은 문자열, tags는 문자열 배열로 줘야 한다"고 적어둔 것뿐이다. 시안 가이드 문서 만들던 감각과 다르지 않다.
2. 옵셔널 — 물음표 하나가 주는 공포
에러 메시지에서 undefined가 튀어나오기 시작하면 당황한다. 원인은 대부분 옵셔널이다.
interface CardProps {
title: string;
thumbnail?: string; // 있을 수도, 없을 수도
}
?가 붙으면 "안 넘겨도 된다"는 뜻이고, 그래서 쓸 때는 없는 경우를 처리해야 한다.
{thumbnail && <img src={thumbnail} alt="" />}
이 패턴 하나면 옵셔널 관련 에러의 대부분이 사라진다. TS가 시비 거는 게 아니라, 빈 이미지가 렌더링되는 사고를 미리 막아주는 것이라고 생각을 바꾸면 편해진다.
3. 유니온 — 클래스 분기하던 그 감각
퍼블리셔라면 btn--primary, btn--ghost 같은 modifier 클래스를 수없이 만들어봤을 거다. 유니온 타입이 정확히 그거다.
interface ButtonProps {
variant: "primary" | "ghost" | "outline";
}
variant에는 저 세 문자열만 들어올 수 있다. 오타로 "primay"를 넘기면 브라우저에서 스타일이 안 먹는 걸 눈으로 찾는 대신, 에디터가 저장하기도 전에 빨간 줄로 알려준다. BEM 네이밍 규칙을 기계가 강제해주는 셈이다.
4. 제네릭 — 지금은 몰라도 된다
<T>가 보이는 순간 책을 덮는 사람이 많다. 나도 그랬다. 결론부터 말하면 제네릭은 처음에 직접 만들 일이 거의 없다. 라이브러리를 쓸 때 읽을 줄만 알면 된다.
const [posts, setPosts] = useState<Post[]>([]);
"이 상태는 Post 배열이다"라고 알려주는 것. 이 정도 읽기만 되면 실무는 굴러간다. 제네릭을 직접 설계하는 건 한참 뒤의 일이고, 그때가 되면 필요해서 자연스럽게 배우게 된다.
다 알 필요 없다
돌아보면 나는 TS 핸드북을 정독하려다 두 번 나가떨어졌고, 그냥 프로젝트를 만들면서 에러가 날 때마다 하나씩 찾아보는 방식으로 익혔다. 실무 컴포넌트 코드의 대부분은 위의 네 가지 안에서 돈다. keyof, 조건부 타입, 유틸리티 타입 조합 같은 건 나중 문제다.
전부 이해하고 시작하려는 태도가 가장 큰 장벽이다. props 타입과 옵셔널, 유니온까지만 잡고 일단 만들자. 나머지 80%는 필요할 때 온다.