์ด ๊ธ์ udemy์ Next.js & React - ์๋ฒฝ ์ ๋ณต ๊ฐ์ด๋ (incl. Two Paths) ๊ฐ์ข๋ฅผ ๋ฐํ์ผ๋ก ์ ๋ฆฌํ ๋ด์ฉ์ ๋๋ค.
69. ๋ชจ๋ ๊ฐ์
์ด๋ฒ ์น์ ์์๋ ์ด๋ฒคํธ ์ฑ์ ๊ฐ๋จํ๊ฒ ๋ง๋ค์ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ์ด๋ฒคํธ ๋ชฉ๋ก์ ํ์ธํ๊ณ , ๊ทธ ์ค์์ ํ ์ด๋ฒคํธ๋ฅผ ์ ํํ ์๋ ์์ต๋๋ค.
- Creat a Complete NextJS Project From Scratch
- Add Static & Dynamic Routes
- Add Regular Components & Connect Everything!
70. ํ๋ก์ ํธ ๊ณํ
- / : Startign PAge (show featured Events)
- /events: Events Page (show all Events)
- /events/<some-id : Event Detail Page (show selected Event)
- /evnets/,,,slug : Filtered Events Page (show filtered Events)
71. ๋ฉ์ธ ํ์ด์ง ์ค์ ํ๊ธฐ
๋ฉ์ธ ํ์ด์ง ์ค์ ํ๊ธฐ
72. ๋๋ฏธ ๋ฐ์ดํฐ & ์ ์ ํ์ผ ์ถ๊ฐํ๊ธฐ
์ด๋ฏธ์ง์ ๊ฒฝ์ฐ ๋ฐ๋์ public ํด๋์ ์ ์ฅํด์ผ ํฉ๋๋ค. ํ์ ํด๋๋ช ์ ๊ผญ images ์ผ ํ์๋ ์๊ณ , public ํด๋์ ์ ์ฅํด๋ ๋ฌด๋ฐฉํ์ง๋ง ๋ฐ๋์ public ํด๋์ ์์ด์ผ ํฉ๋๋ค. public ํด๋๋ Next.js ํ๋ก์ ํธ์์ ํน์ํ ์ญํ ์ ํฉ๋๋ค. ์ด ํด๋์ ์ ์ฅ๋์ด ์๋ ์ด๋ฏธ์ง๋ ๊ธ๊ผด ๊ฐ์ ๋ฐ์ดํฐ๋ค์ Next.js์์ ์ ์ (Static)์ธ ๋ฐ์ดํฐ๋ก ์์ฉํ๊ธฐ ๋๋ฌธ์ CSS๋ HTML ์ฝ๋์์ ์ฐธ์กฐํ ์ ์์ต๋๋ค. public ํด๋์ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํด๋๋ฉด Next.js์์๋ ๊ทธ ํด๋์ ์ ์ฅ๋ ๋ชจ๋ ์ฝํ ์ธ ๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ผ๋ถ๋ก ๊ฐ์ฃผํ๊ธฐ ๋๋ฌธ์ ์ ์ ์ธ ์ฝํ ์ธ ๋ก ํ์ฉํ ์ ์์ต๋๋ค.
73. React ์ปดํฌ๋ํธ ์ถ๊ฐํ๊ธฐ
components>event> event-item.js, event-list.js ํ์ผ์ ์ถ๊ฐํฉ๋๋ค. ๋ณดํต ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ JSX ์ฝ๋๋ฅผ ๊ตฌ์ํ ๋๋ ๊ฑด๋ฌผ์ ์ง์ ๋ ์ฐ์ด๋ ๋ฒฝ๋์ฒ๋ผ ์ฌ๋ฌ ์ปดํฌ๋ํธ๋ก ๋๋์ด์ ์ปดํฌ๋ํธ๋ฅผ ํฉ์ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ์ต๊ด์ ๋ค์ด๋ ๊ฒ์ด ์ข์ต๋๋ค.
74. ๋ ๋ง์ React ์ปดํฌ๋ํธ ์ถ๊ฐํ๊ธฐ & ์ปดํฌ๋ํธ ์ฐ๊ฒฐํ๊ธฐ
react ์ปดํฌ๋ํธ ์ถ๊ฐ
75. Next.js ํ๋ก์ ํธ ๋ด์์ ์ปดํฌ๋ํธ ์คํ์ผ๋ง ํ๊ธฐ
Next.js ํ๋ก์ ํธ๋ module.css๋ฅผ ์ง์ํฉ๋๋ค. ๋น๋ ํ๋ก์ธ์ค๊ฐ CSS ์ฝ๋๋ฅผ ์ถ์ถํด์ ์ ํ์๋ฅผ ๋ฐ๊พธ๊ณ ํด๋น ์ปดํฌ๋ํธ HTML ์ฝ๋์ ๋ฒ์๋ฅผ ์ง์ ํ ๋ค์ ์คํ ์ค์ธ ํ์ด์ง์ ์ฃผ์ ํฉ๋๋ค. module.css ํ์ผ์ import ํ๊ธฐ ์ํด์๋, classes๋ styles ์ค ํ๋๋ฅผ ์ ๋ ฅํด์ค์ผ ํฉ๋๋ค.
<li className={classes.item}></li>
.item {
color: red;
}โ
event-item_๊ณ ์ ํด์๊ฐ ๋ถ์ ํด๋์ค๊ฐ ์๊ฒผ์์ ์ ์ ์์ต๋๋ค.
UI ํ๋ฉด์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
76. ๋ฒํผ & ์์ด์ฝ ์ถ๊ฐํ๊ธฐ
์ง์ ๋์๋ ๋ณด์ด์ง ์์ง๋ง, Link ํ๊ทธ๊ฐ ์์ฒด์ ์ผ๋ก ์ต์ปค ํ๊ทธ๋ฅผ ๋ ๋๋ง ํฉ๋๋ค. ์ฌ์ฉ์ ์ง์ ์คํ์ผ์ ์ ์ฉํ๊ณ ์ ํ๋ค๋ฉด, ํด๋น ์ต์ปค ํ๊ทธ๋ ์ง์ ์ถ๊ฐํด์ค์ผ ํฉ๋๋ค. ๋ค์ ์ฝ๋์์๋ Link ํ๊ทธ๊ฐ ๋ด๋ถ์ ์๋ ์ต์ปค ํ๊ทธ๋ฅผ ๊ฐ์งํ๊ณ ์์ฒด์ ์ผ๋ก ์ต์ปค ํ๊ทธ๋ฅผ ๋ ๋๋ง ํ๋ ๋์ ์ถ๊ฐ๋ ์ต์ปค ํ๊ทธ๋ฅผ ๋ ๋๋ง ํฉ๋๋ค.
๋ณธ ์ต์ปค ํ๊ทธ์๋ href ์์ฑ์ด ์์ต๋๋ค. ์ด๋ Link ํ๊ทธ๊ฐ ์ฌ๊ธฐ ์ ๋ ฅ๋ ๋ด์ฉ์ ๋ฐ๋ผ ์์ฒด์ ์ผ๋ก ์ถ๊ฐํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
export default function Button(props) {
return (
<Link href={props.link}>
<a className={classes.btn}>{props.children}</a>
</Link>
);
}
77. "Event Detail" ํ์ด์ง ์ถ๊ฐํ๊ธฐ (๋์ ๋ผ์ฐํธ)
useRouter hook์ next/router์์ ์ํฌํธํด ์จ ๋ค์, router ๊ฐ์ฒด๋ฅผ ํฌํจํ๋๋ก useRouter๋ฅผ ํธ์ถํฉ๋๋ค. router ๊ฐ์ฒด์์ query ํ๋กํผํฐ์ ์์ธ์ค ํ ์ ์๋๋ฐ, ์ด ํ๋กํผํฐ์๋ ํด๋น ํ์ด์ง๋ก ์ด๋ํ๋ ๋ชจ๋ ๋์ ๊ฒฝ๋ก segment๊ฐ key๋ก ํฌํจ๋์ด ์์ต๋๋ค.
export default function EventDetailPage() {
const router = useRouter();
const eventId = router.query.eventId;
const event = getEventById(eventId);
if (!event) {
return <p>No event found! </p>;
}
return (
<Fragment>
<EventSummary title={event.title} />
<EventLogistics
date={event.date}
address={event.location}
image={event.image}
imageAlt={event.title}
/>
<EventContent>
<p>{event.description}</p>
</EventContent>
</Fragment>
);
}
78. ์ผ๋ฐ์ ์ธ ๋ ์ด์์ ๋ํผ ์ปดํฌ๋ํธ ์ถ๊ฐํ๊ธฐ
ํ์ฌ ๋ค๋น๊ฒ์ด์ ๋ฐ๊ฐ ์์ผ๋ฏ๋ก ๋ชจ๋ ํ์ด์ง ์ปดํฌ๋ํธ์ ๋งจ ์์ <header>๋ฅผ ์ถ๊ฐํ๋ ์์ ์ ํด์ผ ํฉ๋๋ค.
ํ์ง๋ง ์ด ๊ฑ์ฐ์๋ ๋ชจ๋ ํ์ด์ง ์ปดํฌ๋ํธ์ ๋์ผํ ์ฝ๋๋ฅผ ๋ฐ๋ณตํด์ผ ํฉ๋๋ค. (์ค๋ณต!)
์ด ๊ฒฝ์ฐ์ _app.js ํ์ผ์ด ์ค์ํ ์ญํ ์ ํฉ๋๋ค. ์ด ํ์ผ์ด ๋ฃจํธ ์ปดํฌ๋ํธ๋ก์ ์ฌ๋ฌ ํ์ด์ง ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋๋ ๊ณณ์ด๊ธฐ ๋๋ฌธ์ ๋๋ค. Nextj.js๋ _app ์ปดํฌ๋ํธ๋ฅผ ์ด์ฉํด์ ํ์ด์ง ์ปจํ ์ธ ๋ฅผ ์ ๋ฌํ๊ณ , ํ์ด์ง๋ฅผ ์ด๋ํ ๋ ์ฝํ ์ธ ๋ฅผ ํ์ํด์ค๋๋ค.
_app.js
import "../styles/globals.css";
import Layout from "../components/layout/layout";
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
export default MyApp;
layout.js
import { Fragment } from "react";
import MainHeader from "./main-header";
function Layout(props) {
return (
<Fragment>
<MainHeader />
<main>{props.children}</main>
</Fragment>
);
}
export default Layout;
79. All Events ํ์ด์ง ์์
๋ชจ๋ ์ด๋ฒคํธ๊ฐ ๋ณด์ด๋ ํ์ด์ง๋ฅผ ์ถ๊ฐํฉ๋๋ค. ์ด๋ฏธ EventList ์ปดํฌ๋ํธ๊ฐ ์์ผ๋ฏ๋ก ํธํ๊ฒ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
import { getAllEvents } from "../../dummy-data";
import EventList from "../../components/events/event-list";
export default function AllEventsPage() {
const events = getAllEvents();
return (
<div>
<h1>All Events</h1>
<EventList items={events} />
</div>
);
}
80. ์ด๋ฒคํธ ํํฐ๋ง์ ์ํ ํํฐ ์์ ์ถ๊ฐํ๊ธฐ
ํํฐ๋ง์ ์ํด์ events-search.js์ module.css ํ์ผ์ ์ถ๊ฐํฉ๋๋ค.
import Button from "../ui/button";
import classes from "./events-search.module.css";
function EventSearch(props) {
return (
<form className={classes.form}>
<div className={classes.controls}>
<div className={classes.control}>
<label htmlFor="year">Year</label>
<select id="year">
<option value="2021">2021</option>
<option value="2022">2022</option>
</select>
</div>
<div className={classes.control}>
<label htmlFor="month">Month</label>
<select id="month">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">Septemer</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
</div>
</div>
<Button>Find Events</Button>
</form>
);
}
export default EventSearch;
81. "Filtered Events" ํ์ด์ง๋ฅผ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ๋ด๋น๊ฒ์ดํ ํ๊ธฐ
ํ์ฌ๋ form ๋ฒํผ์ ๋๋ ์ ๋ ์คํ๋๋ ์์ ์ด ์๋ฌด๊ฒ๋ ์์ต๋๋ค. submitHandler ๋ผ๋ ํธ๋ฆฌ๊ฑฐ ํจ์๋ฅผ ๋ง๋ค๋๋ก ํ๊ฒ ์ต๋๋ค. form ์์์ obSubmit ํ๋กํผํฐ๋ฅผ ์ถ๊ฐํ๊ณ , submitHandler์ ๊ฐ๋ฆฌํค๋๋ก ํฉ๋๋ค. submitHandler๋ form ์ ์ถ ์ React๋ฅผ ํตํด์ ์คํ๋ฉ๋๋ค.
import { useRef } from "react";
import Button from "../ui/button";
import classes from "./events-search.module.css";
function EventSearch(props) {
const yearInputRef = useRef();
const monthInputRef = useRef();
function submitHandler(event) {
event.preventDefault();
const selectedYear = yearInputRef.current.value;
const selectedMonth = monthInputRef.current.value;
props.onSearch(selectedYear, selectedMonth);
}
return (
<form className={classes.form} onSubmit={submitHandler}>
<div className={classes.controls}>
<div className={classes.control}>
<label htmlFor="year">Year</label>
<select id="year" ref={yearInputRef}>
<option value="2021">2021</option>
<option value="2022">2022</option>
</select>
</div>
<div className={classes.control}>
<label htmlFor="month">Month</label>
<select id="month" ref={monthInputRef}>
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">Septemer</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
</div>
</div>
<Button>Find Events</Button>
</form>
);
}
export default EventSearch;
82. Catch-All ํ์ด์ง ์์์ ๋ฐ์ดํฐ ์ถ์ถํ๊ธฐ
uesRouter ํ ์ ์ฌ์ฉํ์ฌ, router.query.slug ์ ์์ธ์ค ํ์ฌ ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
export default function FilteredEventsPage() {
const router = useRouter();
const filterData = router.query.slug;
if (!filterData) {
return <p className="center">Loading...</p>;
}
const filteredYear = filterData[0];
const filteredMonth = filterData[1];
const numYear = +filteredYear;
const numMonth = +filteredMonth;
if (
isNaN(numYear) ||
isNaN(numMonth) ||
numYear > 2030 ||
numYear < 2021 ||
numMonth < 1 ||
numMonth > 12
) {
return <p>Invalid filter. Please adjust your values! </p>;
}
const filteredEvents = getFilteredEvents({
year: numYear,
month: numMonth,
});
if (!filteredEvents || filteredEvents.length === 0) {
return <p>No events found for the chosen filter! </p>;
}
return (
<div>
<h1>Filtered Events</h1>
</div>
);
}
83. ์ต์ข ๋จ๊ณ
์์ ์ปดํฌ๋ํธ๋ฅผ ์ถ๊ฐํ์ฌ ์คํ์ผ๋ง์ ์์๋ฅผ ์ถ๊ฐํ์ต๋๋ค.
- Resluts Title Component
- Error Alert Component
84. ๋ชจ๋ ์์ฝ
Next.js ์์ ๋ผ์ฐํ ์ด ์ด๋ป๊ฒ ์๋ํ๋์ง, ์ฌ๋ฌ ๋ผ์ฐํธ๋ฅผ ํตํด ์๋ก ๋ค๋ฅธ ํ์ด์ง ํ์ผ์ด ์ด๋ป๊ฒ ์ฝํ๋์ง, ์ฌ๋ฌ๊ทธ ๋ผ์ฐํธ์ ๋จ์ผ ๋์ ๋งค๊ฐ๋ณ์ ๋ผ์ฐํธ์ ์๋ ๋ฐฉ์์ ๋ํด ๋ฐฐ์ ์ต๋๋ค.
๋ผ์ฐํฐ ์ก์ธ์ค๋ฅผ ์ํด useRouter hook ์ฌ์ฉ ๋ฐฉ๋ฒ ๋ํ ์ดํด๋ณด์์ต๋๋ค. ๋ผ์ฐํฐ๋ฅผ ํตํด ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์ ๋ค๋น๊ฒ์ด์ ์ ์คํํ๊ฑฐ๋ URL์ ๋ถํธํ๋ ๋ฐ์ดํฐ์ ์ก์ธ์ค ํ ์๋ ์์์ต๋๋ค.