[๐Ÿค–] TypeScript ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…: ๋ฌธ์ž์—ด ํƒ€์ž…์˜ ๋งˆ๋ฒ•์‚ฌ๋กœ ๋ณ€์‹ ํ•˜๊ธฐ

TypeScript์˜ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์„ ํ™œ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ๋ฌธ์ž์—ด ํŒจํ„ด์„ ์•ˆ์ „ํ•˜๊ฒŒ ํƒ€์ž… ์ถ”๋ก ํ•˜๊ณ , ๊ฐ•๋ ฅํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์‹ค๋ฌด ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ์ž์„ธํžˆ ์•Œ์•„๋ด์š”. ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ํ•œ ๋‹จ๊ณ„ ๋†’์—ฌ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•ด ๋ณด์„ธ์š”.

19๋ถ„
๋‹จ์–ด: 1,716๊ฐœ
๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ
์ •๋ณด

๐Ÿค– ์ด ํฌ์ŠคํŒ…์€ 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 ์—”๋“œํฌ์ธํŠธ๋ฅผ ํƒ€์ž…์œผ๋กœ ์ •์˜ํ•˜์—ฌ ์•ˆ์ •์„ฑ์„ ๋†’์—ฌ ๋ณด์„ธ์š”.

์ด ๊ธฐ๋Šฅ๋“ค์„ ์ง์ ‘ ๊ฒฝํ—˜ํ•ด ๋ณด๋ฉด, ์™œ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์ด '๋ฌธ์ž์—ด ํƒ€์ž…์˜ ๋งˆ๋ฒ•์‚ฌ'๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š”์ง€ ๋ถ„๋ช…ํžˆ ๋А๋ผ์‹ค ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”.
๊ถ๊ธˆํ•œ ์ ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“ ์ง€ ๋Œ“๊ธ€๋กœ ์งˆ๋ฌธํ•ด ์ฃผ์„ธ์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์„ฑ์žฅ์„ ์‘์›ํ•ฉ๋‹ˆ๋‹ค!

๐Ÿ“ฎ ์ฐธ๊ณ 

์—ฐ๊ด€๋œ ํฌ์ŠคํŠธ