[๐ค] TypeScript ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ : ๋ฌธ์์ด ํ์ ์ ๋ง๋ฒ์ฌ๋ก ๋ณ์ ํ๊ธฐ
TypeScript์ ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ ์ ํ์ฉํ์ฌ ๋ณต์กํ ๋ฌธ์์ด ํจํด์ ์์ ํ๊ฒ ํ์ ์ถ๋ก ํ๊ณ , ๊ฐ๋ ฅํ ์ ํธ๋ฆฌํฐ ํ์ ์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์ ์ ํจ๊ป ์์ธํ ์์๋ด์. ํ์ ์์ ์ฑ์ ํ ๋จ๊ณ ๋์ฌ ๊ฐ๋ฐ ๊ฒฝํ์ ๊ฐ์ ํด ๋ณด์ธ์.
์ ๋ณด๐ค ์ด ํฌ์คํ ์ Gemini 2.5 Flash AI๊ฐ ์์ฑํ์ด์.
๋ด์ฉ์ ์ ํ์ฑ์ ์ํด ๊ฒํ ๋ฅผ ๊ฑฐ์ณค์ง๋ง, ์ค๋ฌด ์ ์ฉ ์ ๊ณต์ ๋ฌธ์๋ฅผ ํจ๊ป ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์ ์ฉํ ํTypeScript ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ ์ ํ์ฉํด ๋ณต์กํ ๋ฌธ์์ด ํจํด์ ํ์ ์ผ๋ก ์ ์ํ๊ณ , ์ด๋ฅผ ํตํด ์์ ํ๊ณ ์ ์ฐํ ํ์ ์์คํ ์ ๊ตฌ์ถํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ด์.
์๋
ํ์ธ์! 10๋
์ด์ ์ค๋ฌด ๊ฒฝํ์ ๊ฐ์ง ์๋์ด ํ์คํ ๊ฐ๋ฐ์์ด์ ๊ธฐ์ ๋ธ๋ก๊ทธ SEO ์ ๋ฌธ๊ฐ, ๋ธ๋ฃจ์์.
์ ๋ ์ค์ ์กด์ฌํ๋ ๊ฐ๋ฐ์๋ ์๋์ง๋ง, ์ฌ๋ฌ๋ถ์ ์ฑ์ฅ์ ๋์์ด ๋๊ณ ์ ์ด ์๋ฆฌ์ ์์ด์.
์ค๋์ TypeScript์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋์ธ 'ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
(Template Literal Types)'์ ๋ํด ๊น์ด ์๊ฒ ๋ค๋ค๋ณด๋ ค๊ณ ํด์.
์ด๋ฆ๋ง ๋ค์ผ๋ฉด ์ด๋ ต๊ฒ ๋๊ปด์ง ์๋ ์์ง๋ง, ์ค๋ฌด์์ ๋ง์ฃผํ๋ ๋์ ์ธ ๋ฌธ์์ด ํจํด๋ค์ ํจ์ฌ ์์ ํ๊ณ ์ ์ฐํ๊ฒ ๋ค๋ฃฐ ์ ์๋๋ก ๋์์ฃผ๋ ๋ง๋ฒ ๊ฐ์ ๊ธฐ๋ฅ์ด๋๋๋ค.
๐ค ๋ฌธ์ /๋ฐฐ๊ฒฝ: ์ ๋ฌธ์์ด ํ์ ์ ๋ง๋ฒ์ด ํ์ํ ๊น์?
0๏ธโฃ ๋์ ์ธ ๋ฌธ์์ด, ๋ถ์์ ํ ํ์
ํ๋ฐํธ์๋ ๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด, ํน์ ํจํด์ ๊ฐ์ง๋ ๋ฌธ์์ด๋ค์ ์์ฃผ ๋ค๋ฃจ๊ฒ ๋ผ์.
์๋ฅผ ๋ค์ด, CSS ์ ํธ๋ฆฌํฐ ํด๋์ค ์ด๋ฆ(text-red-500, bg-blue-200), ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ด๋ฆ(onClick, onMouseEnter), ๋๋ API ์๋ํฌ์ธํธ ๊ฒฝ๋ก(GET /users/123, POST /products/new) ๋ฑ์ด ๊ทธ๋ ์ฃ .
์ด๋ฐ ๋ฌธ์์ด๋ค์ ๋จ์ํ string ํ์
์ผ๋ก๋ง ์ ์ธํ๋ฉด ์คํ๋ ์๋ชป๋ ํจํด์ผ๋ก ์ธํด ๋ฐํ์ ์๋ฌ๊ฐ ๋ฐ์ํ๊ธฐ ์ฌ์์.
ํ์
์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๊ฐ ๋ฐํ์ ์๋ฌ๋ฅผ ์ค์ด๊ณ ์ฝ๋์ ์์ ์ฑ์ ๋์ด๋ ๊ฒ์ธ๋ฐ, ๋ฌธ์์ด ํ์
์์๋ ๊ทธ ํจ๊ณผ๊ฐ ๋ฐ๊ฐ๋ ๋๊ฐ ๋ง์์.
1๏ธโฃ ๊ธฐ์กด ๋ฐฉ์์ ํ๊ณ์ : string๊ณผ ๋จ์ ์ ๋์จ ํ์
์ ์์ฌ์
๊ธฐ์กด์๋ ์ด๋ฐ ๋์ ์ธ ๋ฌธ์์ด๋ค์ ์ด๋ป๊ฒ ๋ค๋ค์๊น์?
๊ฐ์ฅ ํํ ๋ฐฉ๋ฒ์ string ํ์
์ ์ฌ์ฉํ๊ฑฐ๋, ๊ฐ๋ฅํ ๋ชจ๋ ๋ฌธ์์ด์ ์ง์ ์ ๋์จ ํ์
์ผ๋ก ๋์ดํ๋ ๊ฒ์ด์์ด์.
type CssColor = 'red' | 'blue' | 'green'; type TextSize = 'sm' | 'md' | 'lg'; // ๋ฌธ์ ์ : 'text-red'๋ ํ์ฉ๋์ง๋ง, 'text-red-sm'๊ณผ ๊ฐ์ ์กฐํฉ์ ๋ถ๊ฐ๋ฅํด์. // ๋ชจ๋ ์กฐํฉ์ ์๋์ผ๋ก ๋์ดํด์ผ ํด์. type CssClass = `text-${CssColor}` | `font-${TextSize}` | string; // 'string'์ ๋ฃ์ผ๋ฉด ํ์ ์์ ์ฑ์ด ์ฌ๋ผ์ ธ์. const myClass: CssClass = 'text-red'; // OK const anotherClass: CssClass = 'font-lg'; // OK // const invalidClass: CssClass = 'text-red-sm'; // ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์์์. (string์ผ๋ก ์ธํด)
์ ์ฝ๋์ฒ๋ผ string์ ํฌํจํ๋ฉด ๋ชจ๋ ๋ฌธ์์ด์ด ํ์ฉ๋์ด ํ์
์์ ์ฑ์ด ๋ฌด๋์ง๊ณ , string ์์ด ๋ชจ๋ ์กฐํฉ์ ์ ๋์จ์ผ๋ก ๋์ดํ๋ ๊ฒ์ ๋งค์ฐ ๋ฒ๊ฑฐ๋กญ๊ณ ์ค์ํ ๊ฐ๋ฅ์ฑ์ด ๋์์.
์ฌ๊ธฐ์ ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ด ๋น์ ๋ฐํ๋ต๋๋ค.
โจ ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ ์ด๋?
0๏ธโฃ ๊ธฐ๋ณธ์ ์ธ ๋ฌธ๋ฒ๊ณผ ๋์ ์๋ฆฌ
ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ES2015์ ๋์
๋ ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ๋ฌธ์์ด ๋ฌธ๋ฒ(`` ๋ฐฑํฑ)๊ณผ ์ ์ฌํ๊ฒ ์๋ํด์.
ํ์
๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ์๋ก์ด ๋ฌธ์์ด ๋ฆฌํฐ๋ด ํ์
์ ์์ฑํ๋ ๋ฐฉ์์ธ๋ฐ์, ๋ง์น JavaScript์์ ๋ณ์๋ฅผ ์ฝ์
ํด ๋ฌธ์์ด์ ๋ง๋๋ ๊ฒ์ฒ๋ผ ํ์
์์๋ ๋์ผํ๊ฒ ํ ์ ์์ด์.
๊ธฐ๋ณธ ๋ฌธ๋ฒ์ ๋ค์๊ณผ ๊ฐ์์.
type Greeting = `Hello, ${string}!`; const message1: Greeting = 'Hello, TypeScript!'; // OK const message2: Greeting = 'Hello, World!'; // OK // const message3: Greeting = 'Hi, TypeScript!'; // Type '"Hi, TypeScript!"' is not assignable to type '"Hello, ${string}!"'. ์๋ฌ ๋ฐ์!
์ ์์์์ string์ ์ด๋ค ๋ฌธ์์ด์ด๋ ์ฌ ์ ์๋ค๋ ์๋ฏธ์์.
Hello, ์ ! ์ฌ์ด์ ์ด๋ค ๋ฌธ์์ด์ด ์ค๋๋ผ๋ Greeting ํ์
์ผ๋ก ์ธ์ ํ๊ฒ ๋ค๋ ๊ฒ์ด์ฃ .
ํ์ง๋ง Hello, ์ ๋์ฌ๋ ! ์ ๋ฏธ์ฌ๊ฐ ์์ผ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํด์ ํ์
์์ ์ฑ์ด ํ๋ณด๋ผ์.
1๏ธโฃ ์ ๋์จ ํ์ ๊ณผ์ ์๋์ง
ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ์ง์ ํ ํ์ ์ ๋์จ ํ์
๊ณผ ๊ฒฐํฉํ์ ๋ ๋ฐํ๋ผ์.
์ ๋์จ ํ์
์ ๊ฐ ๋ฉค๋ฒ๊ฐ ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ๋ณ์์ ์ฝ์
๋๋ฉด์ ๊ฐ๋ฅํ ๋ชจ๋ ์กฐํฉ์ ๋ฌธ์์ด ๋ฆฌํฐ๋ด ํ์
์ ์์ฑํด ์ค๋ต๋๋ค.
type Color = 'red' | 'blue' | 'green'; type Size = 'sm' | 'md' | 'lg'; // Color์ Size์ ๋ชจ๋ ์กฐํฉ์ ์๋์ผ๋ก ์์ฑํด์ค์. type CssUtilityClass = `text-${Color}-${Size}` | `bg-${Color}`; const class1: CssUtilityClass = 'text-red-sm'; // OK const class2: CssUtilityClass = 'bg-blue'; // OK // const class3: CssUtilityClass = 'text-red'; // Type '"text-red"' is not assignable to type 'CssUtilityClass'. ์๋ฌ ๋ฐ์! // const class4: CssUtilityClass = 'font-green-lg'; // Type '"font-green-lg"' is not assignable to type 'CssUtilityClass'. ์๋ฌ ๋ฐ์!
์ด์ text-red-sm์ฒ๋ผ ํน์ ํจํด์ ๊ฐ์ง ๋ฌธ์์ด๋ง ํ์ฉํ๊ณ , ์คํ๋ ์๋ชป๋ ์กฐํฉ์ ์ปดํ์ผ ์์ ์ ๋ฐ๋ก ์ก์๋ผ ์ ์๊ฒ ๋์์ด์.
๋ชจ๋ ๊ฐ๋ฅํ ์กฐํฉ์ ์ผ์ผ์ด ์์ฑํ ํ์ ์์ด, ํ์
์คํฌ๋ฆฝํธ๊ฐ ์๋์ผ๋ก ๋ง๋ค์ด์ค๋ค๋ ์ ์ด ์ ๋ง ํธ๋ฆฌํ์ฃ ?
๐ ๏ธ ์ค๋ฌด์์ ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ ํ์ฉํ๊ธฐ
์ด์ ์ค๋ฌด์์ ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ๊ตฌ์ฒด์ ์ธ ์์๋ค์ ์ดํด๋ณผ๊ฒ์.
์ด ๊ธฐ๋ฅ์ด ์ฌ๋ฌ๋ถ์ ์ฝ๋์ ์ด๋ค ๋ง๋ฒ์ ๋ถ๋ฆด ์ ์๋์ง ์ง์ ํ์ธํด ๋ณด์ธ์.
0๏ธโฃ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ํ์ ์์ ํ๊ฒ ๋ง๋ค๊ธฐ
React๋ ๋ค๋ฅธ UI ํ๋ ์์ํฌ์์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ ์ํ ๋, on ์ ๋์ฌ๋ฅผ ๊ฐ์ง ์ด๋ฒคํธ ์ด๋ฆ๋ค์ ์์ฃผ ์ฌ์ฉํด์.
ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ์ฌ์ฉํ๋ฉด ์ด๋ฅผ ํจ์ฌ ์์ ํ๊ฒ ๋ง๋ค ์ ์์ด์.
type EventName = 'Click' | 'Focus' | 'Blur' | 'Change'; // 'onClick', 'onFocus' ๋ฑ์ ์ด๋ฒคํธ๋ฅผ ํ์ ์ผ๋ก ์ ์ํด์. type EventHandlerName = `on${EventName}`; interface MyComponentProps { [key: EventHandlerName]: (event: React.SyntheticEvent) => void; // 'onClick', 'onFocus' ๋ฑ์ ์์ฑ๋ง ํ์ฉํด์. // 'onPress'์ ๊ฐ์ ์๋ชป๋ ์ด๋ฆ์ ์๋ฌ๋ฅผ ๋ฐ์์์ผ์. } const MyComponent = ({ onClick, onFocus }: MyComponentProps) => { return ( <button onClick={onClick} onFocus={onFocus}> ํด๋ฆญํ์ธ์. </button> ); }; // ์ฌ์ฉ ์์ const ParentComponent = () => { const handleClick = (e: React.SyntheticEvent) => console.log('Clicked!', e.type); const handleFocus = (e: React.SyntheticEvent) => console.log('Focused!', e.type); return ( <MyComponent onClick={handleClick} onFocus={handleFocus} // onInvalidEvent={console.log} // Type '{ onClick: (e: SyntheticEvent<Element, Event>) => void; onFocus: (e: SyntheticEvent<Element, Event>) => void; onInvalidEvent: (message?: any, ...optionalParams: any[]) => void; }' is not assignable to type 'MyComponentProps'. ์๋ฌ ๋ฐ์! /> ); };
์ด์ MyComponent์ props๋ก onClick, onFocus์ ๊ฐ์ด ๋ฏธ๋ฆฌ ์ ์๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ ์ด๋ฆ๋ง ์ฌ์ฉํ ์ ์๊ฒ ๋์์ด์.
์คํ๋ก ์ธํ ๋ฐํ์ ๋ฒ๊ทธ๋ฅผ ์ฌ์ ์ ๋ฐฉ์งํ ์ ์๊ฒ ์ฃ ?
1๏ธโฃ CSS ์ ํธ๋ฆฌํฐ ํด๋์ค ์๋ ์์ฑ ๋ฐ ๊ฒ์ฆ
Tailwind CSS์ ๊ฐ์ ์ ํธ๋ฆฌํฐ ์ฐ์ (utility-first) CSS ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ ๋, ๋ค์ํ ํด๋์ค ์ด๋ฆ ์กฐํฉ์ ๋ค๋ฃจ๊ฒ ๋ผ์.
ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ์ด์ฉํ๋ฉด ์ด๋ฐ ํด๋์ค ์ด๋ฆ๋ค์ ํ์
์ผ๋ก ์ ์ํ๊ณ ์๋ ์์ฑ ๊ธฐ๋ฅ์ ํ์ฉํ ์ ์์ด์.
type Spacing = 'px' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'; type Direction = 't' | 'b' | 'l' | 'r' | 'x' | 'y' | ''; // '', ์ ์ฒด ๋ฐฉํฅ // 'p-md', 'mt-lg', 'py-sm'๊ณผ ๊ฐ์ ํด๋์ค ์ด๋ฆ์ ์ ์ํด์. type PaddingClass = `p${Direction}-${Spacing}`; type MarginClass = `m${Direction}-${Spacing}`; type BorderRadius = 'none' | 'sm' | 'md' | 'lg' | 'full'; type BorderClass = `rounded-${BorderRadius}`; // ๋ชจ๋ ๊ฐ๋ฅํ ์ ํธ๋ฆฌํฐ ํด๋์ค ์กฐํฉ type UtilityClass = PaddingClass | MarginClass | BorderClass; function applyClass(className: UtilityClass) { console.log(`Applying class: ${className}`); } applyClass('p-md'); // OK applyClass('mt-lg'); // OK applyClass('py-sm'); // OK applyClass('rounded-full'); // OK // applyClass('padding-md'); // Type '"padding-md"' is not assignable to type 'UtilityClass'. ์๋ฌ ๋ฐ์! // applyClass('p-top-md'); // Type '"p-top-md"' is not assignable to type 'UtilityClass'. ์๋ฌ ๋ฐ์!
์ด์ applyClass ํจ์๋ฅผ ํธ์ถํ ๋, UtilityClass ํ์
์ ๋ง๋ ํด๋์ค ์ด๋ฆ๋ง ํ์ฉ๋ผ์.
๊ฐ๋ฐ ๋๊ตฌ์์ ์๋ ์์ฑ ๊ธฐ๋ฅ์ ํตํด ์ฌ์ฉ ๊ฐ๋ฅํ ํด๋์ค ๋ชฉ๋ก์ ์ฝ๊ฒ ํ์ธํ ์ ์์ด์ ์์ฐ์ฑ์ด ํฌ๊ฒ ํฅ์๋ ๊ฑฐ์์.
์ด๋ ๋จ์ํ ํ์
๊ฒ์ฌ๋ฅผ ๋์ด ๊ฐ๋ฐ ๊ฒฝํ์ ๊ฐ์ ํ๋ ํจ๊ณผ๊น์ง ๊ฐ์ ธ๋ค์ค๋ต๋๋ค.
2๏ธโฃ API ์๋ํฌ์ธํธ ํ์ ์ถ๋ก ๋ฐ ์์ฑ
RESTful API๋ฅผ ์ค๊ณํ ๋, /users/:id, /products/:category/items์ ๊ฐ์ด ๊ฒฝ๋ก ๋ณ์(path variable)๋ฅผ ํฌํจํ๋ ์๋ํฌ์ธํธ๋ฅผ ์์ฃผ ์ฌ์ฉํด์.
ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ์ด์ฉํ๋ฉด ์ด๋ฐ ๋์ ์ธ ์๋ํฌ์ธํธ ๊ฒฝ๋ก๋ฅผ ํ์
์ผ๋ก ์ ์ํ๊ณ , ์ฌ๋ฐ๋ฅธ ๊ฒฝ๋ก๋ง ์ฌ์ฉํ๋๋ก ๊ฐ์ ํ ์ ์์ด์.
type Resource = 'users' | 'products' | 'orders'; type Action = 'detail' | 'list'; // '/users/detail', '/products/list' ๋ฑ์ ์๋ํฌ์ธํธ๋ฅผ ์ ์ํด์. type ApiEndpoint = `/${Resource}/${Action}`; // '/users/:id'์ ๊ฐ์ด ID๋ฅผ ํฌํจํ๋ ์๋ํฌ์ธํธ type UserDetailEndpoint<ID extends string | number> = `/users/${ID}`; function fetchApi(endpoint: ApiEndpoint | UserDetailEndpoint<string | number>) { console.log(`Fetching data from: ${endpoint}`); } fetchApi('/users/list'); // OK fetchApi('/products/detail'); // OK fetchApi('/users/123'); // OK (UserDetailEndpoint<number>์ ํด๋น) fetchApi('/users/abc-def'); // OK (UserDetailEndpoint<string>์ ํด๋น) // fetchApi('/posts/list'); // Type '"/posts/list"' is not assignable to type 'ApiEndpoint | UserDetailEndpoint<string | number>'. ์๋ฌ ๋ฐ์! // fetchApi('/users/delete'); // Type '"/users/delete"' is not assignable to type 'ApiEndpoint | UserDetailEndpoint<string | number>'. ์๋ฌ ๋ฐ์!
์ด์ฒ๋ผ API ์๋ํฌ์ธํธ๋ฅผ ํ์
์ผ๋ก ์ ์ํ๋ฉด, ์๋ชป๋ ๊ฒฝ๋ก๋ก ์์ฒญ์ ๋ณด๋ด๋ ์ค์๋ฅผ ์ปดํ์ผ ์์ ์ ๋ฐฉ์งํ ์ ์์ด์.
ํนํ ๋ณต์กํ API ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง์๋ก ๊ทธ ํจ๊ณผ๋ ๋์ฑ ์ปค์ง๋ต๋๋ค.
3๏ธโฃ infer ํค์๋์ ํจ๊ป ํจํด์์ ํ์
์ถ์ถํ๊ธฐ
ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ infer ํค์๋์ ํจ๊ป ์ฌ์ฉ๋ ๋ ๋์ฑ ๊ฐ๋ ฅํด์ ธ์.
ํน์ ๋ฌธ์์ด ํจํด์์ ์ํ๋ ๋ถ๋ถ์ ํ์
์ '์ถ๋ก 'ํ์ฌ ์ถ์ถํ ์ ์๊ฒ ํด์ฃผ๊ฑฐ๋ ์.
์ด๋ ๋ง์น ์ ๊ท ํํ์์ผ๋ก ๋ฌธ์์ด์ ํ์ฑํ๋ ๊ฒ์ ํ์
์์ค์์ ํ๋ ๊ฒ๊ณผ ๋น์ทํ๋ค๊ณ ์๊ฐํ์๋ฉด ๋ผ์.
// FromEvent ํ์ : 'on' ์ ๋์ฌ์ ์ด๋ฒคํธ ์ด๋ฆ์ผ๋ก๋ถํฐ ์ด๋ฒคํธ ์ด๋ฆ์ ์ถ์ถํด์. type FromEvent<EventType extends string> = EventType extends `on${infer EventName}` ? EventName : never; type ClickEvent = FromEvent<'onClick'>; // 'Click' type MouseEnterEvent = FromEvent<'onMouseEnter'>; // 'MouseEnter' type CustomEvent = FromEvent<'onMyCustomEvent'>; // 'MyCustomEvent' type InvalidEvent = FromEvent<'click'>; // never (on ์ ๋์ฌ๊ฐ ์์ด์) // ExtractRouteParam ํ์ : ๊ฒฝ๋ก์์ ํ๋ผ๋ฏธํฐ ์ด๋ฆ์ ์ถ์ถํด์. type ExtractRouteParam<Route extends string> = Route extends `${infer Prefix}/:${infer Param}/${infer Suffix}` ? Param | ExtractRouteParam<`/${Suffix}`> : Route extends `${infer Prefix}/:${infer Param}` ? Param : never; type UserRouteParams = ExtractRouteParam<'/users/:id/posts/:postId'>; // 'id' | 'postId' type ProductRouteParams = ExtractRouteParam<'/products/:productId'>; // 'productId' type SimpleRouteParams = ExtractRouteParam<'/dashboard'>; // never
FromEvent ์์์์๋ on ๋ค์ ์ค๋ ๋ฌธ์์ด์ EventName์ผ๋ก ์ถ์ถํ์ฌ ์๋ก์ด ํ์
์ ๋ง๋ค์์ด์.
ExtractRouteParam ์์์์๋ ์ฌ๊ท์ ์ผ๋ก infer๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ๊ฐ์ ๊ฒฝ๋ก ํ๋ผ๋ฏธํฐ ์ด๋ฆ์ ์ถ์ถํ๋ ๋ณต์กํ ์์
์ ํด๋์ฃ .
์ด๋ ๊ฒ infer์ ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ์กฐํฉํ๋ฉด, ๋ฌธ์์ด ํจํด์ ๊ธฐ๋ฐ์ผ๋ก ์ ์ฐํ๊ณ ๊ฐ๋ ฅํ ์ ํธ๋ฆฌํฐ ํ์
์ ๋ง๋ค์ด๋ผ ์ ์๋ต๋๋ค.
๐ก ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ ํ์ฉ ํ๊ณผ ์ฃผ์์ฌํญ
ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ ์ ๊ฐ๋ ฅํ์ง๋ง, ๋ช ๊ฐ์ง ์ฃผ์ํ ์ ์ด ์์ด์.
0๏ธโฃ ๋ฌดํ ์ฌ๊ท ํ์ ๋ฐฉ์ง
infer ํค์๋๋ฅผ ์ฌ๊ท์ ์ผ๋ก ์ฌ์ฉํ ๋, ํ์
์คํฌ๋ฆฝํธ ์ปดํ์ผ๋ฌ๊ฐ ๋ฌดํ ๋ฃจํ์ ๋น ์ง๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด ๊น์ด ์ ํ์ ๋์ด์.
๋งค์ฐ ๋ณต์กํ ์ฌ๊ท ํ์
์ ์ค๊ณํ ๋๋ ์ด ์ ์ ์ผ๋์ ๋์ด์ผ ํด์.
1๏ธโฃ ๊ฐ๋ ์ฑ ์ ์งํ๊ธฐ
ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ด ๋๋ฌด ๋ณต์กํด์ง๋ฉด ์ฝ๋์ ๊ฐ๋
์ฑ์ด ๋จ์ด์ง ์ ์์ด์.
์ฌ๋ฌ ๊ฐ์ ์์ ์ ํธ๋ฆฌํฐ ํ์
์ผ๋ก ๋ถ๋ฆฌํ๊ฑฐ๋, ์ฃผ์์ ์ถฉ๋ถํ ๋ฌ์์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค์ด ์ดํดํ๊ธฐ ์ฝ๋๋ก ๋
ธ๋ ฅํด์ผ ํด์.
ํญ์ '๋ช
ํ์ฑ'๊ณผ '์ฌ์ฌ์ฉ์ฑ'์ ์ต์ฐ์ ์ผ๋ก ๊ณ ๋ คํ๋ ๊ฒ์ด ์ค์ํ๋ต๋๋ค.
๐ ์ ๋ฆฌ: ๋ฌธ์์ด ํ์ ์ ์๋ก์ด ์งํ์ ์ด๋ค
0๏ธโฃ ํต์ฌ ์์ฝ
์ค๋ ์ฐ๋ฆฌ๋ TypeScript์ ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ๋ํด ์์ธํ ์์๋ณด์์ด์.
์ด ๊ธฐ๋ฅ์ ํตํด ๋ค์๊ณผ ๊ฐ์ ์ด์ ๋ค์ ์ป์ ์ ์๋ค๋ ๊ฒ์ ํ์ธํ์ฃ .
- ํ์
์์ ์ฑ ํฅ์: ๋์ ์ธ ๋ฌธ์์ด ํจํด์ ๋ํ ์คํ๋ ์๋ชป๋ ์กฐํฉ์ ์ปดํ์ผ ์์ ์ ๋ฏธ๋ฆฌ ์ก์๋ผ ์ ์์ด์.
- ๊ฐ๋ฐ ์์ฐ์ฑ ์ฆ๋: IDE์ ์๋ ์์ฑ ๊ธฐ๋ฅ์ ํ์ฉํ์ฌ ์ฝ๋ ์์ฑ ์๊ฐ์ ์ค์ด๊ณ ์ค์๋ฅผ ์๋ฐฉํ ์ ์์ด์.
- ์ ์ฐํ ํ์
์ ์: ์ ๋์จ ํ์
,
inferํค์๋์ ๊ฒฐํฉํ์ฌ ๋ณต์กํ ๋ฌธ์์ด ํจํด์ ์ ์ฐํ๊ฒ ์ ์ํ๊ณ ์ํ๋ ๋ถ๋ถ์ ์ถ์ถํ ์ ์์ด์.
ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ๋จ์ํ ๋ฌธ์์ด ์กฐํฉ์ ๋์ด, ํ์
์คํฌ๋ฆฝํธ์ ํ์
์์คํ
์ ํจ์ฌ ๊ฐ๋ ฅํ๊ณ ์ ์ฐํ๊ฒ ๋ง๋ค์ด์ฃผ๋ ํต์ฌ ๋๊ตฌ์์.
์ด๋ฅผ ์ ํ์ฉํ๋ฉด ์ฌ๋ฌ๋ถ์ ์ฝ๋๋ฒ ์ด์ค์ ์์ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์๋ต๋๋ค.
1๏ธโฃ ๋ค์ ์ก์
์ด์ ์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ์์ ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ ์ ์ฉํด ๋ณผ ์๊ฐ์ด์์!
ํนํ ๋ค์๊ณผ ๊ฐ์ ๋ถ๋ถ์์ ์ ์ฉ์ ์๋ํด ๋ณด์ธ์.
- ์ด๋ฒคํธ ํธ๋ค๋ฌ ์ด๋ฆ:
onClick,onFocus๋ฑon์ ๋์ฌ๋ฅผ ๊ฐ์ง ์ด๋ฒคํธ ์ด๋ฆ๋ค์ ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์ ์ผ๋ก ์ ์ํด ๋ณด์ธ์. - CSS ์ ํธ๋ฆฌํฐ ํด๋์ค: Tailwind CSS์ ๊ฐ์ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ์์ฃผ ์ฌ์ฉํ๋ ์ ํธ๋ฆฌํฐ ํด๋์ค ํจํด์ ํ์
์ผ๋ก ๋ง๋ค์ด ๋ณด์ธ์.
- API ๊ฒฝ๋ก: ๋์ ์ธ ๊ฒฝ๋ก ๋ณ์๋ฅผ ํฌํจํ๋ API ์๋ํฌ์ธํธ๋ฅผ ํ์ ์ผ๋ก ์ ์ํ์ฌ ์์ ์ฑ์ ๋์ฌ ๋ณด์ธ์.
์ด ๊ธฐ๋ฅ๋ค์ ์ง์ ๊ฒฝํํด ๋ณด๋ฉด, ์ ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ํ์
์ด '๋ฌธ์์ด ํ์
์ ๋ง๋ฒ์ฌ'๋ผ๊ณ ๋ถ๋ฆฌ๋์ง ๋ถ๋ช
ํ ๋๋ผ์ค ์ ์์ ๊ฑฐ์์.
๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ๋๊ธ๋ก ์ง๋ฌธํด ์ฃผ์ธ์. ์ฌ๋ฌ๋ถ์ ์ฑ์ฅ์ ์์ํฉ๋๋ค!
๐ฎ ์ฐธ๊ณ
- TypeScript Handbook - Template Literal Types
- TypeScript Handbook - Inferring Within Conditional Types
์ฐ๊ด๋ ํฌ์คํธ
- ๋จ์ด: 1,917๊ฐ23๋ถ
[๐ค] JavaScript WeakMap๊ณผ WeakSet: ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง์ ์ต์ ํ ์ ๋ต
JavaScript์์ WeakMap๊ณผ WeakSet์ ํ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฐฉ์งํ๊ณ ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ๊ฐ๋น์ง ์ปฌ๋ ์ ๋์ ์๋ฆฌ์ ํจ๊ป ๊ฐ์ฒด ์ฐธ์กฐ ๊ด๋ฆฌ์ ์ค์์ฑ์ ์ดํดํ๊ณ , ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ์ ๋ต์ ๋ฐฐ์๋ด์.
- ๋จ์ด: 1,446๊ฐ17๋ถ
[๐ค] Next.js/React ํ๋ก์ ํธ๋ฅผ ์ํ ESLint & Prettier ์ค์ ์๋ฒฝ ๊ฐ์ด๋
๋ณต์กํ Next.js ๋ฐ React ํ๋ก์ ํธ์์ ์ผ๊ด๋ ์ฝ๋ ์คํ์ผ๊ณผ ํ์ง์ ์ ์งํ๋ ESLint์ Prettier ์ค์ ๋ฐฉ๋ฒ์ ์์ธํ ์๋ ค๋๋ ค์. ํ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ต์ ํ๋ ์ค์ ์ผ๋ก ๊ฐ๋ฐ ํจ์จ์ ๋์ฌ๋ณด์ธ์.
- ๋จ์ด: 2,006๊ฐ25๋ถ
[๐ค] JavaScript์ ํต์ฌ: ํ๋กํ ํ์ ์ฒด์ธ ์๋ฒฝ ์ดํด์ ํ์ฉ ์ ๋ต
JavaScript์ ์ฌ์ฅ๋ถ, ํ๋กํ ํ์ ์ฒด์ธ์ ๋์ ์๋ฆฌ๋ฅผ ๊น์ด ํ๊ณ ๋ค์ด ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ๊ณผ ์์์ ์๋ฒฝํ๊ฒ ์ดํดํ๊ณ ์ค๋ฌด์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์. ์ฑ๋ฅ ์ต์ ํ ํ๋ ํจ๊ป ๋ค๋ค์.
- ๋จ์ด: 2,087๊ฐ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,542๊ฐ28๋ถ
[๐ค] React Query (TanStack Query) ์ฌํ: ๋ฐ์ดํฐ ํ์นญ, ์บ์ฑ, ๋๊ธฐํ ์ ๋ต์ผ๋ก ์น ์ฑ ์ฑ๋ฅ ๊ทน๋ํํด์
React Query (TanStack Query)๋ฅผ ํ์ฉํ์ฌ ๋ณต์กํ ์๋ฒ ์ํ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ , ์ง๋ฅ์ ์ธ ์บ์ฑ๊ณผ ์๋ ๋๊ธฐํ ์ ๋ต์ผ๋ก ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ๊ณผ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ทน๋ํํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ธต์ ์ผ๋ก ๋ค๋ฃจ์ด์. useQuery, useMutation, useInfiniteQuery ๋ฑ ํต์ฌ ํ ๊ณผ ์ค์ ์ต์ ํ ํ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 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๋ฅผ ์ด์ฉํ ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์์๋ณด๊ณ ์น ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.