0. 설치간 오류
JS로 만들어진 패키지, TS type이 정의된 패키지를 모두 설치해야 TS에서 인식할 수 있다.
어찌보면.. 당연. 패키지의 타입을 정의해서 가져다 줘야 해석을 하던가 말던가 하지..
보통 이런 것들은 @types/~패키지명 으로 정의되어 있음.
@types/~* 라고 정의되어 있는 경우, 모두 type 정의한 옵션 패키지 구나 정도로 생각하면 된다.
만약 @types/~패키지명을 설치 안하면 다음과 같이 display된다.
Module not found: Error: Can't resolve 'styled-components'
패키지명은 예시
tip. 만약 본인이 패키지를 설치했는데 type이 없는 패키지일 경우, @types/설치한 패키지명 으로 install을 해보면 꽤 유명한 것들은 다 설치가 될 것이다.
간단하지만, 그동안 package설치가 어떻게 이루어지는지 명확하게 정리가 안되었는데,
뭔가 좀 더 정확하게 이해된 느낌이다.
1. TypeScript는 왜 사용하나?
타입정의로 인한 에러 방지
타입을 정의함으로써 좀 더 명료한 코드 작성 및 동작 가능 -> 막는다.
number로 받기로하면, number만 입력 & 실행이 가능하다.
JavaScript는 그렇게 안된다. undefined를 return 해버린다. 그래서 문제가 될 수 있다.
TypeScript 컴파일러의 강력한 기능
정적타입검사 및 오류 확인 기능이 굉장히 뛰어나다.
그리고, 이 것들을 Browser로 실행하기 전에 알 수 있다는 점이 굉장히 매력적이다.
2-1. 사용하는 예
import styled from "styled-components";
interface CircleProps {
bgColor: string;
}
const Container = styled.div<CircleProps>` // CircleProps 타입을 사용
width: 200px;
height: 200px;
background-color: ${props => props.bgColor};
border-radius: 50%;
`;
const Circle = ({ bgColor }: CircleProps) => { // CircleProps 내부의 bgColor객체의 Property(속성)을 Props로 사용
return <Container bgColor={bgColor}>박윤종</Container>;
};
// const Circle = (props : CircleProps) => { // 이런 식으로도 사용 가능
// return <Container bgColor={props.bgColor}>박윤종</Container>;
// };
export default Circle;
위의 코드는 Styled-Components를 사용하면서, Type을 정의해준 것이다.
코드 보면, 대략 이해를 할 수 있기 때문에, 나머지는 직접 타이핑 하면서 숙달해나가면 될 것 같다.
타입을 정의에 따른 차이
type CircleProps {}
interface CircleProps {}
위와 같은 방식으로 할 수 있는데,결론부터 얘기하면 interface 만 사용하면 된다.
여러가지 측면에서 interface가 더 유리하다. 관련글
가장 좋은 점을 설명하자면, Interface는 미리 정의한 Type Object의 shape과 비교해서 TypeScript가 미리 알려준다.
Prop Types는 브라우저 실행 후에 알게 된다. (안그럴 떄도 있지만 주로 그러하다)
2-2 Optional Props
import styled from "styled-components";
interface CircleProps {
bgColor: string;
borderColor?: string; // optional = " ?: ", not required
text?: string;
}
const Container = styled.div<CircleProps>`
width: 200px;
height: 200px;
background-color: ${props => props.bgColor};
border-radius: 50%;
border: 4px solid ${props => props.borderColor};
`;
const Circle = (props: CircleProps) => {
return (
<Container
bgColor={props.bgColor}
borderColor={props.borderColor ?? "white"}>
{props.text ?? "default text"}
</Container>
);
};
// 아래와 같은 방식도 가능하다. 위 방식보다 아래 쪽 처럼 하는 것이 나중에 테스트 시 용이해질 가능성이 크다. Mock Data를 넣을 수 있기 때문이다.
// const Circle = ({ bgColor, borderColor, text = "default text" }: CircleProps) => {
// return (
// <Container bgColor={bgColor} borderColor={borderColor ?? "white"}>
// {text ?? "default text"}
// </Container>
// );
// };
export default Circle;
optional은 ?: 로 표기해서 처리한다.
import Circle from "./Circle";
function App() {
return (
<div style={{ background: "purple" }}>
<Circle bgColor="teal" borderColor="black" />
<Circle bgColor="tomato" text="예제텍스트" />
</div>
);
}
export default App;
text props가 있는 경우와 없는 경우는 "default text"가 적용된 것을 알 수 있다.
2-3. State & Type
타입스크립트는 똑똑하다. setState 시, 초기값을 입력하면 초기값에 맞춰서 타입을 자동으로 설정해준다.
없을 경우에만 undefined로 정해질 뿐이다.
useState<number>(0) 이런식으로 <number> 부분을 추가해서 타입을 적어줄 수도 있고, 초기값이 있는 경우는 굳이 안적어줘도 무방하다.
위 코드와 같이 두가지 타입을 갖는 경우는 굉장히 드물고, 보통 1가지 타입만들 넣어서 작업하게 된다.
2-4 Forms
import { useState } from "react";
const App = () => {
const [value, setValue] = useState("");
const onChange = (event: React.FormEvent<HTMLInputElement>) => {
const {
currentTarget: { value }, // value 라는 변수를 만들면서, event.currentTarget 안에 동일한 변수의 값을 넣어준다.
} = event;
// const value = event.currentTarget.value; 과 같은 기능을 하긴 함.
// 객체 분해 할당을 하는 이유는 여러개의 변수를 생성할 때 유용하기 때문.
// 관련자료 구조분해할당 https://mzl.la/3FeCpXp
setValue(value);
};
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log("hello,", value);
};
return (
<div>
<form onSubmit={onSubmit}>
<input
value={value}
onChange={onChange}
type="text"
placeholder="username"
/>
<button>Log in</button>
</form>
</div>
);
};
export default App;
3. styled-components 의 확장
styled-components안에 types가 있는데, 타입스크립트에서 추가적으로 파일을 생성하며 선언병합(Declaration Merging)을 활용해서 확장(extend)해나가는 것이 가능하다. (참고 : 공식문서)
"devDependencies": {
"@types/styled-components": "^5.1.26" // in package.json
},
3-1 styled.d.ts 파일 만들기
// import original module declarations
import "styled-components";
// and extend them!
declare module "styled-components" {
export interface DefaultTheme { // DefaultTheme Spelling 잘 보자...한참 찾은 기억이 있다ㅠ
textColor: string;
bgColor: string;
btnColor: string;
}
}
먼저, src 폴더에 styled.d.ts 파일을 만들어준다. (참고 : 공식문서)
이렇게 함으로써, 타입을 재정의하고 사용할 수 있게 된다.
3-2 theme.ts 파일 만들기
import { DefaultTheme } from "styled-components";
export const lightTheme: DefaultTheme = {
bgColor: "white",
textColor: "black",
btnColor: "tomato",
};
export const darkTheme: DefaultTheme = {
bgColor: "black",
textColor: "white",
btnColor: "teal",
};
/src 에 theme.ts 파일을 만들고 위와 같이 작성한다.
3-3. 실제 사용하기
// Index.tsx
import React from "react";
import ReactDOM from "react-dom";
import { ThemeProvider } from "styled-components";
import App from "./App";
import { lightTheme } from "./theme";
ReactDOM.render(
<React.StrictMode>
<ThemeProvider theme={lightTheme}>
<App />
</ThemeProvider>
</React.StrictMode>,
document.getElementById("root")
);
위와 같이 App 컴포넌트를 ThemeProvider로 감싸고 theme를 전달해준다. (theme변경은 나중에 추가설명)
// App.tsx
import styled from "styled-components";
const Container = styled.div`
background-color: ${props => props.theme.bgColor};
`;
const H1 = styled.h1`
color: ${props => props.theme.textColor};
`;
const App = () => {
return (
<Container>
<H1>Protected</H1>
</Container>
);
};
export default App;
그러고나면, App 컴포넌트 내부에서 props를 통해서 즉각 사용이 가능해진다.
왜냐하면 3-1 과정을 통해서 (styled.d.ts 생성)
styled-components 원본 패키지의 extend(확장)이 이미 완료되었기 떄문이다.
이제 자유롭게 사용하면 된다ㅎㅎLet go
이벤트에 관련된 Type 좀 더 자세한 설명은 다른 글로 추가 설명하겠다.
간단히만 설명하면 이런 것을 말한다.
export default const App = () => {
const onClick = (event: React.FormEvent<HTMLButtonElement>) => {}
// 이 부분 처럼 각종 React Component에 관한 것도 Type 으로 정의해주고
// 어떤 element가 동작하는것인지 정의어야 한다.
// 무작정 any같은 타입을 사용하지 말자.
return (
<form>
<button onClick={onClick}>click me</button>
</form>
);
};
// Form 내부 이벤트가 아니라면 아래와 같이 정의되어진다.
export default const App = () => {
const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {}
return (
<button onClick={onClick}>click me</button>
);
};
React.MouseEvent<HTMLButtonElement>
React.MouseEvent 라는 것은 이벤트가 일어날 타입을 의미하고, <HTMLButtonElement>는 이벤트가 일어나는 대상 인자의 타입을 말한다.
이 부분만 제대로 이해하면 기본적으로 TypeScript에 대한 이해는 모두 마쳐진 것으로 봐야할 것 같다. 나머지는 TypeScript 문법을 자세히 보면서 실력을 늘려나가면 되겠다.
리액트 이벤트 타입에 관련해서 정의된 글은 다음과 같다.
SyntheticEvent https://ko.reactjs.org/docs/events.html
참고 :
노마드코더, React JS 마스터클래스 #3 TypeScript
타입스크립트 type과 interface의 공통점과 차이점
구조분해할당 https://mzl.la/3FeCpXp
styled-components : 타입스크립트로 사용하 (공식문서) https://styled-components.com/docs/api#typescript
TypeScript Declaration Merging (선언 병합) https://www.typescriptlang.org/docs/handbook/declaration-merging.html
SyntheticEvent (합성 이벤트) https://ko.reactjs.org/docs/events.html
'React & TypeScript > React' 카테고리의 다른 글
[React] React.memo로 만드는 성능 최적화 기법 (0) | 2023.01.26 |
---|---|
[React / Ant Design] Ant Design에서 Calendar Localization 하는 법 (0) | 2022.12.19 |
[React] 프론트엔드 폴더 구조 방법론 정리 [펌 + 사견] (0) | 2022.12.14 |
[React / Test] Material UI (mui) + React + Testing Library 적용 간 어려운 점 회고 (1) | 2022.06.22 |
React useEffect (included api 호출) 가 있는 컴포넌트 분리 방법 (0) | 2022.04.29 |
Build 이후 흰 화면만 나오는 이유 (로컬 index.html 안열리는 이유, react-router, 파일 프로토콜, http 프로토콜) (0) | 2021.09.16 |
[React] cypress를 활용한 React 테스트 (feat. 시나리오 테스트, E2E Test) (0) | 2021.08.25 |
댓글