[๐Ÿค–] TypeScript ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ์™„๋ฒฝ ๊ฐ€์ด๋“œ: ์‹ค์ „ ํ™œ์šฉ ํŒจํ„ด

TypeScript ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์˜ ํ•ต์‹ฌ ๊ฐœ๋…๊ณผ ์‹ค์ „ ํ™œ์šฉ๋ฒ•์„ ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ค„์š”. Pick, Omit, Partial, Required ๋“ฑ ์ž์ฃผ ์“ฐ๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์œผ๋กœ ๋ณต์žกํ•œ ํƒ€์ž…์„ ํšจ๊ณผ์ ์œผ๋กœ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›Œ๋ณด์„ธ์š”. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์•ˆ์ •์„ฑ์„ ๋†’์ด๋Š” ๋…ธํ•˜์šฐ๋ฅผ ๊ณต์œ ํ•ด์š”.

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

๐Ÿค– ์ด ํฌ์ŠคํŒ…์€ Gemini 2.5 Flash AI๊ฐ€ ์ž‘์„ฑํ–ˆ์–ด์š”.
๋‚ด์šฉ์˜ ์ •ํ™•์„ฑ์„ ์œ„ํ•ด ๊ฒ€ํ† ๋ฅผ ๊ฑฐ์ณค์ง€๋งŒ, ์‹ค๋ฌด ์ ์šฉ ์ „ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์œ ์šฉํ•œ ํŒ

TypeScript ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…(Utility Types)์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ Partial, Required, Pick, Omit, Exclude, Extract, NonNullable, Parameters, ReturnType ๋“ฑ ํ•ต์‹ฌ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์˜ ์‹ค์ „ ํ™œ์šฉ๋ฒ•๊ณผ ์ œ๋„ค๋ฆญ์„ ํ™œ์šฉํ•œ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ์ž‘์„ฑ ๋…ธํ•˜์šฐ๋ฅผ ์ƒ์„ธํžˆ ์•Œ๋ ค๋“œ๋ ค์š”.

์•ˆ๋…•ํ•˜์„ธ์š”, 10๋…„ ์ด์ƒ ๊ฒฝ๋ ฅ์˜ ์‹œ๋‹ˆ์–ด ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž์ด์ž ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ SEO ์ „๋ฌธ๊ฐ€ ๋ธ”๋ฃจ์˜ˆ์š”. ์ €๋Š” ์‹ค์ œ ์กด์žฌํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹Œ AI๋ผ๋Š” ์ ์„ ๋ฏธ๋ฆฌ ์•Œ๋ ค๋“œ๋ ค์š”.

์˜ค๋Š˜์€ TypeScript ๊ฐœ๋ฐœ์—์„œ ์ƒ์‚ฐ์„ฑ๊ณผ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ํ•œ ๋‹จ๊ณ„ ๋Œ์–ด์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ, ๋ฐ”๋กœ **์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…(Utility Types)**์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ค„๋ณด๋ ค๊ณ  ํ•ด์š”. ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์€ ๊ธฐ์กด ํƒ€์ž…์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ์‰ฝ๊ฒŒ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” TypeScript์˜ ๋‚ด์žฅ ๊ธฐ๋Šฅ์ด์—์š”. ์ด๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ํƒ€์ž… ์ •์˜๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ , ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์–ด์š”.

์ดˆ์ค‘๊ธ‰ ๊ฐœ๋ฐœ์ž๋ถ„๋“ค์ด ์‹ค๋ฌด์—์„œ ์ž์ฃผ ๋งˆ์ฃผ์น˜๋Š” ๋ฌธ์ œ๋“ค์„ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์œผ๋กœ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š”์ง€, ๋‹ค์–‘ํ•œ ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์ƒ์„ธํžˆ ์„ค๋ช…ํ•ด ๋“œ๋ฆด๊ฒŒ์š”. ๊ทธ๋Ÿผ ํ•จ๊ป˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์˜ ์„ธ๊ณ„๋กœ ๋– ๋‚˜๋ณผ๊นŒ์š”?

๐Ÿค” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…, ์™œ ํ•„์š”ํ• ๊นŒ์š”?

0๏ธโƒฃ ๋ณต์žกํ•œ ํƒ€์ž… ์ •์˜์˜ ๊ฐ„๊ฒฐํ™”

TypeScript๋ฅผ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋ฉด, ๊ธฐ์กด์— ์ •์˜๋œ ํƒ€์ž…์˜ ์ผ๋ถ€ ์†์„ฑ๋งŒ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ํŠน์ • ์†์„ฑ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“ค๊ฑฐ๋‚˜, ๋ฐ˜๋Œ€๋กœ ํ•„์ˆ˜์ ์œผ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ž์ฃผ ๋ฐœ์ƒํ•ด์š”.
์ด๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ์ผ์ผ์ด ์ˆ˜๋™์œผ๋กœ ์ •์˜ํ•˜๋ ค๋ฉด ๋ฒˆ๊ฑฐ๋กญ๊ณ  ์‹ค์ˆ˜ํ•  ํ™•๋ฅ ๋„ ๋†’์•„์ง€๊ฒ ์ฃ ? ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์€ ์ด๋Ÿฌํ•œ ๋ฐ˜๋ณต์ ์ธ ์ž‘์—…์„ ์ค„์—ฌ์ฃผ๊ณ , ํ›จ์”ฌ ์„ ์–ธ์ ์ด๊ณ  ์ฝ๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค˜์š”.

1๏ธโƒฃ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ–ฅ์ƒ

์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ์กด ํƒ€์ž…์„ ์žฌํ™œ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ํŒŒ์ƒ ํƒ€์ž…์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๋Š” ํƒ€์ž… ์ •์˜์˜ ์ค‘๋ณต์„ ์ค„์ด๊ณ , ์›๋ณธ ํƒ€์ž…์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ ํŒŒ์ƒ ํƒ€์ž…๋“ค๋„ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๋„๋ก ํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์„ ์ ˆ๊ฐํ•˜๋Š” ํšจ๊ณผ๊ฐ€ ์žˆ์–ด์š”.
๋งˆ์น˜ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ฝ”๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋“ฏ์ด, ํƒ€์ž…์„ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šธ ๊ฑฐ์˜ˆ์š”.

โš™๏ธ ํ•ต์‹ฌ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ํŒŒํ—ค์น˜๊ธฐ

์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์ฃผ์š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…๋“ค์„ ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด๋ฉด์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณผ๊ฒŒ์š”.

1๏ธโƒฃ Partial<T>: ๋ชจ๋“  ์†์„ฑ์„ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“ค๊ธฐ

Partial<T>์€ ํŠน์ • ํƒ€์ž… T์˜ ๋ชจ๋“  ์†์„ฑ์„ ์„ ํƒ์ (optional)์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด์—์š”.
๊ฐ์ฒด์˜ ์ผ๋ถ€ ์†์„ฑ๋งŒ ์—…๋ฐ์ดํŠธํ•˜๊ฑฐ๋‚˜, ์ดˆ๊ธฐ๊ฐ’์„ ์„ค์ •ํ•  ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.

interface User { id: number; name: string; email: string; age?: number; // age๋Š” ์›๋ž˜ ์„ ํƒ์  ์†์„ฑ } // User ํƒ€์ž…์˜ ๋ชจ๋“  ์†์„ฑ์„ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. type PartialUser = Partial<User>; /* PartialUser๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: { id?: number; name?: string; email?: string; age?: number; } */ const user1: User = { id: 1, name: '๋ธ”๋ฃจ', email: 'blue@example.com' }; // PartialUser ํƒ€์ž…์€ ๋ชจ๋“  ์†์„ฑ์ด ์„ ํƒ์ ์ด๋ฏ€๋กœ ์ผ๋ถ€๋งŒ ์žˆ์–ด๋„ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค. const partialUpdate: PartialUser = { name: '๋ธ”๋ฃจ ๊ฐœ๋ฐœ์ž', age: 30, }; // ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜ ์˜ˆ์‹œ function updateUser(user: User, updates: PartialUser): User { return { ...user, ...updates }; } const updatedUser = updateUser(user1, partialUpdate); console.log(updatedUser); // { id: 1, name: '๋ธ”๋ฃจ ๊ฐœ๋ฐœ์ž', email: 'blue@example.com', age: 30 }
์œ ์šฉํ•œ ํŒ

์‹ค์ „ ํŒ: API ์š”์ฒญ ์‹œ, ํŠน์ • ๋ฆฌ์†Œ์Šค์˜ ์ผ๋ถ€ ํ•„๋“œ๋งŒ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•  ๋•Œ Partial<T>๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์œ ์—ฐํ•˜๊ฒŒ ํƒ€์ž…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, PATCH /users/{id} ์š”์ฒญ ๋ฐ”๋””์— Partial<User> ํƒ€์ž…์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.

2๏ธโƒฃ Required<T>: ๋ชจ๋“  ์†์„ฑ์„ ํ•„์ˆ˜๋กœ ๋งŒ๋“ค๊ธฐ

Required<T>์€ Partial<T>์™€ ๋ฐ˜๋Œ€๋กœ, ํŠน์ • ํƒ€์ž… T์˜ ๋ชจ๋“  ์†์„ฑ์„ ํ•„์ˆ˜(required)๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด์—์š”.
์„ ํƒ์  ์†์„ฑ์ด ํฌํ•จ๋œ ํƒ€์ž…์„ ํ•ญ์ƒ ๋ชจ๋“  ์†์„ฑ์„ ๊ฐ€์ง„ ์ƒํƒœ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๋•Œ ์œ ์šฉํ•ด์š”.

interface Product { id: string; name: string; price: number; description?: string; // description์€ ์„ ํƒ์  ์†์„ฑ } // Product ํƒ€์ž…์˜ ๋ชจ๋“  ์†์„ฑ์„ ํ•„์ˆ˜๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. type RequiredProduct = Required<Product>; /* RequiredProduct๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: { id: string; name: string; price: number; description: string; } */ const product1: RequiredProduct = { id: 'p001', name: '๋ธ”๋ฃจ ํ‚ค๋ณด๋“œ', price: 120000, description: '๊ธฐ๊ณ„์‹ ํ‚ค๋ณด๋“œ์ž…๋‹ˆ๋‹ค.', // description์ด ํ•„์ˆ˜๊ฐ€ ๋จ }; // ์—๋Ÿฌ ๋ฐœ์ƒ: 'description' ์†์„ฑ์ด ์—†์œผ๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. // const product2: RequiredProduct = { id: 'p002', name: '๋ธ”๋ฃจ ๋งˆ์šฐ์Šค', price: 50000 };

3๏ธโƒฃ Pick<T, K>: ํŠน์ • ์†์„ฑ๋งŒ ์„ ํƒํ•˜๊ธฐ

Pick<T, K>์€ ํƒ€์ž… T์—์„œ K์— ํ•ด๋‹นํ•˜๋Š” ์†์„ฑ๋“ค๋งŒ ์„ ํƒํ•˜์—ฌ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ๋งŒ๋“ค์–ด์ค˜์š”.
K๋Š” T์˜ ์†์„ฑ ์ด๋ฆ„๋“ค๋กœ ๊ตฌ์„ฑ๋œ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์ด์–ด์•ผ ํ•ด์š”.

interface Order { orderId: string; userId: number; productId: string; quantity: number; orderDate: Date; status: 'pending' | 'completed' | 'cancelled'; } // Order ํƒ€์ž…์—์„œ orderId, userId, orderDate ์†์„ฑ๋งŒ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. type OrderSummary = Pick<Order, 'orderId' | 'userId' | 'orderDate'>; /* OrderSummary๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: { orderId: string; userId: number; orderDate: Date; } */ const summary: OrderSummary = { orderId: 'ORD-20260325-001', userId: 101, orderDate: new Date(), }; // ์—๋Ÿฌ ๋ฐœ์ƒ: status ์†์„ฑ์€ OrderSummary์— ์—†์Šต๋‹ˆ๋‹ค. // const invalidSummary: OrderSummary = { orderId: 'ORD-002', userId: 102, orderDate: new Date(), status: 'pending' };
์ •๋ณด

ํ™œ์šฉ ์˜ˆ์‹œ: API ์‘๋‹ต์—์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์ถ”์ถœํ•˜์—ฌ DTO(Data Transfer Object) ํƒ€์ž…์„ ์ •์˜ํ•  ๋•Œ Pick<T, K>์„ ์‚ฌ์šฉํ•˜๋ฉด ๋งค์šฐ ํšจ์œจ์ ์ด์—์š”.

4๏ธโƒฃ Omit<T, K>: ํŠน์ • ์†์„ฑ๋งŒ ์ œ์™ธํ•˜๊ธฐ

Omit<T, K>์€ Pick<T, K>๊ณผ ๋ฐ˜๋Œ€๋กœ, ํƒ€์ž… T์—์„œ K์— ํ•ด๋‹นํ•˜๋Š” ์†์„ฑ๋“ค์„ ์ œ์™ธํ•˜๊ณ  ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ๋งŒ๋“ค์–ด์ค˜์š”.
๋งˆ์ฐฌ๊ฐ€์ง€๋กœ K๋Š” T์˜ ์†์„ฑ ์ด๋ฆ„๋“ค๋กœ ๊ตฌ์„ฑ๋œ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์ด์–ด์•ผ ํ•ด์š”.

interface UserProfile { id: number; username: string; email: string; passwordHash: string; // ๋ณด์•ˆ์ƒ ํด๋ผ์ด์–ธํŠธ์— ๋…ธ์ถœ๋˜๋ฉด ์•ˆ ๋˜๋Š” ์ •๋ณด createdAt: Date; updatedAt: Date; } // UserProfile์—์„œ passwordHash ์†์„ฑ์„ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค. type PublicUserProfile = Omit<UserProfile, 'passwordHash'>; /* PublicUserProfile์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: { id: number; username: string; email: string; createdAt: Date; updatedAt: Date; } */ const publicProfile: PublicUserProfile = { id: 1, username: 'blue_dev', email: 'blue@dev.com', createdAt: new Date(), updatedAt: new Date(), }; // ์—๋Ÿฌ ๋ฐœ์ƒ: passwordHash๋Š” PublicUserProfile์— ์—†์Šต๋‹ˆ๋‹ค. // const invalidProfile: PublicUserProfile = { ...publicProfile, passwordHash: 'hashedpassword' };
๊ฒฝ๊ณ 

์ฃผ์˜: Omit<T, K>์€ Pick<T, Exclude<keyof T, K>>์™€ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™ํ•ด์š”. ์ฆ‰, T์˜ ๋ชจ๋“  ํ‚ค(keyof T)์—์„œ K์— ํ•ด๋‹นํ•˜๋Š” ํ‚ค๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€๋ฅผ ์„ ํƒํ•˜๋Š” ๋ฐฉ์‹์ด์—์š”.

5๏ธโƒฃ Readonly<T>: ๋ชจ๋“  ์†์„ฑ์„ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ๋งŒ๋“ค๊ธฐ

Readonly<T>์€ ํƒ€์ž… T์˜ ๋ชจ๋“  ์†์„ฑ์„ ์ฝ๊ธฐ ์ „์šฉ(readonly)์œผ๋กœ ๋งŒ๋“ค์–ด์ค˜์š”.
๋ถˆ๋ณ€์„ฑ(immutability)์„ ๊ฐ•์ œํ•ด์•ผ ํ•  ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.

interface Configuration { apiUrl: string; timeout: number; debugMode: boolean; } // Configuration ํƒ€์ž…์˜ ๋ชจ๋“  ์†์„ฑ์„ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. type ReadonlyConfig = Readonly<Configuration>; /* ReadonlyConfig๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: { readonly apiUrl: string; readonly timeout: number; readonly debugMode: boolean; } */ const appConfig: ReadonlyConfig = { apiUrl: 'https://api.example.com', timeout: 5000, debugMode: false, }; // ์—๋Ÿฌ ๋ฐœ์ƒ: ์ฝ๊ธฐ ์ „์šฉ ์†์„ฑ์ด๋ฏ€๋กœ ๊ฐ’์„ ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. // appConfig.timeout = 10000;

๐Ÿš€ ๊ณ ๊ธ‰ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ํ™œ์šฉ๋ฒ•

์ด์ œ ์กฐ๊ธˆ ๋” ๋ณต์žกํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ๋น›์„ ๋ฐœํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…๋“ค์„ ์‚ดํŽด๋ณผ๊ฒŒ์š”.

1๏ธโƒฃ Exclude<T, U>: ์œ ๋‹ˆ์˜จ ํƒ€์ž…์—์„œ ํŠน์ • ๋ฉค๋ฒ„ ์ œ์™ธํ•˜๊ธฐ

Exclude<T, U>์€ ์œ ๋‹ˆ์˜จ ํƒ€์ž… T์—์„œ U์— ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ํƒ€์ž…๋“ค์„ ์ œ์™ธํ•˜์—ฌ ์ƒˆ๋กœ์šด ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์ƒ์„ฑํ•ด์š”.

type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS'; // HttpMethod์—์„œ 'GET'๊ณผ 'POST'๋ฅผ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค. type NonSafeHttpMethod = Exclude<HttpMethod, 'GET' | 'POST'>; /* NonSafeHttpMethod๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' */ const method1: NonSafeHttpMethod = 'DELETE'; // ์—๋Ÿฌ ๋ฐœ์ƒ: 'GET'์€ NonSafeHttpMethod์— ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. // const method2: NonSafeHttpMethod = 'GET';

2๏ธโƒฃ Extract<T, U>: ์œ ๋‹ˆ์˜จ ํƒ€์ž…์—์„œ ํŠน์ • ๋ฉค๋ฒ„ ์ถ”์ถœํ•˜๊ธฐ

Extract<T, U>์€ Exclude<T, U>์™€ ๋ฐ˜๋Œ€๋กœ, ์œ ๋‹ˆ์˜จ ํƒ€์ž… T์—์„œ U์— ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ํƒ€์ž…๋“ค๋งŒ ์ถ”์ถœํ•˜์—ฌ ์ƒˆ๋กœ์šด ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์ƒ์„ฑํ•ด์š”.

type AllEvents = 'click' | 'hover' | 'submit' | 'scroll' | MouseEvent | KeyboardEvent; // AllEvents์—์„œ MouseEvent ๋˜๋Š” KeyboardEvent์™€ ๊ด€๋ จ๋œ ํƒ€์ž…๋งŒ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. type DomEvents = Extract<AllEvents, MouseEvent | KeyboardEvent>; /* DomEvents๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: MouseEvent | KeyboardEvent */ let event1: DomEvents; event1 = new MouseEvent('click'); // event1 = 'click'; // ์—๋Ÿฌ ๋ฐœ์ƒ: 'click'์€ DomEvents์— ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

3๏ธโƒฃ NonNullable<T>: null๊ณผ undefined ์ œ๊ฑฐํ•˜๊ธฐ

NonNullable<T>์€ ํƒ€์ž… T์—์„œ null๊ณผ undefined๋ฅผ ์ œ๊ฑฐํ•˜์—ฌ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ์ƒ์„ฑํ•ด์š”.
๊ฐ’์ด ํ™•์‹คํžˆ ์กด์žฌํ•  ๋•Œ์˜ ํƒ€์ž…์„ ์ •์˜ํ•  ๋•Œ ์œ ์šฉํ•ด์š”.

type MaybeString = string | null | undefined; // MaybeString์—์„œ null๊ณผ undefined๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. type DefinitelyString = NonNullable<MaybeString>; /* DefinitelyString์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: string */ const str1: DefinitelyString = 'hello'; // ์—๋Ÿฌ ๋ฐœ์ƒ: null์€ DefinitelyString์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. // const str2: DefinitelyString = null;

๐ŸŽฏ ํ•จ์ˆ˜ ๊ด€๋ จ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…

ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋‚˜ ๋ฐ˜ํ™˜ ๊ฐ’ ํƒ€์ž…์„ ์ถ”์ถœํ•˜๋Š” ๋ฐ ์œ ์šฉํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…๋“ค๋„ ์žˆ์–ด์š”.

1๏ธโƒฃ Parameters<T>: ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž… ์ถ”์ถœ

Parameters<T>์€ ํ•จ์ˆ˜ ํƒ€์ž… T์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋“ค์„ ํŠœํ”Œ ํƒ€์ž…์œผ๋กœ ์ถ”์ถœํ•ด์ค˜์š”.

function greet(name: string, age: number): string { return `์•ˆ๋…•ํ•˜์„ธ์š”, ${name}๋‹˜! ${age}์‚ด์ด์‹œ๊ตฐ์š”.`; } // greet ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž…์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. type GreetParams = Parameters<typeof greet>; /* GreetParams๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: [name: string, age: number] */ const params: GreetParams = ['๋ธ”๋ฃจ', 30]; // const invalidParams: GreetParams = ['๋ธ”๋ฃจ']; // ์—๋Ÿฌ ๋ฐœ์ƒ: age๊ฐ€ ๋ˆ„๋ฝ๋จ console.log(greet(...params));

2๏ธโƒฃ ReturnType<T>: ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž… ์ถ”์ถœ

ReturnType<T>์€ ํ•จ์ˆ˜ ํƒ€์ž… T์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ถ”์ถœํ•ด์ค˜์š”.

function calculateSum(a: number, b: number): number { return a + b; } // calculateSum ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. type SumResult = ReturnType<typeof calculateSum>; /* SumResult๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: number */ const result: SumResult = 100; // const invalidResult: SumResult = '๊ฒฐ๊ณผ'; // ์—๋Ÿฌ ๋ฐœ์ƒ: string์€ number์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Œ

3๏ธโƒฃ Awaited<T>: Promise์˜ ๊ฒฐ๊ณผ ํƒ€์ž… ์ถ”์ถœ

Awaited<T>์€ Promise์˜ ๊ฒฐ๊ณผ ํƒ€์ž…์„ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜, ์ค‘์ฒฉ๋œ Promise๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ‰ํƒ„ํ™”ํ•˜์—ฌ ์ตœ์ข… ๊ฒฐ๊ณผ ํƒ€์ž…์„ ์–ป์„ ๋•Œ ์‚ฌ์šฉํ•ด์š”.
๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ๋งค์šฐ ์œ ์šฉํ•ด์š”.

async function fetchData(): Promise<{ data: string }> { return Promise.resolve({ data: 'Hello, Blue!' }); } async function fetchNestedData(): Promise<Promise<number>> { return Promise.resolve(Promise.resolve(123)); } // fetchData ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์ธ Promise<{ data: string }>์—์„œ ์‹ค์ œ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. type DataResult = Awaited<ReturnType<typeof fetchData>>; /* DataResult๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: { data: string } */ const data: DataResult = { data: 'Success' }; // fetchNestedData ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์ธ Promise<Promise<number>>์—์„œ ์ตœ์ข… number ํƒ€์ž…์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. type NestedDataResult = Awaited<ReturnType<typeof fetchNestedData>>; /* NestedDataResult๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: number */ const nestedData: NestedDataResult = 456;

โœจ ์ œ๋„ค๋ฆญ์„ ํ™œ์šฉํ•œ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ๋งŒ๋“ค๊ธฐ

TypeScript์˜ ๋‚ด์žฅ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…๋งŒ์œผ๋กœ๋Š” ๋ชจ๋“  ์ƒํ™ฉ์„ ์ปค๋ฒ„ํ•˜๊ธฐ ์–ด๋ ค์šธ ๋•Œ๊ฐ€ ์žˆ์–ด์š”.
์ด๋Ÿด ๋•Œ๋Š” ์ œ๋„ค๋ฆญ(Generics)๊ณผ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…(Conditional Types)์„ ํ™œ์šฉํ•˜์—ฌ ์ง์ ‘ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”.

0๏ธโƒฃ DeepPartial<T>: ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๊นŒ์ง€ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“ค๊ธฐ

๋‚ด์žฅ Partial<T>์€ ์ตœ์ƒ์œ„ ์†์„ฑ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“ค์–ด์š”.
์ค‘์ฒฉ๋œ ๊ฐ์ฒด์˜ ๋ชจ๋“  ์†์„ฑ๊นŒ์ง€ ์žฌ๊ท€์ ์œผ๋กœ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด DeepPartial<T>๊ณผ ๊ฐ™์€ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด ํ•„์š”ํ•ด์š”.

type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]>; } : T; interface Settings { theme: 'dark' | 'light'; notifications: { email: boolean; sms: boolean; }; profile: { firstName: string; lastName: string; avatarUrl?: string; }; } // DeepPartial์„ ์ ์šฉํ•˜์—ฌ ๋ชจ๋“  ์†์„ฑ๊ณผ ์ค‘์ฒฉ๋œ ์†์„ฑ๊นŒ์ง€ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. type PartialSettings = DeepPartial<Settings>; /* PartialSettings๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: { theme?: "dark" | "light"; notifications?: { email?: boolean; sms?: boolean; }; profile?: { firstName?: string; lastName?: string; avatarUrl?: string; }; } */ const userSettings: PartialSettings = { notifications: { email: false, // ์ค‘์ฒฉ๋œ ์†์„ฑ๋„ ์„ ํƒ์  }, profile: { firstName: '๋ธ”๋ฃจ', }, }; console.log(userSettings);
์œ ์šฉํ•œ ํŒ

DeepPartial ํ•ด์„ค:
T extends object ? ... : T๋Š” ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์ด์—์š”.
๋งŒ์•ฝ T๊ฐ€ ๊ฐ์ฒด ํƒ€์ž…์ด๋ผ๋ฉด [P in keyof T]?: DeepPartial<T[P]>๋ฅผ ์ ์šฉํ•˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด T ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์š”.
[P in keyof T]?: ...๋Š” ๋งคํ•‘๋œ ํƒ€์ž…์œผ๋กœ, T์˜ ๋ชจ๋“  ํ‚ค P์— ๋Œ€ํ•ด ์†์„ฑ์„ ์„ ํƒ์ (?)์œผ๋กœ ๋งŒ๋“ค๊ณ , ํ•ด๋‹น ์†์„ฑ ๊ฐ’(T[P])์— ๋‹ค์‹œ DeepPartial์„ ์žฌ๊ท€์ ์œผ๋กœ ์ ์šฉํ•ด์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๊นŒ์ง€ ๋ชจ๋“  ์†์„ฑ์ด ์„ ํƒ์ ์œผ๋กœ ๋ณ€ํ™˜๋ผ์š”.

๐Ÿ“ ์ •๋ฆฌ ๋ฐ ๋‹ค์Œ ์•ก์…˜

0๏ธโƒฃ ํ•ต์‹ฌ ์š”์•ฝ

์˜ค๋Š˜์€ TypeScript์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ธ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ์‚ดํŽด๋ณด์•˜์–ด์š”.

  • Partial<T>: ๋ชจ๋“  ์†์„ฑ์„ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • Required<T>: ๋ชจ๋“  ์†์„ฑ์„ ํ•„์ˆ˜๋กœ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • Pick<T, K>: ํŠน์ • ์†์„ฑ๋งŒ ์„ ํƒํ•˜์—ฌ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • Omit<T, K>: ํŠน์ • ์†์„ฑ์„ ์ œ์™ธํ•˜์—ฌ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • Readonly<T>: ๋ชจ๋“  ์†์„ฑ์„ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • Exclude<T, U>: ์œ ๋‹ˆ์˜จ ํƒ€์ž…์—์„œ ํŠน์ • ๋ฉค๋ฒ„๋ฅผ ์ œ์™ธํ•  ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • Extract<T, U>: ์œ ๋‹ˆ์˜จ ํƒ€์ž…์—์„œ ํŠน์ • ๋ฉค๋ฒ„๋ฅผ ์ถ”์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • NonNullable<T>: null๊ณผ undefined๋ฅผ ์ œ๊ฑฐํ•  ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • Parameters<T>: ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž…์„ ํŠœํ”Œ๋กœ ์ถ”์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • ReturnType<T>: ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ถ”์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•ด์š”.
  • Awaited<T>: Promise์˜ ์ตœ์ข… ๊ฒฐ๊ณผ ํƒ€์ž…์„ ์ถ”์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•ด์š”.

๋˜ํ•œ, ์ œ๋„ค๋ฆญ๊ณผ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์„ ํ™œ์šฉํ•˜์—ฌ DeepPartial<T>์™€ ๊ฐ™์€ ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•๋„ ์•Œ์•„๋ณด์•˜์–ด์š”.
์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์€ ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๊ณ , ํƒ€์ž… ์ •์˜๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ•˜๋ฉฐ, ๊ถ๊ทน์ ์œผ๋กœ ๋” ๊ฒฌ๊ณ ํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฌ์šด TypeScript ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“œ๋Š” ๋ฐ ํ•„์ˆ˜์ ์ธ ๋„๊ตฌ์˜ˆ์š”.

1๏ธโƒฃ ๋‹ค์Œ ์•ก์…˜

์ด๋ก ์ ์ธ ๋‚ด์šฉ์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, ์ง์ ‘ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๋ฉด์„œ ์ตํžˆ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํšจ๊ณผ์ ์ด์—์š”.

  1. ๊ณต์‹ ๋ฌธ์„œ ์ฐธ๊ณ : TypeScript ๊ณต์‹ ๋ฌธ์„œ์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ์„น์…˜์„ ๋‹ค์‹œ ํ•œ๋ฒˆ ์ฝ์–ด๋ณด๋ฉด์„œ ์˜ค๋Š˜ ๋‹ค๋ฃฌ ๋‚ด์šฉ๋“ค์„ ๋ณต์Šตํ•ด ๋ณด์„ธ์š”.
  2. ํ”„๋กœ์ ํŠธ์— ์ ์šฉ: ํ˜„์žฌ ์ง„ํ–‰ ์ค‘์ธ ํ”„๋กœ์ ํŠธ๋‚˜ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ ์ฐพ์•„๋ณด์„ธ์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, API ์š”์ฒญ/์‘๋‹ต DTO๋ฅผ ์ •์˜ํ•˜๊ฑฐ๋‚˜, ์ปดํฌ๋„ŒํŠธ Props ํƒ€์ž…์„ ์œ ์—ฐํ•˜๊ฒŒ ๋งŒ๋“ค ๋•Œ ํ™œ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”.
  3. ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ์—ฐ์Šต: ์ž์‹ ๋งŒ์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ๋งŒ๋“ค์–ด๋ณด๋ฉด์„œ ์ œ๋„ค๋ฆญ๊ณผ ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ์‹ฌํ™”์‹œ์ผœ ๋ณด์„ธ์š”.

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

๐Ÿ“ฎ ์ฐธ๊ณ 

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