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

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

by 퐁당당당 2025. 2. 11.

5.4 Route States

1) Coin images

coin의 이미지파일은, 코인의 코드를 알고 있다면 받을 수 있다. 따라서 map으로 coin리스트를 만드는 곳에 image의 src를 입력하여 보내줄 수 있다.

coins.map(coin=>
<Coin key={coin.id}>
  <img src={`url.../icon/${coin.symbol.toLowerCase()}`}/>
</Coin>

이런식으로 코인의 symbol을 모두 소문자로 바꾼 값을 넣어주면 → 이미지사진을 얻을 수 있다.

여기서 얻어가야 할 점은, api안에 있는 정보로 이미지 등 더 확장된 정보를 얻을 수 있다는 점이다.

2) state

지금부터는 한 페이지에서 다른 페이지로 정보를 전달하는 방법에 대해 알아보자.

지금 Coin페이지에선 coinId 즉, url동적 변수로 전달된 값을 useParams로 전달받아 이 값을 이용하고 있다. 하지만, url동적변수를 통해 정보를 받는 방법 외에 링크에서 넘어오는 state라는 값을 통해 정보를 받을 수도 있다.

지금은 Coins라는 페이지 속 리스트를 따라가면 Coin이 나오는 구조로 페이지가 구성되어 있다.

<Link to={`/${coin.id}`}>

지금은 이런 식으로 Link를 따라갈 수 있도록 코드가 작성되어있다. 하지만 to에 보낼 객체를 조금 더 구체적으로 작성할 수도 있다.

//Coins.tsx
<Link to={{
  pathname:`/${coin.id}`,
  state:{name:coin.name}
}}>
  1. pathname: 넘어갈 링크 이름
  2. state : 전송할 데이터(객체 형태) 데이터의 형태가 객체이므로 프로퍼티 이름을 적어주고, 보낼 값을 적어준다.

이렇게 받고 난 후 Coin페이지에서는 이 값을 어떻게 처리하는 지에 대해 알아보자

(여담이긴 한데, 여기서 정보나 무언가를 전달하는 방식을 배울 때는 항상

  1. 정보를 전달하는 방법
  2. 전달 받은 정보를 끌어다 쓰는 방법
  3. 전달해야 할 정보

이런 순서로 가르치고 있는 것 같다)

3) useLocation

const {state}=useLocation();

이런식으로 보내주면, useLocation 안에 있는 여러 프로퍼티(아까 보냈던 path등..) 중에서 state값만 받아올 것이다.

근데 이렇게 보내면, 아까 useParams와 마찬가지로, Ts는 state변수가 뭔지 알지 못하기 때문에, Interface를 적어서 타입을 명시해주어야 한다.

interface RouterState{
  state:string;
}

const {state}=useLocation<RouterState>();

이렇게 된다면 필요한 정보를 이전 페이지에서 넘겨받을 수 있게 된다.

근데 이런식으로 정보를 전달받는 루트의 문제점도 존재할까?

만약 어떤 사람이 홈페이지 → 세부페이지 의 경로로 들어오는 것이 아니라, 바로 링크를 타고 세부페이지로 들어온다고 가정해보자. 그럼 해당 세부페이지는 아무 정보도 받지 못하는 것이다.

일단 이 오류는 뒤로 하고, 전달받는 state 정보에 따라서 Title을 구성해보자.

<Title> {state?.name||"Loading..."} </Title>
  1. ?.name : state있으면 state.name을 반환할 것
  2. “로딩” : 없으면 Loading을 반환할 것

이 강의에서는 페이지 간에 정보를 전달하는 방식(Link to state)에 대해 알 수 있었다.

5.5 Coin Data

지금부터는 Coin의 데이터를 불러오는 과정에 대해 알아보려고 한다.

우선 우리의 api는 coinId를 이용하여 url을 타고 넘어가 정보를 끌고 올 수 있도록 되어있다. 따라서, 각 페이지에서 받은 coinId를 활용하여 정보를 fetch해오는 과정에 대해 알아보려고 한다.

1) simpifying fetching process

기존에 data를 fetch해오는 과정은 async함수를 활용하여 아래와 같은 코드로 구현했다.

const response=await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`);
const json=await response.json;

즉, response를 기다렸다가(await)저장하고 → 정보를 다 받은 이후, 값을 json으로 바꾸는 것이다.

이 과정을 await의 반복 없이 코드를 좀 줄여보려고 한다.

const response=await 
(await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`)).json();

지금까지 한 건 단순히 await를 두 번 사용해야 구할 수 있는 과정을 하나로 합쳐준 것이다.

그럼 이런 코드를 활용하여 coin에 대한 정보와, coinPrice에 대한 정보를 받아 변수에 저장해보자.

useEffect(()=>{
  (async()=>{
    const response=await (await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`)).json();
    setInfo(response);

    const price=await(await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}`)).json();
    setPriceInfo(price);
  })()
},[])

근데 여기서 문제는 Ts가 response변수와, price변수가 뭔지 알 수 없기 때문에 하나하나씩 설명해줘야 한다는 귀찮음이 있다. 이 문제점은 여기서 보지 말고, 뒤에서 react-query를 활용하여 해결해보기로 하고, 이 단원에서는 링크를 통해 정보를 fetch해오는 과정까지만 알아보자.

(5.6 Data Types단원에서 data를 콘솔에 출력하여 type을 바꿔주는 과정에 대해 설명하고 있으나 생략하려고 한다.)

5.7, 8 Nested Routes

1) New router inside the page

지금부터는 Coin페이지 내부에 새로운 라우터를 달아보려고 한다.

아까 우리가 라우터를 달 때는 기본 페이지(=Coins)에 Swith속 라우터를 여러 개 달아서 출력했었다.

이 단원에서도 동일한 구조로 라우터를 달아줄 것이다. 코드는 동일하지만, 보여지는 부분은 전혀 다른데 그 차이가 왜 발생하는지에 집중하며 강의를 정리해보고자 한다.

2) Router

Coin페이지의 맨 아랫부분에 다음과 같은 코드를 추가할 것이다.

<Switch>
  <Route path={`/${coinId}/Chart`}>
    <Chart/>
  </Route>
  <Route path={`/${coinId}/Price`}>
    <Price/>
  </Route>
</Switch>
  1. path : 하위페이지 (=Coin)까지 도달했으니, 그 이후의 링크만을 쓰는 것이 아니라, 전체 링크를 작성해주어야 한다.
  2. Switch, Route, Page : Switch안에 route를 만들고, 각 라우터 안에 페이지를 작성하는 방식이다.

3) Page

위에서 라우터 즉, 하위페이지(넘어갈 페이지)를 모두 파두었으니, Tab을 활용하여 링크를 만들어보자.

<Tabs>
  <Tab>
    <Link to={`/${coinId}/chart`}>Chart</Link>
  </Tab>
  <Tab >
    <Link to={`/${coinId}/price`}>Price</Link>
  </Tab>
</Tabs>

이와 같이 하면, Tab을 눌렀을 때 링크가 변하게 된다.

이후, Chart라는 페이지와, Price라는 페이지를 만들어 각각 가격 차트와, 시세 정보를 담은 div를 만들 것이다.

여기까지 설정을 하게 된다면 Tab을 누를 때 마다 차트와 가격 정보가 뜨게 된다

4) diff

그런데, 아까 설정했던 라우터와 링크랑 코드가 유사한 것 같은데, 동작하는 방식은 다르다.

아까 설정했던 방식은…

버튼을 누르면 기존 페이지가 언마운트 되고, 새로운 페이지가 마운트 된다

그러나 지금 방식은…

버튼을 누르면 링크만 변하고, 기존 페이지 그대로 아랫부분만 새로 등장하게 된다. 즉, 새로운 페이지로 넘어가는 것 처럼 보이지 않고, 기존 페이지 내부에서 새로 등장하게 되는 것이다.

왜 이런 차이가 발생할까?

(1)

일반적으로 앱을 감싸고 있는 최상위 컴포넌트에 한 번만 작성해주는 것이 일반적이다. Router, Switch를 사용하려면, 최외각에 브라우저 라우터가 존재해야 한다.

하지만, 브라우저 라우터만으로 새 페이지가 되는 것은 아니다.

(2) router

라우터의 역할이 사실은 더 크다. 브라우저 라우터가 새 페이지를 만든다고 하기엔, coin 페이지에 브라우저 라우터를 붙인다고 해서 새로운 페이지로 넘어가지도 않고, 붙여서도 안된다.

따라서 라우터가 페이지 전환에 가장 큰 키를 잡고 있음을 알 수 있다.

만약 coin페이지와 동등한 자격으로 (= 새로운 페이지로 뜰 수 있는 자격) Chart와 Price를 만들고 싶다면, 처음에 설정했던 라우터 안에 파일들을 추가해주면 된다.

function Router() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/:coinId/chart">
          <Chart />
        </Route>
        <Route path="/:coinId/price">
          <Price />
        </Route>
        <Route path="/:coinId">
          <Coin />
        </Route>
        <Route path="/">
          <Coins />
        </Route>
      </Switch>
    </BrowserRouter>
  );
}

즉, Tab전환을 원하는 경우, 처음부터 이곳에 모든 페이지를 명시해두어야 한다. (몇 번째로 접근하는 지는 상관 없다. 동등한 ‘자격’이기만 하면 된다.)

그러나, 한 페이지가 그대로 유지된 채 ‘내부’에서 페이지를 띄우고 싶은 경우라면, 해당 페이지 안에서 라우터를 설정해주어야 한다.

따라서 페이지 전환이나, 띄우기를 설정하는 것은 라우터이다. 이 라우터의 위치에 따라서 동일한 링크 프로퍼티라도 다르게 기능할 수 있다.

5) useRouterMatch

현재 링크를 확인하는 훅이다.

지금처럼, 한 페이지 안에서 링크는 변하지만, 페이지 전반은 유지된 채 일부만 바뀌도록 라우터를 설정한 경우 사용하는 훅인 것 같다(그 외에 링크를 확인해야 할 일도 있겠지만)

const priceMatch=useRouteMatch("/:coinId/price");

이와 같이 훅 안에는, 내가 확인하고 싶은 링크를 적는다. 이렇게 되면 priceMatch라는 변수에는 링크를 확인한 후 결과값이 bool형태로 저장된다.

<Tabs>
  <Tab>
    <Link to={`/${coinId}/chart`}>Chart</Link>
  </Tab>
  <Tab >
    <Link to={`/${coinId}/price`}>Price</Link>
  </Tab>
</Tabs>

아까 이런 식으로 저장된 상황에서 Tab에 코드를 조금 추가해보려고 한다.

<Tab isActive={chartMatch!=null}>
<Tab isActive={priceMatch!=null}>

이렇게 되려면 Tab컴포넌트를 수정해야 할 것이다.

const Tab = styled.span<{ isActive: boolean }>`
  .
  .
  .
  color: ${(props) =>
    props.isActive ? props.theme.accentColor : props.theme.textColor};
`;
  1. <{ isActive: boolean }> : isActive값을 prop으로 받을 것이다
  2. props.isActive? : isActive값을 확인해서 참이라면 → 글자 색을 accentColor로 바꾼다.

이 강의에서는 페이지 안에 페이지. 말 그대로 nested(=중첩된) 페이지를 만드는 방법에 대해 배웠다.

유사한 코드임에도, 이전에 Coins페이지에서의 동작과, 이번 강좌 실습에서의 동작이 다른데 차이가 발생하는 이유에 집중하여 복습해주면 좋을 것 같다.