βββ ...
βββ public
βββ src
β βββ app
β β βββ (form)
β β β βββ arrival # λμ°©μ§ μ ν λΌμ°νΈ
β β β βββ departure # μΆλ°μ§ μ ν λΌμ°νΈ
β β β βββ form # μΌμ λ±λ‘ λΌμ°νΈ
β β β βββ layout.tsx # (form) κ³΅ν΅ λ μ΄μμ
β β βββ (service)
β β β βββ home # ν λΌμ°νΈ
β β β βββ ui # home λΌμ°νΈμμ μ¬μ©νλ μ»΄ν¬λνΈ λͺ¨μ
β β β βββ layout.tsx
β β βββ layout.tsx # λ£¨νΈ λ μ΄μμ
β βββ components # κΈ°λ³Έ μ»΄ν¬λνΈ
β βββ styles
β βββ types # κ³΅ν΅ νμ
β βββ utils
β β βββ date.ts # λ μ§, μκ° κ΄λ ¨ μ νΈ ν¨μ
β β βββ swrFetcher.ts # SWR, data fetchers
βββ tailwind.config.js
βββ next.config.js
βββ README.md
βββ ...
λΉλ λ° μ€ν
// μ’
μμ± μ€μΉ
yarn install λλ yarn
// κ°λ° μλ² μ€ν
yarn dev
Next.js 13, App Router
- Layouts(Root, Nesting) : λ£¨νΈ λ μ΄μμ λ° μ€μ²© λ μ΄μμμ μ¬μ©νμ¬ κ³΅ν΅ ν€λ, νλ¨ κ³ μ λ²νΌ λ±μ λ°°μΉ
- Route Groups : λΌμ°νΈ κ·Έλ£Ήμ ν΅ν΄ κ³΅ν΅ λ μ΄μμ μ μ©
- RSC : μλ² μ»΄ν¬λνΈ, μλ²λ¨μμ λ°μ΄ν° ν¨μΉ λ‘μ§ κ΅¬ν
...
export default async function Page() {
const record = await getSchedulesMap();
const recentSchedule = await getRecentSchedule();
return (
<Suspense
fallback={
<div className='w-full flex justify-center items-center'>
<Spinner />
</div>
}>
{...λλλ§}
</Suspense>
);
}
async function getSchedulesMap(): Promise<Record<string, Schedule[]>> {
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/schedules`);
const schedules: Schedule[] = await res.json();
const record: Record<string, Schedule[]> = {};
schedules?.slice(1)?.forEach((schedule) => {
const date = format(new Date(schedule.departureTime), 'yyyy-MM-dd');
if (record[date]) {
record[date].push(schedule);
} else {
record[date] = [schedule];
}
});
return record;
}
async function getRecentSchedule(): Promise<Schedule> {
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/schedules`);
const schedules: Schedule[] = await res.json();
return schedules[0];
}
- Next.js 13 λ²μ μμ μ 곡νλ μλ² μ»΄ν¬λνΈμ React 18μ μμ€νμ€ μ‘°ν©μΌλ‘ λ°μ΄ν° μμ² μμ μ μ§νν΄λ³΄μμ΅λλ€.
- μ λμνμμΌλ μ΄ν revalidate, μ¬μμ² μ΄μλ‘ λ€μ ν΄λΌμ΄μΈνΈ μ¬μ΄λμμ μμ²νλ κ²μΌλ‘ λ³κ²½νμμ΅λλ€. (fetch ν¨μμ μ΅μ μ μ λ¬νμ¬ μ£ΌκΈ°μ μΌλ‘ revalidateμ μμ²ν μ μμΌλ λλλ§ μ¬μ΄μλ κΈ°λ³ΈμΌλ‘ μΊμλ κ°μ μ¬μ©νμ¬ λ°μ΄ν°κ° staleν κ°μ μ μ§νκΈ°μ μ ν©ν λ°©λ²μ μ°Ύμ§ λͺ»νμ΅λλ€.)
κ΄λ ¨ commit λ΄μ 1 κ΄λ ¨ commit λ΄μ 2
- μ€νμΌλ§μλ tailwindcssλ₯Ό μ¬μ©νμμ΅λλ€.
// GET μμ²
const { data: schedules } = useSWR<Schedule[]>(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/schedules`,
fetcher
);
// POST μμ²
const { trigger, isMutating } = useSWRMutation(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/schedules`,
sendRequest,
{
onSuccess: (data) => {
router.push(`/form/common/?id=${data.commonScheduleId}`);
},
}
);
// μΉ΄μΉ΄μ€ API μμ²
const { data, isLoading } = useSWR(
() =>
latitude &&
longitude &&
`https://dapi.kakao.com/v2/local/geo/coord2regioncode.json?x=${longitude}&y=${latitude}`,
fetcherKakao
);
-
μλ² μν κ΄λ¦¬ λΌμ΄λΈλ¬λ¦¬λ‘ SWRμ μ¬μ©νμ΅λλ€.
-
μ μΈμ μΌλ‘ data κ΄λ¦¬κ° κ°λ₯νκ³ onSuccess APIλ₯Ό ν΅ν΄ λ°μ΄ν° ν¨μΉ μ΄ν λ‘μ§μ ꡬμ±μ΄ μ©μ΄ν©λλ€.
-
GET μμ²μ
useSWR
, POST μμ²μuseSWRMutation
APIλ₯Ό μ¬μ©νμ΅λλ€. -
μΉ΄μΉ΄μ€ API, λ°±μλλ‘ GET, POSTμμ²μ λ°λΌ λ€λ₯Έ fetcher ν¨μλ₯Ό 미리 μμ±νμ¬ μ¬μ©νμ΅λλ€.
if (isLoading || isMutating)
return (
<div className='w-full flex justify-center items-center mt-16'>
<Spinner />
</div>
);
return ...
- SWRμ μ¬μ©νμ¬ λ°μ΄ν° μμ² μ¬μ΄ λ‘λ© UIλ₯Ό ν΅ν΄ μ¬μ©μμκ² λ‘λ© μ€μμ μλ €μ€λλ€.
const [latitude, setLatitude] = useState<number>();
const [longitude, setLongitude] = useState<number>();
const originName = searchParams.get('originName');
const origin = searchParams.get('origin');
const [query, setQuery] = useState('');
const debouceQuery = useDebounce(query, 1000);
const { data, isLoading } = useSWR(
() =>
debouceQuery &&
longitude &&
latitude &&
`https://dapi.kakao.com/v2/local/search/keyword.json?x=${longitude}&y=${latitude}&query=${debouceQuery}`,
fetcherKakao
);
- API μμ²μ μΈμκ° λκΈ°μ μΌλ‘ νμν κ²½μ° μμ κ°μ΄ μμ±νμ¬ μΏΌλ¦¬κ° μ ν¨ν μμ μ λ°μ΄ν° μμ²μ ν μ μμ΅λλ€.
- κ²μ 쿼리 μμ²μ κ²½μ° λ무 μ¦μ μμ²μ λ°©μ§νκΈ° μν΄ λλ°μ΄μ€ λ‘μ§μ λ°λ‘ μ μ©νμ΅λλ€.