Studyyyyy
Next.js - react 본문
소개
React + Express.js + SSR + Router
설치
npx create-next-app@latest
실행
- npm run dev : 개발
- npm run build : 실서버 배포 버전 생성
- npm run start : 실서버 구동
Hello World
pages/index.js
import Head from 'next/head'; import styles from '../styles/Home.module.css'; export default function Home() { return ( <div> Hello World </div> ); } |
Deploy
npm run build : 배포판을 만든다.
npm run start : 배포판을 실행한다.
Vercel
- nextjs은 vercel 의 오픈소스이다.
- 코드를 github로 업로드 한다.
- vercel에서 github 프로젝트를 import 한다.
- push 하면 자동으로 디플로이 된다.
- 브랜치로 push 하면 브랜치별로 다른 서버가 활당된다.
Router
Route rule
- url에 따라서 적당한 ui를 출력해주는 기능
- next.js는 웹서버, php처럼 디렉토리 기반으로 라우팅 하는 기능을 제공
- 모든 페이지는 pages 디렉토리 하위에 있어야 한다.
- [id].js 형식으로 파일명을 만들면 id 부분이 파라미터로 처리된다.
/sub/index.js
index.js는 기본 파일이다.
export default function Sub(){ return <>Sub/index.js</> } |
/sub/[id].js
id는 파라미터로 처리된다. useRouter를 통해서 id 값에 접근할 수 있.
import {useRouter} from 'next/router'; export default function Param(){ const router = useRouter(); const id = router.query.id; return <>Sub/[id].js {id}</> } |
https://stackblitz.com/edit/nextjs-q4kffz?file=pages%2Findex.js
Link
페이지 리로드 없이 페이지를 전환하도록 돕는 컴포넌트.
import Link from 'next/link'; export default function Home() { return ( <div> <h1>index</h1> <ul> <li> <Link href="/sub">/sub/index.js</Link> </li> <li> <Link href="/sub/about">/sub/about.js</Link> </li> <li> <Link href="/sub/1">/sub/1.js</Link> </li> <li> <Link href="/sub/2">/sub/2.js</Link> </li> </ul> </div> ); } |
https://stackblitz.com/edit/nextjs-fbqbdc?file=pages/sub/[id].js
API Route
nextjs는 express 처럼 백엔드 기능이 내장되어 있습니다.
/data/topics.json
[ { "id": 1, "title": "html", "body": "html is .." }, { "id": 2, "title": "css", "body": "css is .." } ] |
/pages/api/topic/index.js
/pages/api 디렉토리는 server side api를 위한 전용공간이다.
여기에 파일을 만들면 클라이언트로 코드가 전송되지 않는다.
import topics from '../../../data/topics.json'; export default (req, res) => { res.status(200).json(topics); }; |
/pages/api/topic/[id].js
import topics from '../../../data/topics.json'; export default (req, res) => { const { id } = req.query; const topic = topics.filter(e => e.id === Number(id))[0] res.status(200).json(topic); }; |
https://stackblitz.com/edit/nextjs-wjbxta?file=pages%2Fapi%2Ftopics%2F[id].js
Server Side Rendering
위의 파일의 페이지 소스 보기 한 결과 비교
=> Nextjs는 서버에서 랜더링 된 결과를 전송한다. 순수한 리액트는 자바스크립트가 로딩 된 후에 웹페이지가 실행된다.
Next.js
https://stackblitz.com/edit/nextjs-32fuf3?file=pages%2Findex.js
<html> <body> <div id="__next" data-reactroot=""><div>Hello World</div></div> </body> </html> |
React
https://stackblitz.com/edit/react-zvroa1?file=src%2FApp.js
<!DOCTYPE html> <html> <body></body> </html> |
- CSR - Client Side Rendering
- SSR - Server Side Rendering
- SSG - Static Site Generation
- ISR - Incremental Static Regeneration
출처 : https://dev.to/pahanperera/visual-explanation-and-comparison-of-csr-ssr-ssg-and-isr-34ea
SSR과 데이터 통신
- SSR에서 가장 어려운 문제가 데이터 통신이다.
- 데이터의 신선도에 따라서 통신 방법이 다르다.
Client Side Rendering : useEffect
- 클라이언트 측에서 통신이 이루어져야 할 때 사용한다.
- 가장 신선하다.
- SEO에 취약하다.
/use_effect.js
import {useEffect, useState} from 'react'; import Link from 'next/link'; export default function(){ const [topics, setTopics] = useState([]); async function loadData(){ const URL = 'https://jsonplaceholder.typicode.com/posts'; const resp = await fetch(URL); const data = await resp.json(); setTopics(topics=>data); } useEffect(()=>{ loadData(); }); const ui = topics.map(e=><li key={e.id}>{e.title}</li>); return <div> <ol> {ui} </ol> <Link href="/">/index.js</Link> </div>; } |
https://stackblitz.com/edit/nextjs-41hvkt?file=pages%2Findex.js,pages%2Fuse_effect.js
Server Side Rendering : getServerSideProps
- getServerSideProps 는 페이지에 진입했을 때 호출된다.
- 리턴값으로 props를 전달하면 default 컴포넌트의 props로 데이터가 전달된다.
- 사용자가 접속 될 때마다 백그라운드에서 실행되고, 이것이 끝나야 응답이 시작된다.
- fetch를 할 때 URL은 주소 전체를 적어야 한다.
import Link from 'next/link'; export default function Main(props){ const topics = props.topics; const ui = topics.map(e=><li key={e.id}>{e.title}</li>); return <div> <ol> {ui} </ol> <Link href="/">/index.js</Link> </div>; } export async function getServerSideProps(context) { const URL = 'https://jsonplaceholder.typicode.com/posts'; const resp = await fetch(URL); const data = await resp.json(); return { props: {topics:data}, } } |
Static Site Generation : getStaticProps
import Link from 'next/link'; export default function Main(props){ const topics = props.topics; const ui = topics.map(e=><li key={e.id}>{e.title}</li>); return <div> <ol> {ui} </ol> <Link href="/">/index.js</Link> </div>; } export async function getServerSideProps(context) { const URL = 'https://jsonplaceholder.typicode.com/posts'; const resp = await fetch(URL); const data = await resp.json(); return { props: {topics:data}, } } |
Incremental Static Regeneration
revalidate를 10으로 지정하면 10초 후에 다시 데이터가 갱신 됩니다.
import Link from 'next/link'; export default function Main(props){ const topics = props.topics; const ui = topics.map(e=><li key={e.id}>{e.title}</li>); return <div> <ol> {ui} </ol> <Link href="/">/index.js</Link> </div>; } export async function getServerSideProps(context) { const URL = 'https://jsonplaceholder.typicode.com/posts'; const resp = await fetch(URL); const data = await resp.json(); return { props: {topics:data}, revalidate:10 } } |
환경변수
환경마다 접속 주소 등이 달라지는 경우 환경변수를 이용한다.
.env.local
이 파일을 최상위 디렉토리에 만들어두면 개발환경이 만들어질 때 환경변수를 읽어서 앱에 공급한다.
API_URL = http://localhost:3000/
API_URL = http://localhost:3000/ |
/pages/rendering/ssr.js
process.env.API_URL 를 통해서 환경변수에 접근할 수 있다.
import Link from 'next/link'; export default function Main(props){ const topics = props.topics; const ui = topics.map(e=><li key={e.id}>{e.title}</li>); return <div> <ol> {ui} </ol> <Link href="/">/index.js</Link> </div>; } export async function getServerSideProps(context) { let URL = process.env.API_URL+'api/topic'; const resp = await fetch(URL); const data = await resp.json(); return { props: {topics:data}, } } |
Vercel