Next.js의 기본중의 기본!! preRendering을 공부해보고 활용해보았습니다.
PreRendering이란 SSR(Server Side Rendering)
을 구현하는 Next.js의 특징이라고 볼 수 있습니다.
자주 쓰이는 React로 구성된 CSR(Client Side Rendering)
같은 경우는 자바스크립트 파일을 브라우저에서 해석해 랜더링하는 방식을 사용합니다. 따라서 처음 서버에서부터 파일들을 받아오는데 오랜시간이 걸리지만 한번 받아오면 클라이언트 측에서 자바스크립트를 해석해 랜더링 하면되므로 화면 깜빡임 없이 부드러운 사용자경험을 유지할 수 있습니다.
이에 반해, SSR 방식은 미리 서버에서 HTML파일로 랜더링해 클라이언트로 전송해주는 방식입니다. 이로인해 서버와 통신을 할 때 미리 만들어둔 HTML파일을 전달받으므로 CSR보다 랜더링 속도가 빠릅니다. 그치만 페이지를 옮길 때 마다 매번 통신을 해서 화면깜빡임이 많아지는 것 아닐까요? 라는 의문이 듭니다.
예전에는 MPA(Multi Page App)
이라고 비슷한 방식이 존재했습니다. 아까 들었던 의문처럼 이 멀티페이지앱은 페이지 이동때마다 다시 서버에 요청하고 받아오는 방식이었기 때문에 화면 깜빡임이 많아 사용자경험이 좋지않았습니다.
그러나 Next.js는 최소한의 Javascript를 이용한 Rendering을 하고 나머지이후에는 차차 javascript파일들을 받아와 클라이언트 측에서 Javascript를 해석하는, 즉 CSR과 비슷한 방식으로 진행 됩니다.
요점은 Next.js 는 처음엔 SSR, 이후엔 CSR 방식이라는 뜻입니다.
그래서 preRendering이라고 부를 수 있는 것입니다. 미리 랜더링하다!!
Next.js에서는 두가지 PreRendering방식을 제공합니다. 바로 Static Generation(정적 생성 방식)과 Server-Side Rendering 방식입니다. Next.js에서는 바로 이 정적 생성 방식을 권장합니다.
주의 예전 블로그들을 보다보니 getInitialProps를 쓰는 것을 보고 따라했다가 낭패당했습니다 ㅜㅜ 옛날 방식이었습니다. 마치 React에서 Hooks를 사용하기를 권장하는 것 처럼 Next.js에서도 getInitialProps보단 이제 설명하는 두가지 PreRendering 방식을 사용하기를 바랍니다.
페이지 정적생성에는 두가지 방법이 있습니다.
기본적으로 page에서 외부 데이터를 가져올 때 사용됩니다. 예를 들면 블로그에서 CMS에 작성한 글을 불러올때 처럼 말이죠.
기본적인 예시는 다음과 같습니다.
다음은 기본 사용 예시였습니다.
params와 return props에도 여러 옵션들이 존재하고 상황에 맞게 사용하시면 됩니다. 해당 옵션들은 Next.js 공식페이지 DOCS에 존재하니 참고하세요!
Next.js에서는 Dynamic Routes를 쉽게 형성 할 수 있습니다.
pages/posts/[id].js
다음 과 같은 폴더구조를 만들면 Next.js가 알아서 pages를 찾아 Routes를 구성합니다. 경로는 [해당url]/posts/1
로 구성되며 id 부분에 원하는 index나 string을 넣어 구성가능합니다.
예시 코드는 다음과 같습니다.
paths만 받는다면 다음과 같이 써도 무방하지만 보통은 데이터도 같이 받아와야 합니다. data를 같이 받을 때는 다음과 같이 getStaticProps
와 같이 사용하면 됩니다.
getStaticProps
함수는 params를 받아 그에 해당하는 paths를 받아옵니다. 미리 생성된 paths는 캐싱되기 때문에 라우터로 페이지를 이동할 때 굉장히 빠른 랜더링 속도를 보여줍니다.
지금까지 배운 정적함수들은 정적페이지를 만드는데 도움을 주는 함수들입니다.
대략적으로 마케팅 페이지, 블로그 포스트, E-커머스 상품 페이지, Docs 등을 사용하는데 유용합니다.
사용자가 보기 전에 미리 랜더링 해야 할 때 유용합니다.
반대로 페이지에 자주 업데이트되는 데이터가 표시되고 모든 요청에 따라 페이지 콘텐츠가 달라지는 경우 유용하지 않습니다.
이러한 경우 SWR이라는 CSR React Hook을 사용하던지 ServerSide Rendering을 사용하라고 합니다. CDN에 캐쉬를 저장하지 않아 속도는 느리지만 항상 최신상태를 유지할 수 있습니다.
이름만 들었을 땐 SSR, 즉 정적생성과 다를게 없게 느껴집니다... 그래서 굉장히 헷갈렸던 부분 중 하나!
다시 말해 Dynamic Rendering이라고 불립니다.
정의를 하고가자면,
getStaticProps
는 빌드 시에 데이터를 가져옵니다. 그래서 한번 빌드되고 나면 정적으로 움직이지 않습니다. 물론 데이터 조작을 따로 하면 가능하겠지만요..
기본적으로 빌드시!!에 처음 가져오는 데이터라고 보시면 됩니다.
getServerSideProps
는 해당 페이지가 요청될 때 마다!! 재 요청됩니다. 따라서 페이지를 이동할 때 마다 새로 불러와 느리지만 동적인 구성이 가능하다는 점이 장점입니다.
그럼 간단히 사용 예시를 봅시다.
정적생성이랑 별 다를게 없죠? ㅎㅎ
어쨋든 이 함수는 정말 필요할 때만 사용하라고 Next.js에선 권고!합니다. 왜냐하면 CDN에 캐싱되지 않기 때문에 느리거든요...
정적생성을 할 수 없을때 사용하도록 합시다~
자! 그럼 지금까지 정적생성을 배웠으니 이 함수들을 사용해서 간단하게 API Fetching 하여 제가 만들고 싶은 웹페이지를 만들어 볼까요? ㅎㅎ
코딩하는 내모습...ㅋㅋㅋㅋㅋㅋ
api fetch는 data가져올 때 마다 매번 해야하기 때문에 컴포넌트로 따로 빼는게 구성상 보기도 편하고 관리하기도 편합니다.
상위 폴더에 api라는 폴더를 만들고 그 안에 api.ts 파일들을 생성합니다.
일단 저는 코인 거래가능한 마켓리스트와 그에 해당하는 분당 차트 정보를 가져 올 api를 생성할 것입니다.
이에 해당하는 트리구조는 다음과 같습니다.
├─ api
│ ├─ index.ts
│ ├─ market.api.ts
│ ├─ candle.api.ts
├─ components
├─ pages
이렇게 api 폴더 안에 ts파일들을 생성합니다.
index.ts는 다른 컴포넌트에서 api함수들을 가져올 때 한번의 import만으로 가져올 수 있도록 모아두는 역할을 합니다.
api.ts 안에는 getApi 함수와 가져온 함수의 타입을 interface로 정해줍니다.
이런식으로 정의해서 import해서 가져다 쓰기 편하게 해줍니다.
api 컴포넌트를 따로 만들었으니 이제 Market 컴포넌트를 만들어 볼까요?
좀 길지만, 간단합니다. MarketList 함수를 만들고 list안에 Item 함수를 만들어 넣었습니다. 나중에 혹시모를 재사용성을 위해 구분한거지 한 함수 안에 같이 만들 수도 있습니다!
또한, import { MarketType } from '../../api';
로 Type을 import 해와 검사할 수 있습니다.
Market Item은 이름을 표현할 수 있게 했습니다. 나중엔 현재가격이나, 거래량 같은것도 표시해야 하지만, 지금은 일단 간단하게 만들어 보았습니다. Item을 클릭 했을 때는 /markets/[market]으로 이동하게끔 설정했습니다. 이 페이지 구성도 만들어야겠죠? 이따 만들어봅시다.
자, 컴포넌트까지 구성을 했는데,, 이 컴포넌트에는 api를 가져오는 함수가 없습니다. 왜 없을까요???
컴포넌트는 그냥 재사용 가능한 상자 같은 역할입니다. 상자 안에 내용물을 채우는 일은 pages 폴더 안에서 해야죠. 어떤 페이지에 어떤 api내용이 들어갈지 거기서 정합시당
pages 폴더는 router를 담당하는 곳입니다. 폴더 혹은 파일 이름만 지정해서 함수를 만들면 그에 해당하는 router가 자동으로 지정됩니다.
이곳에 markets 리스트를 불러오고, 흩어져있던 컴포넌트들을 가져와 화면을 구성해보도록 합니다.
각 컴포넌트들은 제가 미리 만들어둔 컴포넌트 들입니다. 추후에 수정할 계획이기 때문에 따로 소개는 안할거고 그냥 이렇게 컴포넌트를 가져와 page를 만들었다 정도만 알아두면 될 것 같네요 ㅎㅎ
getStaticProps
함수로 markets이라는 props를 가져옵니다! 아까 위에서 배웠던 그대로 사용했죠?
까다로운 부분은 getStaticPaths였습니다. paths를 가져올 api의 markets와 실제로 list에 표시되어있는 markets가 같아야 되는데 build 부분에서 자꾸 맞지 않는다고 오류가 떠서 굉장히 고생했습니다...
특히 이 부분은 따로 api를 직접 만들지 않는이상, 힘들것 같다는 생각을 했네요... 아님 아직 제가 방법을 못 찾았거나?
그래서 제일 자주 사람들이 볼 것 같은, 비트코인과 이더리움만 paths를 추가해 두었습니다.
이제 함수가 많지도 않은것 같은데 코드가 길어지네요... 코드가 길어지면 설명하기도 힘들어지고 보기도 귀찮아지니.. 간단하게만 설명하자면 getStaticPaths
로 BTC와 ETH를 캐싱하고 fallBack: true
속성으로 BTC, ETH 이외의 paths도 한번 들어가고 나오면 캐싱되게끔 한 것입니다.
revalidate
는 지정된 시간마다 api가 바뀌면 재빌드 되게끔 하는 속성이라,, 차트를 매번 가져오기 좋은 속성이라 넣어뒀는데... 원하는대로 동작하질 않네요 ㅜㅜ
이 부분은 getServerSideProps를 사용해본다던지, 좀 더 공부해보고 수정하도록 하겠습니다.
지금까지 데이터를 pre-rendering 하여 정적페이지를 만드는 방법에 대해 배워보고 활용해보았습니다. 정적페이지는 정말 좋은 것 같아요. 미리 랜더링해서 사용자에게 대기 시간 없이 보여준다라...
근데 제 홈페이지 같은 경우 매번 차트를 갱신해야 하기 때문에 정적페이지 위에 ServerSideProps로 가져와야 할 것 같기도 하구요...
추후 추가하면서 블로그를 써보도록 하겠습니다.
내용이 장황하고 길었지만,, 정적페이지를 만드시거나 공부하시려는 분에게 도움이 되었으면 좋겠습니다. 그럼 이만~!!
왕왕도움이 되었습니다ㅠㅠㅠㅠㅠ감사합니다