Crypto-Tracker를 클론코딩하면서 React-Hook, Router, globalStyle 등 많은 React 지식을 쌓을 수 있었다!!📚📚
필자는 클론코딩을 진행하면서 배웠던 점을 이 곳에 기록해보려한다!
기본적으로 Cryto-Tracker는 라우팅이 가능한 웹사이트이다.
필자가 이해한 Routing 기술은 유저와의 상호작용을 통해
유저가 원하는 컴포넌트(페이지)를 보여주고 싶을 때 최적화 된 상태로 렌더링 해주는 기능이다.
사실 이 내용을 이해하려면 SPA, SSR, SSG등에 대한 개념을 어느정도 갖춰야한다.
그렇기 때문에 SPA,SSR,SSG에 대해 학습해보고자 한다. !
SPA는 (Single Page Application)의 약자로 하나의 페이지로 구성된 웹 어플리케이션이다.
웹 어플리케이션에 필요한 모든 정적 리소스를 최초에 한번에 다운하고 (최초 로딩이 길 수 있는 이유), 그 후 새로운 페이지 요청이 있을 때
필요한 데이터를 전달받아 페이지를 구성한다.
그럼으로 최초에 페이지를 로딩한 시점 이후 부터는 페이지 리로딩없이 원하는 부분만 화면 갱신이 가능하다.
그렇기 때문에 사용자는 불편함 없이 빠른 웹 페이지를 볼 수 있다.
리액트는 이러한 SPA 방식이다. 리액트는 React-Router라는 라이브러리를 사용하여
페이지를 요청하고 해당 페이지에 저장된 데이터를 불러온다.
페이지가 전환되지 않더라도 SPA방식의 웹 페이지는 필요한 부분이 변경되었을 때 (count가 변한다던지)
그 부분만 수정해서 렌더링해준다.
페이지가 전환된다면 그때는 페이지를 요청하여 그 부분을 가져온다.
이때 React-query를 사용하면 데이터를 cashe에 저장해주어 리로딩이 필요없게 된다.
Cashe는 글 마지막쯤에 정리를 해보겠다.
하지만 SPA 방식은 데이터가 자주 변하는 웹 어플리케이션에는 유리하지만 그렇지 않은 사이트에서는
오히려 over engineering이 될 수 있다.
왜? SPA 방식은 검색 엔진 최적화 (SEO) 측면에서 상당히 불리하다. 크롤러가 웹페이지에서 읽어갈 수 있는
내용이 달랑 div 밖에 없기 때문이다.
결론: SPA는 하나의 페이지에 브라우저 주소에 따라 원하는 컴포넌트를 제시한다.
프레임워크 : 집, 자동차, 기차
라이브러리 : 도구들
흐름의 차이가 있다. 프레임워크는 전반적은 흐름을 가지고 있지만 라이브러리는 사용자에 따라 흐름이 바뀐다.
MPA는 Multi Page Application의 약자로 새로운 페이지를 요청할 때마다 서버에서 렌더링된 정적 리소스가 다운되는 전통적인 웹페이지 구성방식이다. 즉, 새로운 페이지를 요청하면 불필요한 부분도 다시 렌더링 된다는 것! 단일 페이지이면 상관이 없겠지만 다양한 페이지가 존재하고 또 새로고침 될 부분이 많다면
해당 방식은 추천되지 않는다 !
SSG는 Static Site Generator 즉, 정적 사이트 생성기이다.
누가 접속하든 항상 동일한 내용을 보여주는 웹사이트를 만드는데 최적화 된 방식(변화가 많지 않은 사이트)
ex) 개인블로그, 컨턴츠 변경이 자주일어나지 않는 소규모 웹사이트
SSG로 생성되는 웹사이트는 모든 웹페이지를 미리 완전히 만들어놓고 요청이 들어오면 만들어 놓은
웹 페이지를 그대로 응답만 해준다. 그렇기 때문에 웹사이트의 속도는 매우 빠르다!!
SSG는 SPA방식과 다르게 미리 만들어 놓은 수 많은 웹 페이지로 이루어졌기 때문에
SEO( 검색엔진최적화 ) 측면에서 적합하다.
SSG의 큰 단점은 빌드 시점에서 웹사이트 전체를 매번 다시 만들어 내다 보니 컨텐츠가 자주 업데이트 되는 웹 사이트에서는 큰 비효율이 발생한다.
SSR은 Server-Side Rendering 즉, 서버 측에서 웹 페이지를 렌더링하는 기술이다.
SSG와 가장 큰 차이점은 빌드 타임에 웹 사이트를 미리 만들어 놓는 것이 아니라,
유저로부터 요청이 들어올 때 실시간으로 해당 웹페이지를 만들어낸다는 것이다.
SSR을 사용하면 변경된 데이터가 즉시 웹 페이지에 반영이된다. 따라서 데이터가 수시로 업데이트되고, 개인화 된 경험을 제공해야하는 전자 상거래(E-commerce)플랫폼 SNS에 매우 적합한 렌더링 전략
웹 서버만 있으면 쉽게 호스팅이 가능한 SPA,SSG와 달리 SSR은 서버측에서 웹 페이지를 렌더링하기 위해
어플리케이션 서버가 추가로 필요하다.
이러한 인프라를 구축할 수 있는 중대형 서비스에서 SSR을 많이 사용한다.
SPA개발에 사용되는 유명한 라이브러리는 대부분 SSR프레임워크를 제공한다. ex) Next.js
드림코딩 서버렌더링 유튜브
서버 렌더링 블로그 정리글
코딩알려주는누나 유튜브
리액트에서 가장 많이 쓰이는 라우팅 관련 라이브러리는 React-Router이다.
즉, 컴포넌트마다 고유의 URL을 주고 라우팅 라이브러리를 사용하여 해당 URL로 보낸 후
그곳에 저장되어 있는 데이터를 불러와 렌더링한다!
설치 방법은 npm i react-router-dom
그 후 BrowserRouter 태그로 컴포넌트를 감싸준다 이때 Router 컴포넌트를 감싸주면 된다.
그 후 Routes 태그로 Route 태그를 감싸주는데 Routes는 규칙이 일치하는 라우트 하나만 렌더링
시켜주는 역할을 한다.
function Rounter() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Coins />}></Route>
<Route path="/:CoinId/*" element={<Coin />}></Route>
</Routes>
</BrowserRouter>
);
}
//그리고 App 함수에서
function App() {
return (
<>
<Rounter />
</>
);
}
// 이렇게 사용하면 된다.
Route는 위 코드와 동일하게 2가지의 인자를 받는다.
path, element.
path는 보내고싶은 컴포넌트의 URL을 저장해주고
element는 보내고싶은 컴포넌트를 저장한다.
즉, path 에는 주소를 element에서 그 주소에 있는 컴포넌트를 넣어주면된다.
useParams는 Route에서 path의 변수 "key"와 해당 path의 value를 그대로 받아올 수 있다.
<BrowserRouter>
<Routes>
<Route path="/:CoinId" element={<Coin />}></Route>
</Routes>
</BrowserRouter>
위 코드에서 /bit-coin URL로 접속하면 useParams를 활용하여 :CoinId에 접근할 수 있다.
return은 object형태로 돌려받는다.
/어떤변수 형태의 URL로 이동해서 그곳에서 console.log(useParams())
하면 {CoinId:value} 이렇게 찍힌다.
그리고 해당 value는 Link to 혹은 usenavigate() 와 같은 방법으로 전달 된 변수 값이 리턴된다.
Link to 와 usenavigate()에 대한 설명은 이어 적도록하겠다.
useparams 는 현재 페이지의 path parameter 정보를 담고있다.
uselocation은 현재 페이지의 경로 정보를 담고있다.
즉 useparams는 path 즉 URL에 대한 변수와 변수 value에 대한 정보를 담고있고
uselocation은 URL의 경로에 대한 정보를 담고있는데
uselocation은 state라는 object인자를 통해 더 많은 정보를 담아올 수 있는데 이는
Link to를 학습하며 정리해보겠다.
React-Router 라이브러리를 사용하여 URL과 해당 URL에 대한 정보를 전달해 주었다면
해당 URL로 이동시켜주는 녀석들이 Link to 와 usenavigate이다.
기본적으로 Link to 'react-router-dom' 에서 가져온 컴포넌트이다.
Link 컴포넌트는 페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한 상태에서
HTML5 History API를 사용하여 페이지의 주소만 변경시켜준다.
<Link to={`/${coin.id}`} state={{ name: coin.name }}>
<Img
src={`https://cryptocurrencyliveprices.com/img/${coin.id}.png`}
/>
{coin.name}→
</Link>
위 코드 처럼 JSX 문법을 사용한 html 태그를 감싸면서 사용하면된다.
게다가 Link의 좋은 점은 state를 활용하여 많은 정보를 넘겨줄 수 있다는 점이다.
Link를 활용하여 보낸 state는 해당 페이지에서 uselocation() 훅을 사용하여 가져올 수 있다.
이렇게 하면 좋은 점은 다시 API를 불러오지 않아도 이미 받아온 정보를 통해 빠른 렌더링이 가능하게한다.
- a 태그로 이동하면 상태값을 잃고 페이지를 새로 불러오게 된다. 그렇게 되면 앱이 가지고 있던 모든 정보를
잃게 되고 새로 데이터를 받아오기 때문에 비효율적이다.- Link 컴포넌트로 이동하면 상태값이 보존되고 페이지를 새로 불러오지 않고 브라우저의 주소만 바꿔준다.
styled-components 라이브러리에서 제공하는 함수로 최상위 컴포넌트에 적용하면된다.
사용 방법은 다음과 같다.
import React from "react";
import styled, { createGlobalStyle } from "styled-components";
import Rounter from "./Router";
import { ReactQueryDevtools } from "react-query/devtools";
import { HelmetProvider } from "react-helmet-async";
const GlobalStyle = createGlobalStyle`
@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400&display=swap');
font-family: 'Source Sans Pro', sans-serif;
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
*{
box-sizing: border-box;
}
body{
font-family: 'Sorce Sans Pro',sans-serif;
background-color: ${(props) => props.theme.bgColor};
color: ${(props) => props.theme.textColor};
}
a{
text-decoration:none;
}
`;
function App() {
return (
<>
<GlobalStyle />
<HelmetProvider>
<Rounter />
</HelmetProvider>
<ReactQueryDevtools initialIsOpen={true} />
</>
);
}
export default App;
createGlobalStyle (전역 스타일을 처리함)
전역 스타일을 처리하는 특수 Styled Component를 생성하는 helper 함수입니다.
중첩Router이다 즉, 필자가 이해한 nestedRouter는
한 페이지 위에 또 다른 컴포넌트를 URL에 따라 추가하고 싶다면 사용하는 방법이다.
핵심은 URL에 따라 컴포넌트를 추가한다는 점이다.
사용 방법은 다음과 같다.
function Rounter() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Coins />}></Route>
<Route path="/:CoinId/*" element={<Coin />}></Route>
//여기서 /:CoinId 는 받아온 변수가 들어갈 자리이고 /*는 중첩 라우터를 사용하겠다는 의미이다.
</Routes>
</BrowserRouter>
);
}
function Coin(){
return <div>
<h1>"hahaha"</h1>
<Routes>
<Route path="price" element={<Price CoinId={`${CoinId}`} />} />
<Route path="chart" element={<Chart CoinId={`${CoinId}`} />} />
</Routes>
</div>
}
위와 같이 함수의 return 문에서 Routes를 생성해 /*에 들어갈 URL key를 path에 집어넣고
원하는 컴포넌트를 element를 통해 연결시켜주면 된다.
react-qury는 상태관리에 유용하고 또 데이터를 가져오는데 효율적인 부분이 많다.
캐싱, api자동호출, 비동기적 과정을 선언적으로 관리 등 다양한 장점이 있는데 해당 React-query를
사용하지 않았을 때의 모습과 사용한 후의 모습을 비교하면서 정리해보겠다.
const App()=>{
const [data,setData] = useState("")
const [loading,setLoading] =useState(true)
useEffect(()=>fetch("http://asdasd").then((response)=>response.json()).then((item)=>setData(item))
,[])
return <div>
</div>
}
useQuery를 사용하기 전에 state를 두개 따로 관리 해주어야했고 useEffect를 통해 한번만 호출하도록 했다. 그렇지만 이 모든 과정을 하나로 만들어 주는 것이 useQuery이다.
useQuery는 기본적으로 data와 loading값을 받아온다.
const BASE_URL = "https://api.coinpaprika.com/v1";
export function fetchCoins() {
return fetch(`${BASE_URL}/coins`).then((response) => response.json());
}
//
const App()=>{
const {isLoading,data} = useQuery("CoinApi",fetchCoins)
return <div></div>
}
이게 모든 과정이다. useQuery("key값",return이 promise형태인 함수)
또한 usequery는 받아온 api를 캐시(cashe)에 저장하여 반복되는 데이터는 api를 두번 호출하지 않아도 된다.
캐싱이된 api 데이터를 실시간으로 확인할 수 있다.
QueryClient,QueryClientProvider 이다.
import React from "react";
import ReactDOM from "react-dom/client";
import { QueryClient, QueryClientProvider } from "react-query";
import { ThemeProvider } from "styled-components";
import App from "./App";
import themeMode from "./theme";
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={themeMode}>
<App />
</ThemeProvider>
</QueryClientProvider>
);
이렇게 import해서 사용하면
위 그림과 같이 실시간으로 캐싱된 데이터를 확인할 수 있는 기능이 있다!!!!
즉 정리하면
- fetcher 함수를 먼저 만든다. 이때 함수는 항상 fetch Promise를 반환해야한다.
- usequery("[key값]",fetch함수)
- 데이터를 캐시에 저장하기 때문에 데이터가 저장이 된다.
- ReactDevelopTools를 활용하여 저장된 query를 볼 수 있다.