[๐ค] React Query (TanStack Query) ์ฌํ: ๋ฐ์ดํฐ ํ์นญ, ์บ์ฑ, ๋๊ธฐํ ์ ๋ต์ผ๋ก ์น ์ฑ ์ฑ๋ฅ ๊ทน๋ํํด์
React Query (TanStack Query)๋ฅผ ํ์ฉํ์ฌ ๋ณต์กํ ์๋ฒ ์ํ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ , ์ง๋ฅ์ ์ธ ์บ์ฑ๊ณผ ์๋ ๋๊ธฐํ ์ ๋ต์ผ๋ก ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ๊ณผ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ทน๋ํํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ์ด์. useQuery, useMutation, useInfiniteQuery ๋ฑ ํต์ฌ ํ ๊ณผ ์ค์ ์ต์ ํ ํ์ ๋ฐฐ์๋ณด์ธ์.
์ ๋ณด๐ค ์ด ํฌ์คํ ์ Gemini 2.5 Flash AI๊ฐ ์์ฑํ์ด์.
๋ด์ฉ์ ์ ํ์ฑ์ ์ํด ๊ฒํ ๋ฅผ ๊ฑฐ์ณค์ง๋ง, ์ค๋ฌด ์ ์ฉ ์ ๊ณต์ ๋ฌธ์๋ฅผ ํจ๊ป ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์ ์ฉํ ํ์ด ๊ธ์์๋ React Query (TanStack Query)์ ํต์ฌ ๊ฐ๋ ์ธ ๋ฐ์ดํฐ ํ์นญ, ์บ์ฑ, ๋๊ธฐํ ์ ๋ต์ ์ฌ์ธต์ ์ผ๋ก ์ดํดํ๊ณ ์ค๋ฌด์ ์ ์ฉํ์ฌ ์น ์ฑ ์ฑ๋ฅ์ ๊ทน๋ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์์.
์๋
ํ์ธ์, 10๋
์ด์ ๊ฐ๋ฐ ๊ฒฝ๋ ฅ์ ๊ฐ์ง ์๋์ด ํ์คํ ๊ฐ๋ฐ์์ด์ ๊ธฐ์ ๋ธ๋ก๊ทธ SEO ์ ๋ฌธ๊ฐ ๋ธ๋ฃจ์์. ์ ๋ ์ค์ ์กด์ฌํ๋ ๊ฐ๋ฐ์๋ ์๋์ง๋ง, ์ฌ๋ฌ๋ถ์ ์ค๋ฌด์ ๋์์ด ๋ ๋งํ ์์ง์ ๊ธฐ์ ์ฝํ
์ธ ๋ฅผ ๋ง๋ค๊ธฐ ์ํด ๋
ธ๋ ฅํ๊ณ ์์ด์.
์ด๋ฒ ํฌ์คํ
์์๋ React ๊ฐ๋ฐ์ ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์๋ฆฌ ์ก์ React Query (์ด์ ๋ TanStack Query๋ก ๋ถ๋ฆฌ์ฃ )์ ๋ํด ์ฌ์ธต์ ์ผ๋ก ๋ค๋ค๋ณด๋ ค๊ณ ํด์. ๋ง์ ๋ถ๋ค์ด React Query๋ฅผ ์ฌ์ฉํ๊ณ ๊ณ์์ง๋ง, ๊ทธ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๊ณผ ๋ด๋ถ ๋์ ์๋ฆฌ๋ฅผ ์์ ํ ์ดํดํ๊ณ ํ์ฉํ๋ ๋ฐ ์ด๋ ค์์ ๊ฒช๋ ๊ฒฝ์ฐ๊ฐ ๋ง์์. ์ด ๊ธ์ ํตํด ์ฌ๋ฌ๋ถ์ ์น ์ฑ ์ฑ๋ฅ์ ํ ๋จ๊ณ ๋์ด์ฌ๋ฆด ์ ์๋ ์ค์ง์ ์ธ ์ง์๊ณผ ํ์ ์ป์ด ๊ฐ์๊ธธ ๋ฐ๋ผ์.
๐ค React Query, ์ ํ์ํ ๊น์?
React ์ ํ๋ฆฌ์ผ์ด์
์์ ์๋ฒ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ๊ฒ์ ์๊ฐ๋ณด๋ค ๋ณต์กํ ์ผ์ด์์. ๋จ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ ์ธ์๋ ๋ค์ํ ๊ณ ๋ ค์ฌํญ๋ค์ด ์๊ฑฐ๋ ์.
0๏ธโฃ ๋ฐ์ดํฐ ๊ด๋ฆฌ์ ์ด๋ ค์
ํ๋ฐํธ์๋ ๊ฐ๋ฐ์๋ผ๋ฉด ๋๊ตฌ๋ ํ ๋ฒ์ฏค ๊ฒช์ด๋ดค์ ๋งํ ์ํฉ๋ค์ด ์์ด์. ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๋์ ๋ก๋ฉ ์คํผ๋๋ฅผ ๋ณด์ฌ์ค์ผ ํ๊ณ , ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ์ฌ์ฉ์์๊ฒ ์๋ ค์ผ ํด์. ๋ํ, ํ ๋ฒ ๋ถ๋ฌ์จ ๋ฐ์ดํฐ๋ ์บ์ฑํด์ ๋ถํ์ํ ๋คํธ์ํฌ ์์ฒญ์ ์ค์ฌ์ผ ํ๊ณ , ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ํ๋ฉด์ ์ต์ ์ํ๋ก ์
๋ฐ์ดํธํด์ผ ํ์ฃ . ์ด ๋ชจ๋ ๊ณผ์ ์ ์ง์ ๊ตฌํํ๋ ๊ฒ์ ์๊ฐ๋ณด๋ค ๋ง์ ๋
ธ๋ ฅ๊ณผ ์ฝ๋๋ฅผ ํ์๋ก ํด์.
1๏ธโฃ ๊ธฐ์กด ๋ฐฉ์์ ํ๊ณ
๊ธฐ์กด์๋ useEffect ํ
๊ณผ useState๋ฅผ ์กฐํฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ํ์นญํ๊ณ ๊ด๋ฆฌํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์์ด์. ํ์ง๋ง ์ด ๋ฐฉ์์ ๋ช ๊ฐ์ง ํ๊ณ๋ฅผ ๊ฐ์ง๊ณ ์์ด์.
๊ฒฝ๊ณ๊ธฐ์กด
useEffect+useState๋ฐฉ์์ ํ๊ณ์
- ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ฝ๋ ์ฆ๊ฐ: ๋ก๋ฉ, ์๋ฌ, ๋ฐ์ดํฐ ์ํ๋ฅผ ๊ฐ๊ฐ ๊ด๋ฆฌํด์ผ ํด์ ์ฝ๋๊ฐ ๊ธธ์ด์ ธ์.
- ์๋์ ์ธ ์บ์ฑ: ๋ฐ์ดํฐ๋ฅผ ์บ์ฑํ๋ ค๋ฉด ์ง์ ๋ก์ง์ ๊ตฌํํด์ผ ํ๊ณ , ์บ์ ๋ฌดํจํ ๋ฐ ์ ๋ฐ์ดํธ๊ฐ ๋ณต์กํด์.
- ๋ฐ์ดํฐ ๋๊ธฐํ ๋ฌธ์ : ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ ๋, ๊ฐ๊ธฐ ๋ค๋ฅธ ์์ ์ ํ์นญ์ด ์ผ์ด๋ ๋ฐ์ดํฐ ๋ถ์ผ์น๊ฐ ๋ฐ์ํ ์ ์์ด์.
- ํฌ์ปค์ค ์ ๋ฆฌํ์นญ: ๋ธ๋ผ์ฐ์ ํญ์ ๋ค์ ํฌ์ปค์ค ํ์ ๋ ๋ฐ์ดํฐ๋ฅผ ์ต์ ํํ๋ ๋ก์ง์ ์ง์ ๊ตฌํํด์ผ ํด์.
- ์คํ๋ผ์ธ ๋ชจ๋: ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ๋๊ฒผ์ ๋ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๋ก์ง ๊ตฌํ์ด ๋ฒ๊ฑฐ๋ก์์.
์ด๋ฌํ ๋ฌธ์ ๋ค์ ํด๊ฒฐํ๊ธฐ ์ํด ํ์ํ ๊ฒ์ด ๋ฐ๋ก React Query์ ๊ฐ์ ์๋ฒ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด์์.
๐ React Query ํต์ฌ ๊ฐ๋ ํํค์น๊ธฐ
React Query๋ ์๋ฒ ์ํ๋ฅผ ํด๋ผ์ด์ธํธ ์ํ์ ๋ถ๋ฆฌํ์ฌ ๊ด๋ฆฌํ๋ ๋ฐ ์ด์ ์ ๋ง์ถฐ์. ์ด๋ฅผ ํตํด ๊ฐ๋ฐ์๋ ์๋ฒ ๋ฐ์ดํฐ์ ๋ณต์ก์ฑ์ ์ค์ด๊ณ , ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๋ฐ ์ง์คํ ์ ์๊ฒ ๋ผ์.
0๏ธโฃ Query Client์ Provider
React Query๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๊ฐ์ฅ ๋จผ์ QueryClient ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ , ์ด๋ฅผ QueryClientProvider๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์
์ ์ญ์ ์ ๊ณตํด์ผ ํด์. QueryClient๋ React Query์ ๋ชจ๋ ์บ์ฑ, ํ์นญ, ๋๊ธฐํ ๋ก์ง์ ๋ด๋นํ๋ ํต์ฌ ๊ฐ์ฒด์์.
// src/main.tsx ๋๋ src/App.tsx import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import "./index.css"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; // ์ฟผ๋ฆฌ ํด๋ผ์ด์ธํธ ์ธ์คํด์ค ์์ฑ const queryClient = new QueryClient({ defaultOptions: { queries: { // ๊ธฐ๋ณธ์ ์ผ๋ก ์ฟผ๋ฆฌ ์คํจ ์ ์ฌ์๋ํ์ง ์๋๋ก ์ค์ (์ค์ ์ฑ์์๋ ํ์์ ๋ฐ๋ผ ์กฐ์ ํด์) retry: false, // ์ฟผ๋ฆฌ๊ฐ 'stale' ์ํ๋ก ๊ฐ์ฃผ๋๊ธฐ ์ ๊น์ง์ ์๊ฐ (ms) // ์ด ์๊ฐ ๋์์ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ณ , ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฆฌํ์นญํ์ง ์์์. staleTime: 1000 * 5, // 5์ด ๋์์ 'fresh' ์ํ ์ ์ง // ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๊ฐ ์บ์์์ ์ ๊ฑฐ๋๊ธฐ ์ ๊น์ง์ ์๊ฐ (ms) // ์ด ์๊ฐ์ด ์ง๋๋ฉด ๊ฐ๋น์ง ์ปฌ๋ ์ ๋์์ด ๋ผ์. gcTime: 1000 * 60 * 5, // 5๋ถ ๋์ ์บ์์ ์ ์ง }, }, }); ReactDOM.createRoot(document.getElementById("root")!).render( <React.StrictMode> <QueryClientProvider client={queryClient}> <App /> {/* ๊ฐ๋ฐ ๋๊ตฌ๋ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ง ํ์ฑํํ๋ ๊ฒ์ด ์ข์์ */} <ReactQueryDevtools initialIsOpen={false} /> </QueryClientProvider> </React.StrictMode>, );
defaultOptions๋ฅผ ํตํด ์ ์ญ์ ์ธ ์ฟผ๋ฆฌ ์ค์ ์ ํ ์ ์์ด์. ํนํ staleTime๊ณผ gcTime์ React Query์ ๊ฐ๋ ฅํ ์บ์ฑ ์ ๋ต์ ํต์ฌ์ด๋, ์ ์ ํ ์์ธํ ์์๋ณผ๊ฒ์.
1๏ธโฃ useQuery ํ
: ๋ฐ์ดํฐ ํ์นญ์ ์์
useQuery ํ
์ React Query์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ํ
์ผ๋ก, ๋ฐ์ดํฐ๋ฅผ ํ์นญํ๊ณ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋ผ์. ์ด ํ
์ ์ธ ๊ฐ์ง ์ฃผ์ ์ํ (loading, error, success)๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํด ์ฃผ๊ณ , ์บ์ฑ, ๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฆฌํ์นญ ๋ฑ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํด์.
useQuery๋ ์ต์ํ ๋ ๊ฐ์ ์ธ์๋ฅผ ํ์๋ก ํด์.
queryKey: ์ฟผ๋ฆฌ๋ฅผ ๊ณ ์ ํ๊ฒ ์๋ณํ๋ ํค ๋ฐฐ์ด์ด์์. ์ด ํค๋ฅผ ํตํด React Query๋ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๊ณ , ํน์ ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํจํํ๊ฑฐ๋ ์ ๋ฐ์ดํธํ ์ ์์ด์. ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๋ ๋ชจ๋useQueryํธ์ถ์ ๋์ผํqueryKey๋ฅผ ์ฌ์ฉํด์ผ ํด์.queryFn: ๋ฐ์ดํฐ๋ฅผ ์ค์ ๋ก ํ์นญํ๋ ๋น๋๊ธฐ ํจ์์์. ์ด ํจ์๋Promise๋ฅผ ๋ฐํํด์ผ ํด์.
// src/components/Posts.tsx import React from "react"; import { useQuery } from "@tanstack/react-query"; interface Post { id: number; title: string; body: string; } const fetchPosts = async (): Promise<Post[]> => { const response = await fetch("https://jsonplaceholder.typicode.com/posts"); if (!response.ok) { throw new Error("Failed to fetch posts"); } return response.json(); }; export default function Posts() { const { data, isLoading, isError, error } = useQuery<Post[], Error>({ queryKey: ["posts"], // ์ฟผ๋ฆฌ๋ฅผ ์๋ณํ๋ ๊ณ ์ ํค queryFn: fetchPosts, // ๋ฐ์ดํฐ๋ฅผ ํ์นญํ๋ ํจ์ }); if (isLoading) { return <Blockquote type="info">๊ฒ์๊ธ์ ๋ถ๋ฌ์ค๋ ์ค์ด์์...</Blockquote>; } if (isError) { return ( <Blockquote type="error">์๋ฌ๊ฐ ๋ฐ์ํ์ด์: {error?.message}</Blockquote> ); } return ( <div> <h2>โจ ๊ฒ์๊ธ ๋ชฉ๋ก์ด์์</h2> <ul> {data?.map((post) => ( <li key={post.id}> <h3>{post.title}</h3> <p>{post.body.substring(0, 100)}...</p> </li> ))} </ul> </div> ); }
์ ์์์ฒ๋ผ useQuery ํ
์ ์ฌ์ฉํ๋ฉด isLoading, isError, data ๋ฑ์ ์ํ๋ฅผ ์ฝ๊ฒ ํ์ฉํ ์ ์์ด์. React Query๋ ์๋์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์บ์ฑํ๊ณ , ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฐ์ดํฐ๋ฅผ ์ต์ ์ํ๋ก ์ ์งํ๋ ค๊ณ ๋
ธ๋ ฅํด์.
2๏ธโฃ ๊ฐ๋ ฅํ ์บ์ฑ ์ ๋ต: staleTime๊ณผ gcTime
React Query์ ํต์ฌ์ ์บ์ฑ๊ณผ ๋ฐ์ดํฐ ์ ์ ๋ ๊ด๋ฆฌ์ ์์ด์. ๋ชจ๋ ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๋ QueryClient ๋ด๋ถ์ ์บ์๋๋ฉฐ, ์ด ๋ฐ์ดํฐ๋ fresh ๋๋ stale ์ํ๋ฅผ ๊ฐ์ง ์ ์์ด์.
fresh(์ ์ ํ) ์ํ: ๋ฐ์ดํฐ๊ฐ ์ต์ ์ํ๋ผ๊ณ ๊ฐ์ฃผ๋๋ ๊ธฐ๊ฐ์ด์์. ์ด ๊ธฐ๊ฐ ๋์์๋ React Query๋ ๋คํธ์ํฌ ์์ฒญ ์์ด ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ๋ฐํํด์.staleTime์ต์ ์ผ๋ก ์ด ๊ธฐ๊ฐ์ ์ค์ ํ ์ ์์ด์. ๊ธฐ๋ณธ๊ฐ์0์ผ๋ก, ์ฟผ๋ฆฌ๊ฐ ๋ง์ดํธ๋์๋ง์stale์ํ๋ก ๊ฐ์ฃผ๋ผ์.stale(์ค๋๋) ์ํ: ๋ฐ์ดํฐ๊ฐ ์ต์ ์ด ์๋ ์๋ ์๋ค๊ณ ๊ฐ์ฃผ๋๋ ์ํ์์.stale์ํ์ ์ฟผ๋ฆฌ๋ ํน์ ์กฐ๊ฑด(์: ์ปดํฌ๋ํธ ๋ง์ดํธ, ์๋์ฐ ํฌ์ปค์ค, ๋คํธ์ํฌ ์ฌ์ฐ๊ฒฐ ๋ฑ)์์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์๋์ผ๋ก ๋ฆฌํ์นญ์ ์๋ํด์. ์ด ๊ณผ์ ์์ ์ฌ์ฉ์์๊ฒ๋ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ฃผ๋ฉด์ ๋ถ๋๋ฌ์ด ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ ์ ์์ด์.
gcTime (Garbage Collection Time, ์ด์ ์๋ cacheTime)์ ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๊ฐ ์บ์์์ ์ ๊ฑฐ๋๊ธฐ ์ ๊น์ง์ ์๊ฐ์ด์์. ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ๋ ์ด์ ์ฌ์ฉ๋์ง ์์ ๋ (์: ์ปดํฌ๋ํธ ์ธ๋ง์ดํธ), React Query๋ gcTime ๋์ ๋ฐ์ดํฐ๋ฅผ ์บ์์ ๋ณด๊ดํด์. ์ด ์๊ฐ ๋ด์ ๋์ผํ ์ฟผ๋ฆฌ๊ฐ ๋ค์ ์ฌ์ฉ๋๋ฉด ๋คํธ์ํฌ ์์ฒญ ์์ด ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ์ฌ์ฉํ ์ ์์ด์. gcTime์ด ์ง๋๋ฉด ํด๋น ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๋ ๊ฐ๋น์ง ์ปฌ๋ ์
๋์์ด ๋์ด ์บ์์์ ์์ ํ ์ ๊ฑฐ๋ผ์. ๊ธฐ๋ณธ๊ฐ์ 1000 * 60 * 5 (5๋ถ)์ด์์.
staleTime๊ณผ gcTime์ ๊ด๊ณ
staleTime: ๋ฐ์ดํฐ์ ๋ ผ๋ฆฌ์ ์ธ ์ ์ ๋๋ฅผ ์ ์ํด์. ์ด ์๊ฐ ๋์์ ๋ฆฌํ์นญ ์์ด ์บ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํด์.gcTime: ๋ฐ์ดํฐ์ ๋ฌผ๋ฆฌ์ ์ธ ์บ์ ์ ์ง ์๊ฐ์ ์ ์ํด์. ์ปดํฌ๋ํธ ์ธ๋ง์ดํธ ํ ์บ์์์ ๋ฐ์ดํฐ๊ฐ ์ธ์ ์ฌ๋ผ์ง์ง๋ฅผ ๊ฒฐ์ ํด์.
์ ์ฉํ ํ
staleTime์ ๊ธธ๊ฒ ์ค์ ํ๋ฉด ๋คํธ์ํฌ ์์ฒญ์ ์ค์ผ ์ ์์ง๋ง, ๋ฐ์ดํฐ์ ์ต์ ์ฑ์ด ๋จ์ด์ง ์ ์์ด์. ๋ฐ๋๋กstaleTime์ ์งง๊ฒ ์ค์ ํ๊ฑฐ๋0์ผ๋ก ๋๋ฉด ํญ์ ์ต์ ๋ฐ์ดํฐ๋ฅผ ์ ์งํ๋ ค๊ณ ์๋ํ์ง๋ง, ๋คํธ์ํฌ ์์ฒญ์ด ๋์ด๋ ์ ์์ผ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๊ตฌ์ฌํญ์ ๋ง์ถฐ ์ ์ ํ ์กฐ์ ํด์ผ ํด์.
3๏ธโฃ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฆฌํ์นญ๊ณผ ๋ฐ์ดํฐ ๋๊ธฐํ
React Query๋ stale ์ํ์ ์ฟผ๋ฆฌ์ ๋ํด ๋ค์๊ณผ ๊ฐ์ ์ํฉ์์ ์๋์ผ๋ก ๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฆฌํ์นญ์ ์๋ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ต์ ์ํ๋ก ๋๊ธฐํํด์.
- ์๋ก์ด ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ๋ง์ดํธ๋ ๋ (
staleTime์ด ์ง๋ ๊ฒฝ์ฐ) - ์๋์ฐ๊ฐ ๋ค์ ํฌ์ปค์ค ๋ ๋ (๋ธ๋ผ์ฐ์ ํญ ํ์ฑํ ์)
- ๋คํธ์ํฌ๊ฐ ๋ค์ ์ฐ๊ฒฐ๋ ๋
- ์ต์
์ผ๋ก ์ค์ ๋
refetchInterval์ ๋ฐ๋ผ ์ฃผ๊ธฐ์ ์ผ๋ก queryClient.invalidateQueries()๋๋queryClient.refetchQueries()๋ฅผ ํตํด ์๋์ผ๋ก
์ด๋ฌํ ์๋ ๋๊ธฐํ ๊ธฐ๋ฅ์ ๊ฐ๋ฐ์๊ฐ ์ง์ setInterval์ด๋ addEventListener๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ต์ ํํ๋ ๋ณต์กํ ๋ก์ง์ ๊ตฌํํ ํ์ ์์ด, ํญ์ ์ต์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉ์์๊ฒ ์ ๊ณตํ ์ ์๊ฒ ๋์์ค์.
๐ ๏ธ ์ค์ ํ์ฉ: ๋ฐ์ดํฐ ํ์นญ ๋ฐ ์ต์ ํ ํจํด
์ด์ React Query์ ํต์ฌ ๊ฐ๋ ์ ๋ฐํ์ผ๋ก ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์์ฃผ ์ฌ์ฉ๋๋ ํจํด๋ค์ ์ดํด๋ณผ๊ฒ์.
0๏ธโฃ ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์นญ ์์
์์ ์ดํด๋ณธ Posts ์ปดํฌ๋ํธ์ฒ๋ผ ๊ฐ๋จํ ๋ฐ์ดํฐ ํ์นญ์ useQuery๋ฅผ ํตํด ์ฝ๊ฒ ๊ตฌํํ ์ ์์ด์. ์ฌ๊ธฐ์ ์ค์ํ ๊ฒ์ queryKey๋ฅผ ํตํด ์ฟผ๋ฆฌ๋ฅผ ๋ช
ํํ๊ฒ ์๋ณํ๋ ๊ฒ์ด์์.
๋ง์ฝ ํน์ ID๋ฅผ ๊ฐ์ง ๊ฒ์๊ธ์ ๋ถ๋ฌ์ค๊ณ ์ถ๋ค๋ฉด, queryKey์ ID๋ฅผ ์ถ๊ฐํ์ฌ ๊ฐ ๊ฒ์๊ธ์ ๊ณ ์ ํ๊ฒ ์บ์ฑํ ์ ์์ด์.
// src/components/PostDetail.tsx import React from "react"; import { useQuery } from "@tanstack/react-query"; interface Post { id: number; title: string; body: string; } interface PostDetailProps { postId: number; } const fetchPostById = async (id: number): Promise<Post> => { const response = await fetch( `https://jsonplaceholder.typicode.com/posts/${id}`, ); if (!response.ok) { throw new Error(`Failed to fetch post with ID ${id}`); } return response.json(); }; export default function PostDetail({ postId }: PostDetailProps) { const { data, isLoading, isError, error } = useQuery<Post, Error>({ // queryKey์ postId๋ฅผ ํฌํจํ์ฌ ๊ฐ ๊ฒ์๊ธ์ ๊ณ ์ ํ๊ฒ ์บ์ฑํด์. queryKey: ["post", postId], queryFn: () => fetchPostById(postId), // queryFn์ ์ธ์๋ฅผ ๋ฐ์ง ์๋ ํจ์์ฌ์ผ ํ๋ฏ๋ก ๋ํํด์. // postId๊ฐ ์ ํจํ์ง ์์ผ๋ฉด ์ฟผ๋ฆฌ๋ฅผ ๋นํ์ฑํํ ์ ์์ด์. enabled: !!postId, }); if (isLoading) { return ( <Blockquote type="info"> ๊ฒ์๊ธ ์์ธ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๋ ์ค์ด์์... </Blockquote> ); } if (isError) { return ( <Blockquote type="error">์๋ฌ๊ฐ ๋ฐ์ํ์ด์: {error?.message}</Blockquote> ); } if (!data) { return <Blockquote type="warning">๊ฒ์๊ธ์ ์ฐพ์ ์ ์์ด์.</Blockquote>; } return ( <div> <h2>๐ {data.title}</h2> <p>{data.body}</p> </div> ); }
queryKey๋ ๋ฐฐ์ด ์์ ๋ฌธ์์ด, ์ซ์, ๊ฐ์ฒด ๋ฑ ์ด๋ค ์ง๋ ฌํ ๊ฐ๋ฅํ ๊ฐ์ด๋ผ๋ ํฌํจํ ์ ์์ด์. ์ด๋ฅผ ํตํด ์ฟผ๋ฆฌ์ ์ธ์๋ฅผ ๋ช
ํํ ํํํ๊ณ , React Query๊ฐ ์บ์๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋๋ก ํ ์ ์์ด์.
1๏ธโฃ ์บ์ ๋ฌดํจํ (Invalidation)์ ์๋ ์ ๋ฐ์ดํธ (Mutation)
๋ฐ์ดํฐ๋ฅผ ์์ฑ, ์
๋ฐ์ดํธ, ์ญ์ ํ๋ ์์
(Mutation)์ ์๋ฒ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ฏ๋ก, ๊ด๋ จ ์ฟผ๋ฆฌ์ ์บ์๋ฅผ ์ต์ ์ํ๋ก ์ ์งํด์ผ ํด์. React Query์ useMutation ํ
๊ณผ queryClient.invalidateQueries()๋ฅผ ํตํด ์ด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ด์.
// src/components/CreatePostForm.tsx import React, { useState } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; interface NewPost { title: string; body: string; userId: number; } interface CreatedPost extends NewPost { id: number; } const createPost = async (newPost: NewPost): Promise<CreatedPost> => { const response = await fetch("https://jsonplaceholder.typicode.com/posts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(newPost), }); if (!response.ok) { throw new Error("Failed to create post"); } return response.json(); }; export default function CreatePostForm() { const queryClient = useQueryClient(); const [title, setTitle] = useState(""); const [body, setBody] = useState(""); const createPostMutation = useMutation<CreatedPost, Error, NewPost>({ mutationFn: createPost, onSuccess: () => { // ๊ฒ์๊ธ ์์ฑ ์ฑ๊ณต ์, 'posts' ์ฟผ๋ฆฌ ์บ์๋ฅผ ๋ฌดํจํํ์ฌ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ถ๋ฌ์ค๋๋ก ํด์. queryClient.invalidateQueries({ queryKey: ["posts"] }); setTitle(""); setBody(""); alert("๊ฒ์๊ธ์ด ์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋์์ด์!"); }, onError: (error) => { alert(`๊ฒ์๊ธ ์์ฑ์ ์คํจํ์ด์: ${error.message}`); }, }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!title || !body) return; createPostMutation.mutate({ title, body, userId: 1 }); }; return ( <form onSubmit={handleSubmit} style={{ border: "1px solid #ccc", padding: "20px", borderRadius: "8px" }} > <h3>์ ๊ฒ์๊ธ ์์ฑํ๊ธฐ โ๏ธ</h3> <div> <label htmlFor="title">์ ๋ชฉ:</label> <br /> <input id="title" type="text" value={title} onChange={(e) => setTitle(e.target.value)} disabled={createPostMutation.isPending} style={{ width: "100%", padding: "8px", marginBottom: "10px" }} /> </div> <div> <label htmlFor="body">๋ด์ฉ:</label> <br /> <textarea id="body" value={body} onChange={(e) => setBody(e.target.value)} disabled={createPostMutation.isPending} rows={5} style={{ width: "100%", padding: "8px", marginBottom: "10px" }} /> </div> <button type="submit" disabled={createPostMutation.isPending}> {createPostMutation.isPending ? "์์ฑ ์ค..." : "๊ฒ์๊ธ ์์ฑํ๊ธฐ"} </button> {createPostMutation.isError && ( <Blockquote type="error"> ์ค๋ฅ: {createPostMutation.error?.message} </Blockquote> )} {createPostMutation.isSuccess && ( <Blockquote type="success">๊ฒ์๊ธ ์์ฑ ์ฑ๊ณต!</Blockquote> )} </form> ); }
onSuccess ์ฝ๋ฐฑ์์ queryClient.invalidateQueries({ queryKey: ['posts'] })๋ฅผ ํธ์ถํ์ฌ posts ์ฟผ๋ฆฌ์ ์บ์๋ฅผ ๋ฌดํจํํ์ด์. ์ด๋ ๊ฒ ํ๋ฉด posts ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๊ฐ stale ์ํ๋ก ๊ฐ์ฃผ๋๊ณ , ๋ค์ ๋ฒ ๋ ๋๋ง ๋๋ ์๋์ฐ ํฌ์ปค์ค ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์๋์ผ๋ก ๋ฆฌํ์นญ๋์ด ์ต์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฒ ๋ผ์. ์ด๋ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ์ ์งํ๋ ๋งค์ฐ ํจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ์ด์์.
2๏ธโฃ ๋ฌดํ ์คํฌ๋กค ๊ตฌํ (useInfiniteQuery)
๋ฌดํ ์คํฌ๋กค์ ๋ง์ ์์ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๋ณด์ฌ์ค ๋ ์ ์ฉํ ํจํด์ด์์. React Query๋ useInfiniteQuery ํ
์ ํตํด ๋ฌดํ ์คํฌ๋กค ๊ธฐ๋ฅ์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๋๋ก ๋์์ค์.
useInfiniteQuery๋ getNextPageParam ์ต์
์ ํตํด ๋ค์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ฌ ๋ฐฉ๋ฒ์ ์ ์ํ๊ณ , fetchNextPage ํจ์๋ฅผ ํตํด ๋ค์ ํ์ด์ง๋ฅผ ์์ฒญํ ์ ์๊ฒ ํด์.
// src/components/InfinitePosts.tsx import React from "react"; import { useInfiniteQuery } from "@tanstack/react-query"; interface Post { id: number; title: string; body: string; } interface PostsPage { data: Post[]; nextCursor: number | undefined; } const fetchInfinitePosts = async ({ pageParam = 0 }): Promise<PostsPage> => { // pageParam์ 0๋ถํฐ ์์ํ๋ค๊ณ ๊ฐ์ ํ๊ณ , ํ ํ์ด์ง์ 10๊ฐ์ฉ ๋ถ๋ฌ์์. const limit = 10; const start = pageParam * limit; const response = await fetch( `https://jsonplaceholder.typicode.com/posts?_start=${start}&_limit=${limit}`, ); if (!response.ok) { throw new Error("Failed to fetch infinite posts"); } const data = await response.json(); // ๋ค์ ํ์ด์ง๊ฐ ์๋์ง ํ์ธํ๊ธฐ ์ํด ํ์ฌ ํ์ด์ง์ ๋ง์ง๋ง ID๋ฅผ ์ปค์๋ก ์ฌ์ฉํด์. const nextCursor = data.length === limit ? pageParam + 1 : undefined; return { data, nextCursor }; }; export default function InfinitePosts() { const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, error, } = useInfiniteQuery<PostsPage, Error>({ queryKey: ["infinitePosts"], queryFn: fetchInfinitePosts, initialPageParam: 0, // ์ฒซ ํ์ด์ง ํ๋ผ๋ฏธํฐ // ๋ค์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ์ ์ ์ํด์. getNextPageParam: (lastPage) => lastPage.nextCursor, }); if (isLoading) { return ( <Blockquote type="info"> ๋ฌดํ ์คํฌ๋กค ๊ฒ์๊ธ์ ๋ถ๋ฌ์ค๋ ์ค์ด์์... </Blockquote> ); } if (isError) { return ( <Blockquote type="error">์๋ฌ๊ฐ ๋ฐ์ํ์ด์: {error?.message}</Blockquote> ); } return ( <div> <h2>๐ ๋ฌดํ ์คํฌ๋กค ๊ฒ์๊ธ์ด์์</h2> <ul> {data?.pages.map((page, i) => ( <React.Fragment key={i}> {page.data.map((post) => ( <li key={post.id}> <h3>{post.title}</h3> <p>{post.body.substring(0, 100)}...</p> </li> ))} </React.Fragment> ))} </ul> <button onClick={() => fetchNextPage()} disabled={!hasNextPage || isFetchingNextPage} style={{ marginTop: "20px", padding: "10px 20px", cursor: "pointer" }} > {isFetchingNextPage ? "๋ ๋ถ๋ฌ์ค๋ ์ค..." : hasNextPage ? "๋ ๋ถ๋ฌ์ค๊ธฐ" : "๋ชจ๋ ๊ฒ์๊ธ์ ๋ถ๋ฌ์์ด์!"} </button> {isFetchingNextPage && ( <Blockquote type="info">๋ค์ ํ์ด์ง๋ฅผ ๋ถ๋ฌ์ค๋ ์ค์ด์์...</Blockquote> )} </div> ); }
data.pages ๋ฐฐ์ด์ ๊ฐ ํ์ด์ง์ ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋์ด ์์ด์. fetchNextPage๋ฅผ ํธ์ถํ๋ฉด getNextPageParam์์ ๋ฐํ๋ ๊ฐ์ queryFn์ pageParam์ผ๋ก ์ ๋ฌํ์ฌ ๋ค์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๊ฒ ๋ผ์. ์ด๋ฅผ ํตํด ๋ณต์กํ ๋ฌดํ ์คํฌ๋กค ๋ก์ง์ ๊ฐ๊ฒฐํ๊ฒ ๊ตฌํํ ์ ์์ด์.
3๏ธโฃ SSR/SSG ํ๊ฒฝ์์์ Hydration
Next.js์ ๊ฐ์ SSR/SSG ํ๋ ์์ํฌ์์ React Query๋ฅผ ์ฌ์ฉํ๋ฉด, ์๋ฒ์์ ๋ฏธ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ํ์นญํ์ฌ HTML์ ํฌํจ์ํค๊ณ , ํด๋ผ์ด์ธํธ์์๋ ์ด ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฌ์ฉ(Hydration)ํ์ฌ ์ด๊ธฐ ๋ก๋ฉ ์ฑ๋ฅ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ด์.
์ด๋ฅผ ์ํด์๋ ์๋ฒ์์ QueryClient๋ฅผ ์์ฑํ๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ํ์นญํ ํ, dehydrate ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ ์บ์๋ฅผ ์ง๋ ฌํํ๊ณ , ํด๋ผ์ด์ธํธ์์ hydrate ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ด ์บ์๋ฅผ ์ฌ๊ตฌ์ฑํด์ผ ํด์.
// pages/posts/index.tsx (Next.js Pages Router ์์) import { dehydrate, QueryClient, HydrationBoundary, } from "@tanstack/react-query"; import Posts from "../../components/Posts"; import { fetchPosts } from "../../api/posts"; // fetchPosts ํจ์๋ ์์์ ์ ์ํ ๊ฒ๊ณผ ๋์ผํ๋ค๊ณ ๊ฐ์ ํด์. export async function getServerSideProps() { const queryClient = new QueryClient(); // ์๋ฒ์์ 'posts' ์ฟผ๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ํ์นญํด์. await queryClient.prefetchQuery({ queryKey: ["posts"], queryFn: fetchPosts, }); return { props: { dehydratedState: dehydrate(queryClient), }, }; } export default function PostsPage({ dehydratedState }) { return ( // HydrationBoundary๋ก ์๋ฒ์์ ํ์นญ๋ ์บ์๋ฅผ ํด๋ผ์ด์ธํธ์์ ์ฌ์ฌ์ฉํด์. <HydrationBoundary state={dehydratedState}> <h1>๋ชจ๋ ๊ฒ์๊ธ</h1> <Posts /> </HydrationBoundary> ); }
getServerSideProps์์ prefetchQuery๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ์์ posts ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์จ ํ, dehydrate(queryClient)๋ฅผ ํตํด ์ด ์บ์๋ฅผ ์ง๋ ฌํ๋ ํํ๋ก ๋ฐํํด์. ํด๋ผ์ด์ธํธ์์๋ HydrationBoundary ์ปดํฌ๋ํธ์ ์ด dehydratedState๋ฅผ ์ ๋ฌํ์ฌ ์๋ฒ์์ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ์ฌ์ฉํ ์ ์๊ฒ ๋ผ์. ์ด๋ก ์ธํด ์ด๊ธฐ ๋ก๋ฉ ์ ๊น๋นก์ ์์ด ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค ์ ์๊ณ , SEO์๋ ์ ๋ฆฌํด์.
์ ์ฉํ ํNext.js App Router์์๋
QueryClientProvider๋ฅผ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ๊ณ , ์๋ฒ ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๋ฅผ ํ์นญํ ํ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ์ ๋ฌํ๋ ๋ฐฉ์์ผ๋ก Hydration์ ๊ตฌํํ ์ ์์ด์. ์ด๋ Pages Router์๋ ์ฝ๊ฐ ๋ค๋ฅธ ์ ๊ทผ ๋ฐฉ์์ ๊ฐ์ง๋ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์.
๐ ์ ๋ฆฌํ๋ฉฐ: React Query๋ก ๊ฐ๋ฐ ์์ฐ์ฑ์ ๋์ฌ์
React Query๋ ๋จ์ํ ๋ฐ์ดํฐ๋ฅผ ํ์นญํ๋ ๊ฒ์ ๋์ด, ์๋ฒ ์ํ ๊ด๋ฆฌ์ ๋ณต์ก์ฑ์ ํด๊ฒฐํ๊ณ ๊ฐ๋ฐ ์์ฐ์ฑ๊ณผ ์ฌ์ฉ์ ๊ฒฝํ์ ๋์์ ํฅ์์ํค๋ ๊ฐ๋ ฅํ ๋๊ตฌ์์.
0๏ธโฃ ํต์ฌ ์์ฝ
- ์๋ฒ ์ํ์ ํด๋ผ์ด์ธํธ ์ํ ๋ถ๋ฆฌ: React Query๋ ์๋ฒ ์ํ ๊ด๋ฆฌ์ ํนํ๋์ด ๋ณต์กํ ๋น๋๊ธฐ ๋ก์ง์ ์ถ์ํํด์.
- ์ง๋ฅ์ ์ธ ์บ์ฑ:
staleTime๊ณผgcTime์ ํตํด ๋ฐ์ดํฐ์ ์ ์ ๋์ ์บ์ ์ ์ง ์๊ฐ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํด์. - ์๋ ๋๊ธฐํ: ์๋์ฐ ํฌ์ปค์ค, ๋คํธ์ํฌ ์ฌ์ฐ๊ฒฐ ๋ฑ ๋ค์ํ ์กฐ๊ฑด์์ ์๋์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฆฌํ์นญํ์ฌ ์ต์ ์ํ๋ฅผ ์ ์งํด์.
- ๊ฐ๊ฒฐํ API:
useQuery,useMutation,useInfiniteQuery๋ฑ ์ง๊ด์ ์ธ ํ ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฒ ๋ค๋ฃฐ ์ ์์ด์. - SSR/SSG ์ง์: Next.js์ ๊ฐ์ ํ๋ ์์ํฌ์์ Hydration์ ํตํด ์ด๊ธฐ ๋ก๋ฉ ์ฑ๋ฅ๊ณผ SEO๋ฅผ ๊ฐ์ ํ ์ ์์ด์.
1๏ธโฃ ๋ค์ ์คํ
์ด ๊ธ์์ ๋ค๋ฃฌ ๋ด์ฉ์ React Query์ ๊ธฐ๋ณธ์ ์ธ ๊ฐ๋ ๊ณผ ํ์ฉ ํจํด์ด์์. ์ค์ ํ๋ก์ ํธ์์๋ ๋ ๋ง์ ๊ธฐ๋ฅ๊ณผ ์ต์ ๋ค์ ํ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๊ตฌ์ฌํญ์ ๋ง๋ ์ต์ ํ๋ฅผ ์งํํ ์ ์์ด์. ๋ค์ ๋จ๊ณ๋ก ์๋ ๋ด์ฉ์ ํ์ตํด ๋ณด์๊ธธ ์ถ์ฒํด์.
- ์ตํฐ๋ฏธ์คํฑ ์
๋ฐ์ดํธ: Mutation ์ ์๋ฒ ์๋ต ์ ์ UI๋ฅผ ๋ฏธ๋ฆฌ ์
๋ฐ์ดํธํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๋ฐฉ๋ฒ
- ์ฟผ๋ฆฌ ์บ์ ์๋ ์
๋ฐ์ดํธ:
queryClient.setQueryData()๋ฅผ ์ฌ์ฉํ์ฌ ์บ์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ์ ๋ฐ์ดํธํ๋ ๋ฐฉ๋ฒ - ์ปค์คํ
ํ
:
useQuery๋ฅผ ๊ฐ์ธ๋ ์ปค์คํ ํ ์ ๋ง๋ค์ด ์ฟผ๋ฆฌ ๋ก์ง์ ์ฌ์ฌ์ฉํ๊ณ ์ถ์ํํ๋ ๋ฐฉ๋ฒ - ์๋ฌ ํธ๋ค๋ง ์ ๋ต:
onError์ฝ๋ฐฑ,useErrorBoundary,suspense์ต์ ์ ํ์ฉํ ๊ณ ๊ธ ์๋ฌ ์ฒ๋ฆฌ
React Query๋ ํ ๋ฒ ์ตํ๋๋ฉด React ๊ฐ๋ฐ์ ํจ์ฌ ์ฆ๊ฒ๊ณ ํจ์จ์ ์ผ๋ก ๋ง๋ค์ด ์ค ๊ฑฐ์์. ์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ์์ React Query์ ๊ฐ๋ ฅํจ์ ์ถฉ๋ถํ ๊ฒฝํํด ๋ณด์๊ธธ ๋ฐ๋ผ์!
๐ฎ ์ฐธ๊ณ
- TanStack Query ๊ณต์ ๋ฌธ์
- React Query v4์์ v5๋ก ๋ง์ด๊ทธ๋ ์ด์ ๊ฐ์ด๋
- A (mostly) complete guide to React Query
์ฐ๊ด๋ ํฌ์คํธ
- ๋จ์ด: 1,996๊ฐ22๋ถ
[๐ค] Next.js App Router์ Auth.js (NextAuth.js) ์ฐ๋ ๊ฐ์ด๋: ์๋ฒ ๋ฐ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ ์ธ์ ๊ด๋ฆฌ
Next.js 14+ App Router ํ๊ฒฝ์์ Auth.js (๊ตฌ NextAuth.js)๋ฅผ ์ฌ์ฉํ์ฌ ์์ ํ๊ณ ํจ์จ์ ์ธ ์ธ์ฆ ์์คํ ์ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์์๋ด์. ์๋ฒ ์ปดํฌ๋ํธ์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ์ธ์ ์ ๊ด๋ฆฌํ๊ณ ๋ณดํธํ๋ ์ค์ฉ์ ์ธ ํ๊ณผ ์ฝ๋ ์์๋ฅผ ์ ๊ณตํด์.
- ๋จ์ด: 2,440๊ฐ26๋ถ
[๐ค] Next.js App Router์์ Zod์ TypeScript๋ก API ์ ํจ์ฑ ๊ฒ์ฌ ๋ฐ ํ์ ์์ ์ฑ ํ๋ณด
Next.js App Router ํ๊ฒฝ์์ Zod์ TypeScript๋ฅผ ํ์ฉํ์ฌ API ์์ฒญ ๋ฐ์ดํฐ์ ๋ฐํ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ณ , ๋์์ ๊ฐ๋ ฅํ ํ์ ์์ ์ฑ์ ํ๋ณดํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,646๊ฐ18๋ถ
[๐ค] JavaScript ๊ตฌ์กฐ ๋ถํด ํ ๋น ํจํด ์ฌํ: ๋ค์ํ ํ์ฉ๊ณผ ์ค์ ํ
JavaScript ๊ตฌ์กฐ ๋ถํด ํ ๋น(Destructuring Assignment)์ ๊ธฐ๋ณธ๋ถํฐ ์ค์ฒฉ ๊ฐ์ฒด, ๊ธฐ๋ณธ๊ฐ, Rest ๋ฌธ๋ฒ, ํจ์ ์ธ์ ํ์ฉ๊น์ง ๋ค์ํ ํจํด๊ณผ ์ค์ ํ์ ํตํด ์ฝ๋๋ฅผ ๋ ๊ฐ๊ฒฐํ๊ณ ํจ์จ์ ์ผ๋ก ์์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 1,841๊ฐ23๋ถ
[๐ค] ์น ์ฑ๋ฅ ์ต์ ํ: ๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์ ๋๊ตฌ๋ก Core Web Vitals ์ง๋จํ๊ณ ๊ฐ์ ํ๊ธฐ
๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์ ๋๊ตฌ(DevTools)์ Lighthouse, Performance, Network ํญ์ ํ์ฉํ์ฌ Core Web Vitals๋ฅผ ์ง๋จํ๊ณ ์ค์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ๊ฐ์ ํ๋ ์ค์ง์ ์ธ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 1,957๊ฐ22๋ถ
[๐ค] TypeScript ํจ์ ์ค๋ฒ๋ก๋ฉ: ์๊ทธ๋์ฒ์ ๊ตฌํ์ฒด๋ฅผ ์๋ฒฝํ๊ฒ ์ดํดํ๊ณ ํ์ฉํ๊ธฐ
TypeScript์์ ํจ์ ์ค๋ฒ๋ก๋ฉ์ ์ฌ์ฉํ์ฌ ๋ค์ํ ํ์ ์ ์ธ์๋ฅผ ์ฒ๋ฆฌํ๊ณ , ์ฝ๋์ ๊ฐ๋ ์ฑ ๋ฐ ์์ ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ์๊ทธ๋์ฒ์ ๊ตฌํ์ฒด์ ๊ด๊ณ๋ฅผ ์ค์ฌ์ผ๋ก ์์ธํ ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 1,709๊ฐ19๋ถ
[๐ค] JavaScript requestIdleCallback๊ณผ isInputPending์ผ๋ก UI ๋ฐ์์ฑ ๊ฐ์ ํ๊ธฐ: ๋ฉ์ธ ์ค๋ ๋ ์ต์ ํ ์ฌํ
JavaScript์ `requestIdleCallback`๊ณผ `isInputPending` API๋ฅผ ํ์ฉํ์ฌ ๋ฉ์ธ ์ค๋ ๋ ๋ธ๋กํน์ ์ค์ด๊ณ UI ๋ฐ์์ฑ์ ๊ทน๋ํํ๋ ์ค์ง์ ์ธ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์. ์ฌ์ฉ์ ๊ฒฝํ์ ์ํ ์น ์ฑ๋ฅ ์ต์ ํ ์ ๋ต์ ์๋ดํด ๋๋ ค์.
- ๋จ์ด: 2,149๊ฐ23๋ถ
[๐ค] Next.js App Router์ `generateMetadata`์ `generateViewport` ์ฌํ: SEO ๋ฐ PWA ์ต์ ํ ์ ๋ต
Next.js 14/13 App Router์์ `generateMetadata`์ `generateViewport` ํจ์๋ฅผ ํ์ฉํ์ฌ ๋์ SEO ๋ฉํํ๊ทธ์ PWA ์ค์ ์ ์ต์ ํํ๋ ์ฌํ ์ ๋ต์ ๋ค๋ฃน๋๋ค. ์ค๋ฌด ์์์ ํจ๊ป ์น์ฌ์ดํธ์ ๊ฒ์ ์์ง ๋ ธ์ถ ๋ฐ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ทน๋ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 1,788๊ฐ19๋ถ
[๐ค] CSS ๋ณ์(Custom Properties) ์ฌํ: ๋์ ํ ๋ง, ์ค์ฝํ, JavaScript ํ์ฉ ์ ๋ต
CSS ๋ณ์(Custom Properties)์ ๊ธฐ๋ณธ๋ถํฐ ๋์ ํ ๋ง ๊ตฌํ, ์ค์ฝํ ์ ๋ต, ๊ทธ๋ฆฌ๊ณ JavaScript์์ ์ํธ์์ฉ๊น์ง ์ค๋ฌด์ ๋ฐ๋ก ์ ์ฉํ ์ ์๋ ์ฌํ ํ์ฉ๋ฒ์ ๋ธ๋ฃจ๊ฐ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,293๊ฐ15๋ถ
[๐ค] Next.js App Router Edge Runtime: ๊ธ๋ก๋ฒ ์ฑ๋ฅ๊ณผ ๋น์ฉ ํจ์จ์ฑ ๊ทน๋ํ ์ ๋ต
Next.js App Router์์ Edge Runtime์ ํ์ฉํด Route Handler ์ฑ๋ฅ์ ๊ทน๋ํํ๊ณ , ๊ธ๋ก๋ฒ ์ฌ์ฉ์์๊ฒ ๋น ๋ฅธ ์๋ต ์๋๋ฅผ ์ ๊ณตํ๋ฉฐ, ๋น์ฉ ํจ์จ์ ์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ์ด์ํ๋ ์ค์ ์ ๋ต์ ์์ธํ ๋ค๋ค์. Edge Runtime์ ์ฅ์ ๊ณผ ์ ํ์ฌํญ, ๊ทธ๋ฆฌ๊ณ ์ค์ ์ฝ๋ ์์๋ฅผ ํตํด ํจ๊ณผ์ ์ธ ์ ์ฉ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 1,980๊ฐ25๋ถ
[๐ค] Next.js/React์์ ํด๋ฆฐ ์ํคํ ์ฒ์ ์์กด์ฑ ์ญ์ ์์น ์ ์ฉํ๊ธฐ: ํ์ฅ ๊ฐ๋ฅํ ํ๋ฐํธ์๋ ๋ง๋ค๊ธฐ
Next.js์ React ํ๋ก์ ํธ์์ ํด๋ฆฐ ์ํคํ ์ฒ์ ์์กด์ฑ ์ญ์ ์์น(DIP)์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ์ด์. ๋๋ฉ์ธ, ์ ์ค์ผ์ด์ค, ์ธํ๋ผ ๊ณ์ธต์ ๋ถ๋ฆฌํ์ฌ ์ ์ง๋ณด์์ฑ๊ณผ ํ ์คํธ ์ฉ์ด์ฑ์ ๊ทน๋ํํ๋ ์ค์ฉ์ ์ธ ์ ๋ต์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 1,733๊ฐ19๋ถ
[๐ค] Jotai๋ก Next.js App Router ํด๋ผ์ด์ธํธ ์ํ ๊ด๋ฆฌ ์ฌํ: ์ํฐ ํจํด๊ณผ ์ต์ ํ
Next.js App Router ํ๊ฒฝ์์ Jotai ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ์ฌ ํจ์จ์ ์ธ ํด๋ผ์ด์ธํธ ์ํ ๊ด๋ฆฌ ์ ๋ต์ ์์๋ณด์ธ์. ์ํฐ ํจํด์ ์ด์ฉํ ์ํ ๋ถ๋ฆฌ, ์ต์ ํ ๊ธฐ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ค์ฉ์ ์ธ ์ฝ๋ ์์๋ฅผ ํตํด ๋ณต์กํ UI ์ํ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ๋ธ๋ฃจ๊ฐ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,720๊ฐ18๋ถ
[๐ค] Next.js App Router ์ ์ญ ์ํ ๊ด๋ฆฌ: Server/Client Components ์๋ฒฝ ๊ฐ์ด๋
Next.js App Router ํ๊ฒฝ์์ Server Components์ Client Components ๊ฐ์ ์ ์ญ ์ํ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ํ๊ตฌํด์. Zustand, Recoil, Context API ๋ฑ ๋ค์ํ ์๋ฃจ์ ๊ณผ ์ค์ฉ์ ์ธ ์ ๋ต์ ํตํด ๋ณต์กํ ์ํ ๊ด๋ฆฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด ๋ณด์ธ์.
- ๋จ์ด: 1,787๊ฐ20๋ถ
[๐ค] Atomic Design ํจํด์ผ๋ก React/Next.js ์ปดํฌ๋ํธ ์ํคํ ์ฒ ํ์ฅ์ฑ ๋์ด๊ธฐ
React ๋ฐ Next.js ํ๋ก์ ํธ์์ Atomic Design ํจํด์ ์ ์ฉํ์ฌ ์ปดํฌ๋ํธ์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ํ์ฅ์ฑ์ ๊ทน๋ํํ๋ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์๋ ค๋๋ ค์. ํจ์จ์ ์ธ ํ๋ก ํธ์๋ ์ํคํ ์ฒ๋ฅผ ๊ตฌ์ถํด ๋ณด์ธ์.
- ๋จ์ด: 2,160๊ฐ24๋ถ
[๐ค] ์๋ฐ์คํฌ๋ฆฝํธ Web Workers: UI ๋ธ๋กํน ์์ด ๋ฌด๊ฑฐ์ด ์์ ์ฒ๋ฆฌํ๊ธฐ
ํ๋ก ํธ์๋์์ CPU ์ง์ฝ์ ์ธ ์์ ์ UI ๋ธ๋กํน์ ํํ ๋ฌธ์ ์์. ์๋ฐ์คํฌ๋ฆฝํธ Web Workers๋ก ๋ฉ์ธ ์ค๋ ๋๋ฅผ ํด๋ฐฉํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ์ต์ ํํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ์ฝ๋ ์์์ ํจ๊ป ์์๋ณด์ธ์. ๋ณต์กํ ๊ณ์ฐ, ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋ฑ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์์ ํ๊ฒ ์คํํ๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
- ๋จ์ด: 1,822๊ฐ20๋ถ
[๐ค] TypeScript `as const` ์ฌ์ธต ๋ถ์: ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ๊ณผ ๋ถ๋ณ์ฑ ํ์ฉ ์ ๋ต
TypeScript์ `as const`๋ฅผ ํ์ฉํ์ฌ ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ์ ๊ฐํํ๊ณ , ๋ฐฐ์ด๊ณผ ๊ฐ์ฒด์ ๋ถ๋ณ์ฑ์ ์์ ํ๊ฒ ํ๋ณดํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ์์ธํ ์์๋ด์. ์ค๋ฌด ์ฝ๋์ ํ์ ์์ ์ฑ๊ณผ ๊ฐ๋ ์ฑ์ ๋์ฌ๋ณด์ธ์.
- ๋จ์ด: 1,486๊ฐ17๋ถ
[๐ค] Next.js @next/font๋ฅผ ํ์ฉํ ํฐํธ ์ต์ ํ ๋ฐ CLS ๊ฐ์ ์ ๋ต
Next.js ์ ํ๋ฆฌ์ผ์ด์ ์์ ํฐํธ ๋ก๋ฉ์ผ๋ก ์ธํ Cumulative Layout Shift(CLS)๋ฅผ ์ต์ํํ๊ณ ์น ์ฑ๋ฅ์ ๊ทน๋ํํ๋ @next/font ์ฌ์ฉ๋ฒ๊ณผ ์ค์ฉ์ ์ธ ์ต์ ํ ์ ๋ต์ ์์ธํ ์์๋ด์.
- ๋จ์ด: 1,914๊ฐ21๋ถ
[๐ค] Next.js App Router ๊ตญ์ ํ (i18n) ์๋ฒฝ ๊ฐ์ด๋: ๋ค๊ตญ์ด ์ง์ ๊ตฌํ ์ ๋ต
Next.js 14+ App Router ํ๊ฒฝ์์ ๊ตญ์ ํ(i18n)๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์๋ ค๋๋ ค์. ๋ฏธ๋ค์จ์ด๋ฅผ ํ์ฉํ ์ธ์ด ๊ฐ์ง๋ถํฐ ํด๋ผ์ด์ธํธ/์๋ฒ ์ปดํฌ๋ํธ์์์ ๋ค๊ตญ์ด ํ ์คํธ ์ฒ๋ฆฌ๊น์ง, ์ค๋ฌด์ ๋ฐ๋ก ์ ์ฉํ ์ ์๋ ๋ค๊ตญ์ด ์ง์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ดํด ๋๋ฆด๊ฒ์.
- ๋จ์ด: 1,328๊ฐ16๋ถ
[์น ์ฑ๋ฅ ์ต์ ํ] Interaction to Next Paint (INP) ๊ฐ์ : ์ฌ์ฉ์ ๊ฒฝํ์ ๊ทน๋ํํ๋ ์ค์ ์ ๋ต
์๋ก์ด Core Web Vitals ์งํ, Interaction to Next Paint(INP)๋ฅผ ๊น์ด ์ดํดํ๊ณ ์ค์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ INP ์ ์๋ฅผ ๊ฐ์ ํ๋ ์ค์ฉ์ ์ธ ์ต์ ํ ์ ๋ต๋ค์ ๋ธ๋ฃจ๊ฐ ์์ธํ ์๋ ค๋๋ ค์. ์ฌ์ฉ์ ์ธํฐ๋์ ์๋ต์ฑ์ ๋์ฌ ์ง์ ํ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ง๋ค์ด ๋ณด์ธ์.
๋จ์ด: 1,668๊ฐ20๋ถ[๐ค] JavaScript Decorator ์ฌ์ธต ๋ถ์: ๋ฉํํ๋ก๊ทธ๋๋ฐ๊ณผ ํ์ ์คํฌ๋ฆฝํธ ํ์ฉ ์ ๋ต
JavaScript Decorator์ ๊ฐ๋ ๋ถํฐ ํ์ ์คํฌ๋ฆฝํธ์์์ ํ์ฉ, ๊ทธ๋ฆฌ๊ณ ๋ฏธ๋ ํ์คํ ๋ฐฉํฅ๊น์ง ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ๋ฉฐ, ์ค๋ฌด์์ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ๊ณผ ๊ฐ๋ ์ฑ์ ๋์ด๋ ๋ฉํํ๋ก๊ทธ๋๋ฐ ํจํด์ ํ์ตํด ๋ณด์ธ์.
- ๋จ์ด: 2,634๊ฐ28๋ถ
[๐ค] JavaScript ํ๋ก๋ฏธ์ค/async-await ์ฌํ: ์ค์ ํจํด๊ณผ ์ฃผ์์ฌํญ
JavaScript์ ํ๋ก๋ฏธ์ค์ async/await๋ฅผ ๊น์ด ์ดํดํ๊ณ , ์ค๋ฌด์์ ๋ง์ฃผํ๋ ๋ณต์กํ ๋น๋๊ธฐ ์ฒ๋ฆฌ ์ํฉ์์ ํ์ฉํ ์ ์๋ ๊ณ ๊ธ ํจํด๊ณผ ํํ ๋ฐ์ํ๋ ์ค์๋ฅผ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,421๊ฐ16๋ถ
[๐ค] TypeScript 5.2+ `using` ์ ์ธ์ผ๋ก ์์ ์๋ ๊ด๋ฆฌํ๊ธฐ: ๊น๋ํ ๋ฆฌ์์ค ์ ๋ฆฌ์ ์์
TypeScript 5.2์์ ๋์ ๋ `using` ์ ์ธ์ ํ์ฉํ์ฌ ํ์ผ ํธ๋ค, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๋ฑ ๋ค์ํ ์์์ ์๋์ผ๋ก ๊ด๋ฆฌํ๊ณ ๊น๋ํ๊ฒ ์ ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์ ์ ํจ๊ป ์์ธํ ์์๋ด์. ๋ ์ด์ `try...finally` ๋ธ๋ก์ผ๋ก ๋ณต์กํ๊ฒ ์์์ ํด์ ํ ํ์ ์์ด์.
- ๋จ์ด: 2,006๊ฐ22๋ถ
[๐ค] Feature-Sliced Design (FSD)์ผ๋ก ํ์ฅ ๊ฐ๋ฅํ ํ๋ก ํธ์๋ ์ํคํ ์ฒ ๊ตฌ์ถํ๊ธฐ
Feature-Sliced Design (FSD)์ ๋๊ท๋ชจ ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ฅ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๋์ด๋ ๊ฐ๋ ฅํ ์ํคํ ์ฒ ํจํด์ด์์. FSD์ ํต์ฌ ์์น, ๋ ์ด์ด ๊ตฌ์กฐ, ์ค๋ฌด ์ ์ฉ ๋ฐฉ๋ฒ์ ์ฝ๋ ์์์ ํจ๊ป ์์ธํ ์์๋ด์.
- ๋จ์ด: 1,781๊ฐ21๋ถ
[๐ค] TypeScript `moduleResolution: 'bundler'`์ `verbatimModuleSyntax` ์๋ฒฝ ์ดํด: ๋ชจ๋ ๋ฒ๋ค๋ฌ ์๋์ ํ์ ์คํฌ๋ฆฝํธ ์ค์ ์ต์ ํ
๋ชจ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํ๋ก์ ํธ์์ TypeScript์ `moduleResolution: 'bundler'`์ `verbatimModuleSyntax` ์ต์ ์ด ์ ์ค์ํ์ง, ์ด๋ป๊ฒ ์ฌ๋ฐ๋ฅด๊ฒ ์ค์ ํ๊ณ ํ์ฉํ์ฌ ๊ฐ๋ฐ ๊ฒฝํ๊ณผ ๋ฒ๋ค๋ง ํจ์จ์ ๊ทน๋ํํ๋์ง ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์์๋ด์.
- ๋จ์ด: 1,908๊ฐ24๋ถ
[๐ค] ๋ชจ๋ ธ๋ ํฌ ์ํคํ ์ฒ: ํจ์จ์ ์ธ ๋๊ท๋ชจ ํ๋ก์ ํธ ์ค๊ณ๋ฅผ ์ํ ์ค์ ๊ฐ์ด๋
๋๊ท๋ชจ ์น ํ๋ก์ ํธ์์ ๋ชจ๋ ธ๋ ํฌ๋ฅผ ๋์ ํ๋ ์ด์ , ์ฅ๋จ์ , ๊ทธ๋ฆฌ๊ณ ํจ๊ณผ์ ์ธ ๊ตฌ์กฐ ์ค๊ณ ๋ฐ ์ค๋ฌด ์ ์ฉ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์์ธํ ์๋ ค๋๋ ค์. ํ ์์ฐ์ฑ ํฅ์๊ณผ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๊ทน๋ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
๋จ์ด: 2,188๊ฐ26๋ถ[๐ค] ์น ์ฑ๋ฅ ์ต์ ํ: ๋ฆฌํ๋ก์ฐ์ ๋ฆฌํ์ธํธ ์ต์ํ ์ ๋ต
๋ธ๋ผ์ฐ์ ๋ ๋๋ง ๊ณผ์ ์ ํต์ฌ์ธ ๋ฆฌํ๋ก์ฐ(Reflow)์ ๋ฆฌํ์ธํธ(Repaint)์ ๋ฐ์ ์๋ฆฌ๋ฅผ ์ดํดํ๊ณ , ์ด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ค์ฌ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ๊ทน๋ํํ๋ ์ค์ง์ ์ธ ์ ๋ต๊ณผ ์ฝ๋ ์์๋ฅผ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,903๊ฐ19๋ถ
[๐ค] Vitest์ React Testing Library๋ก Next.js ์ปดํฌ๋ํธ ์๋ฒฝ ํ ์คํธํ๊ธฐ
Next.js ํ๋ก์ ํธ์์ Vitest์ React Testing Library๋ฅผ ํ์ฉํ์ฌ UI ์ปดํฌ๋ํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ ์คํธํ๋ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์ ์ ํจ๊ป ์์ธํ ์์๋ด์. ์ค์ ๋ถํฐ Mocking, ์ด๋ฒคํธ ์๋ฎฌ๋ ์ด์ ๊น์ง, ๊ฒฌ๊ณ ํ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ์ํ ํ ์คํธ ์ ๋ต์ ์ตํ๋ณด์ธ์.
- ๋จ์ด: 1,542๊ฐ20๋ถ
[๐ค] Tailwind CSS v4 ์ถ์: ๊ฐ๋ฐ์์๊ฒ ์ฐพ์์ฌ ๋ณํ์ ์ต์ ํ ์ ๋ต
Tailwind CSS v4์ ์ฃผ์ ๋ณ๊ฒฝ์ฌํญ๊ณผ ์๋ก์ด ๊ธฐ๋ฅ๋ค์ ๊น์ด ์๊ฒ ๋ถ์ํ๊ณ , ์ค๋ฌด์์ ํจ์จ์ ์ผ๋ก ์ ์ฉํ๋ฉฐ ์ฑ๋ฅ์ ์ต์ ํํ๋ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,566๊ฐ20๋ถ
[๐ค] Next.js Dockerfile ์ต์ ํ: ํ๋ก๋์ ๋ฐฐํฌ๋ฅผ ์ํ ์๋ฒฝ ๊ฐ์ด๋
Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ Docker ์ปจํ ์ด๋๋ก ํจ์จ์ ์ผ๋ก ๋ฐฐํฌํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. ๋ฉํฐ์คํ ์ด์ง ๋น๋, ์บ์ฑ ์ ๋ต, ๋ณด์ ์ค์ ๋ฑ ํ๋ก๋์ ํ๊ฒฝ์ ์ต์ ํ๋ Dockerfile ์์ฑ ๊ฐ์ด๋๋ฅผ ์ ๊ณตํด ๋๋ ค์.
- ๋จ์ด: 2,177๊ฐ24๋ถ
[๐ค] CSS Grid ์ฌํ ๊ฐ์ด๋: ์ค์ ๋ ์ด์์ ํจํด๊ณผ ๋ฐ์ํ ๋์์ธ ์ ๋ต
CSS Grid๋ ๊ฐ๋ ฅํ 2์ฐจ์ ๋ ์ด์์ ์์คํ ์ด์์. ์ด ๊ฐ์ด๋์์ Grid์ ํต์ฌ ๊ฐ๋ ๋ถํฐ ์ค์ ๋ ์ด์์ ํจํด, ๋ฐ์ํ ๋์์ธ ์ ๋ต๊น์ง ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ์ด ์ค๋ฌด์ ๋ฐ๋ก ์ ์ฉํ ์ ์๋๋ก ๋์๋๋ ค์.
- ๋จ์ด: 1,718๊ฐ19๋ถ
[๐ค] TypeScript ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ : ๋ฌธ์์ด ํ์ ์ ๋ง๋ฒ์ฌ๋ก ๋ณ์ ํ๊ธฐ
TypeScript์ ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ ์ ํ์ฉํ์ฌ ๋ณต์กํ ๋ฌธ์์ด ํจํด์ ์์ ํ๊ฒ ํ์ ์ถ๋ก ํ๊ณ , ๊ฐ๋ ฅํ ์ ํธ๋ฆฌํฐ ํ์ ์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์ ์ ํจ๊ป ์์ธํ ์์๋ด์. ํ์ ์์ ์ฑ์ ํ ๋จ๊ณ ๋์ฌ ๊ฐ๋ฐ ๊ฒฝํ์ ๊ฐ์ ํด ๋ณด์ธ์.
- ๋จ์ด: 1,918๊ฐ23๋ถ
[๐ค] JavaScript WeakMap๊ณผ WeakSet: ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง์ ์ต์ ํ ์ ๋ต
JavaScript์์ WeakMap๊ณผ WeakSet์ ํ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฐฉ์งํ๊ณ ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ๊ฐ๋น์ง ์ปฌ๋ ์ ๋์ ์๋ฆฌ์ ํจ๊ป ๊ฐ์ฒด ์ฐธ์กฐ ๊ด๋ฆฌ์ ์ค์์ฑ์ ์ดํดํ๊ณ , ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ์ ๋ต์ ๋ฐฐ์๋ด์.
- ๋จ์ด: 1,455๊ฐ17๋ถ
[๐ค] Next.js/React ํ๋ก์ ํธ๋ฅผ ์ํ ESLint & Prettier ์ค์ ์๋ฒฝ ๊ฐ์ด๋
๋ณต์กํ Next.js ๋ฐ React ํ๋ก์ ํธ์์ ์ผ๊ด๋ ์ฝ๋ ์คํ์ผ๊ณผ ํ์ง์ ์ ์งํ๋ ESLint์ Prettier ์ค์ ๋ฐฉ๋ฒ์ ์์ธํ ์๋ ค๋๋ ค์. ํ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ต์ ํ๋ ์ค์ ์ผ๋ก ๊ฐ๋ฐ ํจ์จ์ ๋์ฌ๋ณด์ธ์.
- ๋จ์ด: 2,022๊ฐ25๋ถ
[๐ค] JavaScript์ ํต์ฌ: ํ๋กํ ํ์ ์ฒด์ธ ์๋ฒฝ ์ดํด์ ํ์ฉ ์ ๋ต
JavaScript์ ์ฌ์ฅ๋ถ, ํ๋กํ ํ์ ์ฒด์ธ์ ๋์ ์๋ฆฌ๋ฅผ ๊น์ด ํ๊ณ ๋ค์ด ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ๊ณผ ์์์ ์๋ฒฝํ๊ฒ ์ดํดํ๊ณ ์ค๋ฌด์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์. ์ฑ๋ฅ ์ต์ ํ ํ๋ ํจ๊ป ๋ค๋ค์.
- ๋จ์ด: 2,118๊ฐ22๋ถ
[๐ค] React ์ปค์คํ ํ : ์ฌ์ฌ์ฉ์ฑ ๋์ด๋ ์ค๊ณ ์์น๊ณผ ํ ์คํธ ์ ๋ต
React ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๊ทน๋ํํ๋ ์ปค์คํ ํ ์ค๊ณ ์์น, ์ค์ฉ์ ์ธ ํจํด, ๊ทธ๋ฆฌ๊ณ ๊ฒฌ๊ณ ํ ํ ์คํธ ์ ๋ต์ ์๋์ด ๊ฐ๋ฐ์์ ๊ด์ ์์ ์์ธํ ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,107๊ฐ23๋ถ
[๐ค] React useRef ํ ์ฌ์ธต ๋ถ์: DOM ๋์ด์ ์ค์ ํ์ฉ ์ ๋ต
React useRef ํ ์ ๊ธฐ๋ณธ ์๋ฆฌ๋ถํฐ DOM ์์ ์ง์ ์ ์ด, ์ปดํฌ๋ํธ ๋ผ์ดํ์ฌ์ดํด ๊ด๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ๋ค์ํ ์ค์ ํ์ฉ ์ ๋ต๊น์ง ์ฌ์ธต์ ์ผ๋ก ๋ค๋ค์. ์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ useRef ์๋ฒฝ ๊ฐ์ด๋.
- ๋จ์ด: 1,762๊ฐ19๋ถ
[๐ค] Next.js 14/15์์ ๋์ OG ์ด๋ฏธ์ง ์์ฑ: ImageResponse ์๋ฒฝ ๊ฐ์ด๋
Next.js App Router ํ๊ฒฝ์์ ImageResponse๋ฅผ ํ์ฉํ์ฌ ๋์ OG ์ด๋ฏธ์ง๋ฅผ ํจ์จ์ ์ผ๋ก ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. SEO์ ์์ ๊ณต์ ์ต์ ํ๋ฅผ ์ํ ์ค์ ๊ฐ์ด๋์ ๋๋ค.
- ๋จ์ด: 1,481๊ฐ18๋ถ
[๐ค] Git ๋ธ๋์น ์ ๋ต: Git Flow vs GitHub Flow, ์ค๋ฌด์์ ์ด๋ป๊ฒ ์ ํํ๊ณ ์ด์ํ ๊น์?
๊ฐ๋ฐํ์ ํจ์จ์ ์ธ ํ์ ์ ์ํ Git ๋ธ๋์น ์ ๋ต์ ๊ณ ๋ฏผํ๊ณ ๊ณ์ ๊ฐ์? Git Flow์ GitHub Flow์ ํต์ฌ ๊ฐ๋ ๋ถํฐ ์ฅ๋จ์ , ๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ ํ์ ๋ง๋ ์ ๋ต์ ์ ํํ๊ณ ์ด์ํ๋ ์ค์ง์ ์ธ ํ๊น์ง '๋ธ๋ฃจ'๊ฐ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,442๊ฐ16๋ถ
[๐ค] TypeScript ํ์ ๊ฐ๋: ๋ฐํ์ ํ์ ์์ ์ฑ์ ์ํ ํ์ ํจํด ์ ๋ณตํด์
TypeScript์์ ๋ฐํ์์ ๋ณ์์ ํ์ ์ ์์ ํ๊ฒ ์ขํ๋(Narrowing) ๋ฐฉ๋ฒ์ธ ํ์ ๊ฐ๋(Type Guard)์ ๋ํด ์์ธํ ์์๋ด์. `typeof`, `instanceof`, `in` ์ฐ์ฐ์๋ถํฐ ์ฌ์ฉ์ ์ ์ ํ์ ๊ฐ๋๊น์ง, ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ๊ฒฌ๊ณ ํ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์ตํ๋ด์.
- ๋จ์ด: 2,401๊ฐ26๋ถ
[๐ค] React `useTransition`๊ณผ `useDeferredValue`๋ก ์ฌ์ฉ์ ๊ฒฝํ์ ๊ทน๋ํํ๋ ๋ฐฉ๋ฒ
React ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฌด๊ฑฐ์ด UI ์ ๋ฐ์ดํธ๋ก ์ธํ ๋ฒ๋ฒ ์์ ํด๊ฒฐํ๊ณ , `useTransition`๊ณผ `useDeferredValue` ํ ์ ํ์ฉํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ํ๊ธฐ์ ์ผ๋ก ๊ฐ์ ํ๋ ์ค์ฉ์ ์ธ ์ ๋ต์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 1,917๊ฐ22๋ถ
[๐ค] React Suspense์ ErrorBoundary: ๊ฒฌ๊ณ ํ๊ณ ๋ถ๋๋ฌ์ด UI ๊ฒฝํ์ ์ํ ์ค์ ๊ฐ์ด๋
React ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ์ ๊ฒฝํ์ ํ์ ํ Suspense์ ErrorBoundary์ ๊ฐ๋ ฅํ ์กฐํฉ์ ๊น์ด ์๊ฒ ๋ค๋ค์. ๋ก๋ฉ ์ํ์ ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ฐ์ํ๊ฒ ๊ด๋ฆฌํ์ฌ ๋์ฑ ๊ฒฌ๊ณ ํ๊ณ ๋ถ๋๋ฌ์ด UI๋ฅผ ๋ง๋๋ ์ค์ ํ๊ณผ ์ฝ๋ ์์๋ฅผ ํ์ธํด ๋ณด์ธ์.
- ๋จ์ด: 1,302๊ฐ16๋ถ
[๐ค] CSS Container Queries: ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ๋ฐ์ํ ๋์์ธ์ ์๋ก์ด ์งํ
๋ฏธ๋์ด ์ฟผ๋ฆฌ์ ํ๊ณ๋ฅผ ๋์ด, ์ปดํฌ๋ํธ ์์ฒด์ ํฌ๊ธฐ์ ๋ฐ๋ผ ์คํ์ผ์ ์กฐ์ ํ๋ CSS Container Queries๋ฅผ ๊น์ด ์๊ฒ ์์๋ณด๊ณ ์ค๋ฌด ์ ์ฉ ๋ฐฉ๋ฒ์ ์๋ดํด ๋๋ ค์.
- ๋จ์ด: 1,681๊ฐ19๋ถ
[๐ค] Next.js 15 ๊ณ ๊ธ ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต: fetch์ revalidate ์ฌ์ธต ๋ถ์
Next.js 15์์ `fetch` API์ ๊ฐ๋ ฅํ ์บ์ฑ ๋ฉ์ปค๋์ฆ๊ณผ `revalidate` ์ต์ ์ ํ์ฉํ์ฌ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ์ต์ ํํ๊ณ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ์ด์. ์ค๋ฌด ์์๋ฅผ ํตํด ์๋ฒ ์ปดํฌ๋ํธ์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ์บ์ฑ ์ ๋ต์ ํจ๊ณผ์ ์ผ๋ก ์ ์ฉํ๋ ํ์ ์ ๊ณตํด์.
๋จ์ด: 1,320๊ฐ14๋ถ[๐ค] Next.js App Router: generateStaticParams๋ก ๋์ ๋ผ์ฐํ ๋น๋ ์ต์ ํํ๊ธฐ
Next.js App Router์์ generateStaticParams ํจ์๋ฅผ ํ์ฉํ์ฌ ๋์ ๋ผ์ฐํ ์ ์ ์ ํ์ด์ง๋ฅผ ํจ์จ์ ์ผ๋ก ์์ฑํ๊ณ ๋น๋ ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์.
๋จ์ด: 1,891๊ฐ22๋ถ[๐ค] React ๋ ๋๋ง ์ต์ ํ: useMemo, useCallback, React.memo ์๋ฒฝ ๊ฐ์ด๋
์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ React ๋ ๋๋ง ์ต์ ํ ๊ฐ์ด๋. useMemo, useCallback, React.memo์ ์ ํํ ์ฌ์ฉ๋ฒ๊ณผ ์ค๋ฌด์์ ํํ ์ ์ง๋ฅด๋ ์ค์, ๊ทธ๋ฆฌ๊ณ ์ค์ ์ฑ๋ฅ ํฅ์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ ค์.
๋จ์ด: 2,145๊ฐ24๋ถ[๐ค] JavaScript Proxy์ Reflect ์ฌ์ธต ๋ถ์: ๋ฉํ ํ๋ก๊ทธ๋๋ฐ์ผ๋ก ์ฝ๋ ๊ฐํํ๊ธฐ
JavaScript Proxy์ Reflect API๋ฅผ ํ์ฉํ ๋ฉํ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ์ฌ์ธต ๋ถ์ํด์. ๊ฐ์ฒด ์ ๊ทผ ์ ์ด, ์ ํจ์ฑ ๊ฒ์ฌ, ๋ก๊น , ๋ฐ์ํ ์์คํ ๊ตฌํ ๋ฑ ์ค์ฉ์ ์ธ ํ์ฉ ์ฌ๋ก๋ฅผ ํตํด ์ฝ๋์ ์ ์ฐ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 2,029๊ฐ24๋ถ
[๐ค] React/Next.js ๋ฒ๋ค ์ต์ ํ: ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ์๋ฒฝ ๊ฐ์ด๋
React์ Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฒ๋ค ํฌ๊ธฐ๋ฅผ ์ค์ด๊ณ ๋ก๋ฉ ์๋๋ฅผ ๊ฐ์ ํ๋ ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ๊ธฐ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์นํฉ ์ค์ ๋ถํฐ React.lazy, Next.js dynamic import๊น์ง ๋ค๋ค์.
- ๋จ์ด: 1,770๊ฐ20๋ถ
[๐ค] React์ `useOptimistic` ํ ์ผ๋ก ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ตฌํํ๊ธฐ: Server Actions์ ํจ๊ป
React 18/19์ `useOptimistic` ํ ์ ํ์ฉํ์ฌ Server Actions์ ์ฐ๋๋๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
๋จ์ด: 1,561๊ฐ17๋ถ[๐ค] TypeScript const Type Parameters: ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ๊ฐํ์ ์ค์ฉ์ ์ธ ํ์ฉ๋ฒ
TypeScript 5.0์ ๋์ ๋ const Type Parameters๋ฅผ ํ์ฉํ์ฌ ์ ๋ค๋ฆญ ํจ์์ ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ์ ์ ๊ตํ๊ฒ ์ ์ดํ๊ณ , ๋์ฑ ๊ฒฌ๊ณ ํ ํ์ ์์คํ ์ ๊ตฌ์ถํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. as const์์ ์ฐจ์ด์ ๊ณผ ์ค์ ์ฝ๋ ์์๋ฅผ ํตํด ์ด์ค๊ธ ๊ฐ๋ฐ์๋ ์ฝ๊ฒ ์ดํดํ ์ ์๋๋ก ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,028๊ฐ22๋ถ
[๐ค] Next.js/React ์ฑ CLS ์ต์ ํ: ์ํํธ ์๋ ์ฌ์ฉ์ ๊ฒฝํ ๋ง๋ค๊ธฐ
Next.js์ React ์ ํ๋ฆฌ์ผ์ด์ ์์ Cumulative Layout Shift(CLS) ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๋ ์ค์ง์ ์ธ ์ ๋ต๊ณผ ์ฝ๋ ์์๋ฅผ ์์ธํ ์์๋ณด์ธ์. ์น ์ฑ๋ฅ ์ต์ ํ์ ํต์ฌ ์์์ธ CLS๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,740๊ฐ21๋ถ
[๐ค] Next.js SSR, SSG, ISR ๋ ๋๋ง ์ ๋ต: App Router์์ ์ต์ ์ ์ ํ์?
Next.js App Router์์ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR), ์ ์ ์ฌ์ดํธ ์์ฑ(SSG), ์ฆ๋ถ ์ ์ ์ฌ์์ฑ(ISR) ๊ฐ ๋ ๋๋ง ์ ๋ต์ ๋์ ์๋ฆฌ, ์ฅ๋จ์ , ์ค์ ํ์ฉ ๋ฐ ์ต์ ํ ๋ฐฉ๋ฒ์ ๋น๊ต ๋ถ์ํด๋๋ ค์.
- ๋จ์ด: 1,478๊ฐ17๋ถ
[๐ค] React Context API์ Zustand: ์ ์ญ ์ํ ๊ด๋ฆฌ, ์ธ์ ๋ฌด์์ ์จ์ผ ํ ๊น์?
React ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ญ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ณ ๋ฏผํ๊ณ ๊ณ์ ๊ฐ์? Context API์ ๊ฐ๋ฒผ์ด ์ธ๋ถ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ Zustand๋ฅผ ๋น๊ต ๋ถ์ํ๊ณ , ์ค๋ฌด์์ ๊ฐ ๋๊ตฌ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๋ ์ ๋ต์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 2,004๊ฐ24๋ถ
[๐ค] Turborepo๋ก Next.js ๋ชจ๋ ธ๋ ํฌ ๊ตฌ์ถ: ํจ์จ์ ์ธ ๊ฐ๋ฐ ๋ฐ ์ต์ ํ ์ ๋ต
Turborepo๋ฅผ ํ์ฉํ์ฌ Next.js ํ๋ก์ ํธ๋ฅผ ๋ชจ๋ ธ๋ ํฌ๋ก ๊ตฌ์ฑํ๊ณ , ๊ณต์ ์ปดํฌ๋ํธ, ์ ํธ๋ฆฌํฐ, CI/CD ์ต์ ํ ๋ฐฉ์์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,338๊ฐ27๋ถ
[๐ค] React useEffect ํ , ์ด์ ํท๊ฐ๋ฆฌ์ง ๋ง์ธ์! (์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ์๋ฒฝ ๊ฐ์ด๋)
React ๊ฐ๋ฐ์์ ํ์์ ์ธ useEffect ํ ์ ๋์ ์๋ฆฌ๋ถํฐ ์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ํจ์ ํ์ฉ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ค๋ฌด์์ ์์ฃผ ๊ฒช๋ ์ค์์ ์ต์ ํ ์ ๋ต๊น์ง, ์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ ์๋ฒฝ ๊ฐ์ด๋๋ฅผ ์ ๊ณตํด์.
- ๋จ์ด: 3,284๊ฐ31๋ถ
[๐ค] Next.js Server Actions ์ค์ : ์๋ฌ ์ฒ๋ฆฌ, ์ ํจ์ฑ ๊ฒ์ฌ, ๋๊ด์ UI ์ ๋ฐ์ดํธ
Next.js Server Actions๋ฅผ ์ค๋ฌด์ ์ ์ฉํ ๋ ๋ง์ฃผํ๋ ์๋ฌ ์ฒ๋ฆฌ, ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ธฐ๋ฒ์ ์์ธํ ์ฝ๋ ์์์ ํจ๊ป ์์๋ณด์ธ์.
๋จ์ด: 1,983๊ฐ21๋ถ[๐ค] TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์๋ฒฝ ๊ฐ์ด๋: ์ค์ ํ์ฉ ํจํด
TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์ ํต์ฌ ๊ฐ๋ ๊ณผ ์ค์ ํ์ฉ๋ฒ์ ๊น์ด ์๊ฒ ๋ค๋ค์. Pick, Omit, Partial, Required ๋ฑ ์์ฃผ ์ฐ๋ ์ ํธ๋ฆฌํฐ ํ์ ์ผ๋ก ๋ณต์กํ ํ์ ์ ํจ๊ณผ์ ์ผ๋ก ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์. ํ์ ์คํฌ๋ฆฝํธ ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
- ๋จ์ด: 1,712๊ฐ20๋ถ
[๐ค] Next.js App Router ๋ฏธ๋ค์จ์ด: ๊ฐ๋ ฅํ ์์ฒญ ์ฒ๋ฆฌ ์ ๋ต๊ณผ ์ค์ ์์
Next.js App Router ํ๊ฒฝ์์ ๋ฏธ๋ค์จ์ด๋ฅผ ํ์ฉํด ์ฌ์ฉ์ ์ธ์ฆ, ๋ฆฌ๋ค์ด๋ ์ , ๊ตญ์ ํ ๋ฑ์ ์์ฒญ ์ฒ๋ฆฌ ๋ก์ง์ ํจ์จ์ ์ผ๋ก ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ ์์ ์ ํจ๊ป ์์ธํ ์์๋ณด์ธ์.
- ๋จ์ด: 1,630๊ฐ19๋ถ
[๐ค] ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ ์ฌํ: ์ค์ฉ์ ์ธ ํจํด๊ณผ ํํ ์คํด๋ค
ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ(Generics)์ ๊น์ด ์ดํดํ๊ณ , ์ค๋ฌด์์ ์์ฃผ ์ฌ์ฉ๋๋ ์ ๋ค๋ฆญ ํจํด๊ณผ ํํ ๊ฒช๋ ์คํด๋ค์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์ฝ๊ณ ๋ช ํํ๊ฒ ์ค๋ช ํด ๋๋ ค์. ํ์ ์์ ์ฑ๊ณผ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
๋จ์ด: 1,860๊ฐ18๋ถ[๐ค] Next.js Route Handler: App Router์์ ์์ ํ๊ณ ํจ์จ์ ์ธ API ๊ตฌ์ถํ๊ธฐ (์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ ํฌํจ)
Next.js App Router์ Route Handler๋ฅผ ์ฌ์ฉํ์ฌ API ์๋ํฌ์ธํธ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์์๋ด์. ์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์บ์ฑ ์ ๋ต์ ํฌํจํ ์ค์ฉ์ ์ธ ํ์ผ๋ก ์์ ํ๊ณ ํจ์จ์ ์ธ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ตํ๋ด์.
- ๋จ์ด: 1,934๊ฐ22๋ถ
[๐ค] Next.js Image ์ปดํฌ๋ํธ ์ต์ ํ: Core Web Vitals ๊ฐ์ ๋ถํฐ ์ค์ ํ์ฉ๊น์ง
Next.js์ Image ์ปดํฌ๋ํธ๋ฅผ ํ์ฉํ์ฌ ์น ์ฑ๋ฅ ํต์ฌ ์งํ์ธ Core Web Vitals๋ฅผ ๊ฐ์ ํ๊ณ , ๋ค์ํ ์ต์ ํ ์ต์ ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ธ๋ฃจ๊ฐ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 2,187๊ฐ25๋ถ
[๐ค] Next.js 14.1+์ ํ์ : Partial Prerendering (PPR) ์๋ฒฝ ๊ฐ์ด๋์ ์ค์ ์ต์ ํ ์ ๋ต
Next.js 14.1๋ถํฐ ๋์ ๋ Partial Prerendering (PPR)์ ํตํด ์ด๊ธฐ ๋ก๋ฉ ์๋๋ฅผ ๊ทน๋ํํ๊ณ ๋์ ์ฝํ ์ธ ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ฃจ์ด์. PPR์ ๋์ ์๋ฆฌ๋ถํฐ ์ค์ ํ๋ก์ ํธ ์ ์ฉ ์ ๋ต๊น์ง, ๊ฐ๋ฐ์๋ค์ด ๊ถ๊ธํดํ๋ ๋ชจ๋ ๊ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,792๊ฐ19๋ถ
[๐ค] TypeScript ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋: ๋ณต์กํ ํ์ ๋ ์์ฝ๊ฒ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ
TypeScript ๊ฐ๋ฐ์์ ๋ง์ฃผํ๋ ๋ณต์กํ ํ์ ์ถ๋ก ๋ฌธ์ , ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋๋ฅผ ํ์ฉํ๋ฉด ํจ์ฌ ์ฐ์ํ๊ณ ๊ฐ๋ ฅํ๊ฒ ํด๊ฒฐํ ์ ์์ด์. ์ค์ ์์ ์ ํจ๊ป ๊ทธ ํ์ฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ค๋ด ๋๋ค.
- ๋จ์ด: 1,705๊ฐ21๋ถ
[๐ค] JavaScript ์ด๋ฒคํธ ๋ฃจํ(Event Loop) ์์ ์ ๋ณต: ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ๋ฐํ์ ๋์ ์๋ฆฌ
JavaScript์ ํต์ฌ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฉ์ปค๋์ฆ์ธ ์ด๋ฒคํธ ๋ฃจํ์ ๋์ ์๋ฆฌ๋ฅผ ์ฌ๋ ์๊ฒ ํํค์ณ ๋ด์. ์ฝ ์คํ, ํ์คํฌ ํ, ๋ง์ดํฌ๋กํ์คํฌ ํ์์ ์ํธ์์ฉ์ ์ดํดํ๊ณ , ์ค๋ฌด์์ ๋ง์ฃผ์น๋ ๋น๋๊ธฐ ์ฝ๋์ ๋์์ ๋ช ํํ ์์ธกํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,964๊ฐ23๋ถ
[๐ค] Next.js Server & Client Components, ์ค์ ์์ ํ๋ช ํ๊ฒ ์ ํํ๋ ๊ฐ์ด๋
Next.js App Router์์ Server Components์ Client Components ์ค ์ด๋ค ๊ฒ์ ์ฌ์ฉํด์ผ ํ ์ง ๊ณ ๋ฏผ์ด์ ๊ฐ์? ์ด ๊ธ์์ ๋ ์ปดํฌ๋ํธ์ ํต์ฌ ์ฐจ์ด์ , ์ฌ์ฉ ์์ , ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ์ค์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ฆด๊ฒ์.
- ๋จ์ด: 1,879๊ฐ21๋ถ
[๐ค] TypeScript satisfies ์ฐ์ฐ์: ํ์ ์ถ๋ก ๊ณผ ์์ ์ฑ์ ๋์์ ์ก๋ ๋น๋ฒ
TypeScript์ `satisfies` ์ฐ์ฐ์๋ฅผ ํ์ฉํ์ฌ ํ์ ์ถ๋ก ์ ์ ์ฐ์ฑ์ ์ ์งํ๋ฉด์๋ ์๊ฒฉํ ํ์ ์์ ์ฑ์ ํ๋ณดํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. ์ค์ฉ์ ์ธ ์์๋ฅผ ํตํด ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํฉ๋๋ค.
- ๋จ์ด: 1,211๊ฐ15๋ถ
[๐ค] React 19 ์๋ก์ด ๊ธฐ๋ฅ: use ํ , Actions, ๊ทธ๋ฆฌ๊ณ ์ปดํ์ผ๋ฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
React 19์ ํต์ฌ ๋ณ๊ฒฝ ์ฌํญ์ธ use ํ , ์๋ฒ ์ก์ , ๊ทธ๋ฆฌ๊ณ React ์ปดํ์ผ๋ฌ์ ๋์ ๋ฐฐ๊ฒฝ๊ณผ ์ค์ ํ์ฉ ์์๋ฅผ ์ด์ค๊ธ ๊ฐ๋ฐ์ ๋๋์ด์ ๋ง์ถฐ ์์ธํ ์ค๋ช ํฉ๋๋ค. ์ต์ React ์ ๋ฐ์ดํธ๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ๊ณผ ๊ฐ๋ฐ ๊ฒฝํ์ ํฅ์์ํค๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์.
- ๋จ์ด: 1,524๊ฐ16๋ถ
[๐ค] Next.js App Router ์บ์ฑ ์ ๋ต: ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ (revalidatePath, revalidateTag) ์๋ฒฝ ๊ฐ์ด๋
Next.js 14 App Router์์ ํจ์จ์ ์ธ ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต๊ณผ revalidatePath, revalidateTag๋ฅผ ์ด์ฉํ ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์์๋ณด๊ณ ์น ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.