2주차 과제
📌 노마드코더 react masterclass ch4 CRYPTO TRACKER 클론코딩 수강
설치 방법
터미널에 npm i react-router-dom react-query 입력
React-router-dom
은 어플리케이션에 URL을 할당해주는 역할을 함 (각기 다른 화면을 갖게 해준다)
우리가 ch2, ch3에서 install했던 styled-components, typescript를 그대로 사용한다~
1. src 폴더 내에 routes
폴더를 만들어주고
2. 그 폴더 내에 Coins.tsx
, Coin.tsx
파일을 만들어줌
3. src 폴더 내에 Router.tsx
파일을 만들어줌
Router.tsx
내에는 우리가 Router 기능을 사용할 때 항상 해주던 것처럼 해줄것임
<Router.tsx>
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Coin from "./routes/Coin";
import Coins from "./routes/Coins";
function Router(){
return <BrowserRouter>
<Routes>
<Route path="/:coinId" element={<Coin/>}/>
<Route path="/" element={<Coins/>}/>
</Routes>
</BrowserRouter>
}
export default Router;
Routes
로 감싸고 그 안에 Route
로 원하는 URL 경로를 지정해준다.
아 그리고
npm i --save-dev @types/react-router-dom
해주지 않으면 styled-components 때처럼
typescript가 react-router-dom
을 몰라서 오류가 뜬다고함..
그리고 강의는 react-router-dom
버전 5.3.0 으로 진행되는데 react-router-dom
버전 6.0.0부터 바뀐 점이 많다고,,,
그래서 강의처럼 이전 버전을 사용하려면 터미널에
npm uni react-router-dom
해주고 (우리가 원래 설치했던 react-router-dom
uninstall하는 명령어)
npm i react-router-dom@5.3.0
해주면 됨!
그래도 일단 우리가 프로젝트 할 때마다 예전 버전으로 할 수는 없기 때문에
나는 그냥 최신버전 사용했고 명령어만 최신버전으로 바꿔서 진행했다.
css를 손대보자
styled-components로 전체 도큐먼트에 css를 적용시키는 방법을 알아볼거임
import {createGlobalStyle} from 'styled-components';
해주자
createGlobalStyle
을 사용하면 해당 styled-components를 global하게 사용할 수 있다.
<App.tsx>
const GlobalStyle = createGlobalStyle`
body{
color: red;
}
`;
function App(){
return(
<>
<GlobalStyle/>
<Router/>
</>
);
이러면 화면 속 텍스트들이 red가 됨
추가로 위 코드와 같이 리액트에서는 쓸모없이 div를 남발하는 것이 아닌 Fragment
(일종의 유령 컴포넌트)를 사용할 수 있다!
Fragment
를 이용하면 부모없이 서로 붙어있는 많은 것들 리턴 가능~~
그럼 이 createGlobalStyle
을 사용해 기본으로 브라우저에 할당되어있는 css를 reset해보자~.~
우리가 사용할 수 있게 이미 만들어진 reset css가 있는데 이걸 복사해서 createGlobalStyle
에 넣어주면 되겠지?!
폰트 설정도 해주자
구글폰트에서 원하는 폰트 import 후 사용!
Google Fonts: https://fonts.google.com)
참고로 나는 Source Sans Pro를 사용했다ㅎ,
📌추가로 flatuicolors에서 다양한 색깔의 코드넘버(?)를 팔레트로 확인할 수 있으니 사용하면 좋을듯 🤭
flatuicolors: https://flatuicolors.com/palette/gb
App이 ThemeProvider에 의해 감싸져있기 때문에
theme.ts 속 값들을 App.tsx에서 가져와 사용할 수 있다.
이를 이용해서 추가로 global css 설정을 해주었답니다.!!!
styled-components를 이렇게 전역으로 설정해주면 나중에 우리가 모바일버전사이트를 만들거나 할 때 매우 간편하다고 함 굳굳
일단 코인배열에 대충 값을 때려박아서 화면 출력이 어떻게 되는지 보자
coinpaprika의 api를 사용한다!
젤 상단 3개를 복사해서 coins 배열에 저장해줌!!
그 후 map
으로 name이 출력되게 해주었당 물론 key
값은 id~
styled-components로 css 조정을 좀 더 해준 모습이다😎
근데 이건 관련 없긴한데 →
이 오른쪽화살표래..
나몰랐음ㅎ (오근데 벨로그 마크다운적용도 되네 ← → ㅎㅎ,,)
여기까지 해주고 각 이름을 누르면 해당 페이지로 넘어가게 해보자
Bitcoin을 누르면 Bitcoin 페이지로 넘어가게 해보자는 말
Link
를 사용한다. Link
사용은 react-router-dom에서 import해주면 가능함
<Coins.tsx>
import {Link} from 'react-router-dom';
...
function Coins(){
return(
...
{coins.map((coin) => (
<Coin key={coin.id}>
<Link to={`/${coin.id}`}>{coin.name} →</Link>
</Coin>
))}
...
)
};
이렇게 해주면 이름 클릭 시 각 Coin 페이지로 넘어간다.
즉, 우리가 Coin.tsx에 작성해준 형식인 Coin: {coinID}
가 화면에 출력된다.
(여기서 coinID는 현재 페이지의 params값이었지요)
왜냐면.. Router.tsx에서 <Route path="/:coinId" element={<Coin/>}/>
로 할당해주었기 때문에 Link
로 params
값이 주어져 Coin.tsx로 넘어가는 것임!
API에서 가져오는 데이터 역시 typescript에게 설명해줘야한다.
따라서 데이터를 설명해줄 interface를 만든다.
interface CoinInterface{
id: string,
name: string,
symbol: string,
rank: number,
is_new: boolean,
is_active: boolean,
type: string,
}
이런식으로 ^_^
그리고 우리가 일단 3개의 api를 때려박아놨던 coins 배열은 삭제해준다. 에러가 날 수 잇슴,,
이제 state안에 coins를 다시 만들어준다.
useState
를 import 하고
const [coins, setCoins] = useState([]);
해주자
이러면 typescript는 map 속 coin이 뭔지 몰라 에러가 뜸,,,,,,
그래서 이 coins state가 coin으로 된 array임을 알려야한다.
useState<CoinInterface>([]);
해주면 댐.
(물론 아직 빈배열로 설정되어 아무것도 render되지 않는다)
useEffect
를 사용할 것임
useEffect
를 사용하면 특정시기에만 코드를 실행할 수 있당
컴포넌트 시작 시에 실행될 지, 끝날 때 실행될 지, 변화가 일어날 때마다 실행될 지 설정할 수 있다는 뜻.
(우리는 컴포넌트 처음 시작될 때 한 번만 실행되도록 한다.)
useEffect(()=>{
(async()=>{
const response = await fetch('https://api.coinpaprika.com/v1/coins');
const json = await response.json();
console.log(json);
})();
},[]);
async
와 await
도 사용해준당
response에 api fetch
해주고 response로부터 json
을 받아온다.
이제 콘솔창을 확인해보면?
... 잘 찍히긴 하는데 코인이 44750개나 있다고ㅎ,,,
100개정도만 가져오자
setCoins(json.slice(0,100));
을 추가해주고 console.log(coins) 찍어보면
100개만 잘 가져와진다~
화면에는 이제 100개의 이름이 다 잘 나오는 것을 확인할 수 있음
state안에 코인이 모두 세팅되면 setLoading으로 loading값을 false로 바꿔준다.
그리고 이렇게 설정해주면
loading
이 true일 때는 loading...
문구가 나타나고
코인이 모두 세팅되어 loading
값이 false가 되면
map함수가 작동하여 우리가 설정한대로 출력된다.
async와 await에 대한 이해가 개인적으로 좀 부족한 것 같아서
걍 따로 좀 적어보는 async와 awaitㅇㅔ 관한 내용.,.
일단 async와 await는 자바스크립트의 비동기처리패턴 중 하나로 기존 비동기 처리방식인 callback함수와 promise의 단점을 보완하고 가독성 좋은 코드작성을 돕는다.!!
이게 무슨 말이냐면,,
일반적으로 자바스크립트의 비동기처리코드는 따로 콜백함수를 사용해야 코드의 실행순서보장이 가능함.
( *비동기처리
: 특정 로직이 실행을 끝낼 때까지 기다리는 것이 아니라 나머지 코드를 먼저 실행하는 것)
async와 await를 사용하면 쉽게 비동기처리가 가능하다.
별다른거 없이 그냥 함수 앞에 async 붙이고 함수 내부 로직 중 HTTP 통신을 하는 비동기처리코드 앞에 await를 붙이면 끝!
(이때 주의할 점.. 비동기처리method가 꼭 promise객체를 반환해야함)
Crypto Icon API : https://coinicons-api.vercel.app 사용
이 api url 뒤에
/api/icon/${원하는 코인의 symbol}
을 써주면 그 암호화폐 코인 아이콘을 가져올 수 있음
나는 https://coinicons-api.vercel.app/api/icon/${coin.symbol.toLowerCase()}
를 사용했고 styled-components로 css를 좀 더 손 본 결과
이렇게 만들어졌다.
이제 이 화면 말고 이름을 클릭하면 넘어가는 개별페이지를 조정해보자
이름을 클릭해서 각 코인페이지로 넘어가게 되면
우리는 다시 그 코인의 정보가 불러와질때까지 loading화면만 보게된다.
그런데 browser가 이미 데이터를 가지고 있는데 왜 또 로딩을 봐야하지?!
이 점을 해결하기 위해 우리가 보이지 않는 방식으로 데이터를 어떻게 보내는지 알아보자.
Link로 string 하나만 보내지말고 다른 데이터도 함께 보낼 수 있당ㅋ
<Link to={`/${coin.id}`} state={coin.name}>
그냥 이렇게 해주면 댐.
react router DOM이 보내주는 location object에 접근해보자~
그냥 간단하게 useLocation
import 후
const location = useLocation();
형태로 사용하면 된당
여기서부터 react-router-dom의 버전에 따른 차이가 심해서 머리가 매우 아팠는데,,,
일단 console.log(location) 해보면
이렇게 찍힌다.
useLocation을 사용하니 여러가지 찍히는 걸 알 수 있당
우리는 state에 찍힌 name을 따로 불러와줄거다!!
이를 위해 Location의 interface를 하나 만들어준다.
interface LocationState{
state:{
name: string;
}
};
그 후
...
const {state} = useLocation() as LocationState;
console.log(state.name);
...
해보면 콘솔창에 코인의 이름만 찍힘~.~
그러면 이제
이렇게 해줄 수 있음 우와 굳
이렇게하면 직접 api를 부르지않고 화면에 보여줄 수 있다
즉,, 같은 기능을 빠르게 보여줄 수 있음
왜냐면 이미 우리 브라우저가 Coins화면에서 코인의 name을 가지고 있으니 Coin화면에서 또 api를 다 받아올 때까지 기다릴 필요가 없는 것~!
근데 이러면 누군가 바로 Coin화면을 열었을 때 에러가 난다..
왜냐면 state가 생성되려면 Coins화면을 먼저 거쳐야하기 때문이다.
우리가 Coins화면에서 어떤 코인 이름을 클릭하는 순간 state가 만들어지기 때문!
즉, url을 통해 Coin화면페이지로 바로 접속한다면
우리 app상에서는 state가 만들어지지못해 name이 undefined로 정의되는 것,,,
이걸 해결하기위해
이렇게해준다,, state가 만들어지면 state.name을 보여주고
그렇지않으면 Loading...이란 글씨를 띄운다.
근데 나는 저게안된다,,,,,,,,,,,,,,,,,,,,,,아오
왤까 모르겟다,
어케해야댈까^^,,, ㅎㅏ~
ㄴ 음.. 이러고 20분 뒤에 그대로 다시 실행해보니 된다
개퐉친다 걍 오류났나봄;;;;; 쨋든 저렇게하면 된다^>^
자 이제 상세페이지에서 이름말고 다른 정보도 보여주고싶다..
두가지 URL을 사용한다.
useEffect를 다시 사용한다.
첫번째 URL에서 코인의 정보를 fetch해오자
useEffect(()=>{
(async()=>{
const response = await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`)
const json = await response.json()
})();
},[])
음 이렇게해준다.
근데 이 코드를 더 짧게 할 수 있다고함
useEffect(()=>{
(async()=>{
const infoData = await(
await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`)
).json();
})();
},[])
이렇게~!!!!!!!!!!!
이게 바로 캡슐화임. 이렇게 하면 시간이 절약된당
이제 콘솔창에 infoData를 찍어보면
헉 엄청많다 정보가
이번에는 두번째 url을 fetch해보자
fetch해온걸 콘솔에 찍어보면
여기서 우리 state안에 넣을 걸 고르자
중요한 정보는 모두 quotes 안에 USD에 들어있는 듯
어쨌든.
info와 priceInfo의 state를 만들어주고
setInfo에는 infoData를, setPriceInfo에는 priceData를 넣어준당
근데 아직은 typescript가 info와 priceInfo가 빈 object라고 알고있으므로 이것도 알려줘야함
아귀찮아
콘솔에 찍혀있는 infoData와 priceData에 마우스우클릭해서
저 Store object as global variable
클릭해주자!
그러면 콘솔에 찍힌 각각의 내용이 temp에 저장된다
이렇게~~~!!
그러면 언제든 temp로 다시 불러올 수 있음
그 후 interface를 두개 더 만들어준다.
눈치껏 infoData와 priceData에 관한거겠지
다시 콘솔로 돌아가서
콘솔에 Object.keys(temp1)하면
코인의 정보에 대해 fetch해왔던 그 infoData의 key가 촤르륵 나온다!
근데 이건 너무 많으니,,,
Object.keys(temp1).join() 해주면 key string값만 촤르륵 출력됨
그러면 이제 저걸 복사해서 Info관련 interface에 붙여넣어주고 거기에 type만 정해주면 되겠다..
참고
그 후 콘솔에서 다시 Object.values(temp1).map(v => typeof v)
해주면 이번엔 value값의 type이 무엇인지에 대해 촤르륵 출력된다!!!!!
우왕~~~
그래서 아까처럼 Object.values(temp1).map(v=>typeof v).join()
해주면 하나의 string으로 변한다.
이걸 가져와서 interface를 쉽게 완성할 수 있다!
priceData에 관한 interface도 같은 방식으로 만들어준다.
근데 여기서 또 귀찮은 포인트는 array는 그냥 object로 표시된다.
즉,, interface를 만들어서 또 따로 설명해주어야한다.
하........................
우리가 아까 유의미한 정보라고 판단했던 quotes 속 USD도 결국 array기 때문에 우리는 이 object를 typescript에게 interface로 설명해주어야한다. 하.
haha.. 이렇게 해주고 나면 우리는 priceInfo 속에 있는 많은 정보를 뽑아다가 쓸 수 있다ㅎㅎ
nested router는 route 안에 있는 또다른 route임
웹사이트에서 탭을 사용할 때 유용하당
화면에서 섹션이 여러개 나누어져있을 경우에도 굳이라고 함👍
예를 들어 유저가 URL을 통해 직접적으로 ~/chart
로 들어오면 chart 탭으로 넘어오게 하고 싶다.
이것은 그냥 state로도 할 수 있고 URL을 통해서도 할 수 있는데 nested routes를 이용하여 URL로 해보자!
(URL로 하면 사용자가 바로 접속할 수 있어 사용성이 더 높당)
nested routes를 구현하려면 부모 route의 path 마지막에 /*를 적어 명시적으로 이 route의 내부에서 nested route가 render 될 수 있음을 표시하고 자식 route를 부모 route의 element 내부에 작성하면 된다. 그럼 한 번 해보자~!
이런 식으로 감싸주기~.~
Routes가 상대경로도 지원하기 때문에 path를 저렇게 그냥 써도 동작한다고!
router.tsx에 가서
"/:coinId/*"
해주면 된당
아니면!!!!!
Outlet을 사용해주자
router.tsx에서 chart와 price의 컴포넌트를 import하자
그 후 이 자식 Route들이 어디에 render될 지 부모 element안에 Outlet으로 표시해주면 됨!
(Outlet import는 필요하다 ㅎ)
개인적으로는 Outlet(중첩라우트) 사용이 더 편리한 것 같다,,
이번에는 link를 통해 nested router를 통해 만든 중첩라우트로 넘어가는 것을 해보자~!
Price 탭으로 넘어가는 Link와 Chart탭으로 넘어가는 Link가 필요하겠지?
이건 걍 엄청 간단한데
...
<Link to={`/${coinId}/chart`}>Chart</Link>
<Link to={`/${coinId}/price`}>Price</Link>
...
그냥 이렇게만 추가해주면 된다 너무간단.
useMatch
는 현재 위치를 기준으로 지정된 경로에 대한 일치 데이터를 반환하는 hook임!
즉, useMatch
는 우리가 특정 URL에 있는지 여부를 알려준다.
따라서 useMatch값이 null이 아니라면 그 특정 URL에 있는 것이라고 생각하면 됨~
설치 방법
: 터미널에 npm install react-query
우리가 theme에 접근할 수 있는 themeProvider를 만들어서 index.tsx에서 감싸줬던 것처럼
react-query
에서는 queryClient에 접근할 수 있는 QueryClientProvider를 사용한다.
<index.tsx>
...
const queryClient = new QueryClient();
...
이렇게 선언해주고
react-query
에서 QueryClient를 import 해주자
그 후 QueryClientProvider로 기존의 ThemeProvider와 App을 감싸준다. 물론 QueryClientProvider import가 필요하당
추가로 QueryClientProvider는 client라는 props를 필요로 한다. queryClient를 props로 넘겨주자
우리는 fetcher 함수가 필요한데
const response = await fetch("~~");
const json = await response.json();
이게 fetcher 함수라고 함~ fetch를 하는 함수ㅋ.ㅋ
이 코드를 api.ts라는 파일을 만들어 넣어준다.
<api.ts>
export function fetchCoins(){
return fetch('https://api.coinpaprika.com/v1/coins').then(response =>
response.json()
);
}
fetcher 함수는 무조건 fetch promise를 return 해줘야함!🤓
그리고 다시 Coins.tsx로 돌아와서 원래 fetch 관련 코드들을 싹 주석처리하자
Coin.tsx 속 코드들도 react-query
로 바꿔주자.
coinId처럼 정의되지않은 무언가 있는 경우 함수의 argument로 저렇게 type지정해주면 됨
우리는 이제 useQuery
라는 hook을 사용할거임
react-query
의 hook이므로 import 해주고~
useQuery
는 두가지 argument를 필요로 한당
- queryKey: query의 고유식별자라고 보면 됨
- fetcher 함수: 우리의 경우 fetchCoins겠지
(3번째 argument도 작성할 수 있긴한데 이건 선택적 object임.
3번째에서는 refecthInterval을 ms로 설정할 수 있음,, 고냥 참고~)
<Coins.tsx>
...
const { isLoading, data } = useQuery("allCoins", fetchCoins);
...
react-query가 대박인게 isLoading이라는 boolean값을 return 해주는데 이건 우리 원래 코드의 loading state를 대체해준다
그러면 우리는 useQuery
하나로 fetcher함수도 불러오고 그 함수가 로딩중인지 끝났는지도 isLoading이라는 boolean값으로 알 수 있는 것이당👍👍👍👍
(즉, fetcher 함수가 끝나면 isLoading은 true에서 false가 되겠지)
그리고 fetcher 함수의 실행이 끝나면 data에 함수의 데이터를 넣어준다 wow~
저 한 줄로 복잡한 과정을 생략할 수 있당
이번에는 Coin.tsx를 useQuery
로 수정해줄건데
const {isLoading: infoLoading, data:infoData} = useQuery(["info", coinId], ()=>fetchCoinInfo(`${coinId}`));
const {isLoading: tickersLoading, data: tickersData} = useQuery(["tickers", coinId], ()=>fetchCoinTickers(`${coinId}`));
이렇게 추가해주었다.
querykey
를 저렇게 해준 이유는 우리는 querykey
로 query들을 식별하는데querykey
가 coinid로 그냥 같아져버리기 때문임.React query
는 query를 array로 보기 때문에 array로 만들어준 뒤 앞에 item을 추가해주면 고유한 querykey
를 갖게되는 것이당!이제 새로 만들어준 useQuery에 해당하는 interface를 설정해주고 useQuery 사용으로 바뀐 변수명만 좀 손봐주면 제대로 작동한다!!
react query
에 있는 Devtools
를 import하면
내 캐시에 있는 query를 볼 수 있당
즉, 좀 더 시각화할 수 있음!
import { ReactQueryDevtools } from 'react-query/devtools';
App.tsx에서 Devtools
를 import 해주고 Router 아래에서 render해주자.
<ReactQueryDevtools initialIsOpen={true}/>
해주면 페이지가 이렇게 변한다.
이렇게 우리가 가진 캐시를 확인할 수 있고 캐시 안에 어떤걸 가지고 있는지도 확인할 수 있당
바로 Devtools
로~!
그러나 react query
를 사용하면 해당 데이터를 처음 불러올 때 빼고는 이제 로딩이 뜨지않는다.
이유는 react-query
가 데이터를 캐시에 저장해주기 때문~~👍🤭
해서 우리가 원하는 data가 이미 캐시에 있기 때문에 굳이 API에 다시 접근할 필요가 없는 것.
🤩react query
는 데이터를 유지하고 있다!!!!!!!!!!!🤩
일단 Chart 컴포넌트에 props로 coinId를 설정해주고 interface로 coinId의 타입을 string으로 설정해주자.
그 후 Coin.tsx의 중첩라우터 부분에
<Route path="chart" element={<Chart coinId={coinId!}/>} />
이런식으로ㅇㅇ! 해주면 coinId가 props로 들어가게 됨
이제 api.ts에 가서 fetcher 함수를 하나 더 만들어주고 (* 이때 api는 강의 자체 api 사용했음) Chart.tsx에 가서 사용해주면 된당.
그냥 우리 지금까지 useQuery 쓸 때 했던 것처럼 하면 됨
그러면 이렇게 잘 받아와진다.
GOOD!!
Date.now()를 하면 date가 milliseconds로 출력되고 이걸 1000으로 나누면 seconds로 변환된당!
Math.floor(); 하면 내림처리되고
ex) 1.9를 1로 바꿔줌
Math.ceil(); 하면 올림처리됨
ex) 1.9를 2로 바꿔줌
내가 지금 사용하는 강의 자체 api에는 추가로 더 보내줘야하는 필수parameter가 없는데 start time, end time 등을 보내주어야하는 api의 경우 이걸 사용하자~
참고~
APEXCHARTS : 자바스크립트 chart library
여러 형태의 차트를 가지고 있고 js, angular, vue, react와 통합.
사용방법: 터미널에
npm install --save react-apexcharts apexcharts
입력 후 import 해주기
ApexChart를 사용할 곳에 컴포넌트를 넣고 type, option props를 설정해준다.
그리고 차트 data를 보내줘야하는데 이것은 series라는 props로 쉽게 보내줄 수 있당,
data이름도 지어줄 수 있음 ㅋ.ㅋ
이렇게 해서 실행해보면
wow.. 애니메이션도 적용되어있어서 엄청 있어보임
option만 잘 설정해주면 엄청 쉽게 원하는대로 차트를 만들어줄 수 있다.
APEXCHARTS 공식문서 ← 여기서 option 확인도 가능🤓
typescript가 자동완성도 제공해줄테니 완전 쉽당
나는 api에서 받아온 데이터 중 close(종가)값으로만 구성된 차트를 만들어줄거당
series 속 data에 map을 해주면 되겠지?
...
data: data?.map((price=>price.close)) as number[],
...
이렇게 해준다.
강의에서는 as number[] 빼고 그냥 map해주던데 에러가 나서,,,🤦♂️🤦♂️
댓글 참고해보니..
series의 data[]가 받아야하는값은 number임.
data?.map()에서 제대로 읽어오면 number지만
못읽어오면 undefined가 되어서 이것이 문제가 되는 거라고 함!
그래서 우리는 as number[] 로 형식을 number로 강제해 해결해주는 것이다!
어렵구만,,쨌든 해주면 이렇게 된당
그리고 추가로 option 설정을 해주는데,,,
마우스 hover할 때마다 바뀌는 저 1,2,3,4... 카테고리 값을 날짜로 바꿔주기 위해
...
xaxis:{
...
categories: data?.map((price) =>new Date(price.time_close*1000).toISOString()),
},
...
해주었다. 그런데 왜,,,,외 계속 에러가 뜨니,,,,,
산술 연산의 왼쪽은 'any', 'number', 'bigint' 또는 열거형 형식이어야 합니다.ts(2362)
라는 에러가 계속 뜸.....
구글링 해보니 Date()로 생성한 값을 연산에 사용하기 때문에 오류가 나는 거라고..
typescript에서는 연산가능한 숫자처럼 명시적으로 처리해줘야하기 때문.
그래서 price.time_close 앞에 +(단항연산자)를 붙여주면 new Date()의 결과를 number로 취급하게 되기 때문에 문제가 해결된당
JS에서는 발생하지 않는 typescript에서의 오류라고^^,, 이자식^,,,^
쨌든 저렇게 해주고 types:"datetime",
해서 일,월로 표시되게끔 해주었다
공식문서를 이용하여 조금 더 option을 수정해서 실행시켜보면
와우 이렇게 됨! 멋지군
생각보다 간단히 커스텀이 가능한듯
그리고 공식문서에 DEMOS 카테고리에 가면 이미 만들어진 멋드러진 스타일의 차트 option을 갖다 쓸 수 있다.
우리가 아까 react-query에 대해 공부했을 때 잠깐 언급되었던 3번째 argument로 refetchInterval을 지정해주자.
이렇게하면 설정한 시간간격에 맞춰서 자동으로 refetch될 것이고 그러면 실시간으로 변동되는 값들을 확인할 수 있음!!
예를 들어 이렇게~
...
const {isLoading: tickersLoading, data: tickersData} = useQuery<PriceData>(
["tickers", coinId],
()=>fetchCoinTickers(`${coinId}`),
{
refetchInterval: 5000,
}
);
...
설치: 터미널에
npm i react-helmet
후
타입스크립트에서 사용할거니까npm i --save-dev @types/react-helmet
입력
react helmet은 컴포넌트인데 그 안에서 뭘 render하든 그게 문서의 head로 가게끔 해줌.
설치 후 import 해주고
이렇게 사용해주면 됨! 실행해보면
이렇게 된다,,, 휴
react helmet으로 title말고도 여러가지 할 수 있음~.~
css도 넣을 수 있다고함.
이건 강의에서 수강생들에게 혼자 해보라고 주신 과제같은 것,,
혼자 해본 코드를 작성해보겠다.
이건 그냥 엄청 간단하게 해줬다,,
<Coin.tsx>
...
<BackBtn>
<Link to="/">
<FontAwesomeIcon icon={faHouse} size="2x"/>
</Link>
</BackBtn>
...
고냥 이렇게 해줬다!
styled-components
배웠으니까 BackBtn이라는 styled components 만들어서
거기서 스타일 조정 해주고 FontawesomeIcon
에서 집모양아이콘 가져와서 사용해주었다.
Link
로 메인화면으로 돌아가게끔 해주었다.
누르면 메인으로 돌아가여~
멋지게 만들기를 어떻게 해야 멋질까.. 모르겠어서
일단 걍 값 화면에 출력해둠
멋지게 하고싶었는데 코인 이런걸 아예 몰라서^^,, 이 단어는 뭔 단어인가~,,^^ 했네
음 그냥 data useQuery로 받아와서 interface 설정해주고 거기서 몇개 좀 중요할 것 같은 값만 화면에 출력햇슴. styled components 내장 animation으로 효과도 좀 줬음 하나도 안멋져보여서^_^
<Price.tsx>
...
const fadeInNSlideUp = keyframes`
from{
opacity:0;
transform: translateY(200px);
}
to{
opacity: 1;
}
`;
const PriceLists = styled.ul`
display: block;
text-align: center;
background-color: #3f3671;
border-radius: 10px;
padding: 8px;
font-weight: 400;
margin-bottom: 25px;
animation: ${fadeInNSlideUp};
animation-duration: 0.7s;
...
`;
이게 일단 styled components 부분이고,,,
animation은 fadeIn 효과랑 동시에 위로 올라오게끔 하는 slideUp 효과를 같이 적용시켰다.
고냥 이렇게 해줬음.넵
이건 나중에 하겠음.. 내힘들다