본문 바로가기
프로그래밍/React

# React-Js Master Class #5-1 Crypto Tracker

by 퐁당당당 2025. 2. 9.

5.0 Setup

1) installing styled component, router-dom

npm install --save styled-components

npm i react-router-dom@5.3.0
npm i --save-dev @types/react-router-dom

여기서 실습을 하려면 router-dom 을 5.xx버전으로 입력해야 제대로 실행이 된다.

2) Router, separating pages

지금부터 만들 새로운 페이지들은 모두 src폴더 내에 만들 새로운 폴더 routes 에 저장할 것이다.

routes폴더 안에 Coins, Coin이라는 페이지를 만들 것이다.

//Coins.tsx
function Coins(){
  return <h1>Coin</h1>
}

export default Coin
//Coin.tsx
function Coin(){
  return <h1>Coin</h1>;
}

export default Coin;

보이는 것 처럼 페이지의 구성은 함수 → export로 나뉘어진다.

다음으로는 이 페이지들을 관리하는 파일에 대해 알아보자.

3) Router.tsx

(1) react-router-dom

우선 react-router-dom 에서 세 가지를 import 해준다.

import { BrowserRouter, Switch, Route } from "react-router-dom";

(2) Pages

아까 만들었던 Coins페이지랑, Coin페이지를 import해준다.

import Coin from "./routes/Coin";
import Coins from "./routes/Coins";

(3) return value

function Router(){
  return<BrowserRouter>
    <Switch>
    </Switch>
  </BrowserRouter>
}
  1. 우선 Browser Router를 최외각에 감싸고, 그 안에 switch를 작성한다.
function Router(){
  return<BrowserRouter>
    <Switch>
      <Route path="/:coinId">
        <Coin/>
      </Route>
      <Route path="/">
        <Coins/>
      </Route>
    </Switch>
  </BrowserRouter>
}
  1. form:의 형식으로 쓸 것이다. 즉 현재 page라는 글자가 적힌 부분에 내가 만든 페이지들을 작성할 것이다.
  2. <Route path=””> <page> <Route/>
  3. path=”/” : Home Page라는 뜻이다.
  4. path=”/:coinId”
    • 기본 주소에 /:coinId 붙인 url을 가진 페이지다.
    • :coinId : 동적으로 변하는 부분.(앞에 :붙여서) 사용자에게 값을 받아내는 부분이다.
    • 여기서 변하는 값은 useParams를 이용하여 Coin페이지 안에서 받을 수 있다.

즉 Routes라는 파일은 url과 만들어둔 페이지를 쌍으로 관리하는 곳이다.

이렇게 받은 Router는 최종 출력을 담당하는 App.tsx에 적용을 해준다.

//App.tsx
import Router from "./Router"

function App() {
  return (
    <Router/>
  );
}

export default App;

지금부터는 동적으로 받은 url을 처리하는 방법에 대해 알아보자.

(4) useParams

import { useParams } from "react-router-dom"

function Coin(){
  const {coinId} = useParams();
  return <h1>${coinId}Coin</h1>
}

export default Coin
  1. useParams: 훅 중 하나로, 현재 내가 속해있는 페이지 url의 동적변수를 포함하는 객체를 반환한다.
  2. const {coinId}=useParams(); : coinId라는 값을 받을 것이다.

그런데 Ts를 사용하는 경우 const{coinId} 라고 받으면 → coinId 가 어떤 값인지 알지 못해 문제가 생긴다.

이럴 땐 interface를 만들고 이를 적용해주면 된다 .

interface Params{
  coinId: string;
}

function Coin(){
  const {coinId} = useParams<Params>();
  return <h1>${coinId}Coin</h1>
}
  1. interface Params : useParams를 통해 받을 객체 속 프로퍼티 형식을 작성해준다.
  2. <Params> : 이처럼 훅의 () 앞에 형식을 작성해주면 훅에서 발생하는 값을 받는 변수(=coinId)의 형식을 명시할 수 있음

5.1 Styles

지금까지 styled components를 활용할 때는 ‘특정 요소’에만 적용을 했다면, 지금부터는 브라우저 전체에 이를 적용해보고 싶다.

1) GlobalStyle

브라우저 전체의 출력을 담당하는 파일은 app.tsx이니 코드를 수정해보자.

import { createGlobalStyle } from "styled-components";
import Router from "./Router"

const GlobalStyle=createGlobalStyle`
`
function App() {
  return <>
    <Router/><GlobalStyle/>
  </>
}

export default App;
  1. createGlobalStyle : import해준 이후, GlobalStyle이라는 변수에 다른 컴포넌트를 만들었던 것 처럼 생성해준다.
  2. <></>= fragment : 두 개의 값을 return하고 싶은데, 그러지 못하는 경우, 유령태그로 묶어서 낸다.

이렇게 작성하면, 브라우저 전체가 styled-component의 영향을 받게 된다.

2) Implementing font

  1. https://fonts.google.com 에서 원하는 글씨를 찾는다

  1. get imbeded code를 누른다
  2.  
  3.  

  1. import를 누르고, @뒤에 있는 부분을 복사한다

  1. 아까 만든 Global style 안에 추가한다.복붙한 링크를 저 안에 적어준다. 
  2. body{ font-family:'Source Code Pro', san-serif; }//Source Code Pro를 쓰고, 안되면 san-serif *{ box-sizing: border-box; } a{ text-decoration: none; }//링크에 밑줄 없애는 코드
  3. 이후 global Style안 body에 코드를 추가해준다.
  4. import { createGlobalStyle } from "styled-components"; import Router from "./Router" const GlobalStyle=createGlobalStyle` @import url('https://...'); . . . ` function App() { return <> <Router/><GlobalStyle/> </> } export default App;

즉 폰트를 찾고, 이를 implement하는 방법에 대해 설명하고 있다.

3) Theme

현재까지 배운 내용으로 테마를 적용하려면, 테마 파일을 만들어서, 매번 Theme을 import해와야 한다.

지금부터는 styled-components를 import하면 내가 만들려는 테마의 ‘형식’을 다운받을 수 있도록 코드를 짜보려고 한다.

여기서 짚고 넘어가야 할 부분은 내가 install 한 파일에도 원하는 값을 넣어 커스텀할 수 있다는 것이다.

//styled.d.ts
declare module "styled-componets"{
  export interface DefaultTheme{
    textColor : string;
    bgColor : string;
    btnColor :string;
    accentColor:string;
  }
}
  1. declare module : module을 선언할 것임
  2. "styled-componets" : styled components를 import한 사람들이 접근 가능한 module을 만들 것임
  3. export Interface : DefaultTheme : 디폴트 테마라는 인터페이스 모듈이다.

이런식으로 ‘전체 접근’가능하도록 만든 테마는 어떻게 사용할까?

import { DefaultTheme } from "styled-components";

export const theme:DefaultTheme={

}

이런식으로 ‘형식’을 부여해줄 수 있다. (interface를 export했기 때문에!!)

즉 Theme 단원에서 얻어가야 할 내용은

  1. (…).d.ts : module의 ‘형식’을 선언할 수 있음
  2. declare module : 모듈을 선언할 수 있음 → 외부에서도 더 쉽게 접근 가능

4) Sum

이 단원에서는 우선 styled-component를 전체적으로 적용할 수 있는 방법에 대해 설명하고 있다.

이후 이렇게 global 하게 적용된 styled-component로 사용할 수 있는 가장 대표적인 기능인 ‘폰트’를 적용하는 방법에 대해 배웠고

이후 Theme을 전체적으로 적용할 수 있는 방법에 대해 배웠다

따라서 style 강좌에서는 styled component를 일부의 요소에만 적용하는 것에서 더 나아가 global 하게 적용하도록 배웠다. 초기 세팅에서 부터, 글로벌하게 적용할만한 기능까지(=폰트, 테마) 배웠다.

5.2 Home part One

지금부터는 api로 대량의 data를 받아오기 전에 임의로 data를 만들어서 홈페이지에서 리스트가 출력되도록 한 후, 리스트의 각 요소를 누르면 링크를 타고 넘어가도록 코드를 만들어보려고 한다.

1) List of Coins

function Coins() {
    return <Container>
      <Header>
        <Title> 코인 </Title>
      </Header>
      <CoinList>
        <Coin></Coin>
      </CoinList>
    </Container>
}
export default Coins;

이렇게 만든 CoinList안에 Coin을 여러개 입력해보려고 한다. 그렇다면 CoinList안에 어떤 코드를 입력해야 하는 것인가

현재 세 개의 코인 정보를 coins라는 변수에 배열 형태로 넣어둔 상태다.

<CoinList>
  {coins.map(coin=><Coin key={coin.id}>{coin.name}</Coin>)}
</CoinList>
  1. coins.map : coins라는 배열에 map이라는 함수를 달아서, 배열의 요소 하나하나마다 실행하도록 할 것임
  2. coin ⇒ : coins배열 안의 하나하나의 요소가 coin으로 call back 될 것이다.
  3. key={coin.id} : map 의 각 요소는 반드시 key를 가져야 한다. 또한, 이 key의 값은 배열의 요소(=객체)의 프로퍼티인 id값을 이용한다.

이렇게 못생긴 리스트가 만들어지는데, 우선 이 리스트 요소들을 수정해서 각 버튼을 누르면 → 링크를 타고 넘어가는 페이지를 만들어보려고 한다.

2) Following the link

<CoinList>
  {coins.map(coin=>
  <Coin key={coin.id}>
    <Link to={`/${coin.id}`}>{coin.name}</Link>
  </Coin>)}
</CoinList>

아까 우린 라우터 파일에서 coinId를 동적으로 받을 것임을 명시하였다.

  1. <Link to={/${coin.id}}>
    • Link to : to 뒤에 있는 링크 타고 갈 것임
    • {/${coin.id}} :따라서 /${coin.id}라고 작성하면 coin의 id 프로퍼티에 해당하는 값을 동적으로 받아 따라갈 링크를 만드는 상황임을 알 수 있다.
    • 아까 router.tsx에서 코인 페이지 링크가 /:coinId 임을 명시했다.

결국 정리하면 동적으로 링크를 설정하는 상황에서는

  1. 동적으로 링크 설정 (in Router)
  2. Link to /{value} 라고 하며 따라갈 링크를 생성

3) Mouse Hover Event

지금부터는 마우스를 가져다 올렸을 때 색이 바뀌도록 해보자.

const Coin=styled.li`
  &:hover{
    a{
      color: ${(props)=>props.theme.accentColor};
      display: block;
    }
  }
`

내가 만들었던 Coin 컴포넌트에 이벤트를 추가할 것이다.

  1. &: 지금 현재 대상 (=li)를 가리킨다.
  2. :hover : 이벤트 이름원래는 useClick이나, useHover에 변화 함수를 부여하는 방식으로 진행했었는데, styled component를 활용하면 더 간단하게 작성할 수 있다.
  3. 결국 hover라는 이벤트가 발생한 경우 어떤 함수를 실행할지 {} 안에 적고 있다.

5.3 Home part Two

지금까지는 내가 직접 data를 만들어서 이를 출력했다면, 이제부터는 api에서 data를 받아와서 이를 출력해보려고 한다.

1) Setting + form of data

우선 내가 받아올 api의 각 요소를 구성하는 형태를 Ts에 interface 로 설명해주려 한다.

interface CoinInterface{
  id: string,
  name: string,
  symbol: string,
  rank: number,
  is_new: boolean,
  is_active: boolean,
  type: string,
}

이후 useState를 활용하여 coins라는 배열과, setCoins라는 함수를 사용해보고자 한다.

const [coins, setCoins]=useState<CoinInterface[]>([]);

그냥 부여하면 Ts가 coins의 형식을 알 수 없기 때문에, 훅을 사용해 변수를 받는 경우에는 → 훅 뒤에 Interface를 작성해주며 형태를 명시해줄 수 있다.

2) fetching Api

(1) default setting

기본적으로 fetch 는 useEffect안에서 진행된다.

useEffect(()=>{
    ()();
},[])
  1. useEffect(()⇒{},[]) : useEffect는 (function, array)의 형식으로 진행된다.
  2. 이 때 function은 array안에 있는 요소가 바뀔 때 마다 실행이 된다
  3. 함수를 표현할 때는 ()()의 형태로 표현한 후 앞의 괄호 안에 적어줄 수도 있다.

(2) async, await

useEffect안에 들어갈 async함수에 대해 알아보자.

async()=>{
  const response = await fetch("https://api.coinpaprika.com/v1/coins");
  const json = await response.json();
}
  1. async: async함수
  2. await: 기다려야 할 동작 앞에 붙여준다.(변수 앞에 붙이는 거 아님)
  3. 즉, 저 동작을 기다렸다가 변수 안에 값을 넣어주라는 뜻임
  4. responsejson

(3) slice method

data가 너무 길어서 slice를 사용하여 배열을 줄이려고 한다. slice 기능은 어떻게 사용하는 건지 정리해보자

const a=[1,2,3,4,5]
a.slice(0,3)=[1,2,3,4]
  1. slice(a,b)→ 배열의 순번 a~b까지 잘라서 새로운 배열을 반환

slice method를 활용해보면

async()=>{
  const response = await fetch("https://api.coinpaprika.com/v1/coins");
  const json = await response.json();
  setCoins(json.slice(0,100));
}

받아온 json을 잘라 저장하는 것을 확인해볼 수 있다.

3) Loading

지금부터는 로딩중임을 알려주는 기능을 담아보려고 한다. 이는 마찬가지로 useState를 활용하여 변수를 만들어준다.

이후, 아까 만들었던 fetch함수에서 fetching이 끝나면 loading의 값을 바꿀 수 있도록 코드를 추가하자

const [loading, setLoading]=useState(true);
  useEffect(()=>{
    (async()=>{
        .
        .
        .
      setLoading(false);
    })();
  1. loading, useState : useState를 활용하여 loading이라는 변수에 true값을 할당해준다.
  2. setLoading(false) : 아까 만들었던 fetching함수가 끝이 나면 → loading의 값을 바꿔준다.

이후, 이 기능이 화면에도 보일 수 있도록 return값을 수정해보자

{loading?"Loading...":<CoinList>
  {coins.map(coin=>
  <Coin key={coin.id}>
    <Link to={`/${coin.id}`}>{coin.name}</Link>
  </Coin>)} 
</CoinList>}
  1. loading?
    • true: “Loading…”
    • false: (=after fetching all the data) show the list of the coins