개발을 시작하면서 초반 자바스크립트 잠깐 하고, 몇 달간 리액트만 쭉 사용하다가 타입스크립트를 사용해보고자 타입스크립트 기반의 사이드 프로젝트를 하게 되었다. 프로젝트 전에 어느정도 기초 공부를 했어야 했는데, 순식간에 시작이 되어버려 무지한 상태로 코드를 치려다보니 그냥 복붙만 계속 하고 있었고, 대충 이런느낌인가? 에러만 없음 됐지 뭐~ GPT야 도와줘! 하다가 벌써 레이아웃 완성이 되어버렸다. 그래서 기능구현을 하기 전에 얼렁 타입스크립트를 어느정도는 잡고 가야한다. 🚀
타입스크립트는 언어의 기본 사용법과 문법은 자바스크립트와 동일하지만, 타입을 추가적으로 선언한다는 차이가 있다.
타입스크립트를 사용하면 동적인 자바스크립트의 변수를 정적으로 바꿔주기 때문에 타입스크립트 컴파일 과정에서 에러를 미리 잡아줄 수 있고, 에러에 대한 메세지가 구체적으로 자세하게 나오기 때문에 최근 프론트엔드 개발자들을 중심으로 선호되고 있는 추세라고 한다.
💡 현재 프로젝트에서 레이아웃 구현까지 완료됐다고 했는데, 아직까지는 리액트와 크게 다를게 없이 단지 state
, props
, function
, event
등에 타입을 선언해준다는 점이 기존의 React와의 가장 큰 차이점임을 알 수 있었다.
const [data, setData] = useState<boolean>(false);
아래의 코드 예제와 같이 초기값을 설정해두는 경우에는 따로 타입을 선언하지 않아도 자동으로 타입을 추정해 준다.
type Information = { name: string; description: string };
const [info, setInformation] = useState<Information | null>(null);
참고로 배열인 경우에는 해당 배열이 어떤 타입으로 이루어진 배열인지 추론할 수 있도록 Generics
을 명시하는 것이 좋다. 그리고 만약 다음과 같이 객체의 타입을 선언하는 경우에 타입이 반복 사용되는 경우라면 interface
나 type
을 이용하여 따로 생성해주는 것이 좋다.
// interface
interface loc {
title: string,
y: number,
x: number
}
// type
type loc = {
title: string,
y: number,
x: number
}
const Store:React.FC =()=>{
const [curLoc, setCurLoc] = React.useState<loc>();
return(<></>)
}
스타일 컴포넌트를 사용하는 이유는 컴포넌트 관리와 스타일링을 동시에 사용할 수 있기 때문이다. 특히 스타일 컴포넌트에서 props
를 스타일링할 때 편하게 사용할 수 있기에 더욱 많이 사용되는데 props
를 받을때도 타입을 지정해야 했다. 이 부분에서 어려웠던 점은 하나의 props
를 받을 때, 다수의 props
를 받을 때와 방식이 다르다는 점이다.
아래의 코드와 같이 받는 props의 타입 지정
const Container = styled.div< { age : number } >`
color: ${(props) => (props.age > 20 ? 'red' : 'gray')};
`;
다수의 props를 받을 시 Interface를 꼭 사용 !
// 상속컴포넌트의 타입 상속받기
interface Container {
isActive: boolean;
age: number;
프롭스명: 타입지정;
}
// 상속받은 컴포넌트에 타입 추가하기
const Container = styled(상속받을 컴포넌트명)<Container>`
color: ${(props) => (props.age > 20 ? 'red' : 'gray')};
background-color: ${(props) => (props.isActive ? 'red' : 'gray')};
`;
일단 기본적으로 부모 컴포넌트에서 자식 컴포넌트로 props
를 전달할때, 자식 컴포넌트에서 props의 타입을 지정해줘야 한다. 처음에 아래와 코드와 같이 작성했었는데, 이렇게 작성하면 안된다. props
는 자식컴포넌트에서 타입을 지정해줘야 하기 때문이다.
//잘못된 작성법
export default function MainComments(props: { comment: string }) {
return (
<li>
Seokho__lee {commentItem}
<FiHeart></FiHeart>
</li>
);
}
받아오는 props를 자식 컴포넌트에서 직접 타입을 지정
export default function MainComments({ commentItem }: { comment: string }) {
return (
<li>
Seokho__lee {commentItem}
<FiHeart></FiHeart>
</li>
);
}
1개의 props는 직접 타입을 지정해주면 되지만, 여러개의 props
를 받는다면 다른 형태로 작성해야 한다. 물론 1개의 props
를 받을때처럼 작성해도되긴 하지만 가독성 측면에서 아주 좋지 않다.
결론적으로 다수의 props
를 받을때는 Interface
를 사용하는 것! (type도 사용 가능)
//type을 사용했을 때의 예시
export type UserType = {
name: string
age: number
position: string
}
export default function User ({name, age, position}: UserType) {
return (
<div>
<p>이름: {name}</p>
<p>나이: {age}</p>
<p>포지션: {position}</p>
</div>
)
}
사실 1개 혹은 다수의 props를 받는 것은 매우 간단한 편이고 형태만 알면 쉽게 작성할 수 있다. 하지만 함수를 props
로 받을 때 약간 헷갈릴 수 있다.
일단 자식컴포넌트에서 props
의 타입을 지정할때 Interface
를 자주 사용하게 되는데, Interface
의 이름 앞에 'I'
를 통상적으로 사용한다. I
의 뜻은 Interface
라는 뜻이다.
또한, 함수를 받을때의 형태가 어색한데, '파라미터'의 유무에 따라 따라서 형태가 달라진다.
import { CommentsType } from '../../../Type/Interface';
interface IProps {
commentList: CommentsType;
removeComments: () => void; // 파라미터와 함수의 return이 없을 경우
removeComments: (id: number) => void; // 파라미터가 있고 함수의 return이 없을 경우
}
export default function MainComments({ commentList, removeComments }: IProps) {
return ()
};
React 함수에서도 타입스크립트 사용법은 비슷하다. 즉 들어오는 인자의 타입을 선언해주고, 추가적으로 인자 뒤에 함수의 반환 타입까지 선언해줄 수 있다.
const FunctionName = (id: string) :void => {
/*생략*/
}
React에서 타입스크립트를 적용하면서 가장 까다로운 부분이 바로 이벤트다. React의 요소에 어떤 타입을 선언할지 헷갈릴 수 있기 때문이다.
나는 Event를 작성할때마다 리서칭을 통해서 어떤 타입을 지정해줘야할지 알아보면서 해결했다. 물론 마우스 오버하면 타입을 알려주지만, 나와 같이처음 타입스크립트를 공부하는 사람들은 열심히 리서칭해서 여러 참고 문서와 글을 보면서 지정하는게 좋다고 생각한다.
React.ChangeEvent 로 이벤트가 사용된다.
const updateComment = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setComment(e.target.value);
},
[setComment]
);
React.MouseEventHandler 로 이벤트가 사용된다.
const updateComment = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
setComment(e.target.value);
},
[setComment]
);
React.KeyboardEvent 로 이벤트가 사용된다.
const keyEnter = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
addFeedComment();
}
},
[addFeedComment]
);
🏷 출처