
코딩앙마
강의 목록 : https://www.youtube.com/playlist?list=PLZKTXPmaJk8Lx3TqPlcEAzTL8zcpBz7NP
vscode 확장 패키지 추가
1. - Auto Import - ES6, TS, JSX, TSX
2. - Reactjs code snippets
3. - ESLint
4. - Prettier - Code formatter
Visual Studio Code 폴더/파일 아이콘 변경하기
리액트 프로젝트 생성
npx create-react-app 경로
예) npx create-react-app E:\react-app2
넥스트 프로젝트 생성
$ npx create-next-app nextjs-tutorial
nstall next, react and react-dom in your project: npm install next react react-dom # or yarn add next react react-dom # or pnpm add next react react-dom
공식문서 : https://nextjs.org/docs
개발 서버 실행
$npm run dev
빌드
$npm run build
빌드처리된 앱 실행
$npm run start
1.소개, 페이지 레이아웃 (Intro, Page layout)
2.Dynamic Routes, next/link
3.서버사이드 렌더링 (Server-side Rendering/SSR/Dynamic Rendering)
4.에러 페이지, 환경 변수 (Custom Error Page, Environment Variables)
5.정적 생성(Static Generation) - getStaticProps, getStaticPaths
6.isFallback, getStaticPaths
7.API Routes, 로그인 구현
1.소개, 페이지 레이아웃 (Intro, Page layout)
*create-next-app 으로 설치하면
1.컴파일과 번들링이 자동으로 된다.(webpack 과 babel)
2.자동 리프레쉬 기능으로 수정하면 화면에 바로 반영된다.
3.서버사이드 렌더링이 지원된다.
4.스태틱 파일을 지원합니다.
Semantic UI React -> The official Semantic-UI-React integration.
설치:
$ yarn add semantic-ui-react semantic-ui-css ## Or NPM $ npm install semantic-ui-react semantic-ui-css

_app.js
import '../styles/globals.css'
import 'semantic-ui-css/semantic.min.css'
import Footer from '../src/component/Footer'
import Top from '../src/component/Top'
function MyApp({ Component, pageProps }) {
return (
<div style={{ width: 1000, margin: " 0 auto" }}>
<Top />
<Component {...pageProps} />
<Footer />
</div>
)
}
export default MyApp
/***
* _app.js
* 페이지 전환시 레이아웃을 유지할 수 있습니다.
* 페이지 전환시 상태값을 유지할 수 있습니다.
* componentDidCatch를 이용해서 커스텀 에러 핸들링을 할 수 있습니다.
* 추가적인 데이터를 페이지로 주입시켜주는게 가능합니다.
* 글로벌 CSS 를 이곳에 선언합니다.
*
*/
Custom Document : https://nextjs.org/docs/advanced-features/custom-document
_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
render() {
return (
<Html lang='ko'>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument;
//_document.js 는 서버에서만 랜더링 되지만
//이벤트 핸들러는 작동하지 않는다.
//_document 사용하는 Head 와 _app에서 사용하는 head 는 다르다.
index.js
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
export default function Home() {
return (
<div>
<Head>
<title>Home | 코딩앙마</title>
</Head>
create-next-app 으로 설치하면
<br />
1.컴파일과 번들링이 자동으로 된다.(webpack 과 babel)
<br />
2.자동 리프레쉬 기능으로 수정하면 화면에 바로 반영된다.
<br />
3.서버사이드 렌더링이 지원된다.
<br />
4.스태틱 파일을 지원합니다.
</div>
)
}
컴포넌트
Top.js
/* eslint-disable @next/next/no-img-element */
import React from 'react';
import { Header } from 'semantic-ui-react';
import Gnb from './Gnb';
const Top = () => {
return (
<div>
<div style={{ display: "flex", paddingTop: 20 }}>
<div style={{ flex: "100px 0 0" }}>
<img src="/images/angma.png" alt='logo' style={{ display: "block", width: 80 }} />
</div>
<Header as="h1">코딩앙마</Header>
</div>
<Gnb />
</div >
);
};
export default Top;
Gnb.js
import React, { Component } from 'react'
import { Menu } from 'semantic-ui-react'
const Gnb = () => {
const activeItem = "home";
return (
<Menu inverted>
<Menu.Item
name='home'
active={activeItem === 'home'}
//onClick={this.handleItemClick}
/>
<Menu.Item
name='messages'
active={activeItem === 'messages'}
// onClick={this.handleItemClick}
/>
<Menu.Item
name='friends'
active={activeItem === 'friends'}
//onClick={this.handleItemClick}
/>
</Menu>
);
};
export default Gnb;
Footer.js
import React from 'react';
const Footer = () => {
return (
<div>Copyright @ 코딩앙마. All right reserved.</div>
);
};
export default Footer;
2.Dynamic Routes, next/link
axios 설치
npm i axios yarn add axios
const API_URL = "http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline";
herokuapp.com 데이터가 원할하지 않은 관계로 thedogapi.com api 사용 (무료) - 회원 가입후 apikey 발급후 적용 할것.
https://docs.thedogapi.com/api-reference/breeds/breeds-list
const API_URL = "https://api.thedogapi.com/v1/breeds";
const API_KEY = ApiKey;



index.js
import axios from 'axios';
import Head from 'next/head'
import Image from 'next/image'
import { useEffect, useState } from 'react';
import styles from '../styles/Home.module.css'
import ItemList from './../src/component/ItemList';
import { Header, Divider } from 'semantic-ui-react';
import ApiKey from '../ApiKey.js';
export default function Home() {
const [list, setList] = useState([]);
//const API_URL = "http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline";
//thedogapi.com api 사용
//https://docs.thedogapi.com/api-reference/breeds/breeds-list
const API_URL = "https://api.thedogapi.com/v1/breeds";
const API_KEY = ApiKey;
function getData() {
axios.get(API_URL, {
headers: {
'x-api-key': API_KEY
}
})
.then(res => {
// console.log("res.data :", res.data);
setList(res.data);
});
}
useEffect(() => {
getData();
}, []);
return (
<div>
<Head>
<title>Home | 코딩앙마</title>
</Head>
<Header as="h3" style={{ paddingTop: 40, marginBottom: 40 }}>VIP 분양</Header>
<Divider />
<ItemList list={list.slice(0, 12)} />
<Header as="h3" style={{ paddingTop: 80, marginBottom: 40 }}>베스트 분양</Header>
<Divider />
<ItemList list={list.slice(12, 24)} />
<Header as="h3" style={{ paddingTop: 80, marginBottom: 40 }}>실시간 분양</Header>
<Divider />
<ItemList list={list.slice(24, 60)} />
</div>
)
}
ItemList.js
/* eslint-disable @next/next/no-img-element */
/* eslint-disable jsx-a11y/alt-text */
import { Grid } from 'semantic-ui-react'
import styles from "./ItemList.module.css";
import Link from 'next/link'
const ItemList = ({ list }) => {
function temperament(item) {
if (item !== undefined) {
let temp = item.temperament.split(",");
return (`${temp[0]}, ${temp[1]}, ${temp[2]}`);
} else {
return null;
}
}
return (
<div>
{/* divided */}
<Grid columns={3} >
<Grid.Row >
{list.map((item) => (
<Grid.Column key={item.id} alt={item.name}>
<Link href={`/view/${item.name}`}>
<a>
<div className={styles.wrap}>
{/* <img src={item.image_link} alt={item.name} className={styles.img_item} /> */}
<img src={item.image.url} alt={item.name} className={styles.img_item} />
<strong className={styles.tit_item}>{item.name}</strong>
<span className={styles.txt_info}><span className={styles.bold}>품종</span> : {item.bred_for}</span>
<span className={styles.txt_info}>
몸무게 : {item.weight.metric}kg, 수명 : {item.life_span}
</span>
<strong className={styles.txt_info}>기질: {temperament(item)}</strong>
</div>
</a>
</Link>
</Grid.Column>
))}
</Grid.Row>
</Grid>
</div >
);
};
export default ItemList;
ItemList.module.css
.wrap{
padding-bottom: 20px;
text-align: center;
}
.img_item{
display: block;
margin: 0 auto;
max-width: 320px;
aspect-ratio: 16 / 9;
max-height: 180px;
}
.tit_item{
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
width: 160px;
margin: 10px auto;
}
.txt_info{
display: block;
margin-bottom: 10px;
color: #999;
text-align: left;
}
.num_price{
font-size: 17px;
color: #00bcd4;
font-weight: bold;
}
.bold{
font-weight: bold;
}
[id].js
import { useRouter } from 'next/router'
import Axios from 'axios';
import { useEffect, useState } from 'react';
import API_KEY from './../../ApiKey';
import Item from './../../src/component/Item';
const Post = () => {
const router = useRouter()
const { id } = router.query;
const [item, setItem] = useState({});
// const API_URL = `http://makeup-api.herokuapp.com/api/v1/products/${id}.json`;
//thedogapi.com api 사용
//https://docs.thedogapi.com/api-reference/breeds/breeds-list
const API_URL = `https://api.thedogapi.com/v1/breeds/search`;
function getData() {
console.log("id : ", id);
Axios.get(API_URL, {
headers: {
'x-api-key': API_KEY,
Authorization: `token ${API_KEY}`
},
params: {
name: id
}
}).then(res => {
console.log("res.dat : ", res.data);
setItem(res.data)
});
}
useEffect(() => {
if (id && id !== undefined) {
getData();
}
}, [id]);
return <Item item={item[0]} />
}
export default Post
Item.js
/* eslint-disable @next/next/no-img-element */
import { Button } from 'semantic-ui-react';
import styles from './Item.module.css';
/*
bred_for: "Small rodent hunting, lapdog"
breed_group: "Toy"
height: {imperial: '9 - 11.5', metric: '23 - 29'}
id: 1
life_span: "10 - 12 years"
name: "Affenpinscher"
origin: "Germany, France"
reference_image_id: "BJa4kxc4X"
temperament: "Stubborn, Curious, Playful, Adventurous, Active, Fun-loving"
weight: {imperial: '6 - 13', metric: '3 - 6'}
*/
const Item = ({ item }) => {
return (
<>{item !== undefined &&
<>
<div className={styles.wrap}>
<div className={styles.img_item}>
<img src={`https://cdn2.thedogapi.com/images/${item.reference_image_id}.jpg`} alt={item.name} />
</div>
<div className={styles.info_item}>
<strong className={styles.tit_item}>{item.name}</strong>
</div>
<div className={styles.info_item}>
<Button color="orange">분양 받기</Button>
</div>
</div>
<div className={styles.wrap2}>
{item.bred_for && <div className={styles.txt_info}><span>품종:</span>{item.bred_for}</div>}
{item.breed_group && <div className={styles.txt_info}><span>품종 그룹:</span>{item.breed_group}</div>}
<div className={styles.txt_info}><span>몸무게:</span> {item.weight.metric}kg</div>
<div className={styles.txt_info}><span>신장:</span> {item.height.metric}cm</div>
<div className={styles.txt_info}><span>수명 :</span> {item.life_span}</div>
{item.origin && <div className={styles.txt_info}><span>혈통 :</span> {item.origin}</div>}
<div className={styles.txt_info}><span>기질: </span>{item.temperament}</div>
</div>
</>
}
</>
);
};
export default Item;
Item.module.css
.wrap {
margin-top: 10px;
display: flex;
padding: 10px 0 20px 0x;
/* border-bottom: 1px solid #ccc; */
}
.wrap2 {
padding: 10px 0 40px 0x;
border-bottom: 1px solid #ccc;
}
.img_item {
flex: 70% 0 0;
}
.img_item img {
display: block;
width: 80%;
height: auto;
aspect-ratio: 16/9;
box-shadow: 5px 5px 5px rgba(0, 0, 0,0.2);
}
.info_item {
display: flex;
justify-content: center;
align-items: center;
}
.tit_item {
display: block;
font-size: 2.2rem;
margin-right: 20px;
line-height: 1.5;
}
.num_price {
display: block;
font-size: 34px;
margin-top: 20px;
color: #00bcd4;
}
.txt_info {
display: block;
font-size: 17px;
margin: 20px 0 40px;
}
.txt_info span{
margin-right: 20px;
}
3.서버사이드 렌더링 (Server-side Rendering/SSR/Dynamic Rendering)
Next js 모든 페이지 사전 레더링 (Pre-rendering)
더 좋은 퍼포먼스
검색엔진최적화 (SEO)
1.정적 생성
2.Server Side Rendering (SSR, Dynamic Rendering)
차이점은 언제 html 파일을 생성하는 가
[정적 생성]
- 프로젝트가 빌드하는 시점에 html 파일들이 생성
- 모든 요청에 재사용
- 퍼포먼스 이유로, 넥스트 js 는 정적 생성을 권고
- 정적 생성된 페이지들은 CDN에 캐시
- getStaticProps / getStaticPaths
[서버사이드 렌더링]은 매 요청마다 html 을 생성
- 항상 최신 상태 유지
- getServerSideProps
next.js는 프리렌더링(pre-rendering) 기능을 제공합니다.
말 그대로 사전에 미리 html을 렌더링 한다는 것인데요 즉 html을 미리 생성하고 최소한의js를 연결시킨 후 클라이언트에서 요청이 들어오면 해당 html을 로드하면서 나머지js를 불러와 화면에 렌더링 시켜주는 것이죠.
next.js는 주로 두가지 프리렌더링 방법을 제공합니다
- Server-side rendering: SSR
- Static site rendering: SSG
next.js의 프리렌더링 으로 각 page에 getServerSideProps ,getStaticProps, getStaticPaths 를 사용해서 데이터를 가져올수 있습니다
요청에 따라 페이지가 데이터를 계속 업데이트해야한다 ! 그러면 getServerSideProps 를 사용하자!
요청이 들어와도 데이터의 변화는 없고 미리 렌더링해두어도 괜찮다 그러면 getStaticProps 를 사용하자!
1. getServerSideProps (SSR)
getServerSideProps 는 요청할때마다 html이 생성되기 때문에 데이터가 계속 업데이트 됩니다.
요청할때마다 데이터를 계속 불러오는 것이죠.
그래서 데이터를 새로 받아오면 그 데이터로 페이지가 렌더링 됩니다.
//page
function Page({ data }) {
...
}
export async function getServerSideProps() {
const res = await axios.get(`https://localholst:3065/user`)
const data = res.data
return { props: { data } }
}
page를 사용자가 요청하면 getServerSideProps 를 먼저 실행후 프론트가 서버에 직접요청 후 데이터를 받아와서 page 컴포넌트에 date를 props로 전달하여 렌더링 할 수 있습니다.
getServerSideProps 는 계속 데이터가 바뀌어야하는 페이지의 경우 사용합니다.
2. getStaticProps, getStaticPaths (SSG)
html이 빌드타임에 생성됩니다.
빌드할때 데이터를 가져와서 html 을 생성후 사용자의 요청이 들어올때마다 빌드된 html 을 재사용합니다.
//pages/users/[id].js
function Page({ data }) {
const router = useRouter()
if (router.isFallback) {
return <div>Loading...</div>
}
...
}
export async function getStaticPaths() {
const posts = await axios.get("https://jsonplaceholder.typicode.com/posts");
const paths = posts.map(({ id }) => ({ params: { id: `${id}` } }));
// params: {id : '1'},{id : '2'}...
return {
paths,
fallback: true,
};
}
export async function getStaticProps() {
const res = await axios.get(`https://localholst:3065/user`)
const data = res.data
return { props: { data } }
}
출처 :https://chaeyoung2.tistory.com/53
[id].js
import { useRouter } from 'next/router'
import Axios from 'axios';
import { useEffect, useState } from 'react';
import API_KEY from './../../ApiKey';
import Item from './../../src/component/Item';
import { Loader } from 'semantic-ui-react';
import Head from 'next/head';
const Post = ({ item }) => {
// const router = useRouter()
// const { id } = router.query;
// const [item, setItem] = useState({});
// const [isLoading, setIsLoading] = useState(true);
// // const API_URL = `http://makeup-api.herokuapp.com/api/v1/products/${id}.json`;
// //thedogapi.com api 사용
// //https://docs.thedogapi.com/api-reference/breeds/breeds-list
// const API_URL = `https://api.thedogapi.com/v1/breeds/search`;
// function getData() {
// //console.log("id : ", id);
// Axios.get(API_URL, {
// headers: {
// 'x-api-key': API_KEY,
// Authorization: `token ${API_KEY}`
// },
// params: {
// name: id
// }
// }).then(res => {
// setItem(res.data);
// setIsLoading(false);
// });
// }
// useEffect(() => {
// if (id && id !== undefined) {
// getData();
// }
// }, [id]);
return (
<>
{/* {isLoading && <div style={{ padding: "300px 0" }}>
<Loader inline="centered" active>Loading</Loader>
</div>
}
{!isLoading && <Item item={item[0]} />} */}
{item &&
<>
<Head>
<title>{item[0].name}</title>
<meta name="description" content={item[0].bred_for}></meta>
</Head>
<Item item={item[0]} />
</>
}
</>
)
}
export default Post;
export async function getServerSideProps(context) {
const id = context.params.id
const API_URL = `https://api.thedogapi.com/v1/breeds/search?name=${id}`;
const res = await Axios.get(API_URL, {
headers: { 'x-api-key': API_KEY, Authorization: `token ${API_KEY}` }
});
const data = res.data;
return {
props: {
item: data
}
}
}
4.에러 페이지, 환경 변수 (Custom Error Page, Environment Variables)
Gnb.js
import { Menu } from 'semantic-ui-react'
import { useRouter } from 'next/router';
const Gnb = () => {
const router = useRouter();
let activeItem;
if (router.pathname === "/") {
activeItem = "home";
} else if (router.pathname === "/about") {
activeItem = "about";
}
function goLink(e, data) {
if (data.name === "home") {
router.push("/");
} else if (data.name === "about") {
router.push("/about");
}
}
return (
<Menu inverted>
<Menu.Item name='home' active={activeItem === 'home'} onClick={goLink} />
<Menu.Item name='about' active={activeItem === 'about'} onClick={goLink} />
<Menu.Item name='Contact Us' active={activeItem === 'contact'}
onClick={() => {
router.push("/contact");
}}
/>
</Menu>
);
};
export default Gnb;
*에러페이지 설정
개발 모드 실행이 아니라 빌드 모드 실행 해서 500 에러 페이지 확인
$ npm run build
$ npm start
nextjs 공식 문서 500 : https://nextjs.org/docs/advanced-features/custom-error-page#500-page
nextjs 에서 기본적으로 에러페이지는 표시되며, 커스텀 페이지를 만들기 위해서는
pages 디렉토리에 다음과 같은 파일 명으로 404.js, _error.js 파일을 생성 하면 끝이다.
1 )404.js

import { Icon } from 'semantic-ui-react';
export default function Error404() {
return (
<div style={{
padding: "200px 0", textAlign: "center", fontSize: 30
}}>
<Icon name="warning circle" color='red' />
404 : 페이지를 찾을 수 없습니다.
</div>
);
}
2)_error.js

function Error({ statusCode }) {
return (
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: 'An error occurred on client'}
</p>
)
}
Error.getInitialProps = ({ res, err }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404
return { statusCode }
}
export default Error
* 환경 변수 설정
root 디렉토리에 다음과 같은 두개 파일 생성

1).env.production
name=DEVELOPMENT NEXT_PUBLIC_NAME=DEVELOPMENT NEXT_PUBLIC_MAKEUP_API_URL=http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline NEXT_PUBLIC_DOG_API_URL=https://api.thedogapi.com/v1/breeds
2).env.development
name=PRODUCTION NEXT_PUBLIC_NAME=PRODUCTION NEXT_PUBLIC_MAKEUP_API_URL=http://makeup-api.herokuapp.com/api/v1/products.json?brand=dior NEXT_PUBLIC_DOG_API_URL=https://api.thedogapi.com/v1/breeds
사용법
1) nodejs 환경 에서는 process.env.변수명 으로 사용
[id].js 파일 예
process.env.name 와 같이 접근한다
//nodejs 환경
export async function getServerSideProps(context) {
const id = context.params.id
const API_URL = `https://api.thedogapi.com/v1/breeds/search?name=${id}`;
const res = await Axios.get(API_URL, {
headers: { 'x-api-key': API_KEY, Authorization: `token ${API_KEY}` }
});
const data = res.data;
return {
props: {
item: data,
name: process.env.name
}
}
}
2) browser 환경에서는 process.env.NEXT_PUBLIC_변수명 으로 사용한다.
index.js 파일 예
process.env.NEXT_PUBLIC_NAME 로 사용
const API_URL = process.env.NEXT_PUBLIC_DOG_API_URL;
const ENV = process.env.NEXT_PUBLIC_NAME;
const API_KEY = ApiKey;
console.log("프젝트환경 : ", ENV, " , API_URL : ", API_URL);
5.정적 생성(Static Generation) - getStaticProps, getStaticPaths
Pre-rendering(사전 렌더링) 기본적으로 모든 페이지 pre-render 사전에 HTML 파일들을 만든다는 의미 퍼포먼스 향상,
SEO
Pre-rendering(사전 렌더링)
Static Generation : 정적 생성
Server-side Rendering : 서버 사이드 렌더링
Static Generation : 정적 생성
- 마케팅 페이지
- 블로그 게시물
- 제품 목록
- 도움말 , 문서
Server-side Rendering
- 항상 최신 상태 유지
- 관리자 페이지
- 분석 차트




Static Generation : 정적 생성으로 설정한 페이지는
npm run build 시 html 파일들이 생성되거나 또는 운영중에 해당 페이지 로딩 및 접속시 html 파일들이 생성 된다.
한번 html 파일들에 의해 다음 접속시에는 빠르게 해당 페이지를 접속할 수 있게 된다.

예1) Server-side Rendering
index.js
/* eslint-disable react-hooks/exhaustive-deps */
import axios from 'axios';
import Head from 'next/head'
import Image from 'next/image'
import { useEffect, useState } from 'react';
import styles from '../styles/Home.module.css'
import ItemList from './../src/component/ItemList';
import { Header, Divider, Loader } from 'semantic-ui-react';
import ApiKey from '../ApiKey.js';
export default function Home({ list }) {
return (
<div>
<Head>
<title>Home | 강아지 분양소 | 도그파크</title>
<meta name="description" content='도그 파크 홈입니다.' ></meta>
</Head>
<>
<Header as="h3" style={{ paddingTop: 40, marginBottom: 40 }}>VIP 분양</Header>
<Divider />
<ItemList list={list.slice(0, 12)} />
<Header as="h3" style={{ paddingTop: 80, marginBottom: 40 }}>베스트 분양</Header>
<Divider />
<ItemList list={list.slice(12, 24)} />
<Header as="h3" style={{ paddingTop: 80, marginBottom: 40 }}>실시간 분양</Header>
<Divider />
<ItemList list={list.slice(24, 60)} />
</>
</div>
)
}
//nodejs 환경
export async function getServerSideProps(context) {
const API_URL = process.env.NEXT_PUBLIC_DOG_API_URL;
const API_KEY = ApiKey;
const res = await axios.get(API_URL, {
headers: { 'x-api-key': API_KEY, Authorization: `token ${API_KEY}` }
});
const data = res.data;
return {
props: {
list: data,
name: process.env.name
}
}
}
예2 ) Static Generation : 정적 생성
cosmetics.js
/* eslint-disable react-hooks/exhaustive-deps */
import axios from 'axios';
import Head from 'next/head'
import CosmeticsItemList from '../src/component/CosmeticsItemList';
import { Header, Divider, Loader } from 'semantic-ui-react';
/**
* 정적 페이지 테스트
* @param {*} param0
* @returns
*/
export default function Cosmetics({ list }) {
console.log(list);
return (
<div>
<Head>
<title>Home | 강아지 분양소 | 도그파크</title>
<meta name="description" content='도그 파크 홈입니다.' ></meta>
</Head>
<>
<Header as="h3" style={{ paddingTop: 40 }}>
베스트 상품
</Header>
<Divider />
<CosmeticsItemList list={list.slice(0, 9)} />
<Header as="h3" style={{ paddingTop: 40 }}>
신상품
</Header>
<Divider />
<CosmeticsItemList list={list.slice(9)} />
</>
</div>
)
}
export async function getStaticProps() {
const apiUrl = process.env.apiUrl;
const res = await axios.get(apiUrl);
const data = res.data;
return {
props: {
list: data,
neme: process.env.name
}
}
}
//740, 730, 729 3개의 아이디만 정적인 html 파일로 컴파일 되어 보여 줌
//fallback false 740, 730, 729 아닌 아이디는 404 페이지를 보여 줌
detail/[id].js
import Axios from "axios";
import Head from "next/head";
import Item from "../../src/component/CosmeticsItem";
const Post = ({ item, name }) => {
return (
<>
{item && (
<>
<Head>
<title>{item.name}</title>
<meta name="description" content={item.description}></meta>
</Head>
{name} 환경 입니다.
<Item item={item} />
</>
)}
</>
);
};
export default Post;
export async function getStaticPaths() {
//740, 730, 729 3개의 아이디만 정적인 html 파일로 컴파일 되어 보여 줌
//fallback false 740, 730, 729 아닌 아이디는 404 페이지를 보여 줌
return {
paths: [
{ params: { id: "740" } },
{ params: { id: "730" } },
{ params: { id: "729" } },
],
fallback: true,
};
}
export async function getStaticProps(context) {
const id = context.params.id;
const apiUrl = `http://makeup-api.herokuapp.com/api/v1/products/${id}.json`;
const res = await Axios.get(apiUrl);
const data = res.data;
return {
props: {
item: data,
name: process.env.name,
},
};
}
6.isFallback, getStaticPaths
detail/[id].js
import Axios from "axios";
import Head from "next/head";
import Item from "../../src/component/CosmeticsItem";
import { useRouter } from 'next/router';
import { Loader } from "semantic-ui-react";
const Post = ({ item, name }) => {
const router = useRouter();
//console.log("isFallback : ", router.isFallback)
//로딩시 fallback 은 처음 true 였다가 false 변경 처리된다.
//isFallback : true
//isFallback: false
if (router.isFallback) {
return (
<div style={{ padding: "100px 0" }}>
<Loader active inline="centered">
Loading
</Loader>
</div>
)
}
return (
<>
{item && (
<>
<Head>
<title>{item.name}</title>
<meta name="description" content={item.description}></meta>
</Head>
{name} 환경 입니다.
<Item item={item} />
</>
)}
</>
);
};
export default Post;
export async function getStaticPaths() {
//740, 730, 729 3개의 아이디만 정적인 html 파일로 컴파일 되어 보여 줌
//fallback false 740, 730, 729 아닌 아이디는 404 페이지를 보여 줌
const apiUrl = process.env.apiUrl;
const res = await Axios.get(apiUrl);
const data = res.data;
return {
// paths: [
// { params: { id: "740" } },
// { params: { id: "730" } },
// { params: { id: "729" } },
// ],
paths: data.slice(0, 9).map((item) => ({
params: { id: item.id.toString() }
})),
fallback: true,
};
}
export async function getStaticProps(context) {
const id = context.params.id;
const apiUrl = `http://makeup-api.herokuapp.com/api/v1/products/${id}.json`;
const res = await Axios.get(apiUrl);
const data = res.data;
return {
props: {
item: data,
name: process.env.name,
},
};
}
7.API Routes, 로그인 구현


pages/login.js

import { Button, Form } from "semantic-ui-react"
import Axios from 'axios';
import { useRouter } from 'next/router';
const Login = () => {
const router = useRouter();
function login() {
Axios.post('/api/login', {
}).then(res => {
if (res.status === 200) {
//로그인 성공
router.push("/admin");
}
})
}
return (
<div style={{ padding: "100px 0", textAlign: "center" }} >
<Form>
<Form.Field inline>
<input placeholder="ID" />
</Form.Field>
<Form.Field inline>
<input type="password" placeholder="Password" />
</Form.Field>
<Button color="blue" onClick={login} >Login</Button>
</Form>
</div>
);
};
export default Login;
pages/admin.js

import { useRouter } from 'next/router';
import Axios from 'axios';
import { useEffect, useState } from 'react';
import { Button } from 'semantic-ui-react';
const Admin = () => {
const router = useRouter();
const [isLogin, setIsLogin] = useState(false);
function checkLogin() {
Axios.get("/api/isLogin").then((res) => {
if (res.status === 200 && res.data.name) {
//로그인
setIsLogin(true);
} else {
//로그인
router.push("/login");
}
});
}
function logout() {
Axios.get("/api/MyLogout")
.then(res => {
if (res.status === 200) {
router.push("/");
}
})
}
useEffect(() => {
checkLogin();
}, []);
return (
<>admin
{isLogin && <Button onClick={logout} >Logout</Button>}
</>
);
};
export default Admin;
pages/api/isLogin.js
export default function handler(req, res) {
res.statusCode = 200;
res.json({ name: req.cookies.a_name });
}
pages/api/login.js
export default function handler(req, res) {
// res.statusCode = 200;
// res.json({ name: null });
if (req.method === "POST") {
res.setHeader("Set-Cookie", "a_name=Mike;Max-Age=3600;HttpOnly,Secure");
res.statusCode = 200;
res.json({ message: "ok" });
}
}
pages/api/MyLogout.js
/* eslint-disable import/no-anonymous-default-export */
export default (req, res) => {
res.setHeader("Set-Cookie", "a_name=Mike;Max-Age=0;HttpOnly,Secure");
res.statusCode = 200;
res.json({ message: "ok" });
};
소스 : https://github.com/braverokmc79/nextjs-tutorial














댓글 ( 4)
댓글 남기기