일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- 앱개발
- K디지털기초역량훈련
- 웹개발
- 국비지원코딩
- 국비지원파이썬
- 고용노동부국비지원
- 러닝핏인강
- Udemy
- K디지털크레딧
- 플러터
- 부트캠프
- 0원코딩인강
- 러닝핏습관챌린지
- 웅진씽크빅
- ios
- 인사이드아웃
- Flutter
- IT개발캠프
- 러닝핏
- 유데미
- 내일배움투어
- 코딩국비지원
- 내일배움카드인강
- 개발
- 국비코딩
- 개발자부트캠프
- 스나이퍼팩토리
- 내일배움카드사용처
- 습관챌린지
- 안드로이드
- Today
- Total
매일 땡기는 마라 코딩
[노마드 코더] NextJS 시작하기(2) 본문
NextJS 시작하기 – 노마드 코더 Nomad Coders
The React Framework for Production
nomadcoders.co
GitHub - nomadcoders/nextjs-fundamentals: Learning NextJS by Building a Tiny Movie Website.
Learning NextJS by Building a Tiny Movie Website. Contribute to nomadcoders/nextjs-fundamentals development by creating an account on GitHub.
github.com
#2 PRACTICE PROJECT
#2.1 Patterns
- children은 react.js가 사용하는 prop로, 하나의 component를 다른 component 안에 넣을 때 쓸 수 있다.
- _app.js 파일의 크기를 키우지 않게 하기 위해 아래 코드와 같이 Layout 컴포넌트를 만들어 childern을 사용할 수 있다.
app.js
import Layout from "@/components/Layout";
export default function App({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
Layout.js
import NavBar from "./NavBar";
export default function Layout({children}) {
return <>
<NavBar/>
<div>{children}</div>
</>
}
- next.js에서는 head component 패키지를 제공해 준다. 다음과 같이 사용할 수 있다.
Heads.js
import Head from "next/head";
export default function Heads({title}){
return <Head>
<title>{title} | Next Movies</title>
</Head>
}
#2.1 Fetching Data
- api key 연동 및 데이터 불러오기
- map, async await에 대한 선수 지식이 필요하다.
index.js
import { useEffect, useState } from "react";
import Heads from "@/components/Heads";
const API_KEY = "<key>";
export default function Home() {
const [movies, setMovies] = useState([]);
useEffect(() => {
(async () => {
const { results } = await (
await fetch(
`https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`
)
).json();
setMovies(results);
})();
}, []);
return (
<div>
<Heads title="Home" />
{!movies && <h4>Loading...</h4>}
{movies?.map((movie) => (
<div key={movie.id}>
<h4>{movie.original_title}</h4>
</div>
))}
</div>
);
}
#2.2 Redirect and Rewrite
- API가 유료거나 사용량이 정해져 있는 경우에 API를 소스코드에서 숨기는 것이 좋다.
- next.js에는 유저가 접속한 경로를 변경하는 redirect와 reauest에 마스크를 씌우는 것 같은 rewrite라는 기능이 있다.
- redirects를 하는 방법은 다음과 같다.
- 유저가 특정 sorce로 방문할 경우,
- 특정 destination으로 보낸다.
- permanet가 영구적인지 아닌지 설정한다.
- redirects를 할 때 :로 소스의 특정 주소를 유지하게 하거나, *로 특정 위치의 뒷 부분 주소를 모두 감지하여 유지하게 할 수 있다.
- rewrites를 하는 방법은 다음과 같다.
- sorce에 해당하는 request 경로를,
- destination 경로에 매핑한다.
next.config.js
//const API_KEY = "<key>";
const API_KEY = process.env.API_KEY;
module.exports = {
reactStrictMode: true,
async redirects(){
return [
{
source:"/old-blog/:path",
destination:"/new-sexy-blog/:path",
permanent: false,
},
// {
// 다른 내용을 작성할 수 있다.
// },
];
},
async rewrites() {
return [
{
source: "/api/movies",
destination: `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`,
},
];
}
}
index.js
//생략
useEffect(() => {
(async () => {
const { results } = await (await fetch(`/api/movies`)).json();
setMovies(results);
})();
}, []);
//생략
.env
# github 연동 안 되도록 해야 한다.
API_KEY=<key>
.gitignore
//생략
# vercel
.vercel
.env
//생략
#2.3 Server Side Rendering
- 로딩 화면을 보여 주지 않고, fetch나 데이터 작업을 모두 마치고 API 로딩이 완료되었을 때 렌더링을 하도록 하고 싶다면?
- 페이지가 오직 server side render만 할 수 있게 선택하게 하는 함수 getServerSideProps를 사용하면 된다.
- 함수 안에 적은 코드는 client 쪽이 아닌, server(백엔드) 쪽에서 작동한다.
- client에게 보여지지 않기 때문에 함수 안에 Key와 같은 중요 코드를 적어 숨길 수도 있다.
- 함수 내에서 return하는 값을 props로써 page에게 줄 수 있다.
- API load가 느릴 경우 유저가 아무것도 보지 못한 채로 오래 기다릴 수 있다는 단점이 있다.
- 소스 코드가 HTML로 이루어지게 되기 때문에, JS가 비활성화되어도 사이트 내용을 볼 수 있다.
- search engine에 유리하다.
index.js
import Heads from "@/components/Heads";
export default function Home({results}) {
return (
<div>
<div className="container">
<Heads title="Home"/>
{results?.map((movie) => (
<div className="movie" key={movie.id}>
<img src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`} />
<h4>{movie.original_title}</h4>
</div>
))}
</div>
//중략
</div>
);
}
export async function getServerSideProps(){
const { results } = await (await fetch(`http://localhost:3000/api/movies`)).json();
return{
props: {
results,
},
};
}
#2.5 Dynamic Routes
- URL에 변수를 넣는 방법.
- /movies/all을 URL을 만들고 싶다면? -> movies 폴더를 만든 뒤, 폴더 안에 all.js 파일 생성.
- /movies URL도 만들고 싶다면? -> movies 폴더 안에 index.js 파일 생성.
- /movies/12와 같이 id 값으로 URL을 만들고 싶다면? -> movies 폴더 안에 [아이디명].js 파일 생성. 예를 들어 [id].js
/movies/[id].js
import { useRouter } from "next/router";
export default function Detail(){
const router = useRouter();
console.log(router);
return "deail";
}
#2.6 Movie Detail
- 이전에 만든 URL과 영화를 매핑해 영화 컴포넌트를 클릭하면 영화 id가 포함된 URL로 이동하게 하는 방법.
- 기본적인 방법.
index.js
//생략
<Link href={`/movies/${movie.id}`} key={movie.id}>
<div className="movie" key={movie.id}>
<img src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`} />
<h4>{movie.original_title}</h4>
</div>
</Link>
//생략
- router hook을 사용한 방법.
index.js
//생략
const router = useRouter();
const onClick = (id) =>{
router.push(`/movies/${id}`)
}
return (
<div>
<div className="container">
<Heads title="Home"/>
{results?.map((movie) => (
<div onClick={()=>onClick(movie.id)} className="movie" key={movie.id}>
<img src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`} />
<h4>{movie.original_title}</h4>
</div>
))}
</div>
//생략
next.config.js
//생략
async rewrites() {
return [
{
source: "/api/movies",
destination: `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`,
},
{
source: "/api/movies/:id",
destination: `https://api.themoviedb.org/3/movie/:id?api_key=${API_KEY}`,
},
];
}
- URL에서 URL로 state를 넘겨 주는 방법과 유저로부터 숨기는 방법.
index.js
//생략
const onClick = (id, title) => {
router.push(
{
pathname: `/movies/${id}`,
query: {
id,
title,
},
},
`/movies/${id}`
);
}
return (
<div>
<div className="container">
<Heads title="Home" />
{results?.map((movie) => (
<div onClick={() => onClick(movie.id, movie.original_title)} className="movie" key={movie.id}>
<img src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`} />
<h4>
<Link href={{
pathname: `/movies/${movie.id}`,
query: {
title: movie.original_title,
},
}}
as = {`/movies/${movie.id}`}
>
{movie.original_title}
</Link>
</h4>
</div>
))}
</div>
//생략
[id].js
import { useRouter } from "next/router";
export default function Detail(){
const router = useRouter();
return <div>
<h4>{router.query.title || "Loding..."}</h4>
</div>
}
- 여기서, router.query.title은 유저가 홈페이지에서 상세페이지로 넘어올 때만 존재한다. 시크릿 모드에서 바로 URL에 접속할 경우, Loading...이 출력된다.
#2.7 Catch All URL
- 홈페이지에서 배너를 클릭해서 접근하지 않고 URL 타이핑으로 접근해도 내용을 확인할 수 있도록 만들기 위한 방법.
- 먼저, [id].js의 이름을 [...id].js로 바꿔 준다. query 확인 시 배열을 받아오는 것을 볼 수 있다.
index.js
//생략
const onClick = (id, title) => {
router.push(`/movies/${title}/${id}`)
};
return (
<div>
<div className="container">
<Heads title="Home" />
{results?.map((movie) => (
<div onClick={() => onClick(movie.id, movie.original_title)} className="movie" key={movie.id}>
<img src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`} />
<h4>
<Link href={`/movies/${movie.original_title}/${movie.id}`}>
{movie.original_title}
</Link>
</h4>
</div>
))}
</div>
//생략
[...id].js
import Heads from "@/components/Heads";
import { useRouter } from "next/router";
export default function Detail() {
const router = useRouter();
const [title, id] = router.query.params || []; //SSR 방식이기 때문에 js가 다운로드 되기 전이라, 빈 배열을 만들고, 값을 가져와 뿌려 주는 것.
// console.log(router)
return (
<div>
<Heads title={title}/>
<h4>{title}</h4>
</div>
)
}
export function getServerSideProps({ params: { params } }) {
return {
props: {
params
},
}
}
#2.8 404 Pages
- 404 페이지를 커스텀하려면 pages 폴더에 404.js 파일을 만들어 코드를 작성하면 된다.
404.js
export default function NotFound(){
return "What are u doing here?";
}
강의 꾿... 이긴 한데, 모르거나 이해가 안 되는 내용이 너무 많아 복습이 필요할 것 같다.
이후 강의는 캐럿마켓 클론코딩이라고 하는데 가격이 후덜덜해서 고민 좀 ;-;
참고
https://velog.io/@deli-ght/nextrewrite%EC%99%80-redirect
next.js의 rewrite와 redirect
tldr; 유저가 어떤 path로 접근하는 경우, 특정 사이트로 옮겨주는 next 자체 기능 2가지
velog.io