노마드 코더 첫번째 프로젝트 시작
/ -> All coins
/:id -> /btc -> Coin Detail
(nested)
/btc/information
/btc/chart
$npx create-react-app "이름" —template typescript
$npm i --save react react-dom typescript
$npm i --save-dev @types/react @types/react-dom @types/node
$npm i react-router-dom@5.3.0
(강의가 이전버전의 router dom 으로 제작되어 기존 버전을 사용)
$npm i react-query
react-router 를 6버전만 써봤기에 어떤점이 달라졌는지 알아보고 싶었고,
나중에 router 6버전으로 고쳐봐도 재밌을 것 같다.
tsconfig.json 파일을 생성하려면
tsc --init
CRA 태그들을 사용하려면 tsconfig.json 에서
"jsx":"react-jsx"
로 변경해주어야 함
가장먼저 index.tsx 에서
const root = ReactDOM.createRoot(document.getElementById("root"));
이 부분을
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
이렇게 바꿔주어야 함
const Coin = () => {
const { coinId } = useParams();
return <h1>Coin : {coinId}</h1>;
};
위의 구문은 js에서는 아무런 문제가 없다.
그러나 우리의 귀여운 ts 에서는
Property 'coinId' does not exist on type '{}'
이라는 에러를 띄운다.
ts가 봤을 때 해당 객체에 들어간 object 내부의 값이 어떤 type 인지 모르기 때문이다.
interface RouteParams {
coinId: string;
}
const Coin = () => {
const { coinId } = useParams<RouteParams>();
return <h1>Coin : {coinId}</h1>;
};
이렇게 useParams 로 들어올 object의 type을 명시해주어야만 사용이 가능하다.
전역으로 사용할 global style을 적용해보자.
// ./styles/GlobalStyle.tsx
import { createGlobalStyle } from "styled-components";
export const GlobalStyle = createGlobalStyle`
...
`
전역적으로 공통적인 속성,
흔히 사용하는 border:0, padding:0, box-sizing:border-box 같은 스타일을
index.css 가 아닌 styled-components 로 적용할 수 있는 키워드.
첫째, 전역에서 사용할 theme 파일을 만들어준다.
// ./style/theme.tsx
import { DefaultTheme } from "styled-components";
export const theme: DefaultTheme = {
bgColor: "#2f3640",
textColor: "#f5f6fa",
accentColor: "#44bd32",
};
둘째, 적용할 대상을 감싸준다. index.tsx 에서 찾으면 됨.
// ./index.tsx
import { ThemeProvider } from "styled-components";
import { theme } from "./styles/theme";
root.render(
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
)
가장 중요한 셋째, *.d.ts 파일로 전역타입 추론을 해준다.
// ./styles/styled.d.ts
import "styled-components";
declare module "styled-components" {
export interface DefaultTheme {
bgColor: string;
textColor: string;
accentColor: string;
}
}
→ === →
해당 키워드로 화살표를 나타내줄 수 있다 !
useEffect(() => {
(async () => {
const response = await fetch("https://api.coinpaprika.com/v1/coins");
const json = await response.json();
setCoins(json.slice(0, 50));
})();
}, []);
이거 정말 많이 고민한 문제였는데,
useEffect 내에서 ()(); 같은 즉시실행 함수를 사용해
변수나 함수를 정의해주지 않아도 바로 실행하도록 설계할 수 있다.
const Coins = () => {
const [coins, setCoins] = useState<CoinInterface[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
(async () => {
const response = await fetch("https://api.coinpaprika.com/v1/coins");
const json = await response.json();
setCoins(json.slice(0, 50));
setLoading(false);
})();
}, []);
return (
<Container>
<Header>
<Title>Coins</Title>
</Header>
{loading ? (
<Loading>Loading...</Loading>
) : (
<CoinsList>
{coins.map((coin) => (
<Coin key={coin.id}>
<Link to={`/${coin.id}`}>{coin.name} →</Link>
</Coin>
))}
</CoinsList>
)}
</Container>
);
};
페이지가 렌더링 될 때 API fetch 요청을 해서 가져온 데이터를 state에 저장하여 mapping 하는 로직
Loading 의 boolean 값이 true 일 때 로딩 페이지를 보여주고,
API fetch 요청이 끝나면 Loading state를 false로 바꾸어 불러온 데이터를 보여준다.
위 로직에는 아주 치명적인 단점이 있다.
페이지를 오갈 때 마다, 렌더링이 될 때마다 API 요청을 하여 데이터를 mapping 하는 로직을 재실행 한다는 것.
React Query 를 사용하면 그렇게 하지 않아도 데이터가 state에 남아있을 수 있나 ?
타입스크립트 이 친구 정말정말 까다로워서 뭐 하나 그냥 넘어가는게 없다.
tsc init 을 해주어야 tsconfig.json 이 생기는 것도 마음에 들고,
직관적으로 너 틀렸어! 하고 말해주는게 너무 마음에 든다.
오류때문에 헤메긴 했지만 조금 더 친해지면 잘 지낼 수 있을 것 같은 친구다. 🥰
그것과 별개로 오랜만에 강의 보면서 소소하게 코딩하니까
힐링하는 느낌이 강하게 든다.
실전프로젝트 주간이라서 언제까지 이렇게 마음놓고 공부할 수는 없겠지만.. 🥲