ํ™ˆ์œผ๋กœ
โ† ์ฑ…์žฅ์œผ๋กœ

๐Ÿ“˜ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ

๊ณต๋ถ€๋ฐฉ ์ž๋ฃŒ๋Š” keysub๊ฐ€ AI๋ฅผ ํ™œ์šฉํ•ด ์ž‘์„ฑยทํŽธ์ง‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

1. Introduction ๐Ÿš€

๋‚ด์šฉ ํŽผ์น˜๊ธฐ / ์ ‘๊ธฐ

1.1 ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ž€ ๋ฌด์—‡์ธ๊ฐ€? ๐Ÿค”

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ(TypeScript)๋Š” ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ์—์„œ ๊ฐœ๋ฐœํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ, ์‰ฝ๊ฒŒ ๋งํ•ด "์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์— 'ํƒ€์ž…(ํ˜•์‹)'์„ ์ž…ํžŒ ์–ธ์–ด"์ž…๋‹ˆ๋‹ค.

1.2 ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์ƒ์œ„ ์ง‘ํ•ฉ(Superset)์œผ๋กœ์„œ์˜ ์œ„์น˜ ๐Ÿ”

1.2.1. ์ƒ์œ„ ์ง‘ํ•ฉ(Superset)์ด๋ž€ ์˜๋ฏธ

์ˆ˜ํ•™์—์„œ ๋งํ•˜๋Š” ํฌํ•จ ๊ด€๊ณ„๋ฅผ ์ƒ๊ฐํ•˜๋ฉด ์‰ฝ์Šต๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ '์ž‘์€ ์›'์ด๋ผ๋ฉด, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๊ทธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ์™„์ „ํžˆ ํฌํ•จํ•˜๋Š” '๋” ํฐ ์›'์ž…๋‹ˆ๋‹ค. * ํ˜ธํ™˜์„ฑ: ๋ชจ๋“  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋Š” ์œ ํšจํ•œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๊ธฐ์กด .js ํŒŒ์ผ์˜ ํ™•์žฅ์ž๋งŒ .ts๋กœ ๋ฐ”๊ฟ”๋„ ๋ฌธ์ œ์—†์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. * ํ”Œ๋Ÿฌ์Šค ์•ŒํŒŒ: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๊ธฐ์กด ๋ฌธ๋ฒ• ์œ„์— '์ •์  ํƒ€์ž… ์‹œ์Šคํ…œ'์ด๋ผ๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ฅผ ์ถ”๊ฐ€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

1.2.2. ์™œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์œ„์— ํƒ€์ž…์„ ์–น์—ˆ์„๊นŒ?

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

1.2.3. ๋™์ž‘ ๋ฐฉ์‹: ๋ธŒ๋ผ์šฐ์ €๋Š” ์ดํ•ดํ•˜์ง€ ๋ชปํ•œ๋‹ค? โš™๏ธ

๋ธŒ๋ผ์šฐ์ €๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ง์ ‘ ์ฝ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ '์ปดํŒŒ์ผ(๋˜๋Š” ํŠธ๋žœ์ŠคํŒŒ์ผ)' ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. 1. .ts ํŒŒ์ผ๋กœ ์ฝ”๋“œ๋ฅผ ์งญ๋‹ˆ๋‹ค 2. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํƒ€์ž…์„ ์ฒดํฌํ•˜๊ณ  ์—๋Ÿฌ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค 3. ์ด์ƒ์ด ์—†์œผ๋ฉด ํƒ€์ž…์„ ์ œ๊ฑฐํ•œ ์ˆœ์ˆ˜ .js ํŒŒ์ผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค 4. ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด ์ตœ์ข…์ ์ธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค


1.3 ๋™์  ํƒ€์ž… ์–ธ์–ด(JS)์™€ ์ •์  ํƒ€์ž… ์–ธ์–ด(TS)์˜ ์ฐจ์ด โš–๏ธ

๋™์  ํƒ€์ž…(JS)๊ณผ ์ •์  ํƒ€์ž…(TS)์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด๋Š” 'ํƒ€์ž…์„ ์–ธ์ œ ๊ฒฐ์ •ํ•˜๋А๋ƒ'์™€ '์—๋Ÿฌ๋ฅผ ์–ธ์ œ ๋ฐœ๊ฒฌํ•˜๋А๋ƒ'์— ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌ๋ถ„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ (Dynamic Type) ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ (Static Type)
ํƒ€์ž… ๊ฒฐ์ • ์‹œ์  ์‹คํ–‰ ์‹œ(Runtime) ๋ณ€์ˆ˜์— ๊ฐ’์ด ํ• ๋‹น๋  ๋•Œ ์ž๋™์œผ๋กœ ๊ฒฐ์ •๋จ ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ(Compile time) ๊ฐœ๋ฐœ์ž๊ฐ€ ํƒ€์ž…์„ ์ง์ ‘ ๋ช…์‹œํ•จ
์—๋Ÿฌ ํ™•์ธ ์‹œ์  ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•ด ๋ด์•ผ ์—๋Ÿฌ๋ฅผ ์•Œ ์ˆ˜ ์žˆ์Œ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•˜๊ธฐ ์ „ ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๋ฅผ ์žก์Œ
์œ ์—ฐ์„ฑ/์•ˆ์ „์„ฑ ๋งค์šฐ ์œ ์—ฐํ•˜์ง€๋งŒ, ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํƒ€์ž… ์—๋Ÿฌ ๋ฐœ์ƒ ํ™•๋ฅ ์ด ๋†’์Œ ์—„๊ฒฉํ•˜์ง€๋งŒ, ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ๊ณผ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Œ

1.3.1. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ (๋™์  ํƒ€์ž…) ๐Ÿƒ

๋ณ€์ˆ˜ ํ•˜๋‚˜์— ์ˆซ์ž๋„ ๋„ฃ์—ˆ๋‹ค๊ฐ€, ๋ฌธ์ž์—ด๋„ ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

let value = 10;      // ์ฒ˜์Œ์—” ์ˆซ์ž
value = "์•ˆ๋…•";      // ๊ฐ‘์ž๊ธฐ ๋ฌธ์ž์—ด๋กœ ๋ฐ”๊ฟ”๋„ OK (์‹คํ–‰ ์ „๊นŒ์ง€ ๋ฌธ์ œ ๋ชจ๋ฆ„)

1.3.2. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ (์ •์  ํƒ€์ž…) ๐Ÿ›ก๏ธ

๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค ๋•Œ ์–ด๋–ค ํƒ€์ž…์ด ๋“ค์–ด๊ฐˆ์ง€ ๋ฏธ๋ฆฌ ์•ฝ์†ํ•ฉ๋‹ˆ๋‹ค.

let value: number = 10;
value = "์•ˆ๋…•";      // ์—๋Ÿฌ ๋ฐœ์ƒ! "์ˆซ์ž ํƒ€์ž…์— ๋ฌธ์ž์—ด์„ ๋„ฃ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."

2. ๊ธฐ๋ณธ ํƒ€์ž… (Basic Types) ๐Ÿ’Ž

๋‚ด์šฉ ํŽผ์น˜๊ธฐ / ์ ‘๊ธฐ

2.1 ๊ธฐ๋ณธ ์ž๋ฃŒํ˜• ๐Ÿ“‹

2.1.1. string: ํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ

let userName: string = "Alice";
let greeting: string = `Hello, ${userName}`; // ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด๋„ ๊ฐ€๋Šฅ

2.1.2. number: ์ •์ˆ˜, ์‹ค์ˆ˜ ๊ตฌ๋ถ„ ์—†์ด ๋ชจ๋“  ์ˆซ์ž ์ €์žฅ

let age: number = 25;
let pi: number = 3.14;

2.1.3. boolean: ์ฐธ(true) ๋˜๋Š” ๊ฑฐ์ง“(false)

let isLoggedIn: boolean = true;
let hasToken: boolean = false;

2.1.4. null & undefined

๋ณดํ†ต ๋‹จ๋…์œผ๋กœ ์“ฐ๊ธฐ๋ณด๋‹ค "๊ฐ’์ด ์—†์Œ"์„ ๋ช…์‹œํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

let empty: null = null;
let notFound: undefined = undefined;

2.2 ๋ฐฐ์—ด(Array)๊ณผ ํŠœํ”Œ(Tuple) ๐Ÿ—๏ธ

๋ฐฐ์—ด๊ณผ ํŠœํ”Œ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ์กฐํ™”ํ•  ๋•Œ ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ๋ฐฐ์—ด์€ ๊ฐ™์€ ํƒ€์ž…์˜ ๋ชฉ๋ก์„, ํŠœํ”Œ์€ ์ •ํ•ด์ง„ ์ˆœ์„œ์™€ ๊ฐœ์ˆ˜์˜ ๋ชฉ๋ก์„ ๋‹ค๋ฃฐ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

2.2.1 ๋ฐฐ์—ด ํƒ€์ž… ์„ ์–ธ ๋ฐฉ์‹: type[] vs Array

// ๋ฐฉ๋ฒ• 1: ํƒ€์ž…[] (๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ž„)
const productList: string[] = ["์•„์ดํฐ", "๊ฐค๋Ÿญ์‹œ", "๋งฅ๋ถ"];

// ๋ฐฉ๋ฒ• 2: Array<ํƒ€์ž…> (์ œ๋„ค๋ฆญ ๋ฐฉ์‹)
const priceList: Array<number> = [1000, 2000, 3000];

2.2.2 ๊ฐ์ฒด ๋ฐฐ์—ด ๐Ÿ“ฆ

๋ณดํ†ต ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” 'ํšŒ์› ๋ชฉ๋ก', '์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ' ๊ฐ™์€ ํ˜•ํƒœ

2.2.2.1. ๊ฐ์ฒด ๋ฐฐ์—ด์„ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ• : [1. ๊ฐ์ฒด์˜ ์ƒ๊น€์ƒˆ ์ •์˜] โ†’ [2. ๋ฐฐ์—ด์— ์ ์šฉ]

  • interface์™€ []์˜ ์กฐํ•ฉ (๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ž„)
// 1. ๊ฐ์ฒด์˜ ์„ค๊ณ„๋„(Interface) ์ž‘์„ฑ
interface User {
  id: number;
  name: string;
}

// 2. ํ•ด๋‹น ํƒ€์ž…์„ ๋ฐฐ์—ด์— ์ ์šฉํ•˜๊ธฐ
const users: User[] = [
  { id: 1, name: "Kim" },
  { id: 2, name: "Lee" }
];

users.push({ id: 3, name: "Park" }); // ์ž์œ ๋กญ๊ฒŒ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ
  • type ๋ณ„์นญ๊ณผ Array<>์˜ ์กฐํ•ฉ
type User = {
  id: number;
  name: string;
};

const users: Array<User> = [
  { id: 1, name: "Kim" }
];
  • ์œ ์—ฐํ•œ ํƒ€์ž… ์ •์˜ (์„ ํƒ์  ์†์„ฑ). ์ด๋•Œ๋Š” ?๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค
interface User {
  id: number;
  name: string;
  email?: string; // ์ด๋ฉ”์ผ์€ ๊ฐ’์ด ์žˆ๋Š” ๊ฒฝ์šฐ String, ์—†์œผ๋ฉด undefined (undefined ๊ฐ€๋Šฅ)
}

const userList: User[] = [
  { id: 1, name: "Kim", email: "kim@test.com" },
  { id: 2, name: "Lee" } // email์ด ์—†์–ด๋„ ์—๋Ÿฌ๊ฐ€ ๋‚˜์ง€ ์•Š์Œ
];
  • ์ฝ๊ธฐ ์ „์šฉ ๋ฐฐ์—ด (Readonly)
const colors: readonly string[] = ["Red", "Blue"];
colors.push("Green"); // ์—๋Ÿฌ! (์ถ”๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ)

2.2.3 ํŠœํ”Œ (Tuple) ๐Ÿฑ

  • ํŠœํ”Œ(Tuple) ์€ ํ•œ๋งˆ๋””๋กœ '์ˆœ์„œ์™€ ๊ฐœ์ˆ˜๊ฐ€ ์ •ํ•ด์ง„ ๋ฐ์ดํ„ฐ ๋ฌถ์Œ'์„ ๋œปํ•˜๋Š” ์ˆ˜ํ•™/์ปดํ“จํ„ฐ ๊ณตํ•™ ์šฉ์–ด์ž…๋‹ˆ๋‹ค. ์ผ์ƒ์ƒํ™œ๋กœ ๋น„์œ ํ•˜์ž๋ฉด '์„ธํŠธ ๋ฉ”๋‰ด' ์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.
    "ํ–„๋ฒ„๊ฑฐ ์„ธํŠธ"๋ผ๊ณ  ํ•˜๋ฉด [ํ–„๋ฒ„๊ฑฐ, ์ฝœ๋ผ, ๊ฐ์žํŠ€๊น€]์ด๋ผ๋Š” ์ˆœ์„œ์™€ ๊ตฌ์„ฑ์ด ๋”ฑ ์ •ํ•ด์ ธ ์žˆ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ํŠœํ”Œ์€ ๋ฐฐ์—ด์˜ ํ•˜์œ„ ํƒ€์ž…์œผ๋กœ, ์š”์†Œ์˜ ๊ฐœ์ˆ˜๊ฐ€ ๊ณ ์ •๋˜๊ณ  ๊ฐ ์š”์†Œ์˜ ํƒ€์ž…์ด ๋ฏธ๋ฆฌ ์ •ํ•ด์ง„ ๋ฐฐ์—ด์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

2.2.3.1 ๊ธฐ๋ณธ ์„ ์–ธ๊ณผ ํ• ๋‹น ๐Ÿ“

๊ฐ ์ธ๋ฑ์Šค์— ๋“ค์–ด๊ฐˆ ํƒ€์ž…์„ ์ˆœ์„œ๋Œ€๋กœ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.

let person: [string, number];

person = ["Alice", 30]; // โœ… OK
// person = [30, "Alice"]; // โŒ Error: ์ˆœ์„œ๊ฐ€ ๋‹ค๋ฆ„
// person = ["Alice", 30, true]; // โŒ Error: ์ •ํ•ด์ง„ ๊ธธ์ด๋ฅผ ์ดˆ๊ณผํ•จ

2.2.3.2 ์„ ํƒ์  ์š”์†Œ (Optional Elements) โ“

ํŠน์ • ์š”์†Œ๊ฐ€ ์žˆ์„ ์ˆ˜๋„ ์žˆ๊ณ  ์—†์„ ์ˆ˜๋„ ์žˆ๋‹ค๋ฉด ?๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‹จ, ์„ ํƒ์  ์š”์†Œ๋Š” ํ•ญ์ƒ ๋’ค์ชฝ์— ์œ„์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

let point: [number, number, number?];

point = [10, 20];      // โœ… OK
point = [10, 20, 30];  // โœ… OK

2.2.3.3 ๋‚˜๋จธ์ง€ ์š”์†Œ (Rest Elements) ๐ŸŒŠ

ํŠœํ”Œ์˜ ์ผ๋ถ€๋ถ„๋งŒ ํƒ€์ž…์„ ์ง€์ •ํ•˜๊ณ , ๋‚˜๋จธ์ง€๋Š” ๊ฐ€๋ณ€์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

let logger: [string, ...number[]];

logger = ["Error", 404, 500, 502]; // โœ… ์ฒซ ๋ฒˆ์งธ๋Š” ๋ฌธ์ž์—ด, ๋‚˜๋จธ์ง€๋Š” ์ˆซ์ž ๋ฐฐ์—ด

2.2.3.4 ์ฝ๊ธฐ ์ „์šฉ ํŠœํ”Œ (Readonly Tuple) ๐Ÿ”’

ํŠœํ”Œ์€ ์ผ๋ฐ˜ ๋ฐฐ์—ด์ฒ˜๋Ÿผ push๋‚˜ pop์ด ๊ฐ€๋Šฅํ•˜์—ฌ ๊ณ ์ •๋œ ๊ธธ์ด๊ฐ€ ๊นจ์งˆ ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์™„์ „ํžˆ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด readonly๋ฅผ ๋ถ™์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

const readOnlyTuple: readonly [number, string] = [1, "Fixed"];
// readOnlyTuple.push(2); // โŒ Error: 'readonly' ํƒ€์ž…์—๋Š” push ๋ฉ”์„œ๋“œ๊ฐ€ ์—†์Œ

2.2.3.5 ๋ผ๋ฒจ๋ง๋œ ํŠœํ”Œ (Labeled Tuple Elements) ๐Ÿท๏ธ [๊ถŒ์žฅ]

๊ฐ ์š”์†Œ๊ฐ€ ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋Š”์ง€ ์ด๋ฆ„์„ ๋ถ™์—ฌ ๊ฐ€๋…์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type UserInfo = [id: number, name: string, email: string];

const user: UserInfo = [1, "Kim", "kim@example.com"];

2.2.3.6 ๐Ÿ’ก ํŠœํ”Œ vs ์ธํ„ฐํŽ˜์ด์Šค/๊ฐ์ฒด โš–๏ธ

๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ๊ฐ์ฒด({})๋ฅผ ์“ธ์ง€ ํŠœํ”Œ([])์„ ์“ธ์ง€ ๊ณ ๋ฏผ๋œ๋‹ค๋ฉด ๋‹ค์Œ ๊ธฐ์ค€์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

  • ๊ฐ์ฒด: ๋ฐ์ดํ„ฐ์˜ ์˜๋ฏธ๊ฐ€ ํ‚ค(Key) ์ด๋ฆ„์œผ๋กœ ์„ค๋ช…๋˜์–ด์•ผ ํ•  ๋•Œ (๋Œ€๋ถ€๋ถ„์˜ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ).
  • ํŠœํ”Œ: ์ˆœ์„œ ์ž์ฒด๊ฐ€ ์ค‘์š”ํ•œ ์˜๋ฏธ๋ฅผ ๊ฐ–๊ฑฐ๋‚˜, 2~3๊ฐœ์˜ ์งง์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ๋„˜๊ธธ ๋•Œ (์˜ˆ: ์ขŒํ‘œ [x, y], ์ƒํƒœ ๊ด€๋ฆฌ [state, setter]).

๐Ÿš€ 2.3 ์ถ”๊ฐ€๋œ ํƒ€์ž… (TS ์ „์šฉ ์‹œ์Šคํ…œ ํƒ€์ž…)

๐Ÿ“ฆ 2.3.1 ์—ด๊ฑฐํ˜•(Enum)

Enum(์—ด๊ฑฐํ˜•)์€ ์—ฐ๊ด€๋œ ์•„์ดํ…œ๋“ค์„ ํ•˜๋‚˜์˜ ์ง‘ํ•ฉ์œผ๋กœ ๋ฌถ์–ด ๊ด€๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์„ ๋†’์ด๊ณ , ์ •ํ•ด์ง„ ๊ฐ’ ์™ธ์— ๋‹ค๋ฅธ ๊ฐ’์ด ๋“ค์–ด์˜ค์ง€ ๋ชปํ•˜๊ฒŒ ์ œํ•œํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿšฆ 2.3.1.1 ์ƒํƒœ ์ฝ”๋“œ ๊ด€๋ฆฌ

๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ๋Š” ์ฃผ๋ฌธ ์ƒํƒœ, ์œ ์ € ๊ถŒํ•œ, HTTP ์‘๋‹ต ์ฒ˜๋ฆฌ ๋“ฑ์ž…๋‹ˆ๋‹ค.

// 1. ์„ ์–ธ: ์ฃผ๋ฌธ ์ƒํƒœ๋ฅผ ์ •์˜
enum OrderStatus {
  Pending = "PENDING",     // ๋Œ€๊ธฐ ์ค‘
  Shipped = "SHIPPED",     // ๋ฐฐ์†ก ์ค‘
  Delivered = "DELIVERED", // ์™„๋ฃŒ
  Cancelled = "CANCELLED"  // ์ทจ์†Œ
}

// 2. ํ™œ์šฉ: ํ•จ์ˆ˜ ์ธ์ž์— ํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉ
function updateOrderStatus(id: number, status: OrderStatus) {
  console.log(`์ฃผ๋ฌธ๋ฒˆํ˜ธ ${id}์˜ ์ƒํƒœ๋ฅผ ${status}๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.`);
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
updateOrderStatus(1024, OrderStatus.Shipped); 

// ์—๋Ÿฌ ๋ฐœ์ƒ: ์ •์˜๋˜์ง€ ์•Š์€ ๋ฌธ์ž์—ด์€ ๋„ฃ์„ ์ˆ˜ ์—†์Œ
// updateOrderStatus(1025, "READY"); 
โšก 2.3.1.2 const enum

const๋ฅผ ๋ถ™์ด๋ฉด ์ปดํŒŒ์ผ ์‹œ์ ์— Enum ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ๊ฐ’์„ ์ง์ ‘ ์ฑ„์›Œ ๋„ฃ์–ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊ฐ€๋ฒผ์›Œ์ง‘๋‹ˆ๋‹ค.

const enum Role {
  Admin = "ADMIN",
  User = "USER"
}
โš–๏ธ 2.3.1.3 ์ˆซ์žํ˜• Enum๊ณผ ๋ฌธ์ž์—ด Enum์˜ ์ฐจ์ด

๋‘ ๋ฐฉ์‹์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด๋Š” '๊ฐ’์ด ์ž๋™์œผ๋กœ ํ• ๋‹น๋˜๋Š”๊ฐ€'์™€ '๋””๋ฒ„๊น… ์‹œ ์–ผ๋งˆ๋‚˜ ๋ช…ํ™•ํ•œ๊ฐ€'์— ์žˆ์Šต๋‹ˆ๋‹ค.

1๏ธโƒฃ ๊ฐ’์˜ ์ž๋™ ํ• ๋‹น (Auto-incrementing) * ์ˆซ์žํ˜• Enum: ๊ฐ’์„ ์ƒ๋žตํ•˜๋ฉด 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ 1์”ฉ ์ž๋™์œผ๋กœ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. * ๋ฌธ์ž์—ด Enum: ์ž๋™ ํ• ๋‹น์ด ์—†์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ฉค๋ฒ„์˜ ๊ฐ’์„ ์ผ์ผ์ด ์ˆ˜๋™์œผ๋กœ ๋„ฃ์–ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// ์ˆซ์žํ˜•: 0, 1, 2 ์ž๋™ ํ• ๋‹น
enum Direction { Up, Down, Left } 

// ๋ฌธ์ž์—ด: ๋ฐ˜๋“œ์‹œ ๋ช…์‹œํ•ด์•ผ ํ•จ
enum DirectionStr { 
  Up = "UP", 
  Down = "DOWN", 
  Left = "LEFT" 
}

2๏ธโƒฃ ๋””๋ฒ„๊น…๊ณผ ๋กœ๊ทธ (๋กœ๊ทธ์˜ ๊ฐ€๋…์„ฑ) * ์ˆซ์žํ˜•: ๋กœ๊ทธ๋ฅผ ์ฐ์œผ๋ฉด 0, 1 ๊ฐ™์€ ์ˆซ์ž๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ์ง  ์‚ฌ๋žŒ์ด ์•„๋‹ˆ๋ฉด ์ด๊ฒŒ ์–ด๋–ค ์ƒํƒœ์ธ์ง€ ์•Œ๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. * ๋ฌธ์ž์—ด: ๋กœ๊ทธ์— "UP", "DOWN" ์ฒ˜๋Ÿผ ์˜๋ฏธ๊ฐ€ ๋ช…ํ™•ํ•œ ํ…์ŠคํŠธ๊ฐ€ ์ฐํž™๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋ณด๊ฑฐ๋‚˜ ์—๋Ÿฌ ๋กœ๊ทธ๋ฅผ ๋ถ„์„ํ•  ๋•Œ ํ›จ์”ฌ ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

3๏ธโƒฃ ์—ญ๋ฐฉํ–ฅ ๋งคํ•‘ (Reverse Mapping) * ์ˆซ์žํ˜•: ์ด๋ฆ„์œผ๋กœ ๊ฐ’์„ ์ฐพ์„ ์ˆ˜๋„ ์žˆ๊ณ , ๊ฐ’์œผ๋กœ ์ด๋ฆ„์„ ์ฐพ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. (Direction[0] -> "Up") * ๋ฌธ์ž์—ด: ์ด๋ฆ„์œผ๋กœ ๊ฐ’๋งŒ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ญ๋ฐฉํ–ฅ ๋งคํ•‘์„ ์ง€์›ํ•˜์ง€ ์•Š์•„ ๋” ์•ˆ์ „ํ•˜๊ณ  ์—„๊ฒฉํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ”“ 2.3.2 Any (๋ฌด์—‡์ด๋“  ํ—ˆ์šฉ)

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

let value: any = 10;
value = "์•ˆ๋…•"; // ์—๋Ÿฌ ์•ˆ ๋‚จ
value.toUpperCase(); // ์—๋Ÿฌ ์•ˆ ๋‚จ (๋งŒ์•ฝ ์ˆซ์ž์˜€๋‹ค๋ฉด ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ ๋ฐœ์ƒ ์œ„ํ—˜)

๐Ÿ” 2.3.3 Unknown (์•ˆ์ „ํ•œ Any)

  • any์ฒ˜๋Ÿผ ์–ด๋–ค ํƒ€์ž…์˜ ๊ฐ’๋„ ์ €์žฅํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ๋ฐ˜๋“œ์‹œ ํƒ€์ž…์„ ํ™•์ธ(ํƒ€์ž… ๊ฐ€๋“œ)ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. any๋ณด๋‹ค ํ›จ์”ฌ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.
  • API ์‘๋‹ต์ฒ˜๋Ÿผ ๋ญ๊ฐ€ ์˜ฌ์ง€ ์ง„์งœ ๋ชจ๋ฅผ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
let value: unknown = "Hello";

// value.toUpperCase(); // ์—๋Ÿฌ! (unknown ํƒ€์ž…์ด๋ผ ๋ฐ”๋กœ ์‚ฌ์šฉ ๋ถˆ๊ฐ€)

if (typeof value === "string") {
  value.toUpperCase(); // ํƒ€์ž… ํ™•์ธ์„ ํ–ˆ์œผ๋ฏ€๋กœ ์ด์ œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ!
}

๐Ÿ’จ 2.3.4 Void (๋น„์–ด์žˆ์Œ)

  • ํ•จ์ˆ˜๊ฐ€ ์•„๋ฌด๊ฒƒ๋„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • return ๋ฌธ์ด ์—†๋Š” ํ•จ์ˆ˜๋‚˜, ๋‹จ์ˆœํžˆ ์ฝ˜์†”๋งŒ ์ฐ๋Š” ํ•จ์ˆ˜์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
function sayHi(): void {
  console.log("Hi!");
  // return 1; // ์—๋Ÿฌ! ๋ฐ˜ํ™˜๊ฐ’์ด ์žˆ์œผ๋ฉด ์•ˆ ๋จ
}

๐Ÿšซ 2.3.5 Never (์ ˆ๋Œ€ ์ผ์–ด๋‚  ์ˆ˜ ์—†์Œ)

ํ•จ์ˆ˜๊ฐ€ ๋๋‚˜์ง€ ์•Š๊ฑฐ๋‚˜, ํ•ญ์ƒ ์—๋Ÿฌ๋ฅผ ๋˜์ ธ์„œ ๋ฐ˜ํ™˜๊ฐ’์ด๋ผ๋Š” ๊ฐœ๋… ์ž์ฒด๊ฐ€ ์กด์žฌํ•  ์ˆ˜ ์—†์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
๋ฌดํ•œ ๋ฃจํ”„, ์˜๋„์ ์ธ ์—๋Ÿฌ ๋ฐœ์ƒ ํ•จ์ˆ˜์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

function throwError(msg: string): never {
  throw new Error(msg); // ํ•จ์ˆ˜๊ฐ€ ๋๋‚˜์ง€ ์•Š๊ณ  ์—ฌ๊ธฐ์„œ ๋ฉˆ์ถค
}

function infiniteLoop(): never {
  while (true) {} // ์˜์›ํžˆ ์‹คํ–‰๋จ
}

3. ์ธํ„ฐํŽ˜์ด์Šค์™€ ํƒ€์ž… ๋ณ„์นญ (Interface & Type Alias) ๐Ÿท๏ธ

๋‚ด์šฉ ํŽผ์น˜๊ธฐ / ์ ‘๊ธฐ

3.1 Interface์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ ๐Ÿ—๏ธ

์ธํ„ฐํŽ˜์ด์Šค๋Š” interface ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •์˜ํ•˜๋ฉฐ, ๊ฐ์ฒด๊ฐ€ ๊ฐ€์ ธ์•ผ ํ•  ์†์„ฑ(Property)๊ณผ ๋ฉ”์„œ๋“œ(Method)๋ฅผ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.

interface User {
  id: number;
  name: string;
  email: string;
}

const worker: User = {
  id: 1,
  name: "Kim",
  email: "kim@example.com"
};

3.2 ๊ฐ์ฒด์˜ ํ˜•ํƒœ(Shape) ์ •์˜ ๐Ÿ“

'๊ฐ์ฒด์˜ ํ˜•ํƒœ(Shape)๋ฅผ ์ •์˜ํ•œ๋‹ค'๋Š” ๋ง์€, ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ ์–ด๋–ค ์ด๋ฆ„์˜ ํ‚ค(Key)๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ๊ทธ ํ‚ค์— ์–ด๋–ค ํƒ€์ž…์˜ ๊ฐ’(Value)์ด ๋‹ด๊ฒจ์•ผ ํ•˜๋Š”์ง€๋ฅผ ๋ฏธ๋ฆฌ ๊ฒฐ์ •ํ•ด๋‘๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

3.2.1 ์„ค๊ณ„๋„(Blueprint)๋กœ์„œ์˜ ์—ญํ•  ๐Ÿ“‹

ํ˜„์‹ค ์„ธ๊ณ„์—์„œ ๊ฑด๋ฌผ์„ ์ง€์„ ๋•Œ ์„ค๊ณ„๋„๊ฐ€ ํ•„์š”ํ•˜๋“ฏ, ์ฝ”๋“œ์—์„œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ๋•Œ "์ด ๊ฐ์ฒด๋Š” ๋ฐ˜๋“œ์‹œ id๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•˜๊ณ , name์€ ๋ฌธ์ž์—ด์ด์–ด์•ผ ํ•ด" ๋ผ๊ณ  ๊ทœ์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

// 1. ํ˜•ํƒœ(Shape)๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ •์˜
interface Product {
  name: string;
  price: number;
}

// 2. ์ •์˜๋œ ํ˜•ํƒœ์— ๋งž์ถฐ ๊ฐ์ฒด ์ƒ์„ฑ
const laptop: Product = {
  name: "MacBook Pro",
  price: 2500000
};

// โŒ ์—๋Ÿฌ ๋ฐœ์ƒ: Product ํ˜•ํƒœ์— ์—†๋Š” 'color'๋ฅผ ๋„ฃ๊ฑฐ๋‚˜ ํƒ€์ž…์„ ํ‹€๋ฆฌ๋ฉด ๊ฐ์ง€ํ•จ
const phone: Product = {
  name: "iPhone",
  price: "150๋งŒ์›", // Error: 'string'์€ 'number' ํƒ€์ž…์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Œ
  color: "silver"   // Error: 'Product' ํ˜•์‹์— 'color'๊ฐ€ ์—†์Œ
};

3.2.2 "๋• ํƒ€์ดํ•‘(Duck Typing)"๊ณผ ๊ตฌ์กฐ์  ํƒ€์ดํ•‘ ๐Ÿฆ†

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ํฅ๋ฏธ๋กœ์šด ์ ์€ '๊ตฌ์กฐ์  ํƒ€์ดํ•‘(Structural Typing)' ๋ฐฉ์‹์„ ์“ด๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฆ„์ด ๋‹ฌ๋ผ๋„ ๋‚ด๋ถ€ ๊ตฌ์กฐ(Shape)๊ฐ€ ๊ฐ™์œผ๋ฉด ๊ฐ™์€ ํƒ€์ž…์œผ๋กœ ์ธ์ •ํ•ด ์ค๋‹ˆ๋‹ค.

interface Point {
  x: number;
  y: number;
}

function printPoint(p: Point) {
  console.log(`${p.x}, ${p.y}`);
}

// ๋ช…์‹œ์ ์œผ๋กœ Point ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์“ฐ์ง€ ์•Š์•˜์ง€๋งŒ, ํ˜•ํƒœ(Shape)๊ฐ€ ๊ฐ™์œผ๋ฏ€๋กœ ํ†ต๊ณผ!
const myObj = { x: 10, y: 20, z: 30 }; 
printPoint(myObj); 

3.3 ์„ ํƒ์  ์†์„ฑ(?)๊ณผ ์ฝ๊ธฐ ์ „์šฉ ์†์„ฑ(readonly) ๐Ÿ”’

3.3.1 ์„ ํƒ์  ์†์„ฑ (Optional Properties: ?) โ“

์†์„ฑ ์ด๋ฆ„ ๋’ค์— ๋ฌผ์Œํ‘œ(?)๋ฅผ ๋ถ™์—ฌ์„œ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์†์„ฑ์ด ๊ฐ์ฒด์— ์žˆ์–ด๋„ ๋˜๊ณ  ์—†์–ด๋„ ๋  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์‹ค๋ฌด ํ™œ์šฉ: ํšŒ์›๊ฐ€์ž… ์‹œ ํ•„์ˆ˜ ์ •๋ณด(ID, ๋น„๋ฐ€๋ฒˆํ˜ธ)์™€ ์„ ํƒ ์ •๋ณด(์ž๊ธฐ์†Œ๊ฐœ, ์›น์‚ฌ์ดํŠธ ์ฃผ์†Œ)๋ฅผ ๊ตฌ๋ถ„ํ•  ๋•Œ ์ž์ฃผ ์“ฐ์ž…๋‹ˆ๋‹ค.
  • ํŠน์ง•: ๊ฐ’์„ ๋„ฃ์ง€ ์•Š์œผ๋ฉด ์ž๋™์œผ๋กœ undefined๊ฐ€ ํ• ๋‹น๋œ ๊ฒƒ์ฒ˜๋Ÿผ ์ทจ๊ธ‰๋ฉ๋‹ˆ๋‹ค.
interface UserProfile {
  id: string;          // ํ•„์ˆ˜
  nickname: string;    // ํ•„์ˆ˜
  bio?: string;        // ์„ ํƒ (์žˆ์„ ์ˆ˜๋„, ์—†์„ ์ˆ˜๋„ ์žˆ์Œ)
  website?: string;    // ์„ ํƒ
}

const user1: UserProfile = {
  id: "user01",
  nickname: "ํƒœ๊ถŒ๋ธŒ์ด"
  // bio์™€ website๊ฐ€ ์—†์–ด๋„ ์—๋Ÿฌ๊ฐ€ ๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
};

3.3.2 ์ฝ๊ธฐ ์ „์šฉ ์†์„ฑ (Readonly Properties: readonly) ๐Ÿ›‘

์†์„ฑ ์ด๋ฆ„ ์•ž์— readonly ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์ž…๋‹ˆ๋‹ค. ๊ฐ์ฒด๊ฐ€ ์ฒ˜์Œ ์ƒ์„ฑ๋  ๋•Œ๋งŒ ๊ฐ’์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๊ณ , ๊ทธ ์ดํ›„์—๋Š” ๊ฐ’์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

  • ์‹ค๋ฌด ํ™œ์šฉ: API ํ‚ค, ๊ณ ์œ  ID, ์„ค์ •๊ฐ’(Config) ๋“ฑ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์ค‘์— ๋ฐ”๋€Œ๋ฉด ์•ˆ ๋˜๋Š” ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ํŠน์ง•: ๋Ÿฐํƒ€์ž„์ด ์•„๋‹Œ ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๋ฅผ ์žก์•„์ค๋‹ˆ๋‹ค.
interface Config {
  readonly apiKey: string;
  readonly domain: string;
}

const myConfig: Config = {
  apiKey: "ABC-12345",
  domain: "https://example.com"
};

// โŒ ์—๋Ÿฌ ๋ฐœ์ƒ: ์ฝ๊ธฐ ์ „์šฉ ์†์„ฑ์ด๋ฏ€๋กœ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
myConfig.apiKey = "XYZ-99999"; 

3.3.3 (์ฐธ์กฐ) ReadonlyArray\ ํƒ€์ž… ๐Ÿ“š

  • ๋ฐฐ์—ด ์ „์ฒด๋ฅผ ์ˆ˜์ • ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ  ์‹ถ์„ ๋•Œ๋Š” ReadonlyArray<number>๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • push, pop ๊ฐ™์€ ๋ฐฐ์—ด ์ˆ˜์ • ๋ฉ”์„œ๋“œ๋ฅผ ์•„์˜ˆ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ฒŒ ๋ง‰์•„์ค๋‹ˆ๋‹ค.
const numbers: ReadonlyArray<number> = [1, 2, 3];

// โŒ ์—๋Ÿฌ ๋ฐœ์ƒ: 'push'๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ
numbers.push(4); 

3.4 ๋™์  ํ‚ค๋ฅผ ๋‹ค๋ฃจ๋Š” ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜ (Index Signature) ๐Ÿ”‘

์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•  ๋•Œ, ์†์„ฑ์˜ ์ด๋ฆ„(Key)์„ ๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์—†๊ฑฐ๋‚˜ ์†์„ฑ์˜ ๊ฐœ์ˆ˜๊ฐ€ ์œ ๋™์ ์ผ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

3.4.1 ๊ธฐ๋ณธ ๋ฌธ๋ฒ• ๐Ÿ“

๋Œ€๊ด„ํ˜ธ [] ์•ˆ์— ํ‚ค์˜ ํƒ€์ž…๊ณผ ๊ฐ’์˜ ํƒ€์ž…์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

interface SalaryInfo {
  // [key: ํ‚คํƒ€์ž…]: ๊ฐ’ํƒ€์ž…
  [role: string]: number;
}

const teamSalary: SalaryInfo = {
  developer: 5000,
  designer: 4500,
  manager: 5500,
  // ์–ด๋–ค ํ‚ค๋ฅผ ์ถ”๊ฐ€ํ•ด๋„ ๊ฐ’์ด ์ˆซ์ž๋ผ๋ฉด ํ†ต๊ณผ๋ฉ๋‹ˆ๋‹ค.
};

3.4.2 ์‹ค๋ฌด์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ  ๐Ÿ’ก

  • ๋™์  ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ: API ์‘๋‹ต์—์„œ ํ‚ค ๊ฐ’์ด ์‚ฌ์šฉ์ž ID๋‚˜ ๋‚ ์งœ์ฒ˜๋Ÿผ ๊ณ„์† ๋ณ€ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.
  • ๊ฐ์ฒด๋ฅผ ์‚ฌ์ „(Dictionary)์ฒ˜๋Ÿผ ์“ธ ๋•Œ: ํŠน์ • ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋งคํ•‘ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
interface ErrorMessages {
  [code: string]: string;
}

const apiErrors: ErrorMessages = {
  "404": "ํŽ˜์ด์ง€๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
  "500": "์„œ๋ฒ„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.",
  "403": "๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.",
};

3.4.3 ์ฃผ์˜์‚ฌํ•ญ ๋ฐ ๊ทœ์น™ โš ๏ธ

  1. ํ•„์ˆ˜ ์†์„ฑ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ: ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ์žˆ์œผ๋ฉด ๋‹ค๋ฅธ ๋ช…์‹œ์  ์†์„ฑ๋„ ๊ทธ ๊ทœ์น™์„ ๋”ฐ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. typescript interface UserInventory { [itemName: string]: number; gold: number; // โœ… ๊ฐ€๋Šฅ (์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜์™€ ํƒ€์ž… ์ผ์น˜) // username: string; // โŒ ์—๋Ÿฌ: ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ๋ชจ๋“  ๊ฐ’์„ number๋กœ ๊ทœ์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ }
  2. ์—ฌ๋Ÿฌ ํƒ€์ž… ํ—ˆ์šฉํ•˜๊ธฐ: ๋งŒ์•ฝ ๊ฐ’์— ์—ฌ๋Ÿฌ ํƒ€์ž…์„ ๋„ฃ๊ณ  ์‹ถ๋‹ค๋ฉด ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. typescript interface FlexibleConfig { [key: string]: string | number | boolean; version: number; // โœ… ์ด์ œ string | number | boolean ๋ฒ”์œ„ ์•ˆ์— ์žˆ์œผ๋ฏ€๋กœ ๊ฐ€๋Šฅ }
  3. ํ‚ค์˜ ํƒ€์ž…: ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜์˜ ํ‚ค ํƒ€์ž…์€ ๋ณดํ†ต string, number, symbol๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

3.4.4 (์ฐธ์กฐ) Record\ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ๐Ÿ› ๏ธ

์‹ค๋ฌด์—์„œ๋Š” ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹ , ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” Record๋ฅผ ์จ์„œ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

  • interface Obj { [key: string]: number; }
  • type Obj = Record<string, number>; (์œ„์™€ ๋™์ผํ•œ ์˜๋ฏธ)

3.5 Type Alias (ํƒ€์ž… ๋ณ„์นญ) ์‚ฌ์šฉ๋ฒ• ๐Ÿท๏ธ

ํƒ€์ž… ๋ณ„์นญ(Type Alias)์€ ๋ง ๊ทธ๋Œ€๋กœ ํŠน์ • ํƒ€์ž…์ด๋‚˜ ์ธํ„ฐํŽ˜์ด์Šค์— ์ƒˆ๋กœ์šด ์ด๋ฆ„(๋ณ„๋ช…)์„ ๋ถ™์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

3.5.1 ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ• ๐Ÿ“

๊ฐ์ฒด๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ผ๋ฐ˜ ๋ณ€์ˆ˜ ํƒ€์ž…, ์œ ๋‹ˆ์˜จ ํƒ€์ž… ๋“ฑ ๋ชจ๋“  ํƒ€์ž…์— ์ด๋ฆ„์„ ๋ถ™์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// 1. ๊ธฐ๋ณธ ํƒ€์ž…์— ๋ณ„์นญ ๋ถ™์ด๊ธฐ
type MyString = string;
const name: MyString = "Kim";

// 2. ๊ฐ์ฒด ํƒ€์ž… ์ •์˜ (Interface์™€ ์œ ์‚ฌ)
type User = {
  id: number;
  name: string;
};

const worker: User = { id: 1, name: "Kim" };

3.5.2 Interface vs Type Alias ๐Ÿค

์‹ค๋ฌด์—์„œ interface ๋Œ€์‹  type์„ ์“ฐ๋Š” ๊ฒฐ์ •์ ์ธ ์ด์œ ๋Š” ๋ฐ”๋กœ ์•„๋ž˜ ๊ธฐ๋Šฅ๋“ค ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

3.5.2.1 ์œ ๋‹ˆ์˜จ ํƒ€์ž… (Union Types) ๐Ÿšฅ

"A ์ด๊ฑฐ๋‚˜ B์ด๋‹ค"๋ผ๋Š” ํƒ€์ž…์„ ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. (Interface๋Š” ๋ถˆ๊ฐ€๋Šฅ)

type Status = "success" | "error" | "loading";

const currentStatus: Status = "success"; // โœ… OK
const wrongStatus: Status = "pending";  // โŒ ์—๋Ÿฌ: ์ง€์ •๋œ 3๊ฐœ ๋ฌธ์ž์—ด ์™ธ์—” ํ—ˆ์šฉ ์•ˆ ํ•จ
3.5.2.2 ํŠœํ”Œ (Tuple) ๐Ÿ“ฆ

๋ฐฐ์—ด์˜ ์š”์†Œ ๊ฐœ์ˆ˜์™€ ๊ฐ ์ˆœ์„œ์˜ ํƒ€์ž…์„ ๊ณ ์ •ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

type Point = [number, number];
const coordinates: Point = [10, 20]; // โœ… OK
3.5.2.3 ๊ต์ฐจ ํƒ€์ž… (Intersection Types) โž•

๋‘ ๊ฐœ ์ด์ƒ์˜ ํƒ€์ž…์„ ํ•˜๋‚˜๋กœ ํ•ฉ์น  ๋•Œ & ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

type Name = { name: string };
type Age = { age: number };

type Person = Name & Age; // { name: string, age: number } ์™€ ๊ฐ™์Œ
3.5.2.4 ๋ณต์žกํ•œ ํƒ€์ž… ๊ด€๋ฆฌ โš™๏ธ

API ํ˜ธ์ถœ ๊ฒฐ๊ณผ๋‚˜ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ๋ฅผ ์ •์˜ํ•  ๋•Œ ๋งค์šฐ ์ง๊ด€์ ์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type ResponseData = {
  id: number;
  content: string;
};

// ์œ ๋‹ˆ์˜จ๊ณผ ๊ฐ์ฒด ํƒ€์ž…์„ ์กฐํ•ฉํ•œ ์‹ค๋ฌดํ˜• ํŒจํ„ด
type ApiResponse = 

  | { status: "success"; data: ResponseData }
  | { status: "error"; message: string };

function handleResponse(response: ApiResponse) {
  if (response.status === "success") {
    console.log(response.data.content);
  } else {
    console.log(response.message);
  }
}

3.5.3 (์ฐธ์กฐ) ์ƒ์†(extends)? ๐ŸŒฒ

์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ, ํด๋ž˜์Šค์˜ ์ƒ์† ๊ฐœ๋…๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฐฉ์‹: ์ƒˆ๋กœ์šด ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๊ธฐ์กด ์ธํ„ฐํŽ˜์ด์Šค์˜ ์†์„ฑ์„ ๋ฌผ๋ ค๋ฐ›์Šต๋‹ˆ๋‹ค.
  • ํŠน์ง• (์—„๊ฒฉํ•จ): ๋ถ€๋ชจ์™€ ์ž์‹ ๊ฐ„์— ์†์„ฑ ํƒ€์ž…์ด ์ถฉ๋Œํ•˜๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ๋ฏธ๋ฆฌ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

// โŒ ์ถฉ๋Œ ๋ฐœ์ƒ ์‹œ ์—๋Ÿฌ interface Cat extends Animal { name: number; // Error: 'Animal'์˜ 'name(string)'๊ณผ ํƒ€์ž…์ด ์•ˆ ๋งž์Œ! }


3.5.4 ๋ฌด์—‡์„ ์จ์•ผ ํ• ๊นŒ? ๐Ÿค”

  • ๊ฐ์ฒด์˜ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•˜๊ณ  ์ƒ์†(extends)์ด ๋นˆ๋ฒˆํ•˜๋‹ค๋ฉด? โ†’ Interface
  • ์œ ๋‹ˆ์˜จ(|), ๊ต์ฐจ(&), ๊ธฐ๋ณธ ํƒ€์ž… ๋ณ„์นญ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด? โ†’ Type Alias

4. ํ•จ์ˆ˜ ๐Ÿš€

๋‚ด์šฉ ํŽผ์น˜๊ธฐ / ์ ‘๊ธฐ

4.1. ํ•จ์ˆ˜ ํƒ€์ž… ์ •์˜ โœ๏ธ

4.1.1. ๊ธฐ๋ณธ์ ์ธ ํ•จ์ˆ˜ ํƒ€์ž… ์ •์˜

๊ฐ€์žฅ ๊ธฐ์ดˆ์ ์ด๋ฉด์„œ๋„ ํ•„์ˆ˜์ ์ธ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค. ์ธ์ž์™€ ๋ฐ˜ํ™˜ ํƒ€์ž…(Return Type)์„ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.

function add(a: number, b: number): number {
  return a + b;
}

// ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ ๋ฐฉ์‹
const multiply = (a: number, b: number): number => a * b;

4.1.2. ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ๊ธฐ๋ณธ๊ฐ’ (Optional & Default) โš™๏ธ

ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ?๋ฅผ ์‚ฌ์šฉํ•ด ์„ ํƒ์ ์œผ๋กœ ๋ฐ›๊ฑฐ๋‚˜, ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function greet(name: string, greeting?: string): string {
  return `${greeting || 'Hello'}, ${name}`;
}

console.log(greet("์ฒ ์ˆ˜")); // "Hello, ์ฒ ์ˆ˜"
console.log(greet("์˜ํฌ", "์•ˆ๋…•")); // "์•ˆ๋…•, ์˜ํฌ"

4.2. ํ•จ์ˆ˜ ํƒ€์ž… ๋ณ„์นญ (Function Type Alias) ๐Ÿท๏ธ

ํ•จ์ˆ˜ ์ž์ฒด์˜ ํƒ€์ž…์„ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด๋‘๊ณ  ์žฌ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ๋•Œ ์‹ค๋ฌด์—์„œ ๋งค์šฐ ๋งŽ์ด ์“ฐ์ž…๋‹ˆ๋‹ค.

type Formatter = (text: string, uppercase: boolean) => string;

const myFormatter: Formatter = (str, isUpper) => {
  return isUpper ? str.toUpperCase() : str.toLowerCase();
};

4.3. ํ•จ์ˆ˜ ์˜ค๋ฒ„๋กœ๋”ฉ (Function Overloading) ๐Ÿ”„

์ „๋‹ฌ๋ฐ›๋Š” ์ธ์ž์˜ ํƒ€์ž…์— ๋”ฐ๋ผ ๋ฐ˜ํ™˜๋˜๋Š” ํƒ€์ž…์ด ๋‹ฌ๋ผ์งˆ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. API ์‘๋‹ต ์ฒ˜๋ฆฌ๋‚˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

// ์„ ์–ธ๋ถ€: ์–ด๋–ค ์กฐํ•ฉ์ด ๊ฐ€๋Šฅํ•œ์ง€ ์ •์˜
function getValue(v: string): string;
function getValue(v: number): number;

// ๊ตฌํ˜„๋ถ€: ์‹ค์ œ ๋กœ์ง (any ๋˜๋Š” ์œ ๋‹ˆ์˜จ ํƒ€์ž… ์‚ฌ์šฉ)
function getValue(v: any): any {
  return v;
}

const str = getValue("ABC"); // ํƒ€์ž…์ด string์œผ๋กœ ์ถ”๋ก ๋จ
const num = getValue(123);   // ํƒ€์ž…์ด number๋กœ ์ถ”๋ก ๋จ

4.4. ๊ฐ์ฒด ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น (Destructuring) ๐Ÿ“ฆ

React ์ปดํฌ๋„ŒํŠธ์˜ Props๋‚˜ ๋ณต์žกํ•œ ์˜ต์…˜ ๊ฐ์ฒด๋ฅผ ๋ฐ›์„ ๋•Œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

interface UserConfig {
  id: string;
  isAdmin: boolean;
}

// ์ธ์ž ๋‹จ๊ณ„์—์„œ ํƒ€์ž…์„ ๋ฐ”๋กœ ์ •์˜ํ•˜๊ฑฐ๋‚˜ interface๋ฅผ ํ™œ์šฉ
function loginUser({ id, isAdmin }: UserConfig): void {
  console.log(`ID: ${id}, Admin: ${isAdmin}`);
}

4.4.1. ์™œ ์ด๋ ‡๊ฒŒ ์“ฐ๋‚˜์š”? (Before vs After) ๐Ÿค”

๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น์„ ์•ˆ ์“ฐ๋ฉด ๋งค๋ฒˆ user.id, user.isAdmin์ฒ˜๋Ÿผ ๊ฐ์ฒด๋ช…์„ ๋ฐ˜๋ณตํ•ด์„œ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น ๋ฏธ์‚ฌ์šฉ
function loginUser(user: UserConfig) {
  console.log(user.id);      // ๋งค๋ฒˆ user. ๋ฅผ ๋ถ™์—ฌ์•ผ ํ•จ
  console.log(user.isAdmin);
}

// ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น ์‚ฌ์šฉ
function loginUser({ id, isAdmin }: UserConfig) {
  console.log(id);           // ๋ฐ”๋กœ ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ!
  console.log(isAdmin);
}

4.4.2. ๋ฌธ๋ฒ• ์ƒ์„ธ ๋ถ„์„ ๐Ÿ”

({ id, isAdmin }: UserConfig)๋ฅผ ์ชผ๊ฐœ๋ณด๋ฉด ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

  • { id, isAdmin }: "์ธ์ž๋กœ ๊ฐ์ฒด๊ฐ€ ๋“ค์–ด์˜ฌ ํ…๋ฐ, ๊ทธ ์•ˆ์˜ id ์†์„ฑ๊ณผ isAdmin ์†์„ฑ์„ ์ฐพ์•„์„œ ๊ฐ™์€ ์ด๋ฆ„์˜ ๋ณ€์ˆ˜๋กœ ๋งŒ๋“ค์–ด์ค˜!"๋ผ๋Š” ๋ช…๋ น์ž…๋‹ˆ๋‹ค.
  • : UserConfig: "๊ทธ๋Ÿฐ๋ฐ ๊ทธ ๊ฐ์ฒด๋Š” UserConfig๋ผ๋Š” ํƒ€์ž… ๊ทœ๊ฒฉ์„ ์ง€์ผœ์•ผ ํ•ด!"๋ผ๋Š” ํƒ€์ž… ์ •์˜์ž…๋‹ˆ๋‹ค.

4.5. ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ ๐Ÿน

4.5.1. ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ (Arrow Functions)

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋Š” ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š” ๋ฐฉ์‹์ด ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.

4.5.1.1. ํ•จ์ˆ˜ ์„ ์–ธ๊ณผ ๋™์‹œ์— ํƒ€์ž… ์ •์˜ (๊ฐ€์žฅ ์ผ๋ฐ˜์ )
const add = (a: number, b: number): number => a + b;
4.5.1.2. ๋ณ€์ˆ˜์— ํ•จ์ˆ˜ ํƒ€์ž…์„ ๋ฏธ๋ฆฌ ์ง€์ • (์ถ”์ฒœ!) โœจ
type CalculateFn = (a: number, b: number) => number;

const multiply: CalculateFn = (a, b) => a * b; // ์ธ์ž์˜ ํƒ€์ž…์„ ์ƒ๋žตํ•ด๋„ ์ž๋™์œผ๋กœ ์ถ”๋ก ๋จ

4.6. Rest ํŒŒ๋ผ๋ฏธํ„ฐ (๋‚˜๋จธ์ง€ ๋งค๊ฐœ๋ณ€์ˆ˜) ๐Ÿ—๏ธ

4.6.1. ์ธ์ž์˜ ๊ฐœ์ˆ˜๊ฐ€ ์ •ํ•ด์ง€์ง€ ์•Š์•˜์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” Rest ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋ฐ˜๋“œ์‹œ ๋ฐฐ์—ด ํƒ€์ž…์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

// ...numbers: number[] ๋Š” ์ธ์ž(๋งค๊ฐœ๋ณ€์ˆ˜)๋กœ ์ˆซ์ž๋“ค๋งŒ ๋“ค์–ด์žˆ๋Š” ๋ฐฐ์—ด์„ ๋ฐ›๊ฒ ๋‹ค.
// : number ๋Š” ์ด ํ•จ์ˆ˜๋Š” ๊ณ„์‚ฐ์„ ๋๋‚ด๊ณ  ๊ฒฐ๊ณผ๋ฌผ๋กœ ์ˆซ์ž(number) ํ•˜๋‚˜๋ฅผ ๋Œ๋ ค์ฃผ๊ฒ ๋‹ค!

function sum(...numbers: number[]): number {
  return numbers.reduce((acc, cur) => acc + cur, 0);
}

console.log(sum(1, 2, 3));       // 6
console.log(sum(10, 20, 30, 40)); // 100

4.6.2. API ๋กœ๊ฑฐ (Rest + Destructuring) ๐Ÿชต

ํŠน์ • ๊ณ ์ • ์ธ์ž์™€ ๋‚˜๋จธ์ง€ ์˜ต์…˜๋“ค์„ ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ด ๋‘ ๊ฐœ๋…์„ ๊ฒฐํ•ฉ

interface LogOptions {
  level: 'info' | 'error' | 'warn';
  silent?: boolean;
}

// ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋Š” message, ๋‚˜๋จธ์ง€๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์˜ต์…˜ ๊ฐ์ฒด๋“ค์„ ๋ฐฐ์—ด๋กœ ๋ฐ›์Œ
const logMessage = (message: string, ...options: LogOptions[]) => {
  console.log(`[Message]: ${message}`);
  console.log(`[Options]:`, options);
};

logMessage(
  "์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ฑ๊ณต", 
  { level: 'info' }, 
  { level: 'warn', silent: true }
);

4.7. ์ฝœ๋ฐฑ(Callback) ํ•จ์ˆ˜์˜ ํƒ€์ž… ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์ •์˜ ๐Ÿ“ž

์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์ „๋‹ฌ๋˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งํ•˜์ฃ . ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ์ด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ "์–ด๋–ค ์ธ์ž๋ฅผ ๋ฐ›๊ณ , ๋ฌด์—‡์„ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€" ๋ฏธ๋ฆฌ ๊ทœ๊ฒฉ(์‹œ๊ทธ๋‹ˆ์ฒ˜)์„ ์ •ํ•ด๋‘˜ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4.7.1. ๊ธฐ๋ณธ ์ •์˜ ๋ฐฉ์‹ (์ธ๋ผ์ธ)

function processUserInput(name: string, callback: (n: string) => void) {
  // 1. name์„ ์ฒ˜๋ฆฌํ•˜๊ณ 
  console.log(`์‚ฌ์šฉ์ž ์ด๋ฆ„: ${name}`);

  // 2. ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚˜๋ฉด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰
  callback(name);
}

processUserInput("์ฒ ์ˆ˜", (userName) => {
  console.log(`${userName}๋‹˜, ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!`);
});

4.7.2. ๋ณ„์นญ(Type Alias) ์‚ฌ์šฉ ๋ฐฉ์‹ (์‹ค๋ฌด ๊ถŒ์žฅ) ๐Ÿ’ก

์‹ค๋ฌด์—์„œ๋Š” ์ฝœ๋ฐฑ ํƒ€์ž…์ด ๋ณต์žกํ•ด์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„, ๋”ฐ๋กœ ์ด๋ฆ„์„ ๋ถ™์—ฌ ์ •์˜ํ•˜๋Š” ๋ฐฉ์‹์„ ํ›จ์”ฌ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.

// ์ฝœ๋ฐฑ ํƒ€์ž… ์ •์˜ (์„ค๊ณ„๋„)
type Checker = (id: string, info: { role: string }) => boolean;

// 2. ์‹คํ–‰ ํ•จ์ˆ˜: ๋‚ด๋ถ€์—์„œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ์ฝœ๋ฐฑ์— ๋˜์ ธ์คŒ
const run = (id: string, callback: Checker) => {
  const isOk = callback(id, { role: 'admin' }); // ๊ฐ์ฒด ์ „๋‹ฌ!
  console.log(isOk ? "์Šน์ธ" : "๊ฑฐ๋ถ€");
};

// 3. ์‹ค์ œ ์‚ฌ์šฉ: ๋˜์ ธ์ค€ ๊ฐ์ฒด(info)๋ฅผ ๊บผ๋‚ด์„œ ๋กœ์ง ์งœ๊ธฐ
run("user01", (id, info) => {
  return info.role === 'admin'; // admin์ด๋ฉด true ๋ฐ˜ํ™˜
});

4.7.3. ์ฝœ๋ฐฑ์ด ์„ ํƒ ์‚ฌํ•ญ์ผ ๋•Œ (?) โ“

์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์ค„ ๋•Œ๋„ ์žˆ๊ณ  ์•ˆ ์ค„ ๋•Œ๋„ ์žˆ๋‹ค๋ฉด ?๋ฅผ ๋ถ™์ž…๋‹ˆ๋‹ค. ์ด๋•Œ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ && ์—ฐ์‚ฐ์ž๋‚˜ ์กฐ๊ฑด๋ฌธ์„ ์จ์„œ ์•ˆ์ „ํ•˜๊ฒŒ ํ˜ธ์ถœํ•˜๋Š” ๊ฒŒ ๊ตญ๋ฃฐ์ž…๋‹ˆ๋‹ค.

function fetchData(url: string, onSuccess?: (data: any) => void) {
  // ... ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋กœ์ง ...
  const data = { id: 1, content: "Hello" };

  // onSuccess๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ์‹คํ–‰ํ•ด๋ผ! (&& ์—ฐ์‚ฐ์ž ํ™œ์šฉ)
  onSuccess && onSuccess(data);
}

4.7.4. ๊ฐ€๋ณ€ ์ธ์ž(...args)๋ฅผ ๋ฐฐ์—ด ํƒ€์ž…์œผ๋กœ ๋ฌถ์–ด ์ฒ˜๋ฆฌํ•˜๊ธฐ ๐Ÿ–‡๏ธ

// ...tags ๋’ค์˜ : string[] ์€ "๋ฌธ์ž์—ด๋“ค๋งŒ ๋ชจ์ธ ๋ฐฐ์—ด"์ด๋ผ๋Š” ๋œป
// ๋ฐ˜๋“œ์‹œ number[], string[] ์ฒ˜๋Ÿผ ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค
// ๋ฌด์กฐ๊ฑด ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๋งจ ๋งˆ์ง€๋ง‰์— ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
function makeHashTags(...tags: string[]): string {

  // ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋Š” map, filter, reduce ๊ฐ™์€ ๋ฐฐ์—ด ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ์–ด ํŽธํ•ฉ๋‹ˆ๋‹ค
  return tags.map(t => `#${t}`).join(' ');
}

// 1๊ฐœ๋ฅผ ๋„ฃ๋“  3๊ฐœ๋ฅผ ๋„ฃ๋“  'tags'๋ผ๋Š” ๋ฐฐ์—ด๋กœ ์ฒ˜๋ฆฌ๋จ
console.log(makeHashTags("ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ"));           // "#ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ"
console.log(makeHashTags("JS", "TS", "React")); // "#JS #TS #React"

5. ํด๋ž˜์Šค(Class)์™€ ๊ฐ์ฒด ์ง€ํ–ฅ ๐Ÿ—๏ธ

๋‚ด์šฉ ํŽผ์น˜๊ธฐ / ์ ‘๊ธฐ

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ํด๋ž˜์Šค ๋ฌธ๋ฒ•์— ํƒ€์ž… ์ •์˜์™€ ์ ‘๊ทผ ์ œ์–ด์ž๊ฐ€ ์ถ”๊ฐ€๋œ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.


5.1 ํด๋ž˜์Šค ์‚ฌ์šฉ ์‹œ ํ•ต์‹ฌ ์ฒดํฌ๋ฆฌ์ŠคํŠธ โœ…

  • ** new ํ‚ค์›Œ๋“œ**: ํด๋ž˜์Šค๋Š” ์„ค๊ณ„๋„์ผ ๋ฟ์ด๋ฏ€๋กœ, ๋ฐ˜๋“œ์‹œ new ํด๋ž˜์Šค๋ช…()์œผ๋กœ ๊ฐ์ฒด(์ธ์Šคํ„ด์Šค)๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ** this ์‚ฌ์šฉ**: ํด๋ž˜์Šค ๋‚ด๋ถ€์˜ ๋ณ€์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ์— ์ ‘๊ทผํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ this.์„ ๋ถ™์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ** ์ดˆ๊ธฐํ™” ํ•„์ˆ˜**: ์„ ์–ธ๋งŒ ํ•˜๊ณ  ๊ฐ’์„ ํ• ๋‹นํ•˜์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. (๋˜๋Š” name!: string์ฒ˜๋Ÿผ ํ™•์ • ๊ธฐํ˜ธ๋ฅผ ์จ์•ผ ํ•จ)

5.1.1 ์ •์„์ ์ธ ์„ ์–ธ ๋ฐฉ๋ฒ• ๐Ÿ“

class Robot {
  // 1. ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ ํƒ€์ž… ์„ ์–ธ
  name: string;
  model: string;

  // 2. ์ƒ์„ฑ์ž(Constructor) ์ •์˜
  constructor(name: string, model: string) {
    this.name = name;
    this.model = model;
  }

  // 3. ๋ฉ”์„œ๋“œ ์ •์˜
  move(): void {
    console.log(`${this.name}(${this.model})์ด ์›€์ง์ž…๋‹ˆ๋‹ค. ๐Ÿค–`);
  }
}

// ์‚ฌ์šฉ ๋ฐฉ๋ฒ• (์ธ์Šคํ„ด์Šค ์ƒ์„ฑ)
const myRobot = new Robot("ํƒœ๊ถŒV", "V1");
myRobot.move(); // "ํƒœ๊ถŒV(V1)์ด ์›€์ง์ž…๋‹ˆ๋‹ค."

5.1.2 ์‹ค๋ฌดํ˜• ๋‹จ์ถ• ์„ ์–ธ โšก

์‹ค๋ฌด์—์„œ๋Š” ์œ„ ๋ฐฉ์‹์ด ๋ฒˆ๊ฑฐ๋กœ์›Œ ์•„๋ž˜์ฒ˜๋Ÿผ ์ƒ์„ฑ์ž ๋งค๊ฐœ๋ณ€์ˆ˜์— ์ ‘๊ทผ ์ œ์–ด์ž(public, private ๋“ฑ)๋ฅผ ์ง์ ‘ ๋ถ™์—ฌ์„œ ๋ณ€์ˆ˜ ์„ ์–ธ๊ณผ ํ• ๋‹น์„ ํ•œ ๋ฒˆ์— ๋๋ƒ…๋‹ˆ๋‹ค.

class User {
  // ์ƒ์„ฑ์ž์— ์ ‘๊ทผ ์ œ์–ด์ž๋ฅผ ๋ถ™์ด๋ฉด ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ ์„ ์–ธ + ํ• ๋‹น์ด ์ž๋™์œผ๋กœ ๋จ!
  constructor(public nickname: string, private email: string) {}

  showInfo() {
    console.log(`์ด๋ฆ„: ${this.nickname}, ์ด๋ฉ”์ผ: ${this.email} ๐Ÿ‘ค`);
  }
}

const user = new User("๊ฟ€ํŒ๋Ÿฌ", "tip@example.com");
console.log(user.nickname); // โœ… OK (public)
// console.log(user.email);   // โŒ ์—๋Ÿฌ! (private์ด๋ผ ์™ธ๋ถ€ ์ ‘๊ทผ ๋ถˆ๊ฐ€)

5.2 ์ ‘๊ทผ ์ œ์–ด์ž 3์ข… ์„ธํŠธ ๐Ÿ”

์ œ์–ด์ž ํŠน์ง• ์„ค๋ช…
public ๊ณต๊ฐœ ์–ด๋””์„œ๋“  ์ ‘๊ทผ ๊ฐ€๋Šฅ (๊ธฐ๋ณธ๊ฐ’)
private ๋น„๊ณต๊ฐœ ํ•ด๋‹น ํด๋ž˜์Šค ๋‚ด๋ถ€์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ (์™ธ๋ถ€/์ž์‹ ๋ถˆ๊ฐ€)
protected ๋ณดํ˜ธ ํด๋ž˜์Šค ๋‚ด๋ถ€ + ์ƒ์†๋ฐ›์€ ์ž์‹๊นŒ์ง€๋Š” ์ ‘๊ทผ ๊ฐ€๋Šฅ
class Employee {
  public name: string;             // ๋ˆ„๊ตฌ๋‚˜ ๋ณผ ์ˆ˜ ์žˆ์Œ
  private salary: number;          // ๊ธ‰์—ฌ๋Š” ๋ณธ์ธ๋งŒ(ํด๋ž˜์Šค ๋‚ด๋ถ€) ์•Œ ์ˆ˜ ์žˆ์Œ
  protected department: string;    // ๋ถ€์„œ ์ •๋ณด๋Š” ์ž์‹ ํด๋ž˜์Šค๊นŒ์ง€๋งŒ ๊ณต์œ 

  constructor(name: string, salary: number, department: string) {
    this.name = name;
    this.salary = salary;
    this.department = department;
  }

  public getSalaryInfo() {
    return `${this.name}์˜ ๊ธ‰์—ฌ๋Š” ๋น„๋ฐ€์ž…๋‹ˆ๋‹ค. ๐Ÿคซ`; // ๋‚ด๋ถ€์—์„œ๋Š” salary ์ ‘๊ทผ ๊ฐ€๋Šฅ
  }
}

class Manager extends Employee {
  public changeDept(newDept: string) {
    this.department = newDept; // โœ… protected๋ผ์„œ ์ž์‹์€ ์ ‘๊ทผ ๊ฐ€๋Šฅ!
    // this.salary = 0;        // โŒ ์—๋Ÿฌ! private์€ ์ž์‹๋„ ์ ‘๊ทผ ๋ถˆ๊ฐ€
  }
}

const worker = new Employee("๊น€์ฒ ์ˆ˜", 5000, "๊ฐœ๋ฐœํŒ€");
console.log(worker.name);       // โœ… OK
// console.log(worker.salary);  // โŒ ์—๋Ÿฌ! (์™ธ๋ถ€ ์ ‘๊ทผ ๋ถˆ๊ฐ€)

5.3 private vs JS์˜ # (๋ฌด์Šจ ์ฐจ์ด?) ๐Ÿค”

๋‘˜ ๋‹ค ์™ธ๋ถ€ ์ ‘๊ทผ์„ ๋ง‰์ง€๋งŒ ๊ฒฐ์ •์ ์ธ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • 5.3.1 private (TS ๋ฌธ๋ฒ•):
    • ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๋ฅผ ์žก์•„์ค๋‹ˆ๋‹ค.
    • ์‹ค์ œ JS๋กœ ๋ณ€ํ™˜๋˜๋ฉด ์ผ๋ฐ˜ ๋ณ€์ˆ˜์™€ ๋˜‘๊ฐ™์ด ๋ณด์ž…๋‹ˆ๋‹ค (๋Ÿฐํƒ€์ž„ ๊ฐ•์ œ๋ ฅ์€ ์—†์Œ).
  • 5.3.2 # (JS ํ‘œ์ค€ ๋ฌธ๋ฒ•):
    • ๋Ÿฐํƒ€์ž„(์‹คํ–‰ ์‹œ์ )์—์„œ๋„ ์™„์ „ํžˆ ์ˆจ๊ฒจ์ง‘๋‹ˆ๋‹ค.
    • ๋ธŒ๋ผ์šฐ์ € ์ž์ฒด์—์„œ ์•„์˜ˆ ์ ‘๊ทผ์„ ๋ชป ํ•˜๊ฒŒ ๋ง‰์•„๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.

5.4 ์ถ”์ƒ ํด๋ž˜์Šค (Abstract Class) ๐Ÿงฑ

์ถ”์ƒ ํด๋ž˜์Šค๋Š” ํ•œ๋งˆ๋””๋กœ '๋ฏธ์™„์„ฑ ์„ค๊ณ„๋„'์ž…๋‹ˆ๋‹ค. ์Šค์Šค๋กœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜๋Š” ์—†์ง€๋งŒ, ์ž์‹ ํด๋ž˜์Šค๋“ค์ด ๋ฐ˜๋“œ์‹œ ๊ฐ€์ ธ์•ผ ํ•  ๊ณตํ†ต ๊ธฐ๋Šฅ์„ ๋ฌผ๋ ค์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

5.4.1 abstract ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•œ ๊ณตํ†ต ๋ผˆ๋Œ€ ๊ตฌ์ถ•

์ถ”์ƒ ํด๋ž˜์Šค ์•ˆ์—๋Š” ์ผ๋ฐ˜ ๋ฉ”์„œ๋“œ(๊ตฌํ˜„ ์™„๋ฃŒ)์™€ ์ถ”์ƒ ๋ฉ”์„œ๋“œ(์ด๋ฆ„๋งŒ ์ •์˜)๋ฅผ ์„ž์–ด์„œ ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

abstract class Animal {
  // 1. ๊ณตํ†ต ๊ธฐ๋Šฅ (๊ตฌํ˜„ ์™„๋ฃŒ): ์ž์‹๋“ค์ด ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ
  breath() {
    console.log("์ˆจ์„ ์‰ฝ๋‹ˆ๋‹ค. ๐Ÿ’จ");
  }

  // 2. ์ถ”์ƒ ๋ฉ”์„œ๋“œ (๋ผˆ๋Œ€๋งŒ): ์ž์‹์ด "๋ฐ˜๋“œ์‹œ" ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ
  abstract makeSound(): void;
}

class Dog extends Animal {
  // abstract ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ ์•ˆ ํ•˜๋ฉด ์—๋Ÿฌ ๋ฐœ์ƒ!
  makeSound() {
    console.log("๋ฉ๋ฉ! ๐Ÿถ");
  }
}

const myDog = new Dog();
myDog.breath();    // "์ˆจ์„ ์‰ฝ๋‹ˆ๋‹ค." (๋ถ€๋ชจ๊บผ ์‚ฌ์šฉ)
myDog.makeSound(); // "๋ฉ๋ฉ!" (๋ณธ์ธ์ด ๊ตฌํ˜„ํ•œ ๊ฒƒ ์‚ฌ์šฉ)
// const animal = new Animal(); // โŒ ์—๋Ÿฌ! ์ถ”์ƒ ํด๋ž˜์Šค๋Š” ์ง์ ‘ ๊ฐ์ฒด ์ƒ์„ฑ ๋ถˆ๊ฐ€

5.4.2 ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„(implements)๊ณผ์˜ ์—ญํ•  ์ฐจ์ด

๊ตฌ๋ถ„ ์ถ”์ƒ ํด๋ž˜์Šค (extends) ์ธํ„ฐํŽ˜์ด์Šค (implements)
์—ญํ•  ๊ธฐ๋ณธ ๋ฒ ์ด์Šค ๋ฌผ๋ ค์ฃผ๊ธฐ (์ƒ์†) ๊ทœ๊ฒฉ/ํ˜•์‹ ๋งž์ถ”๊ธฐ (์žฅ์ฐฉ)
๊ตฌํ˜„๋ถ€ ๋ฉ”์„œ๋“œ์˜ ์‹ค์ œ ์ฝ”๋“œ ํฌํ•จ ๊ฐ€๋Šฅ ์ฝ”๋“œ ํฌํ•จ ๋ถˆ๊ฐ€ (์„ ์–ธ๋งŒ ํ•จ)
๋ชฉ์  "๋„ˆ๋Š” ๋‚ด ์ž์‹์ด๋‹ˆ ๊ณตํ†ต ๊ธฐ๋Šฅ์„ ์จ๋ผ" "๋„ˆ๋Š” ์ด ๊ธฐ๋Šฅ(๋ฉ”์„œ๋“œ)์„ ๋ฐ˜๋“œ์‹œ ๊ฐ€์ ธ๋ผ"
๋‹ค์ค‘ ์‚ฌ์šฉ ๋‹จ ํ•˜๋‚˜๋งŒ ์ƒ์† ๊ฐ€๋Šฅ ์—ฌ๋Ÿฌ ๊ฐœ ๋™์‹œ์— ๊ตฌํ˜„ ๊ฐ€๋Šฅ

6. ๊ณ ๊ธ‰ ํƒ€์ž… (Advanced Types) ๐Ÿš€

๋‚ด์šฉ ํŽผ์น˜๊ธฐ / ์ ‘๊ธฐ

6.1. ์œ ๋‹ˆ์–ธ ํƒ€์ž… (Union, |) ๐Ÿ”€

  • "A ๋˜๋Š” B"๋ผ๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜์ผ ์ˆ˜ ์žˆ์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • API ์‘๋‹ต์ด๋‚˜ ์ƒํƒœ๊ฐ’์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ฃผ๋กœ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.
// string์ด๊ฑฐ๋‚˜ number์ผ ์ˆ˜ ์žˆ์Œ
let id: string | number;

id = "user_01"; // โœ… OK
id = 123;       // โœ… OK

// ์‹ค๋ฌด ์˜ˆ์‹œ: ๋กœ๋”ฉ ์ƒํƒœ ์ฒ˜๋ฆฌ
type Status = "success" | "error" | "loading";

function printStatus(status: Status) {
  console.log(`ํ˜„์žฌ ์ƒํƒœ๋Š” ${status}์ž…๋‹ˆ๋‹ค.`);
}

printStatus("success"); // โœ… OK
// printStatus("pending"); // โŒ ์—๋Ÿฌ! (์ •ํ•ด์ง„ ๋ฌธ์ž์—ด ์™ธ์—๋Š” ๊ฑฐ๋ถ€)

6.2. ์ธํ„ฐ์„น์…˜ ํƒ€์ž… (Intersection, &) ๐Ÿค

  • "A ๊ทธ๋ฆฌ๊ณ  B"๋ผ๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ํƒ€์ž…์„ ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์„œ ๋ชจ๋“  ์†์„ฑ์„ ๋‹ค ๊ฐ€์ง€๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • ์œ ์ € ์ •๋ณด์™€ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ํ•ฉ์น  ๋•Œ ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
interface Person {
  name: string;
}

interface Worker {
  employeeId: number;
}

// Person๊ณผ Worker์˜ ๋ชจ๋“  ์†์„ฑ์„ ๊ฐ€์ ธ์•ผ ํ•จ
type Employee = Person & Worker;

const newEmployee: Employee = {
  name: "๋ฐ•๋Œ€๋ฆฌ",
  employeeId: 9901
};

6.3. ํƒ€์ž… ์ถ”๋ก ๊ณผ ๋‹จ์–ธ ๐Ÿ”

6.3.1. ํƒ€์ž… ์ถ”๋ก  (Type Inference) โœจ

  • ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ  ๋ฐ”๋กœ ๊ฐ’์„ ํ• ๋‹นํ•˜๋ฉด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๊ทธ ๊ฐ’์„ ๋ณด๊ณ  ํƒ€์ž…์„ ์ž๋™์œผ๋กœ ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ช…ํ™•ํ•œ ๊ฒฝ์šฐ์—๋Š” ์ถ”๋ก ์— ๋งก๊ธฐ๋Š” ๊ฒƒ์ด ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค.
let name = "์ฒ ์ˆ˜"; // string์œผ๋กœ ์ž๋™ ์ถ”๋ก 
let age = 25;      // number๋กœ ์ž๋™ ์ถ”๋ก 

// name = 123;     // โŒ ์—๋Ÿฌ! ์ด๋ฏธ string์œผ๋กœ ์ถ”๋ก ๋๊ธฐ ๋•Œ๋ฌธ

6.3.2. ํƒ€์ž… ๋‹จ์–ธ (Type Assertion, as) ๐Ÿ› ๏ธ

  • ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ด๋‹น ํƒ€์ž…์— ๋Œ€ํ•ด ๋” ํ™•์‹คํžˆ ์•Œ๊ณ  ์žˆ์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ํ‚ค์›Œ๋“œ: as (๊ฐ€์žฅ ๊ถŒ์žฅ๋˜๋Š” ๋ฐฉ์‹)
  • โš ๏ธ ์‹ค์ œ๋กœ ํƒ€์ž…์ด ๋‹ค๋ฅด๋ฉด ๋Ÿฐํƒ€์ž„์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
6.3.2.1. ๋‹จ์–ธ์œผ๋กœ ๊ตฌ์ฒด์ ์ธ ํƒœ๊ทธ ์ง€์ •ํ•˜๊ธฐ ๐Ÿท๏ธ

document.querySelector๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Element๋ผ๋Š” ๋„“์€ ๋ฒ”์œ„๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • ํƒœ๊ทธ์˜ ์†์„ฑ(์˜ˆ: input์˜ value, img์˜ src)์„ ์“ฐ๋ ค๋ฉด as๋กœ ํƒ€์ž…์„ ์ขํ˜€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// TS๋Š” ์ด๊ฒŒ ๊ทธ๋ƒฅ Element์ธ์ค„ ์•Œ์ง€๋งŒ, ๊ฐœ๋ฐœ์ž๋Š” input ํƒœ๊ทธ์ธ ๊ฑธ ์••๋‹ˆ๋‹ค.
const myInput = document.getElementById("user-id") as HTMLInputElement;

// ๋‹จ์–ธ ๋•๋ถ„์— value ์†์„ฑ์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ด์ง
console.log(myInput.value); 
6.3.2.2. null ์ฒดํฌ ํŒจํ„ด (์•ˆ์ „ํ•œ ์ ‘๊ทผ) ๐Ÿ›ก๏ธ

์š”์†Œ๊ฐ€ ํ™”๋ฉด์— ์—†์„ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‹ค๋ฌด์—์„œ๋Š” Optional Chaining(?.)์ด๋‚˜ ์กฐ๊ฑด๋ฌธ์„ ํ•„์ˆ˜๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

const button = document.querySelector('.submit-btn') as HTMLButtonElement;

// ํŒจํ„ด A: Optional Chaining (๊ถŒ์žฅ)
button?.click(); 

// ํŒจํ„ด B: ์กฐ๊ฑด๋ฌธ (๋ณต์žกํ•œ ๋กœ์ง์ผ ๋•Œ)
if (button) {
  button.disabled = true;
  button.innerText = "์ „์†ก ์ค‘...";
}
6.3.2.3. ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์˜ e.target ์ฒ˜๋ฆฌ ๐ŸŽฏ

์ด๋ฒคํŠธ ํ•จ์ˆ˜ ์•ˆ์—์„œ e.target์„ ์“ธ ๋•Œ๋„ ํƒ€์ž… ๋‹จ์–ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. target์€ ์–ด๋–ค ์š”์†Œ์ผ์ง€ TS๊ฐ€ ํ™•์‹ ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

const handleInput = (e: Event) => {
  // e.target์„ ํ•„์š”ํ•œ ํƒ€์ž…์œผ๋กœ ๋‹จ์–ธํ•˜์—ฌ ์‚ฌ์šฉ
  const target = e.target as HTMLInputElement;
  console.log(target.value);
};

document.querySelector('#search')?.addEventListener('input', handleInput);
6.3.2.4. ์ž์ฃผ ์“ฐ์ด๋Š” HTML ํƒ€์ž… ๋ชฉ๋ก ๐Ÿ“‹
  • HTMLInputElement (input)
  • HTMLButtonElement (button)
  • HTMLAnchorElement (a ํƒœ๊ทธ)
  • HTMLImageElement (img ํƒœ๊ทธ)
  • HTMLFormElement (form ํƒœ๊ทธ)

6.4. ํƒ€์ž… ๊ฐ€๋“œ (Type Guards) ๐Ÿ‘ฎโ€โ™‚๏ธ

ํƒ€์ž… ๊ฐ€๋“œ๋Š” ๋ง ๊ทธ๋Œ€๋กœ "ํƒ€์ž…์„ ์ง€ํ‚ค๋Š” ํŒŒ์ˆ˜๊พผ"์ž…๋‹ˆ๋‹ค. ์œ ๋‹ˆ์–ธ ํƒ€์ž…์ฒ˜๋Ÿผ ์—ฌ๋Ÿฌ ํƒ€์ž…์ด ์„ž์—ฌ ์žˆ์„ ๋•Œ, ๋Ÿฐํƒ€์ž„์—์„œ ํƒ€์ž…์„ ํ•˜๋‚˜๋กœ ํ™•์ • ์ง€์–ด ์•ˆ์ „ํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์งค ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

6.4.1. typeof: ์›์‹œ ํƒ€์ž… ๋ถ„๊ธฐ ๐Ÿ“

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ string, number, boolean ๊ฐ™์€ ๊ธฐ๋ณธ ํƒ€์ž…์„ ๊ตฌ๋ณ„ํ•  ๋•Œ ์”๋‹ˆ๋‹ค.

function printId(id: string | number) {
  if (typeof id === "string") {
    // ์ด ์•ˆ์—์„œ id๋Š” 100% string์ž…๋‹ˆ๋‹ค.
    console.log(id.toUpperCase());
  } else {
    // ์—ฌ๊ธฐ์„œ๋Š” ์ž๋™์œผ๋กœ number๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
    console.log(id.toFixed(2));
  }
}

6.4.2. instanceof: ํด๋ž˜์Šค ๊ฐ์ฒด ๋ถ„๊ธฐ ๐Ÿ—๏ธ

์–ด๋–ค ๊ฐ์ฒด๊ฐ€ ํŠน์ • ํด๋ž˜์Šค๋กœ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด์กŒ๋Š”์ง€ ํ™•์ธํ•  ๋•Œ ์”๋‹ˆ๋‹ค.

class Dog { bark() { console.log("๋ฉ๋ฉ"); } }
class Cat { meow() { console.log("์•ผ์˜น"); } }

function speak(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark(); // Dog ํƒ€์ž…์œผ๋กœ ํ™•์ •
  } else {
    animal.meow(); // Cat ํƒ€์ž…์œผ๋กœ ํ™•์ •
  }
}

6.4.3. ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œ (is ํ‚ค์›Œ๋“œ) ๐Ÿ‘ค

๋ณต์žกํ•œ ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ํŒ๋ณ„ํ•˜๋Š” ๋ณ„๋„์˜ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋ฐ˜ํ™˜ ํƒ€์ž…์— ์ธ์ž is ํƒ€์ž…์„ ์ ์–ด์ฃผ๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

interface User { name: string; role: string; }
interface Guest { name: string; }

// "person์ด User ํƒ€์ž…์ธ์ง€ ํ™•์ธํ•˜๋Š” ํ•จ์ˆ˜" ์ •์˜
function isUser(person: User | Guest): person is User {
  return (person as User).role !== undefined;
}

function welcome(person: User | Guest) {
  if (isUser(person)) {
    // ์ด ๋ธ”๋ก ์•ˆ์—์„œ person์€ User ํƒ€์ž…์œผ๋กœ ๋Œ€์ ‘๋ฐ›์Šต๋‹ˆ๋‹ค.
    console.log(`๊ด€๋ฆฌ์ž ${person.name}๋‹˜ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.`);
  } else {
    console.log(`๋ฐฉ๋ฌธ์ž ${person.name}๋‹˜ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.`);
  }
}

// ์‹ค์ „ ์˜ˆ์‹œ ํ˜ธ์ถœ
const admin = { name: "๊ฐ•ํŒ€์žฅ", role: "ADMIN" };
const guest = { name: "์ต๋ช…" };

welcome(admin); // ์ถœ๋ ฅ: "๊ด€๋ฆฌ์ž ๊ฐ•ํŒ€์žฅ๋‹˜ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค."
welcome(guest); // ์ถœ๋ ฅ: "๋ฐฉ๋ฌธ์ž ์ต๋ช…๋‹˜ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค."

// ๋ฐฐ์—ด์„ ํ™œ์šฉํ•œ ๋ฐ˜๋ณต ์ฒ˜๋ฆฌ
const people: (User | Guest)[] = [
  { name: "์ด๋Œ€๋ฆฌ", role: "EDITOR" },
  { name: "๊ธธ๋™" }
];

people.forEach(p => welcome(p));

7. ์ œ๋„ค๋ฆญ (Generics) ๐Ÿ“ฆ

๋‚ด์šฉ ํŽผ์น˜๊ธฐ / ์ ‘๊ธฐ

7.1. ์ œ๋„ค๋ฆญ์˜ ๊ฐœ๋… ๐Ÿ’ก

์ œ๋„ค๋ฆญ(Generics)์€ ํ•œ๋งˆ๋””๋กœ ํƒ€์ž…์„ ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๋Š” 'ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ' ๊ฐœ๋…์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ ํƒ€์ž…์„ ๋ฏธ๋ฆฌ ์ •ํ•˜์ง€ ์•Š๊ณ , ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ํƒ€์ž…์„ ๋„˜๊ฒจ์ฃผ์–ด ์žฌ์‚ฌ์šฉ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

7.1.1. ์ œ๋„ค๋ฆญ์ด ์™œ ํ•„์š”ํ•œ๊ฐ€์š”? ๐Ÿค”

  • [๋ฐฉ๋ฒ• 1] ํƒ€์ž…๋ณ„๋กœ ํ•จ์ˆ˜๋ฅผ ๋‹ค ๋งŒ๋“ ๋‹ค (๋น„ํšจ์œจ) ๐Ÿ˜ญ typescript function echoNumber(item: number): number { return item; } function echoString(item: string): string { return item; } // ํƒ€์ž…์ด ๋Š˜์–ด๋‚  ๋•Œ๋งˆ๋‹ค ํ•จ์ˆ˜๋ฅผ ๊ณ„์† ๋ณต์‚ฌํ•ด์•ผ ํ•จ...
  • [๋ฐฉ๋ฒ• 2] any ํƒ€์ž…์„ ์“ด๋‹ค (์œ„ํ—˜) โŒ typescript function echoAny(item: any): any { return item; } const result = echoAny(123); // result์˜ ํƒ€์ž…์ด any๊ฐ€ ๋˜์–ด๋ฒ„๋ฆผ. ์ˆซ์ž์ธ์ง€ ๋ฌธ์ž์—ด์ธ์ง€ ์ถ”์ ์ด ์•ˆ ๋จ
  • [๋ฐฉ๋ฒ• 3] ์ œ๋„ค๋ฆญ์„ ์“ด๋‹ค โœ… ```typescript // ๋Š” "์–ด๋–ค ํƒ€์ž…์ด๋“  ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋นˆ ๊ณต๊ฐ„"์ด๋ผ๋Š” ๋œป function echo(item: T): T { return item; }

    const r1 = echo(100); // T๋Š” number๊ฐ€ ๋จ const r2 = echo("Hi"); // T๋Š” string์ด ๋จ ```

7.1.2. ํ™œ์šฉ : API ์‘๋‹ต ์ฒ˜๋ฆฌ ๐ŸŒ

interface Response<T> {
  data: T;           // ์‹ค์ œ ๋ฐ์ดํ„ฐ์˜ ํƒ€์ž…์€ ๊ทธ๋•Œ๊ทธ๋•Œ ๋‹ค๋ฆ„!
  status: number;
  message: string;
}

// ์œ ์ € ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ๋•Œ
const userRes: Response<{ name: string }> = {
  data: { name: "์ฒ ์ˆ˜" },
  status: 200,
  message: "์„ฑ๊ณต"
};

// ์ƒํ’ˆ ๋ชฉ๋ก์„ ๋ฐ›์„ ๋•Œ
const itemRes: Response<string[]> = {
  data: ["์‚ฌ๊ณผ", "๋ฐ”๋‚˜๋‚˜"],
  status: 200,
  message: "์„ฑ๊ณต"
};

7.2. ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜์™€ ์ธํ„ฐํŽ˜์ด์Šค ๐Ÿ› ๏ธ

7.2.1. ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜ (Generic Functions) โš™๏ธ

ํ•จ์ˆ˜ ์ด๋ฆ„ ๋’ค์— <T>๋ฅผ ๋ถ™์—ฌ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ T๋Š” ๊ด€์Šต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ด๋ฆ„์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

// ๊ด„ํ˜ธ ์•ž <T>๋Š” ์ด ํ•จ์ˆ˜์—์„œ T๋ผ๋Š” ํƒ€์ž… ๋ณ€์ˆ˜๋ฅผ ์“ฐ๊ฒ ๋‹ค๋Š” ์„ ์–ธ์ž…๋‹ˆ๋‹ค.
function getFirstElement<T>(arr: T[]): T {
  return arr[0];
}

// ์‚ฌ์šฉ ์‹œ์ ์— ํƒ€์ž…์„ ๋ช…์‹œํ•˜๊ฑฐ๋‚˜, TS๊ฐ€ ์ถ”๋ก ํ•˜๊ฒŒ ๋‘ก๋‹ˆ๋‹ค.
const str = getFirstElement<string>(["A", "B"]); // T๋Š” string
const num = getFirstElement([1, 2, 3]);           // T๋Š” number (์ž๋™ ์ถ”๋ก )

7.2.2. ์ œ๋„ค๋ฆญ ์ธํ„ฐํŽ˜์ด์Šค (Generic Interfaces) ํ…œํ”Œ๋ฆฟ ๐Ÿ“‘

์ธํ„ฐํŽ˜์ด์Šค ์ด๋ฆ„ ๋’ค์— <T>๋ฅผ ๋ถ™์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ๊ฐ™์€๋ฐ ๋‚ด๋ถ€ ๊ฐ’์˜ ํƒ€์ž…๋งŒ ๋ฐ”๋€” ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

// ์–ด๋–ค ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ(T)๋“  ๋‹ด์„ ์ˆ˜ ์žˆ๋Š” ๋ฐ•์Šค ์„ค๊ณ„๋„
interface Box<T> {
  label: string;
  content: T;
}

const stringBox: Box<string> = { label: "ํŽธ์ง€", content: "์•ˆ๋…•ํ•˜์„ธ์š”!" };
const numberBox: Box<number> = { label: "๋‚˜์ด", content: 25 };

7.2.3. HTTP ์‘๋‹ต ์ฒ˜๋ฆฌ ์˜ˆ์ œ ๐Ÿ“ก

interface ApiResponse<T> {
  success: boolean;
  data: T;          // ์—ฌ๊ธฐ์— ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ฌ์ง€ ์ œ๋„ค๋ฆญ์œผ๋กœ ๊ฒฐ์ •!
  error?: string;
}

interface User { id: number; name: string; }

function fetchUser(): ApiResponse<User> {
  return { success: true, data: { id: 1, name: "์ฒ ์ˆ˜" } };
}

const response = fetchUser();
console.log(response.data.name); // 'User' ํƒ€์ž…์ž„์„ ์•Œ๊ธฐ์— ์ž๋™์™„์„ฑ ๊ฐ€๋Šฅ!

7.2.4. ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ œ๋„ค๋ฆญ ๋ณ€์ˆ˜ ์‚ฌ์šฉ (<T, U, K>) ๐Ÿ–‡๏ธ

์„œ๋กœ ๋‹ค๋ฅธ ํƒ€์ž…์„ ๊ฒฐํ•ฉํ•˜๊ฑฐ๋‚˜ ๊ด€๊ณ„๋ฅผ ๋งบ์–ด์ค„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

function merge<T, U>(objA: T, objB: U): T & U {
  return { ...objA, ...objB };
}

const combined = merge({ name: '์ฒ ์ˆ˜' }, { age: 25 });
console.log(combined.name); // OK
console.log(combined.age);  // OK

7.2.5. ์™œ T, U ์ธ๊ฐ€์š”? ๐Ÿ“

๋ณดํ†ต ๊ด€์Šต์ ์œผ๋กœ ์•„๋ž˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. - T: Type (์ฒซ ๋ฒˆ์งธ) - U: Usually (T ๋‹ค์Œ ๋ฌธ์ž) - K: Key - V: Value - E: Element


7.3. ์ œ๋„ค๋ฆญ ์ œ์•ฝ ์กฐ๊ฑด (Constraints) โš“

7.3.1. ์™œ ์ œ์•ฝ ์กฐ๊ฑด์ด ํ•„์š”ํ•œ๊ฐ€? (๋ฌธ์ œ ์ƒํ™ฉ) โš ๏ธ

๋‹จ์ˆœํžˆ <T>๋งŒ ์“ฐ๋ฉด TS๋Š” ์–ด๋–ค ์†์„ฑ์ด ๋“ค์–ด์˜ฌ์ง€ ๋ชฐ๋ผ ์—๋Ÿฌ๋ฅผ ๋ƒ…๋‹ˆ๋‹ค.

function printLength<T>(item: T) {
  // โŒ ์—๋Ÿฌ: 'T' ํ˜•์‹์— 'length' ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
  console.log(item.length); 
}

7.3.2. extends๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ๐Ÿค

"T๋Š” ์ ์–ด๋„ ํŠน์ • ์†์„ฑ์„ ๊ฐ€์ง„ ํƒ€์ž…์ด์–ด์•ผ ํ•ด"๋ผ๊ณ  ๊ณ„์•ฝ์„œ๋ฅผ ์“ฐ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

interface LengthWise { length: number; }

// T๋Š” ๋ฐ˜๋“œ์‹œ LengthWise ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ์กฑํ•ด์•ผ ํ•จ
function printLength<T extends LengthWise>(item: T): void {
  console.log(`๊ธธ์ด: ${item.length}`); // โœ… ์ด์ œ ์•ˆ์‹ฌํ•˜๊ณ  ์‚ฌ์šฉ ๊ฐ€๋Šฅ!
}

printLength("Hello");   // โœ… OK
printLength([1, 2, 3]); // โœ… OK
// printLength(123);    // โŒ ์—๋Ÿฌ! (์ˆซ์ž๋Š” length๊ฐ€ ์—†์Œ)

7.3.3. ํŠน์ • ์†์„ฑ ์ถ”์ถœ (keyof) ๐Ÿ”‘

"K๋Š” ๋ฐ˜๋“œ์‹œ T์˜ ํ‚ค(Key) ์ค‘ ํ•˜๋‚˜์—ฌ์•ผ ํ•œ๋‹ค"๋Š” ์ œ์•ฝ์ž…๋‹ˆ๋‹ค.

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const person = { name: "์ฒ ์ˆ˜", age: 25 };

getProperty(person, "name"); // โœ… OK
// getProperty(person, "hobby"); // โŒ ์—๋Ÿฌ! (๊ฐ์ฒด์— hobby ํ‚ค๊ฐ€ ์—†์Œ)

8. ์œ ํ‹ธ๋ฆฌํ‹ฐ (Utility Types & Modules) ๐Ÿ› ๏ธ

๋‚ด์šฉ ํŽผ์น˜๊ธฐ / ์ ‘๊ธฐ

์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…(Utility Types)์€ ์ด๋ฏธ ์ •์˜ํ•ด๋‘” ํƒ€์ž…์„ ์ƒํ™ฉ์— ๋งž๊ฒŒ ๋ณ€ํ˜•ํ•ด์„œ ์“ฐ๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” 'ํƒ€์ž… ์ „์šฉ ๋„๊ตฌ'์ž…๋‹ˆ๋‹ค.

8.1. ์ฃผ์š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ๐Ÿงฐ

8.1.1. Partial<T> : ๋ชจ๋“  ์†์„ฑ์„ ์„ ํƒ ์‚ฌํ•ญ์œผ๋กœ! โ“

๊ธฐ์กด ํƒ€์ž…์—์„œ ์›ํ•˜๋Š” ์†์„ฑ๋งŒ ๊ณจ๋ผ์„œ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. Partial<User>๊ฐ€ ๋ถ™์€ ๊ณณ์—๋Š” ์†์„ฑ ์ค‘ ์ผ๋ถ€๋งŒ ๊ณจ๋ผ์„œ ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

interface User {
  id: number;
  name: string;
  age: number;
}

function updateUser(id: number, fieldsToUpdate: Partial<User>) {
  // ์‹ค์ œ ์—…๋ฐ์ดํŠธ ๋กœ์ง...
}

updateUser(1, { name: "์ƒˆ์ด๋ฆ„" }); // โœ… age๋‚˜ id๊ฐ€ ์—†์–ด๋„ ์—๋Ÿฌ ์•ˆ ๋‚จ!

8.1.2. Pick<T, K> : ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ์™์™! ๐Ÿ“

๊ธฐ์กด ํƒ€์ž…์—์„œ ์›ํ•˜๋Š” ์†์„ฑ๋งŒ ๊ณจ๋ผ์„œ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

// User์—์„œ 'name'๊ณผ 'age'๋งŒ ๊ณจ๋ผ์„œ ์“ฐ๊ณ  ์‹ถ์„ ๋•Œ
type UserPreview = Pick<User, "name" | "age">;

const profile: UserPreview = {
  name: "์ฒ ์ˆ˜",
  age: 20
};
// profile.id; // โŒ ์—๋Ÿฌ! Pick์œผ๋กœ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์Œ

8.1.3. Omit<T, K> : ํŠน์ • ๊ฒƒ๋งŒ ๋นผ๊ณ  ์ „๋ถ€! โœ‚๏ธ

๊ธฐ์กด ํƒ€์ž…์—์„œ ํŠน์ • ์†์„ฑ๋งŒ ์ œ๊ฑฐํ•˜๊ณ  ๋‚˜๋จธ์ง€๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

// User์—์„œ 'id'๋งŒ ๋นผ๊ณ  ๋‚˜๋จธ์ง€๋Š” ๋‹ค ์“ฐ๊ณ  ์‹ถ์„ ๋•Œ
type NewUser = Omit<User, "id">;

const newUser: NewUser = {
  name: "์˜ํฌ",
  age: 27
  // id ์†์„ฑ์€ ๋„ฃ์œผ๋ฉด ์˜คํžˆ๋ ค ์—๋Ÿฌ๋‚จ!
};

8.1.4. Readonly<T> : ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ! ๐Ÿ”’

๋ชจ๋“  ์†์„ฑ์„ ์ˆ˜์ • ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

const frozenUser: Readonly<User> = { id: 1, name: "์ฒ ์ˆ˜", age: 20 };
// frozenUser.name = "๊ธธ๋™"; // โŒ ์—๋Ÿฌ! ์ฝ๊ธฐ ์ „์šฉ์ž„

8.1.5. Record<K, T> : ํ‚ค-๊ฐ’ ํ˜•ํƒœ์˜ ๊ฐ์ฒด ํƒ€์ž… ๋งคํ•‘ ๐Ÿ—บ๏ธ

"ํŠน์ • ํ‚ค(K)๋“ค์„ ๋ชจ์•„์„œ ๊ฐ’(T)์˜ ํƒ€์ž…์„ ๊ณ ์ •ํ•˜๊ณ  ์‹ถ์„ ๋•Œ" ์‚ฌ์šฉํ•˜๋ฉฐ, ๊ฐ์ฒด์˜ '์ง€๋„(Map)'๋‚˜ '์‚ฌ์ „'์„ ๋งŒ๋“ค ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

8.1.5.1. ๊ธฐ๋ณธ ๋ฌธ๋ฒ• ๐Ÿ“
// Record<ํ‚ค_ํƒ€์ž…, ๊ฐ’_ํƒ€์ž…>
const scores: Record<string, number> = {
  "math": 100,
  "english": 90,
};
8.1.5.2. ์‹ค๋ฌด ์˜ˆ์ œ: ์ •ํ•ด์ง„ ์นดํ…Œ๊ณ ๋ฆฌ ๊ด€๋ฆฌ ๐Ÿ—‚๏ธ

ํŠน์ • ๋ฌธ์ž์—ด ์ง‘ํ•ฉ์„ ํ‚ค๋กœ ๊ณ ์ •ํ•  ๋•Œ ์ง„์งœ ์œ„๋ ฅ์„ ๋ฐœํœ˜ํ•ฉ๋‹ˆ๋‹ค.

type Page = "home" | "about" | "contact";

interface PageInfo {
  title: string;
}

// "home", "about", "contact" ์™ธ์˜ ํ‚ค๋Š” ํ—ˆ์šฉํ•˜์ง€ ์•Š์Œ!
const nav: Record<Page, PageInfo> = {
  home: { title: "ํ™ˆ" },
  about: { title: "์†Œ๊ฐœ" },
  contact: { title: "์—ฐ๋ฝ์ฒ˜" },
};

// nav.blog = { title: "๋ธ”๋กœ๊ทธ" }; // โŒ ์—๋Ÿฌ! Page ํƒ€์ž…์— 'blog'๊ฐ€ ์—†์Œ
8.1.5.3. Record์™€ ๋‹ค๋ฅธ ์œ ํ‹ธ๋ฆฌํ‹ฐ ์กฐํ•ฉ ๐Ÿค

๊ฐ์ฒด์˜ ๊ทœ๊ฒฉ์„ ๋”ฑ ์ •ํ•ด๋ฒ„๋ฆฌ๋Š” ๋„๊ตฌ๋กœ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.

// ์œ ์ €๋“ค์˜ ID(number)๋ฅผ ํ‚ค๋กœ ํ•˜๊ณ , ์œ ์ € ์ •๋ณด(User)๋ฅผ ๊ฐ’์œผ๋กœ ํ•˜๋Š” ๊ฐ์ฒด
const userList: Record<number, User> = {
  101: { id: 101, name: "์ฒ ์ˆ˜", age: 20 },
  102: { id: 102, name: "์˜ํฌ", age: 25 },
};

8.2. ๋ชจ๋“ˆ ์‹œ์Šคํ…œ ๐Ÿ“ฆ

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๋ชจ๋“ˆ ์‹œ์Šคํ…œ์€ ์ฝ”๋“œ๋ฅผ ์—ฌ๋Ÿฌ ํŒŒ์ผ๋กœ ๋‚˜๋ˆ„๊ณ  ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” '๋ถ€ํ’ˆ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ'์ž…๋‹ˆ๋‹ค.

8.2.1. ๋‚ด๋ณด๋‚ด๊ธฐ (Export) ๐Ÿ“ค

๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ์ด๋ฆ„ํ‘œ๋ฅผ ๋ถ™์—ฌ์ฃผ๋Š” ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. - ์ด๋ฆ„ ๋ถ™์—ฌ ๋‚ด๋ณด๋‚ด๊ธฐ (Named Export): ํ•œ ํŒŒ์ผ์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋‚ด๋ณด๋‚ผ ๋•Œ - ๊ธฐ๋ณธ ๋‚ด๋ณด๋‚ด๊ธฐ (Default Export): ํ•œ ํŒŒ์ผ์˜ '๋Œ€ํ‘œ'๋ฅผ ํ•˜๋‚˜๋งŒ ๋‚ด๋ณด๋‚ผ ๋•Œ

// math.ts
export const PI = 3.14; // Named Export

export default function add(a: number, b: number) { // Default Export
  return a + b;
}

8.2.2. ๊ฐ€์ ธ์˜ค๊ธฐ (Import) ๐Ÿ“ฅ

๋‹ค๋ฅธ ํŒŒ์ผ์— ์žˆ๋Š” ๋ถ€ํ’ˆ์„ ๊ฐ€์ ธ์˜ค๋Š” ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

// app.ts
import add, { PI } from './math'; // Default๋Š” ์ค‘๊ด„ํ˜ธ ์—†์ด, Named๋Š” ์ค‘๊ด„ํ˜ธ ์•ˆ์—!

console.log(add(1, 2)); // 3
console.log(PI);        // 3.14

8.2.3. ํƒ€์ž… ์ „์šฉ ๋ชจ๋“ˆ (Import Type) ๐Ÿ—๏ธ

์‹คํ–‰ ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹Œ '์„ค๊ณ„๋„(ํƒ€์ž…)'๋งŒ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

// types.ts
export interface User { id: number; name: string; }

// main.ts
import type { User } from './types'; // "๋‚˜๋Š” ํƒ€์ž…๋งŒ ์“ธ ๊ฑฐ์•ผ"๋ผ๊ณ  ๋ช…์‹œ

const user: User = { id: 1, name: "์ฒ ์ˆ˜" };

9 tsconfig.json ํ•ต์‹ฌ ์˜ต์…˜๋“ค

๋‚ด์šฉ ํŽผ์น˜๊ธฐ / ์ ‘๊ธฐ

tsconfig.json์€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ์ปดํŒŒ์ผ ํ™˜๊ฒฝ์„ ์„ค์ •ํ•˜๋Š” ํ•ต์‹ฌ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. ์‹ค๋ฌด์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ์กฐ์ •ํ•˜๋Š” 5๊ฐ€์ง€ ํ•ต์‹ฌ ์˜ต์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

9.1. ์ฃผ์š” ์ปดํŒŒ์ผ ์˜ต์…˜

  • target: ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์–ด๋–ค ๋ฒ„์ „์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋ณ€ํ™˜ํ• ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. (์˜ˆ: ES6, ESNext)
  • module: ๋ชจ๋“ˆ ์‹œ์Šคํ…œ ๋ฐฉ์‹์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. (ํ”„๋ก ํŠธ์—”๋“œ ์ตœ์‹  ๋„๊ตฌ๋Š” ์ฃผ๋กœ ESNext, Node.js๋Š” CommonJS)
  • strict: ๋ชจ๋“  ํƒ€์ž… ๊ฒ€์‚ฌ ์˜ต์…˜์„ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. ์‹ค๋ฌด์—์„œ๋Š” true ์„ค์ •์„ ๊ฐ•๋ ฅํžˆ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.
  • outDir: ์ปดํŒŒ์ผ๋œ ๊ฒฐ๊ณผ๋ฌผ(.js)์ด ์ €์žฅ๋  ํด๋”๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • paths: ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ ์ ˆ๋Œ€ ๊ฒฝ๋กœ(์˜ˆ: @/components/...)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ค์ •ํ•˜์—ฌ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.

9.2. ์‹ค๋ฌด์šฉ ์ถ”์ฒœ ์„ค์ • ์˜ˆ์‹œ

{
  "compilerOptions": {
    "target": "ESNext",               // ์ตœ์‹  JS ๋ฒ„์ „์œผ๋กœ ๋ณ€ํ™˜
    "module": "ESNext",               // ์ตœ์‹  ๋ชจ๋“ˆ ์‹œ์Šคํ…œ ์‚ฌ์šฉ
    "strict": true,                   // ์—„๊ฒฉํ•œ ํƒ€์ž… ๊ฒ€์‚ฌ ํ™œ์„ฑํ™” (ํ•„์ˆ˜)
    "outDir": "./dist",               // ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์€ dist ํด๋”์— ์ €์žฅ
    "rootDir": "./src",               // ์†Œ์Šค ์ฝ”๋“œ๋Š” src ํด๋”์—์„œ ์‹œ์ž‘
    "baseUrl": ".",                   // ๊ฒฝ๋กœ ํ•ด์„์˜ ๊ธฐ์ค€์ 
    "paths": {
      "@/*": ["src/*"]                // @/ ๊ธฐํ˜ธ๋กœ src ํด๋” ์ ‘๊ทผ ํ—ˆ์šฉ
    },
    "esModuleInterop": true,          // CommonJS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜ธํ™˜์„ฑ ํ—ˆ์šฉ
    "skipLibCheck": true,             // ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํƒ€์ž… ๊ฒ€์‚ฌ ์ƒ๋žต (๋นŒ๋“œ ์†๋„ ํ–ฅ์ƒ)
    "forceConsistentCasingInFileNames": true // ํŒŒ์ผ ์ด๋ฆ„์˜ ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ๊ฐ•์ œ
  },
  "include": ["src/**/*"],            // src ํด๋” ๋‚ด ๋ชจ๋“  ํŒŒ์ผ ํฌํ•จ
  "exclude": ["node_modules"]         // ๊ฒ€์‚ฌ์—์„œ ์ œ์™ธํ•  ํด๋”
}

9.3. ์š”์•ฝ

  • ์„ค๊ณ„: target๊ณผ module๋กœ ๊ฒฐ๊ณผ๋ฌผ์˜ ํ˜ธํ™˜์„ฑ์„ ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ์•ˆ์ „: strict๋ฅผ ํ†ตํ•ด ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ตฌ์กฐ: rootDir, outDir, paths๋กœ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.