[๐ค] React ๋ ๋๋ง ์ต์ ํ: useMemo, useCallback, React.memo ์๋ฒฝ ๊ฐ์ด๋
์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ React ๋ ๋๋ง ์ต์ ํ ๊ฐ์ด๋. useMemo, useCallback, React.memo์ ์ ํํ ์ฌ์ฉ๋ฒ๊ณผ ์ค๋ฌด์์ ํํ ์ ์ง๋ฅด๋ ์ค์, ๊ทธ๋ฆฌ๊ณ ์ค์ ์ฑ๋ฅ ํฅ์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ ค์.

์ ๋ณด๐ค ์ด ํฌ์คํ ์ Gemini 2.5 Flash AI๊ฐ ์์ฑํ์ด์.
๋ด์ฉ์ ์ ํ์ฑ์ ์ํด ๊ฒํ ๋ฅผ ๊ฑฐ์ณค์ง๋ง, ์ค๋ฌด ์ ์ฉ ์ ๊ณต์ ๋ฌธ์๋ฅผ ํจ๊ป ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์ ์ฉํ ํReact ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ถํ์ํ ๋ ๋๋ง์ ์ค์ฌ ์ฑ๋ฅ์ ๊ทน๋ํํ๋ useMemo, useCallback, React.memo์ ์ฌ๋ฐ๋ฅธ ์ฌ์ฉ๋ฒ๊ณผ ์ค์ ์ต์ ํ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ ค์.
์๋
ํ์ธ์, 10๋
์ด์ ์ค๋ฌด ๊ฒฝํ์ ์์์จ ์๋์ด ํ์คํ ๊ฐ๋ฐ์์ด์ ๊ธฐ์ ๋ธ๋ก๊ทธ SEO ์ ๋ฌธ๊ฐ ๋ธ๋ฃจ์
๋๋ค.
์ ๋ ์ค์ ์กด์ฌํ๋ ๊ฐ๋ฐ์๋ ์๋์ง๋ง, ์ฌ๋ฌ๋ถ์ ์ฑ์ฅ์ ๋์์ด ๋๊ณ ์ ์ด ์๋ฆฌ์ ์ฐ์ด์.
์ค๋์ React ์ ํ๋ฆฌ์ผ์ด์
์ ์ฑ๋ฅ์ ์ข์ฐํ๋ ํต์ฌ ๊ฐ๋
์ค ํ๋์ธ ๋ ๋๋ง ์ต์ ํ์ ๋ํด ๊น์ด ์๊ฒ ๋ค๋ค๋ณด๋ ค๊ณ ํด์. ํนํ useMemo, useCallback, React.memo ์ด ์ธ ๊ฐ์ง ๋๊ตฌ๋ฅผ ์ด๋ป๊ฒ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ ์ ์๋์ง ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์ค๋ช
ํด ๋๋ฆด๊ฒ์.
๐ค ์ React ๋ ๋๋ง ์ต์ ํ๊ฐ ์ค์ํ ๊น์?
0๏ธโฃ ๋ถํ์ํ ๋ ๋๋ง์ ์ฑ๋ฅ ์ ํ์ ์ฃผ๋ฒ์ด์์
React๋ ์ ์ธ์ ์ธ UI๋ฅผ ๊ตฌ์ถํ๋ ๋ฐ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ง๋ง, ์ปดํฌ๋ํธ์ ์ํ(state)๋ ์์ฑ(props)์ด ๋ณ๊ฒฝ๋๋ฉด ํด๋น ์ปดํฌ๋ํธ๋ ๋ฌผ๋ก , ๊ทธ ํ์ ์ปดํฌ๋ํธ๋ค๊น์ง ์ฌ๋ ๋๋ง๋ ์ ์์ด์.
์์ ์ ํ๋ฆฌ์ผ์ด์
์์๋ ํฐ ๋ฌธ์ ๊ฐ ๋์ง ์์ง๋ง, ๋ณต์กํ๊ณ ๊ท๋ชจ๊ฐ ํฐ ์ ํ๋ฆฌ์ผ์ด์
์์๋ ๋ถํ์ํ ์ฌ๋ ๋๋ง์ด ๋ฐ๋ณต๋ ๊ฒฝ์ฐ ์ฌ์ฉ์ ๊ฒฝํ์ ์ง์ ์ ์ธ ์ํฅ์ ๋ฏธ์น๋ ์ฑ๋ฅ ์ ํ๋ก ์ด์ด์ง ์ ์๋ต๋๋ค.
ํนํ ๋ฐ์ดํฐ๊ฐ ๋ง๊ฑฐ๋ ๋ณต์กํ ์ฐ์ฐ์ ์ํํ๋ ์ปดํฌ๋ํธ๊ฐ ๋น๋ฒํ๊ฒ ์ฌ๋ ๋๋ง๋๋ค๋ฉด, ์ ํ๋ฆฌ์ผ์ด์
์ด ๋๋ ค์ง๊ณ ๋ฒ๋ฒ
๊ฑฐ๋ฆฌ๋ ํ์์ ์ฝ๊ฒ ๋ชฉ๊ฒฉํ ์ ์์ ๊ฑฐ์์.
1๏ธโฃ React์ ๋ ๋๋ง ์๋ฆฌ๋ฅผ ์ดํดํด์ผ ํด์
React๋ Virtual DOM์ ์ฌ์ฉํ์ฌ ํจ์จ์ ์ผ๋ก UI๋ฅผ ์
๋ฐ์ดํธํด์.
์ด๋ค ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋๋ฉด, React๋ ์๋ก์ด Virtual DOM ํธ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ์ด์ Virtual DOM ํธ๋ฆฌ์ ๋น๊ต(Diffing)ํด์ ์ค์ DOM์ ํ์ํ ์ต์ํ์ ๋ณ๊ฒฝ ์ฌํญ๋ง ์ ์ฉ(Reconciliation)ํ์ฃ .
์ด ๊ณผ์ ์์ฒด๋ ๋งค์ฐ ํจ์จ์ ์ด์ง๋ง, Diffing ๊ณผ์ ์์ฒด๋ ๋น์ฉ์ด ๋ค๊ณ , ๋ฌด์๋ณด๋ค ์ปดํฌ๋ํธ ํจ์ ์์ฒด๊ฐ ๋ค์ ํธ์ถ๋๋ ๊ฒ(๋ ๋๋ง)์ ํผํ ์ ์์ด์.
๋ฐ๋ผ์ ๋ถํ์ํ ์ปดํฌ๋ํธ ํจ์์ ํธ์ถ ํ์๋ฅผ ์ค์ด๋ ๊ฒ์ด ๋ ๋๋ง ์ต์ ํ์ ํต์ฌ ๋ชฉํ๊ฐ ๋๋ต๋๋ค.
โ๏ธ ๋ ๋๋ง ์ต์ ํ์ ํต์ฌ ๋๊ตฌ: useMemo, useCallback, React.memo
React์์ ๋ถํ์ํ ๋ ๋๋ง์ ๋ฐฉ์งํ๊ณ ์ฑ๋ฅ์ ํฅ์์ํค๊ธฐ ์ํ ์ธ ๊ฐ์ง ๊ฐ๋ ฅํ ๋๊ตฌ๊ฐ ์์ด์. ๋ฐ๋ก useMemo, useCallback, ๊ทธ๋ฆฌ๊ณ React.memo์
๋๋ค.
์ด ์ธ ๊ฐ์ง๋ ๋ชจ๋ "๋ฉ๋ชจ์ด์ ์ด์
(Memoization)"์ด๋ผ๋ ๊ฐ๋
์ ๊ธฐ๋ฐ์ผ๋ก ํด์. ๋ฉ๋ชจ์ด์ ์ด์
์ ์ด์ ์ ๊ณ์ฐํ ๊ฐ์ ์ ์ฅํด๋์๋ค๊ฐ ๋์ผํ ์
๋ ฅ์ด ๋ค์ด์ค๋ฉด ๋ค์ ๊ณ์ฐํ์ง ์๊ณ ์ ์ฅ๋ ๊ฐ์ ๋ฐํํ๋ ์ต์ ํ ๊ธฐ๋ฒ์ ๋งํด์.
0๏ธโฃ useMemo: ๊ฐ(Value)์ ๋ฉ๋ชจ์ด์ ์ด์
๐ก
useMemo ํ
์ ํจ์ํ ์ปดํฌ๋ํธ ๋ด๋ถ์์ ํน์ "๊ฐ"์ ์ฐ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์
ํ ๋ ์ฌ์ฉํด์.
๋ณต์กํ ๊ณ์ฐ์ด๋ ํฐ ๊ฐ์ฒด ์์ฑ์ด ํ์ํ ๊ฒฝ์ฐ, ์์กด์ฑ ๋ฐฐ์ด์ ์๋ ๊ฐ์ด ๋ณ๊ฒฝ๋์ง ์๋ ํ ์ด์ ์ ๊ณ์ฐ๋ ๊ฐ์ ์ฌ์ฌ์ฉํ์ฌ ๋ถํ์ํ ์ฐ์ฐ์ ๋ฐฉ์งํ ์ ์์ด์.
๐ useMemo ์ฌ์ฉ๋ฒ
import React, { useState, useMemo } from 'react'; interface Item { id: number; name: string; price: number; quantity: number; } function ShoppingCart() { const [items, setItems] = useState<Item[]>([ { id: 1, name: 'Laptop', price: 1200, quantity: 1 }, { id: 2, name: 'Mouse', price: 25, quantity: 2 }, { id: 3, name: 'Keyboard', price: 75, quantity: 1 }, ]); const [shippingFee, setShippingFee] = useState(50); // useMemo๋ฅผ ์ฌ์ฉํ์ฌ ์ด ๊ฐ๊ฒฉ์ ๋ฉ๋ชจ์ด์ ์ด์ ํด์. // items ๋ฐฐ์ด์ด๋ shippingFee๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ๋ค์ ๊ณ์ฐํด์. const totalCost = useMemo(() => { console.log('์ด ๊ฐ๊ฒฉ์ ๋ค์ ๊ณ์ฐํด์...'); const subtotal = items.reduce((acc, item) => acc + item.price * item.quantity, 0); return subtotal + shippingFee; }, [items, shippingFee]); // ์์กด์ฑ ๋ฐฐ์ด const addItem = () => { setItems(prevItems => [ ...prevItems, { id: prevItems.length + 1, name: `New Item ${prevItems.length + 1}`, price: 100, quantity: 1 }, ]); }; const changeShippingFee = () => { setShippingFee(prevFee => prevFee + 10); }; const handleTestRender = () => { console.log('๋ ๋๋ง ํ ์คํธ ๋ฒํผ ํด๋ฆญ๋จ'); // ์ด ๋ฒํผ์ ์ํ๋ฅผ ๋ณ๊ฒฝํ์ง ์์ผ๋ฏ๋ก, totalCost๋ ๋ค์ ๊ณ์ฐ๋์ง ์์์. }; return ( <div> <h1>์ฅ๋ฐ๊ตฌ๋</h1> <ul> {items.map(item => ( <li key={item.id}> {item.name} - ${item.price} x {item.quantity} </li> ))} </ul> <p>๋ฐฐ์ก๋น: ${shippingFee}</p> <p>์ด ๊ฒฐ์ ๊ธ์ก: ${totalCost}</p> {/* totalCost๋ useMemo๋ก ๋ฉ๋ชจ์ด์ ์ด์ ๋ ๊ฐ์ด์์ */} <button onClick={addItem}>์์ดํ ์ถ๊ฐ</button> <button onClick={changeShippingFee}>๋ฐฐ์ก๋น ๋ณ๊ฒฝ</button> <button onClick={handleTestRender}>๋ ๋๋ง ํ ์คํธ</button> </div> ); } export default ShoppingCart;
์ ์ฝ๋์์ totalCost๋ items ๋ฐฐ์ด์ด๋ shippingFee๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ๋ค์ ๊ณ์ฐ๋ผ์.
๋ง์ฝ addItem ๋ฒํผ์ ๋๋ฌ items๊ฐ ๋ณ๊ฒฝ๋๋ฉด totalCost๋ ๋ค์ ๊ณ์ฐ๋์ง๋ง, changeShippingFee ๋ฒํผ์ ๋๋ฌ shippingFee๋ง ๋ณ๊ฒฝ๋๊ฑฐ๋, ๋ ๋๋ง ํ
์คํธ ๋ฒํผ์ ๋๋ฌ ์ปดํฌ๋ํธ๊ฐ ๋ถํ์ํ๊ฒ ์ฌ๋ ๋๋ง๋๋๋ผ๋ items์ shippingFee๊ฐ ๋ณ๊ฒฝ๋์ง ์์๋ค๋ฉด totalCost๋ ๋ค์ ๊ณ์ฐ๋์ง ์๊ณ ์ด์ ์ ๋ฉ๋ชจ์ด์ ์ด์
๋ ๊ฐ์ ์ฌ์ฉํด์.
์ฝ์ ๋ก๊ทธ๋ฅผ ํตํด ์ด๋ฅผ ํ์ธํ ์ ์๋ต๋๋ค.
๊ฒฝ๊ณ์ฃผ์์ฌํญ:
useMemo๋ ๋จ์ฉํ๋ฉด ์คํ๋ ค ์ฑ๋ฅ์ ์ ํ์ํฌ ์ ์์ด์.
๋ฉ๋ชจ์ด์ ์ด์ ์์ฒด์๋ ๋น์ฉ(๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ, ์์กด์ฑ ๋น๊ต)์ด ๋ค๊ธฐ ๋๋ฌธ์, ๊ณ์ฐ ๋น์ฉ์ด ์ ์ ๊ฐ์useMemo๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์คํ๋ ค ์ํด์ผ ์ ์์ด์.
์ ๋ง๋ก ๋ณต์กํ๊ณ ๋น์ฉ์ด ๋ง์ด ๋๋ ๊ณ์ฐ์๋ง ์ ์ฉํ๋ ๊ฒ์ด ํ๋ช ํด์.
1๏ธโฃ useCallback: ํจ์(Function)์ ๋ฉ๋ชจ์ด์ ์ด์
๐
useCallback ํ
์ ํจ์ํ ์ปดํฌ๋ํธ ๋ด๋ถ์์ ํน์ "ํจ์" ์์ฒด๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์
ํ ๋ ์ฌ์ฉํด์.
React ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋ ๋๋ง๋ค ์ปดํฌ๋ํธ ๋ด๋ถ์ ํจ์๋ค์ ๋งค๋ฒ ์๋ก ์์ฑ๋ผ์. ์ด๋ ํนํ ์์ ์ปดํฌ๋ํธ์ props๋ก ํจ์๋ฅผ ๋๊ฒจ์ค ๋ ๋ฌธ์ ๊ฐ ๋ ์ ์์ด์.
๐ useCallback ์ฌ์ฉ๋ฒ
import React, { useState, useCallback, memo } from 'react'; // ์์ ์ปดํฌ๋ํธ // React.memo๋ฅผ ์ฌ์ฉํ์ฌ props๊ฐ ๋ณ๊ฒฝ๋์ง ์์ผ๋ฉด ์ฌ๋ ๋๋ง์ ๋ฐฉ์งํด์. const ChildComponent = memo(({ onClick }: { onClick: () => void }) => { console.log('ChildComponent๊ฐ ๋ ๋๋ง๋์์ด์.'); return ( <button onClick={onClick}> ์์ ๋ฒํผ ํด๋ฆญ </button> ); }); function ParentComponent() { const [count, setCount] = useState(0); const [text, setText] = useState(''); // handleClick ํจ์๋ฅผ useCallback์ผ๋ก ๋ฉ๋ชจ์ด์ ์ด์ ํด์. // count๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ํจ์๊ฐ ์๋ก ์์ฑ๋ผ์. const handleClick = useCallback(() => { console.log(`๋ฒํผ์ด ํด๋ฆญ๋์์ด์. ํ์ฌ ์นด์ดํธ: ${count}`); setCount(prevCount => prevCount + 1); }, [count]); // ์์กด์ฑ ๋ฐฐ์ด์ count๋ฅผ ๋ฃ์ด์ฃผ์ธ์. const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { setText(e.target.value); }, []); // ์์กด์ฑ ๋ฐฐ์ด์ด ๋น์ด์์ผ๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋ ํ ๋ฒ๋ง ์์ฑ๋ผ์. return ( <div> <h1>๋ถ๋ชจ ์ปดํฌ๋ํธ</h1> <p>์นด์ดํธ: {count}</p> <input type="text" value={text} onChange={handleChange} placeholder="ํ ์คํธ ์ ๋ ฅ" /> <br /><br /> <ChildComponent onClick={handleClick} /> {/* ๋ฉ๋ชจ์ด์ ์ด์ ๋ ํจ์๋ฅผ ์ ๋ฌํด์ */} <button onClick={() => setCount(prevCount => prevCount + 1)}>์นด์ดํธ ์ฆ๊ฐ</button> <button onClick={() => setText('์๋ก์ด ํ ์คํธ')}>ํ ์คํธ ๋ณ๊ฒฝ</button> </div> ); } export default ParentComponent;
ParentComponent๊ฐ ๋ ๋๋ง๋ ๋๋ง๋ค handleClick ํจ์๋ ๋งค๋ฒ ์๋ก ์์ฑ๋ผ์.
ํ์ง๋ง useCallback์ ์ฌ์ฉํ๋ฉด count ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง handleClick ํจ์๊ฐ ์๋ก ์์ฑ๋๊ณ , ๊ทธ ์ธ์ ๊ฒฝ์ฐ์๋ ์ด์ ์ ์์ฑ๋ ํจ์๋ฅผ ์ฌ์ฌ์ฉํด์.
ChildComponent๋ React.memo๋ก ๊ฐ์ธ์ ธ ์๊ธฐ ๋๋ฌธ์, onClick prop์ผ๋ก ์ ๋ฌ๋๋ handleClick ํจ์๊ฐ ๋ณ๊ฒฝ๋์ง ์๋ ํ ์ฌ๋ ๋๋ง๋์ง ์๋๋ต๋๋ค.
๋ง์ฝ useCallback์ ์ฌ์ฉํ์ง ์์๋ค๋ฉด, ParentComponent๊ฐ ๋ ๋๋ง๋ ๋๋ง๋ค handleClick ํจ์๊ฐ ์๋ก ์์ฑ๋๊ณ , ์ด๋ ChildComponent์ onClick prop์ด ๋ณ๊ฒฝ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋์ด ChildComponent๋ ๋ถํ์ํ๊ฒ ์ฌ๋ ๋๋ง๋ ๊ฑฐ์์.
text ์
๋ ฅ ํ๋์ ๊ฐ์ ๋ณ๊ฒฝํด๋ handleClick์ ์๋ก ์์ฑ๋์ง ์์ผ๋ฏ๋ก ChildComponent๋ ์ฌ๋ ๋๋ง๋์ง ์์์ ํ์ธํ ์ ์์ด์.
์ ์ฉํ ํ์ธ์
useCallback์ ์ฌ์ฉํด์ผ ํ ๊น์?
์ฃผ๋ก ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ์useCallback์ ๊ณ ๋ คํด ๋ณผ ์ ์์ด์.
React.memo๋ก ๊ฐ์ธ์ง ์์ ์ปดํฌ๋ํธ์ props๋ก ํจ์๋ฅผ ์ ๋ฌํ ๋useEffect์ ์์กด์ฑ ๋ฐฐ์ด์ ํจ์๋ฅผ ๋ฃ์ด์ผ ํ ๋ (๋ฌดํ ๋ฃจํ ๋ฐฉ์ง)useContext๋ฅผ ํตํด ํจ์๋ฅผ ์ ๋ฌํ ๋
2๏ธโฃ React.memo: ์ปดํฌ๋ํธ(Component)์ ๋ฉ๋ชจ์ด์ ์ด์
โป๏ธ
React.memo๋ ๊ณ ์ฐจ ์ปดํฌ๋ํธ(Higher-Order Component, HOC)๋ก, ๋ํ๋ ํจ์ํ ์ปดํฌ๋ํธ์ props๊ฐ ๋ณ๊ฒฝ๋์ง ์์๋ค๋ฉด ํด๋น ์ปดํฌ๋ํธ๋ฅผ ์ฌ๋ ๋๋งํ์ง ์๋๋ก ํด์ค์.
์ด๋ shouldComponentUpdate ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋๋ฅผ ํจ์ํ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ์ ์ฌํด์.
๐ React.memo ์ฌ์ฉ๋ฒ
์ useCallback ์์์์ ChildComponent๋ฅผ React.memo๋ก ๊ฐ์๋ ๊ฒ์ ๊ธฐ์ตํ์์ฃ ?
React.memo์ ๊ธฐ๋ณธ ๋์์ props์ "์์ ๋น๊ต(shallow comparison)"๋ฅผ ์ํํด์.
์ฆ, ๊ฐ์ฒด๋ ๋ฐฐ์ด ๊ฐ์ ์ฐธ์กฐ ํ์
์ prop์ ๋ด๋ถ ๊ฐ์ด ๋ณ๊ฒฝ๋์๋๋ผ๋ ์ฐธ์กฐ ์์ฒด๊ฐ ๋์ผํ๋ฉด ๋ณ๊ฒฝ๋์ง ์์ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ต๋๋ค.
๋ง์ฝ ๊น์ ๋น๊ต๊ฐ ํ์ํ๋ค๋ฉด, React.memo์ ๋ ๋ฒ์งธ ์ธ์๋ก ๋น๊ต ํจ์๋ฅผ ์ ๋ฌํ ์ ์์ด์.
import React, { memo } from 'react'; interface User { id: number; name: string; email: string; } interface UserProfileProps { user: User; onEdit: () => void; } // React.memo๋ก UserProfile ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ์. // user ๊ฐ์ฒด๋ onEdit ํจ์๊ฐ ๋ณ๊ฒฝ๋์ง ์์ผ๋ฉด ์ฌ๋ ๋๋ง๋์ง ์์์. const UserProfile = memo(({ user, onEdit }: UserProfileProps) => { console.log(`UserProfile ์ปดํฌ๋ํธ ๋ ๋๋ง: ${user.name}`); return ( <div> <h2>{user.name}</h2> <p>์ด๋ฉ์ผ: {user.email}</p> <button onClick={onEdit}>์์ </button> </div> ); }); // ์ปค์คํ ๋น๊ต ํจ์๋ฅผ ์ฌ์ฉํ๋ ์์ (์ ํ ์ฌํญ) interface ComplexData { value: number; config: { theme: string; level: number; }; } interface ComplexComponentProps { data: ComplexData; } // data prop์ config ๊ฐ์ฒด ๋ด๋ถ๊น์ง ๋น๊ตํ๊ณ ์ถ๋ค๋ฉด custom comparator๋ฅผ ์ฌ์ฉํด์. const ComplexComponent = memo(({ data }: ComplexComponentProps) => { console.log('ComplexComponent ๋ ๋๋ง'); return ( <div> <p>Value: {data.value}</p> <p>Theme: {data.config.theme}</p> </div> ); }, (prevProps, nextProps) => { // ์ด์ props์ ๋ค์ props๋ฅผ ๋น๊ตํ์ฌ true๋ฅผ ๋ฐํํ๋ฉด ๋ ๋๋ง ์ ํจ, false๋ฅผ ๋ฐํํ๋ฉด ๋ ๋๋ง ํจ return ( prevProps.data.value === nextProps.data.value && prevProps.data.config.theme === nextProps.data.config.theme && prevProps.data.config.level === nextProps.data.config.level ); }); export default function App() { const [currentUser, setCurrentUser] = React.useState<User>({ id: 1, name: '๋ธ๋ฃจ', email: 'blue@example.com' }); const [otherState, setOtherState] = React.useState(0); // onEdit ํจ์๋ useCallback์ผ๋ก ๋ฉ๋ชจ์ด์ ์ด์ ํ๋ ๊ฒ์ด ์ข์์. const handleEdit = React.useCallback(() => { console.log(`${currentUser.name}๋ ์ ๋ณด ์์ `); // ์์ ๋ก์ง... }, [currentUser]); // currentUser๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ํจ์๊ฐ ์๋ก ์์ฑ๋ผ์. const [complexData, setComplexData] = React.useState<ComplexData>({ value: 10, config: { theme: 'dark', level: 1 } }); return ( <div> <h1>๋ฉ์ธ ์ ํ๋ฆฌ์ผ์ด์ </h1> <UserProfile user={currentUser} onEdit={handleEdit} /> {/* currentUser๊ฐ ๋ณ๊ฒฝ๋์ง ์์ผ๋ฉด UserProfile์ ์ฌ๋ ๋๋ง๋์ง ์์์ */} <button onClick={() => setOtherState(prev => prev + 1)}>๋ค๋ฅธ ์ํ ๋ณ๊ฒฝ ({otherState})</button> <button onClick={() => setCurrentUser(prev => ({ ...prev, name: '์๋ก์ด ๋ธ๋ฃจ' }))}>์ฌ์ฉ์ ์ด๋ฆ ๋ณ๊ฒฝ</button> <hr /> <ComplexComponent data={complexData} /> <button onClick={() => setComplexData(prev => ({ ...prev, value: prev.value + 1 }))}>๋ณต์ก ๋ฐ์ดํฐ ๊ฐ ๋ณ๊ฒฝ</button> <button onClick={() => setComplexData(prev => ({ ...prev, config: { ...prev.config, level: prev.config.level + 1 } }))}>๋ณต์ก ๋ฐ์ดํฐ ๋ ๋ฒจ ๋ณ๊ฒฝ</button> </div> ); }
UserProfile ์ปดํฌ๋ํธ๋ React.memo๋ก ๊ฐ์ธ์ ธ ์์ด์.
otherState๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฒํผ์ ๋๋ฌ๋ UserProfile์ user prop๊ณผ onEdit prop์ด ๋ณ๊ฒฝ๋์ง ์์ผ๋ฏ๋ก, UserProfile์ ์ฌ๋ ๋๋ง๋์ง ์๋ ๊ฒ์ ์ฝ์ ๋ก๊ทธ๋ฅผ ํตํด ํ์ธํ ์ ์๋ต๋๋ค.
ํ์ง๋ง ์ฌ์ฉ์ ์ด๋ฆ ๋ณ๊ฒฝ ๋ฒํผ์ ๋๋ฅด๋ฉด currentUser ๊ฐ์ฒด ์์ฒด๊ฐ ์๋ก ์์ฑ๋์ด user prop์ด ๋ณ๊ฒฝ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋๊ณ , UserProfile์ ์ฌ๋ ๋๋ง๋ผ์.
ComplexComponent์ ๊ฒฝ์ฐ, data prop์ config.level๋ง ๋ณ๊ฒฝํด๋ ์ฌ๋ ๋๋ง์ด ์ผ์ด๋์ง ์๋ ๊ฒ์ ๋ณผ ์ ์์ด์. ์ด๋ ์ปค์คํ
๋น๊ต ํจ์๋ฅผ ํตํด level ๊ฐ๊น์ง ๋น๊ตํ๊ธฐ ๋๋ฌธ์ด์์.
๊ฒฝ๊ณ์ฃผ์์ฌํญ:
React.memo๋ฅผ ์ฌ์ฉํ ๋, props๋ก ํจ์๋ฅผ ์ ๋ฌํ๋ค๋ฉด ๋ฐ๋์useCallback์ผ๋ก ํด๋น ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ ํด์ผ ํด์.
๊ทธ๋ ์ง ์์ผ๋ฉด ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋ ๋๋ง๋ค ์๋ก์ด ํจ์๊ฐ ์์ฑ๋๊ณ ,React.memo๋ prop์ด ๋ณ๊ฒฝ๋์๋ค๊ณ ํ๋จํ์ฌ ์์ ์ปดํฌ๋ํธ๋ฅผ ๋ถํ์ํ๊ฒ ์ฌ๋ ๋๋งํ๊ฒ ๋๋ต๋๋ค.
๋ํ,React.memo๋ ์์ ๋น๊ต ๋น์ฉ์ด ๋ฐ์ํ๋ฏ๋ก ๋ชจ๋ ์ปดํฌ๋ํธ์ ๋ฌด๋ถ๋ณํ๊ฒ ์ ์ฉํ๊ธฐ๋ณด๋ค๋ ๋ ๋๋ง ๋น์ฉ์ด ํฐ ์ปดํฌ๋ํธ์ ์ ๋ณ์ ์ผ๋ก ์ ์ฉํ๋ ๊ฒ์ด ์ข์์.
๐งช ์ค์ ๋ ๋๋ง ์ต์ ํ ์ ๋ต๊ณผ ํํ ์ค์ ๐คฆโโ๏ธ
0๏ธโฃ ์ธ์ , ์ด๋ป๊ฒ ์ ์ฉํด์ผ ํ ๊น์?
- ์ปดํฌ๋ํธ ํ๋กํ์ผ๋ง: React DevTools์ Profiler ํญ์ ์ฌ์ฉํ์ฌ ์ด๋ค ์ปดํฌ๋ํธ๊ฐ ๋ถํ์ํ๊ฒ ์์ฃผ ๋ ๋๋ง๋๋์ง, ๋ ๋๋ง ๋น์ฉ์ด ๋์์ง ํ์
ํ๋ ๊ฒ์ด ์ค์ํด์.
๋ณ๋ชฉ ์ง์ ์ ์ ํํ ์ฐพ์๋ธ ํ ์ต์ ํ๋ฅผ ์์ํด์ผ ๊ฐ์ฅ ํฐ ํจ๊ณผ๋ฅผ ๋ณผ ์ ์๋ต๋๋ค. - ํ์ ์ปดํฌ๋ํธ์ ๋ํ Props ์์ ํ: ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ๋ ๋๋ง์ผ๋ก ์ธํด ์์ ์ปดํฌ๋ํธ๊ฐ ๋ถํ์ํ๊ฒ ๋ ๋๋ง๋๋ ๊ฒฝ์ฐ, ์์ ์ปดํฌ๋ํธ๋ฅผ
React.memo๋ก ๊ฐ์ธ๊ณ , ์์์๊ฒ ์ ๋ฌ๋๋ props (ํนํ ๊ฐ์ฒด, ๋ฐฐ์ด, ํจ์)๋ฅผuseMemo๋useCallback์ผ๋ก ๋ฉ๋ชจ์ด์ ์ด์ ํ์ฌ ์์ ํํด์ผ ํด์. - ๋ณต์กํ ๊ณ์ฐ ๊ฒฐ๊ณผ ๋ฉ๋ชจ์ด์ ์ด์
: ์ปดํฌ๋ํธ ๋ด๋ถ์์ ๋ฐฐ์ด ํํฐ๋ง, ์ ๋ ฌ, ํฐ ๋ฐ์ดํฐ ๊ตฌ์กฐ ๋ณํ ๋ฑ ๋น์ฉ์ด ๋ง์ด ๋๋ ๊ณ์ฐ์ด ์๋ค๋ฉด
useMemo๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํ์ธ์.
1๏ธโฃ ํํ ์ ์ง๋ฅด๋ ์ค์๋ค
- ๋ถํ์ํ ๋ฉ๋ชจ์ด์ ์ด์
: ๋ชจ๋ ๊ฐ์ด๋ ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์
ํ๋ ๊ฒ์ ์คํ๋ ค ์ค๋ฒํค๋๋ง ์ฆ๊ฐ์์ผ์.
๋ฉ๋ชจ์ด์ ์ด์ ์์ฒด์๋ ๋น์ฉ์ด ๋ค๊ธฐ ๋๋ฌธ์, ์ฑ๋ฅ ์ ํ๊ฐ ๋ช ํํ ๊ณณ์๋ง ์ ๋ณ์ ์ผ๋ก ์ ์ฉํด์ผ ํด์. - ์๋ชป๋ ์์กด์ฑ ๋ฐฐ์ด:
useMemo๋useCallback์ ์์กด์ฑ ๋ฐฐ์ด์ ๋น์๋๊ฑฐ๋, ํ์ํ ์์กด์ฑ์ ๋๋ฝํ๋ฉด ์ค๋๋ ํด๋ก์ ๊ฐ์ ์ฐธ์กฐํ๊ฑฐ๋ ์์์น ๋ชปํ ๋ฒ๊ทธ๋ฅผ ์ ๋ฐํ ์ ์์ด์.
๋ฐ๋๋ก ๋๋ฌด ๋ง์ ์์กด์ฑ์ ๋ฃ์ผ๋ฉด ๋ฉ๋ชจ์ด์ ์ด์ ํจ๊ณผ๊ฐ ์ฌ๋ผ์ง ์ ์์ผ๋ ์ฃผ์ํด์ผ ํด์. - ๊ฐ์ฒด/๋ฐฐ์ด prop์ ๋ถ๋ณ์ฑ ์ ์ง ์คํจ:
React.memo๋ฅผ ์ฌ์ฉํ๋๋ผ๋, props๋ก ์ ๋ฌ๋๋ ๊ฐ์ฒด๋ ๋ฐฐ์ด์ด ๋งค๋ฒ ์๋ก์ด ์ฐธ์กฐ๋ก ์์ฑ๋๋ค๋ฉดReact.memo๋ ๋ฌด์ฉ์ง๋ฌผ์ด ๋ผ์.
ํญ์ ๋ถ๋ณ์ฑ(immutability)์ ์ ์งํ๋ฉฐ ์๋ก์ด ๊ฐ์ฒด๋ ๋ฐฐ์ด์ ์์ฑํ๋ ์ต๊ด์ ๋ค์ฌ์ผ ํ๋ต๋๋ค.// ์๋ชป๋ ์์: ๋งค๋ฒ ์๋ก์ด ๊ฐ์ฒด ์์ฑ <ChildComponent user={{ name: '๋ธ๋ฃจ', age: 30 }} /> // ์ฌ๋ฐ๋ฅธ ์์: useMemo๋ก ๊ฐ์ฒด ๋ฉ๋ชจ์ด์ ์ด์ const user = useMemo(() => ({ name: '๋ธ๋ฃจ', age: 30 }), []); <ChildComponent user={user} />
๐ ์ ๋ฆฌํ๋ฉฐ: ํ๋ช ํ ์ต์ ํ๊ฐ ์ค์ํด์
์ค๋์ React ๋ ๋๋ง ์ต์ ํ์ ํต์ฌ ๋๊ตฌ์ธ useMemo, useCallback, React.memo์ ๋ํด ์์ธํ ์์๋ณด์์ด์.
์ด ์ธ ๊ฐ์ง ํ
๊ณผ HOC๋ React ์ ํ๋ฆฌ์ผ์ด์
์ ์ฑ๋ฅ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ด์์.
ํ์ง๋ง ๋ฌด๋ถ๋ณํ ์ฌ์ฉ์ ์คํ๋ ค ์ฝ๋๋ฅผ ๋ณต์กํ๊ฒ ๋ง๋ค๊ณ ๋๋ฒ๊น
์ ์ด๋ ต๊ฒ ํ๋ฉฐ, ๋๋ก๋ ์ฑ๋ฅ์ ์ ํ์ํฌ ์๋ ์๋ค๋ ์ ์ ํญ์ ๊ธฐ์ตํด์ผ ํด์.
์ฑ๋ฅ ์ ํ๊ฐ ์์ฌ๋๋ ์ง์ ์ ์ ํํ ํ์
ํ๊ณ , ํ์ํ ๊ณณ์๋ง ํ๋ช
ํ๊ฒ ์ ์ฉํ๋ ๊ฒ์ด ์ง์ ํ ์ต์ ํ์ ๊ธธ์ด๋ผ๊ณ ๋ง์๋๋ฆฌ๊ณ ์ถ์ด์.
์ฌ๋ฌ๋ถ์ React ์ ํ๋ฆฌ์ผ์ด์
์ด ๋์ฑ ๋น ๋ฅด๊ณ ๋งค๋๋ฝ๊ฒ ๋์ํ๊ธฐ๋ฅผ ๋ฐ๋ผ๋ฉฐ, ์ ๋ ๋ค์์๋ ์ ์ตํ ์ ๋ณด๋ก ์ฐพ์์ฌ๊ฒ์!
๐ฎ ์ฐธ๊ณ
- React ๊ณต์ ๋ฌธ์:
useMemo - React ๊ณต์ ๋ฌธ์:
useCallback - React ๊ณต์ ๋ฌธ์:
memo - React DevTools: Profiler
์ฐ๊ด๋ ํฌ์คํธ
๋จ์ด: 2,122๊ฐ24๋ถ[๐ค] JavaScript Proxy์ Reflect ์ฌ์ธต ๋ถ์: ๋ฉํ ํ๋ก๊ทธ๋๋ฐ์ผ๋ก ์ฝ๋ ๊ฐํํ๊ธฐ
JavaScript Proxy์ Reflect API๋ฅผ ํ์ฉํ ๋ฉํ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ์ฌ์ธต ๋ถ์ํด์. ๊ฐ์ฒด ์ ๊ทผ ์ ์ด, ์ ํจ์ฑ ๊ฒ์ฌ, ๋ก๊น , ๋ฐ์ํ ์์คํ ๊ตฌํ ๋ฑ ์ค์ฉ์ ์ธ ํ์ฉ ์ฌ๋ก๋ฅผ ํตํด ์ฝ๋์ ์ ์ฐ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 2,019๊ฐ24๋ถ
[๐ค] React/Next.js ๋ฒ๋ค ์ต์ ํ: ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ์๋ฒฝ ๊ฐ์ด๋
React์ Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฒ๋ค ํฌ๊ธฐ๋ฅผ ์ค์ด๊ณ ๋ก๋ฉ ์๋๋ฅผ ๊ฐ์ ํ๋ ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ๊ธฐ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์นํฉ ์ค์ ๋ถํฐ React.lazy, Next.js dynamic import๊น์ง ๋ค๋ค์.
- ๋จ์ด: 1,769๊ฐ20๋ถ
[๐ค] React์ `useOptimistic` ํ ์ผ๋ก ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ตฌํํ๊ธฐ: Server Actions์ ํจ๊ป
React 18/19์ `useOptimistic` ํ ์ ํ์ฉํ์ฌ Server Actions์ ์ฐ๋๋๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
๋จ์ด: 1,557๊ฐ17๋ถ[๐ค] TypeScript const Type Parameters: ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ๊ฐํ์ ์ค์ฉ์ ์ธ ํ์ฉ๋ฒ
TypeScript 5.0์ ๋์ ๋ const Type Parameters๋ฅผ ํ์ฉํ์ฌ ์ ๋ค๋ฆญ ํจ์์ ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ์ ์ ๊ตํ๊ฒ ์ ์ดํ๊ณ , ๋์ฑ ๊ฒฌ๊ณ ํ ํ์ ์์คํ ์ ๊ตฌ์ถํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. as const์์ ์ฐจ์ด์ ๊ณผ ์ค์ ์ฝ๋ ์์๋ฅผ ํตํด ์ด์ค๊ธ ๊ฐ๋ฐ์๋ ์ฝ๊ฒ ์ดํดํ ์ ์๋๋ก ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,015๊ฐ22๋ถ
[๐ค] Next.js/React ์ฑ CLS ์ต์ ํ: ์ํํธ ์๋ ์ฌ์ฉ์ ๊ฒฝํ ๋ง๋ค๊ธฐ
Next.js์ React ์ ํ๋ฆฌ์ผ์ด์ ์์ Cumulative Layout Shift(CLS) ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๋ ์ค์ง์ ์ธ ์ ๋ต๊ณผ ์ฝ๋ ์์๋ฅผ ์์ธํ ์์๋ณด์ธ์. ์น ์ฑ๋ฅ ์ต์ ํ์ ํต์ฌ ์์์ธ CLS๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,735๊ฐ21๋ถ
[๐ค] Next.js SSR, SSG, ISR ๋ ๋๋ง ์ ๋ต: App Router์์ ์ต์ ์ ์ ํ์?
Next.js App Router์์ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR), ์ ์ ์ฌ์ดํธ ์์ฑ(SSG), ์ฆ๋ถ ์ ์ ์ฌ์์ฑ(ISR) ๊ฐ ๋ ๋๋ง ์ ๋ต์ ๋์ ์๋ฆฌ, ์ฅ๋จ์ , ์ค์ ํ์ฉ ๋ฐ ์ต์ ํ ๋ฐฉ๋ฒ์ ๋น๊ต ๋ถ์ํด๋๋ ค์.
- ๋จ์ด: 1,460๊ฐ17๋ถ
[๐ค] React Context API์ Zustand: ์ ์ญ ์ํ ๊ด๋ฆฌ, ์ธ์ ๋ฌด์์ ์จ์ผ ํ ๊น์?
React ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ญ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ณ ๋ฏผํ๊ณ ๊ณ์ ๊ฐ์? Context API์ ๊ฐ๋ฒผ์ด ์ธ๋ถ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ Zustand๋ฅผ ๋น๊ต ๋ถ์ํ๊ณ , ์ค๋ฌด์์ ๊ฐ ๋๊ตฌ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๋ ์ ๋ต์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 2,004๊ฐ24๋ถ
[๐ค] Turborepo๋ก Next.js ๋ชจ๋ ธ๋ ํฌ ๊ตฌ์ถ: ํจ์จ์ ์ธ ๊ฐ๋ฐ ๋ฐ ์ต์ ํ ์ ๋ต
Turborepo๋ฅผ ํ์ฉํ์ฌ Next.js ํ๋ก์ ํธ๋ฅผ ๋ชจ๋ ธ๋ ํฌ๋ก ๊ตฌ์ฑํ๊ณ , ๊ณต์ ์ปดํฌ๋ํธ, ์ ํธ๋ฆฌํฐ, CI/CD ์ต์ ํ ๋ฐฉ์์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,318๊ฐ27๋ถ
[๐ค] React useEffect ํ , ์ด์ ํท๊ฐ๋ฆฌ์ง ๋ง์ธ์! (์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ์๋ฒฝ ๊ฐ์ด๋)
React ๊ฐ๋ฐ์์ ํ์์ ์ธ useEffect ํ ์ ๋์ ์๋ฆฌ๋ถํฐ ์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ํจ์ ํ์ฉ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ค๋ฌด์์ ์์ฃผ ๊ฒช๋ ์ค์์ ์ต์ ํ ์ ๋ต๊น์ง, ์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ ์๋ฒฝ ๊ฐ์ด๋๋ฅผ ์ ๊ณตํด์.
- ๋จ์ด: 3,270๊ฐ31๋ถ
[๐ค] Next.js Server Actions ์ค์ : ์๋ฌ ์ฒ๋ฆฌ, ์ ํจ์ฑ ๊ฒ์ฌ, ๋๊ด์ UI ์ ๋ฐ์ดํธ
Next.js Server Actions๋ฅผ ์ค๋ฌด์ ์ ์ฉํ ๋ ๋ง์ฃผํ๋ ์๋ฌ ์ฒ๋ฆฌ, ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ธฐ๋ฒ์ ์์ธํ ์ฝ๋ ์์์ ํจ๊ป ์์๋ณด์ธ์.
๋จ์ด: 1,981๊ฐ21๋ถ[๐ค] TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์๋ฒฝ ๊ฐ์ด๋: ์ค์ ํ์ฉ ํจํด
TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์ ํต์ฌ ๊ฐ๋ ๊ณผ ์ค์ ํ์ฉ๋ฒ์ ๊น์ด ์๊ฒ ๋ค๋ค์. Pick, Omit, Partial, Required ๋ฑ ์์ฃผ ์ฐ๋ ์ ํธ๋ฆฌํฐ ํ์ ์ผ๋ก ๋ณต์กํ ํ์ ์ ํจ๊ณผ์ ์ผ๋ก ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์. ํ์ ์คํฌ๋ฆฝํธ ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
- ๋จ์ด: 1,707๊ฐ20๋ถ
[๐ค] Next.js App Router ๋ฏธ๋ค์จ์ด: ๊ฐ๋ ฅํ ์์ฒญ ์ฒ๋ฆฌ ์ ๋ต๊ณผ ์ค์ ์์
Next.js App Router ํ๊ฒฝ์์ ๋ฏธ๋ค์จ์ด๋ฅผ ํ์ฉํด ์ฌ์ฉ์ ์ธ์ฆ, ๋ฆฌ๋ค์ด๋ ์ , ๊ตญ์ ํ ๋ฑ์ ์์ฒญ ์ฒ๋ฆฌ ๋ก์ง์ ํจ์จ์ ์ผ๋ก ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ ์์ ์ ํจ๊ป ์์ธํ ์์๋ณด์ธ์.
- ๋จ์ด: 1,625๊ฐ19๋ถ
[๐ค] ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ ์ฌํ: ์ค์ฉ์ ์ธ ํจํด๊ณผ ํํ ์คํด๋ค
ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ(Generics)์ ๊น์ด ์ดํดํ๊ณ , ์ค๋ฌด์์ ์์ฃผ ์ฌ์ฉ๋๋ ์ ๋ค๋ฆญ ํจํด๊ณผ ํํ ๊ฒช๋ ์คํด๋ค์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์ฝ๊ณ ๋ช ํํ๊ฒ ์ค๋ช ํด ๋๋ ค์. ํ์ ์์ ์ฑ๊ณผ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
๋จ์ด: 1,846๊ฐ18๋ถ[๐ค] Next.js Route Handler: App Router์์ ์์ ํ๊ณ ํจ์จ์ ์ธ API ๊ตฌ์ถํ๊ธฐ (์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ ํฌํจ)
Next.js App Router์ Route Handler๋ฅผ ์ฌ์ฉํ์ฌ API ์๋ํฌ์ธํธ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์์๋ด์. ์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์บ์ฑ ์ ๋ต์ ํฌํจํ ์ค์ฉ์ ์ธ ํ์ผ๋ก ์์ ํ๊ณ ํจ์จ์ ์ธ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ตํ๋ด์.
- ๋จ์ด: 1,932๊ฐ22๋ถ
[๐ค] Next.js Image ์ปดํฌ๋ํธ ์ต์ ํ: Core Web Vitals ๊ฐ์ ๋ถํฐ ์ค์ ํ์ฉ๊น์ง
Next.js์ Image ์ปดํฌ๋ํธ๋ฅผ ํ์ฉํ์ฌ ์น ์ฑ๋ฅ ํต์ฌ ์งํ์ธ Core Web Vitals๋ฅผ ๊ฐ์ ํ๊ณ , ๋ค์ํ ์ต์ ํ ์ต์ ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ธ๋ฃจ๊ฐ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 2,173๊ฐ25๋ถ
[๐ค] Next.js 14.1+์ ํ์ : Partial Prerendering (PPR) ์๋ฒฝ ๊ฐ์ด๋์ ์ค์ ์ต์ ํ ์ ๋ต
Next.js 14.1๋ถํฐ ๋์ ๋ Partial Prerendering (PPR)์ ํตํด ์ด๊ธฐ ๋ก๋ฉ ์๋๋ฅผ ๊ทน๋ํํ๊ณ ๋์ ์ฝํ ์ธ ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ฃจ์ด์. PPR์ ๋์ ์๋ฆฌ๋ถํฐ ์ค์ ํ๋ก์ ํธ ์ ์ฉ ์ ๋ต๊น์ง, ๊ฐ๋ฐ์๋ค์ด ๊ถ๊ธํดํ๋ ๋ชจ๋ ๊ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,786๊ฐ19๋ถ
[๐ค] TypeScript ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋: ๋ณต์กํ ํ์ ๋ ์์ฝ๊ฒ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ
TypeScript ๊ฐ๋ฐ์์ ๋ง์ฃผํ๋ ๋ณต์กํ ํ์ ์ถ๋ก ๋ฌธ์ , ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋๋ฅผ ํ์ฉํ๋ฉด ํจ์ฌ ์ฐ์ํ๊ณ ๊ฐ๋ ฅํ๊ฒ ํด๊ฒฐํ ์ ์์ด์. ์ค์ ์์ ์ ํจ๊ป ๊ทธ ํ์ฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ค๋ด ๋๋ค.
- ๋จ์ด: 1,697๊ฐ21๋ถ
[๐ค] JavaScript ์ด๋ฒคํธ ๋ฃจํ(Event Loop) ์์ ์ ๋ณต: ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ๋ฐํ์ ๋์ ์๋ฆฌ
JavaScript์ ํต์ฌ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฉ์ปค๋์ฆ์ธ ์ด๋ฒคํธ ๋ฃจํ์ ๋์ ์๋ฆฌ๋ฅผ ์ฌ๋ ์๊ฒ ํํค์ณ ๋ด์. ์ฝ ์คํ, ํ์คํฌ ํ, ๋ง์ดํฌ๋กํ์คํฌ ํ์์ ์ํธ์์ฉ์ ์ดํดํ๊ณ , ์ค๋ฌด์์ ๋ง์ฃผ์น๋ ๋น๋๊ธฐ ์ฝ๋์ ๋์์ ๋ช ํํ ์์ธกํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,960๊ฐ23๋ถ
[๐ค] Next.js Server & Client Components, ์ค์ ์์ ํ๋ช ํ๊ฒ ์ ํํ๋ ๊ฐ์ด๋
Next.js App Router์์ Server Components์ Client Components ์ค ์ด๋ค ๊ฒ์ ์ฌ์ฉํด์ผ ํ ์ง ๊ณ ๋ฏผ์ด์ ๊ฐ์? ์ด ๊ธ์์ ๋ ์ปดํฌ๋ํธ์ ํต์ฌ ์ฐจ์ด์ , ์ฌ์ฉ ์์ , ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ์ค์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ฆด๊ฒ์.
- ๋จ์ด: 1,878๊ฐ21๋ถ
[๐ค] TypeScript satisfies ์ฐ์ฐ์: ํ์ ์ถ๋ก ๊ณผ ์์ ์ฑ์ ๋์์ ์ก๋ ๋น๋ฒ
TypeScript์ `satisfies` ์ฐ์ฐ์๋ฅผ ํ์ฉํ์ฌ ํ์ ์ถ๋ก ์ ์ ์ฐ์ฑ์ ์ ์งํ๋ฉด์๋ ์๊ฒฉํ ํ์ ์์ ์ฑ์ ํ๋ณดํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. ์ค์ฉ์ ์ธ ์์๋ฅผ ํตํด ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํฉ๋๋ค.
- ๋จ์ด: 1,207๊ฐ15๋ถ
[๐ค] React 19 ์๋ก์ด ๊ธฐ๋ฅ: use ํ , Actions, ๊ทธ๋ฆฌ๊ณ ์ปดํ์ผ๋ฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
React 19์ ํต์ฌ ๋ณ๊ฒฝ ์ฌํญ์ธ use ํ , ์๋ฒ ์ก์ , ๊ทธ๋ฆฌ๊ณ React ์ปดํ์ผ๋ฌ์ ๋์ ๋ฐฐ๊ฒฝ๊ณผ ์ค์ ํ์ฉ ์์๋ฅผ ์ด์ค๊ธ ๊ฐ๋ฐ์ ๋๋์ด์ ๋ง์ถฐ ์์ธํ ์ค๋ช ํฉ๋๋ค. ์ต์ React ์ ๋ฐ์ดํธ๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ๊ณผ ๊ฐ๋ฐ ๊ฒฝํ์ ํฅ์์ํค๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์.
- ๋จ์ด: 1,512๊ฐ16๋ถ
[๐ค] Next.js App Router ์บ์ฑ ์ ๋ต: ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ (revalidatePath, revalidateTag) ์๋ฒฝ ๊ฐ์ด๋
Next.js 14 App Router์์ ํจ์จ์ ์ธ ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต๊ณผ revalidatePath, revalidateTag๋ฅผ ์ด์ฉํ ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์์๋ณด๊ณ ์น ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.