- client, server component๋ก๋ถํฐ data๋ฅผ fetch ํ๋ ๋ฒ
- streaming์ ์ฌ์ฉํ๋ ๋ฒ
- suspense๋ฅผ ์ฌ์ฉํ๋ ๋ฒ
- loading fallback์ ์ฌ์ฉํ๋ ๋ฒ
- error boundary๋ฅผ ์ฌ์ฉํ๋ ๋ฒ
API URL
client component์์ data๋ฅผ fetch ํ๋ ๋ฒ
useEffect, useState๋ฅผ ์ฌ์ฉํด data๋ฅผ fetch
export default function Home() {
const [isLoading, setIsLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovies = async () => {
const response = await fetch('https://nomad-movies.nomadcoders.workers.dev/movies');
const json = await response.json();
setMovies(json);
setIsLoading(false);
};
useEffect(() => {
getMovies();
}, []);
return <div>{isLoading ? 'Loading...' : JSON.stringify(movies)}</div>;
}
- client component์์๋ metadata๋ฅผ export ํ ์ ์์
- client์์ fetch ํ ๊ฒฝ์ฐ ์ฌ์ฉ์์๊ฒ ๋ก๋ฉ ์ํ๋ฅผ ๋ณด์ฌ์ค์ผ ํ๋ฉฐ ๋ก๋ฉ ์ํ๋ฅผ ์ง์ ํ์ธํ๊ณ ๊ตฌํํด์ผ ํจ
- ๋ธ๋ผ์ฐ์ ๊ฐ API์ ์์ฒญ์ ๋ณด๋ด๊ธฐ ๋๋ฌธ์ ๋ณด์์ ์ทจ์ฝํจ
server component์์ data๋ฅผ fetch ํ๋ ๋ฒ
useEffect, useState ์์ด data๋ฅผ fetch
const URL = 'https://nomad-movies.nomadcoders.workers.dev/movies';
async function getMovies() {
await new Promise(resolve => setTimeout(resolve, 5000)); // ์๋ต์ด 5์ด ๊ฑธ๋ฆฐ๋ค๊ณ ๊ฐ์
const response = await fetch(URL);
return await response.json();
}
export default async function Home() {
const movies = await getMovies();
return <div>{JSON.stringify(movies)}</div>;
}
- ์๋์ผ๋ก fetch๋ url์ ์บ์ฑํจ
- server์์ fetchํด๋ ๋ฐฑ์๋์ ์๋ต์ ๊ธฐ๋ค๋ฆฌ๋ ์๊ฐ์ด ๋ฐ์ํ๊ณ , ๋ก๋ฉ์ค์ผ ๊ฒฝ์ฐ ์ฌ์ฉ์์๊ฒ ์ด๋ ํ UI๋ ๋ ธ์ถ๋์ง ์์
์บ์๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ
- fetch ์ต์
์ฌ์ฉํ๊ธฐ
- no-store๋ฅผ ์ฌ์ฉํ์ฌ ํญ์ ์ต์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
- ๋งค ์์ฒญ๋ง๋ค API๋ฅผ ํธ์ถํ๋ฏ๋ก ์ฑ๋ฅ์ ์ํฅ์ ์ค ์ ์์
const URL = 'https://nomad-movies.nomadcoders.workers.dev/movies'; async function getMovies() { - const response = await fetch(URL); + const response = await fetch(URL, { cache: 'no-store' }); return await response.json(); } export default async function Home() { const movies = await getMovies(); return <div>{JSON.stringify(movies)}</div>; }
- revalidate ๊ธฐ๋ฅ ์ฌ์ฉํ๊ธฐ
- revalidate ์ต์ ์ ์ฌ์ฉํ์ฌ ์ผ์ ์๊ฐ๋ง๋ค ์บ์๋ฅผ ๊ฐฑ์
- ์ด ๋จ์
const URL = 'https://nomad-movies.nomadcoders.workers.dev/movies'; async function getMovies() { - const response = await fetch(URL); + const response = await fetch(URL, { next: { revalidate: 60 } }); return await response.json(); } export default async function Home() { const movies = await getMovies(); return <div>{JSON.stringify(movies)}</div>; }
server side์์ fetchํ๋ ๊ฒฝ์ฐ ์๋ต์ ๊ธฐ๋ค๋ฆฌ๋ ๋์ ๋ ๋๋ง ์์ ์ด ์ด๋ฃจ์ด ์ง์ง ์๋๋ฐ, ์ด๋ ๋ก๋ฉ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํด ๋ก๋ฉ UI๋ฅผ ๋ ธ์ถ ์ํฌ ์ ์์
loading component์ ์๋ ๋ฐฉ์
- ๋ก๋ฉ ์ปดํฌ๋ํธ ์ฌ์ฉ ์ ์ฌ์ฉ์์๊ฒ๋ ๋ก๋ฉ UI๋ฅผ ๋ ธ์ถํ์ง๋ง ์ค์ ๋ก๋ ๋ฐฑ์๋์์ ๋ก๋ฉ์ค์ด๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ๋ ๋ฐฑ์๋ ์์ ์ด ์๋ฃ๋์ง ์์๋ค๊ณ ์๊ฐํจ
- streaming์ ์ฌ์ฉํ๋ฉด์ Next๋ ํ์ด์ง์ HTML์ ์ฒญํฌ๋ก ๋๋์ด ๋จผ์ ์ค๋น๋ ์ฒญํฌ๋ฅผ ๋ธ๋ผ์ฐ์ ์๊ฒ ๋ฐํํ๊ณ await์ด ๋๋๋ฉด client์์ loading component๋ฅผ await ๋ component๋ก ๊ต์ฒด
- UI๊ฐ ๋ ๋๋ง ๋๊ธฐ ์ ์ ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ๋ก๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ํ์ด์ง์ ์ผ๋ถ๋ฅผ ๋น ๋ฅด๊ฒ ๋ ธ์ถํ ์ ์์
loading component ์ฌ์ฉ ๋ฒ
- ํ์ผ ๋ช
์ ํญ์
loading
์ด์ด์ผ ํจ page
ํ์ผ๊ณผ ๋์ผํ ๊ฒฝ๋ก์ ์์ด์ผ ํจ
์ง๋ ฌ ์์ฒญ ๋ฐฉ์
async function getMovie(id: string) {
console.log(`Fetching movie: ${Date.now()}`);
await new Promise(resolve => setTimeout(resolve, 5000)); // ์๋ต์ด 5์ด ๊ฑธ๋ฆฐ๋ค๊ณ ๊ฐ์
const response = await fetch(`${API_URL}/${id}`);
return response.json();
}
async function getVideos(id: string) {
console.log(`Fetching videos: ${Date.now()}`);
await new Promise(resolve => setTimeout(resolve, 5000)); // ์๋ต์ด 5์ด ๊ฑธ๋ฆฐ๋ค๊ณ ๊ฐ์
const response = await fetch(`${API_URL}/${id}/videos`);
return response.json();
}
export default async function MovieDetail({ params: { id } }: { params: { id: string } }) {
console.log('start fetching');
const movie = await getMovie(id);
const videos = await getVideos(id);
console.log('end fetching');
}
output
- movie, videos fetch๊ฐ ์์ฐจ์ ์ผ๋ก ์คํ๋จ
- ์์ฒญ ์๊ฐ ์ด 10์ด + API ์๋ต ์๊ฐ -> ์ต์ 10์ด ์ด์ ์์
start fetching
Fetching movie: 1717382782714
Fetching videos: 1717382788223
end fetching
GET /movies/653346 200 in 11006ms
๋ณ๋ ฌ ์์ฒญ ๋ฐฉ์
export default async function MovieDetail({ params: { id } }: { params: { id: string } }) {
console.log('start fetching');
- const movie = await getMovie(id);
- const videos = await getVideos(id);
+ const [movie, videos] = await Promise.all([getMovie(id), getVideos(id)]);
console.log('end fetching');
}
output
- movie, videos fetch๊ฐ ๋์์ ์คํ๋จ
- ์์ฒญ ์๊ฐ ์ด 5์ด + API ์๋ต ์๊ฐ -> ์ต์ 5์ด ์ด์ ์์
start fetching
Fetching movie: 1717383063742
Fetching videos: 1717383063742
end fetching
GET /movies/653346 200 in 5528ms
Suspense๋ก ๋ณ๋ ฌ ์์ฒญ์ ๋ถ๋ฆฌํด์ ๋ชจ๋ ์๋ต์ด ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ์๋ต์ด ๋๋ ๋ฐ์ดํฐ๋ถํฐ ๋ฐ๋ก ๋ณด์ฌ์ฃผ๊ธฐ
- ๊ฐ ์์ฒญ์ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌ
MovieInfo- movie info์ ๊ดํ ๋ฐ์ดํฐ๋ง fetch
MovieVideosasync function getMovie(id: string) { console.log(`Fetching movie: ${Date.now()}`); await new Promise(resolve => setTimeout(resolve, 5000)); // ์๋ต์ด 5์ด ๊ฑธ๋ฆฐ๋ค๊ณ ๊ฐ์ const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/${id}`); return response.json(); } export default async function MovieInfo({ id }: { id: string }) { const movie = await getMovie(id); return <h6>{JSON.stringify(movie)}</h6>; }
- movie videos์ ๊ดํ ๋ฐ์ดํฐ๋ง fetch
async function getVideos(id: string) { console.log(`Fetching videos: ${Date.now()}`); await new Promise(resolve => setTimeout(resolve, 3000)); // ์๋ต์ด 3์ด ๊ฑธ๋ฆฐ๋ค๊ณ ๊ฐ์ const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/${id}/videos`); return response.json(); } export default async function MovieVideos({ id }: { id: string }) { const videos = await getVideos(id); return <h6>{JSON.stringify(videos)}</h6>; }
- ์์์์ ๊ฐ ์ปดํฌ๋ํธ๋ฅผ Suspense๋ก wrappingํ๊ณ fallback ์ง์
import MovieVideos from '../../../../components/movie-videos'; import MovieInfo from '../../../../components/movie-info'; import { Suspense } from 'react'; export default async function MovieDetail({ params: { id } }: { params: { id: string } }) { return ( <div> <Suspense fallback={<h1>Loading movie info</h1>}> <MovieInfo id={id} /> </Suspense> <Suspense fallback={<h1>Loading movie videos</h1>}> <MovieVideos id={id} /> </Suspense> </div> ); }
error.tsx ํ์ผ๋ก ์๋ฌ ๋ฐ์ ์ ๋ณด์ฌ์ง UI๋ฅผ ๋ ธ์ถ ์ํฌ ์ ์์
error handling ๋ฐฉ๋ฒ
- ํ์ผ ๋ช
์ ํญ์
error
์ฌ์ผ ํจ page
ํ์ผ๊ณผ ๋์ผํ ๊ฒฝ๋ก์ ์์ด์ผ ํจ