๐Ÿ’ก ์œ ์—ฐํ•˜๊ณ ! ํƒ€์ž…์ด ๋ณด์žฅ๋˜๊ณ , ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’์€ ์ œ๋„ค๋ฆญ(Generics)!!

์ œ๋„ค๋ฆญ์ด๋ž€?

์„ ์–ธ ์‹œ์ ์ด ์•„๋‹ˆ๋ผ ์ƒ์„ฑ ์‹œ์ ์— ํƒ€์ž…์„ ๋ช…์‹œํ•˜์—ฌ ํ•˜๋‚˜์˜ ํƒ€์ž…๋งŒ์ด ์•„๋‹Œ ๋‹ค์–‘ํ•œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ธฐ๋ฒ•

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



์ œ๋„ค๋ฆญ ๊ธฐ๋ณธ ๋ฌธ๋ฒ•

โœ”๏ธ ๊ธฐ๋ณธ ์ฝ”๋“œ

// text๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์— ๊ฐ’์„ ๋„˜๊ฒจ ๋ฐ›์•„ text๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
const getText = (text) => {
  return text;
};

// ํŒŒ๋ผ๋ฏธํ„ฐ์— ์–ด๋–ค ๊ฐ’์„ ๋„˜๊ฒจ์ฃผ๋”๋ผ๋„ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.
getText("april"); // 'april'
getText(20); // 10
getText(true); // true


โœ”๏ธ ์ œ๋„ค๋ฆญ ์ฝ”๋“œ

// ์ œ๋„ค๋ฆญ ๊ธฐ๋ณธ ๋ฌธ๋ฒ•์ด ์ ์šฉ๋œ ํ•จ์ˆ˜
const getText = <T>(text: T): T => {
  return text;
};

// ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, ํ•จ์ˆ˜ ์•ˆ์—์„œ ์‚ฌ์šฉํ•  ํƒ€์ž…์„ ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋‹ค
getText<string>("april"); // 'april'
getText<number>(20); // 10
getText<boolean>(true); // true


โœ”๏ธ ๋‘ ๊ฐœ ์ด์ƒ์˜ ์ œ๋„ค๋ฆญ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ

// ๋‘ ๊ฐœ์˜ ์ œ๋„ค๋ฆญ์ด ์ ์šฉ๋œ ํ•จ์ˆ˜
const getUserInfo = <T, K>(name: T, age: K): [T, K] => {
  return [name, age];
};

// ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, ํ•จ์ˆ˜ ์•ˆ์—์„œ ์‚ฌ์šฉํ•  ํƒ€์ž…์„ ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋‹ค
getUserInfo("april", 20);



์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ? ๐Ÿค”

ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ํƒ€์ž…์ด ์—ฌ๋Ÿฌ ์ข…๋ฅ˜์ผ ๋•Œ any๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, any ํƒ€์ž…์€ ํƒ€์ž… ๊ฒ€์‚ฌ๋ฅผ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค

์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์ฝ”๋“œ

const getText = (text: any): any => {
  return text;
};


์ œ๋„ค๋ฆญ ์ ์šฉ ์ฝ”๋“œ

๋จผ์ € ํ•จ์ˆ˜์˜ ์ด๋ฆ„ ๋ฐ”๋กœ ๋’ค์— <T> ๋ผ๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ํ•จ์ˆ˜์˜ ์ธ์ž์™€ ๋ฐ˜ํ™˜ ๊ฐ’์— ๋ชจ๋‘ T ๋ผ๋Š” ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•˜๋ฉด, ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋„˜๊ธด ํƒ€์ž…์— ๋Œ€ํ•ด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ถ”์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

๐Ÿ’ก ๋”ฐ๋ผ์„œ, ํ•จ์ˆ˜์˜ ์ž…๋ ฅ ๊ฐ’์— ๋Œ€ํ•œ ํƒ€์ž…๊ณผ ์ถœ๋ ฅ ๊ฐ’์— ๋Œ€ํ•œ ํƒ€์ž…์ด ๋™์ผํ•œ์ง€ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

const getText = <T>(text: T): T => {
  return text;
};



์ œ๋„ค๋ฆญ ์˜ˆ์ œ ์ฝ”๋“œ

๋ฐฐ์—ด์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋กœ ์ตํžˆ๋Š”, ์ œ๋„ค๋ฆญ ์˜ˆ์ œ ์ฝ”๋“œ

โœ”๏ธ ์ œ๋„ค๋ฆญ ์‚ฌ์šฉ ์ „

์ˆซ์ž ๋ฐฐ์—ด์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜์™€ ๋ฌธ์ž ๋ฐฐ์—ด์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜ ๐Ÿ˜ณ ์ค‘๋ณต๋œ ์ฝ”๋“œ๊ฐ€ ๋งŽ๋‹คโ€ฆ ๐Ÿ˜ฌ๐Ÿ˜ฎโ€๐Ÿ’จ

const makeNumberArray = (defaultValue: number, size: number): number[] => {
  const arr: number[] = [];
  for (let i = 0; i < size; i++) {
    arr.push(defaultValue);
  }
  return arr;
};

const makeStringArray = (defaultValue: string, size: number): string[] => {
  const arr: string[] = [];
  for (let i = 0; i < size; i++) {
    arr.push(defaultValue);
  }
  return arr;
};

const arr1 = makeNumberArray(1, 10);
const arr2 = makeStringArray("empty", 10);


โœ”๏ธ ์ œ๋„ค๋ฆญ์œผ๋กœ ์ฝ”๋“œ ๋ฆฌํŽ™ํ† ๋ง ํ•˜๊ธฐ

const makeArray = <T>(defaultValue: T, size: number): T[] => {
  const arr: T[] = [];
  for (let i = 0; i < size; i++) {
    arr.push(defaultValue);
  }
  return arr;
};

const arr1 = makeArray<number>(1, 10);
const arr2 = makeArray<string>("empty", 5);

// makeArray ํ•จ์ˆ˜์˜ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์•Œ๋ฉด ํƒ€์ž… T๋„ ์•Œ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—,
// ํ˜ธ์ถœ ์‹œ ํƒ€์ž… T์˜ ์ •๋ณด๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ „๋‹ฌํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
const arr3 = makeArray(1, 10);
const arr4 = makeArray(1, 10);



extends ํ‚ค์›Œ๋“œ๋กœ ์ œ๋„ค๋ฆญ ํƒ€์ž… ์ œํ•œํ•˜๊ธฐ

๋ฆฌ์•กํŠธ์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ API๋Š” ์ž…๋ ฅ ๊ฐ€๋Šฅํ•œ ๊ฐ’์˜ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฆฌ์•กํŠธ์˜ ์†์„ฑ๊ฐ’ ์ „์ฒด๋Š” ๊ฐ์ฒด ํƒ€์ž…๋งŒ ํ—ˆ์šฉ๋œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ์ œ๋„ค๋ฆญ์€ **ํƒ€์ž…์˜ ์ข…๋ฅ˜๋ฅผ ์ œํ•œ**ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.


โœ”๏ธ ์˜ˆ์ œ ์ฝ”๋“œ

// ์ œ๋„ค๋ฆญ T ํƒ€์ž…์„ number | string์— ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ํƒ€์ž…์œผ๋กœ ์ œํ•œ
const identity = <T extends number>(p1: T): T => {
  return p1;
};

identity(1);
identity("a");
identity([]); // ํƒ€์ž… ์—๋Ÿฌ


โœ”๏ธ ์˜ˆ์ œ ์ฝ”๋“œ

// ์ œ๋„ค๋ฆญ T ํƒ€์ž…์„ number | string์— ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ํƒ€์ž…์œผ๋กœ ์ œํ•œ
const identity = <T extends number>(p1: T): T => {
  return p1;
};

identity(1);
identity("a");
identity([]); // ํƒ€์ž… ์—๋Ÿฌ


โœ”๏ธ ์˜ˆ์ œ ์ฝ”๋“œ2

interface IPerson {
  name: string;
  age: number;
}

interface IKorean extends IPerson {
  liveInSeoul: boolean;
}

// ์ œ๋„ค๋ฆญ T๋Š” IPerson์— ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ํƒ€์ž…์ด์–ด์•ผ ํ•œ๋‹ค
// ์ œ๋„ค๋ฆญ K๋Š” IPerson์˜ ์†์„ฑ ์ด๋ฆ„์ด์–ด์•ผ ํ•œ๋‹ค.
// ์ฐธ๊ณ ๋กœ keyof๋Š” ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ชจ๋“  ์†์„ฑ ์ด๋ฆ„์„ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค์–ด ์ค€๋‹ค.
const swapProperty = <T extends IPerson, K extends keyof IPerson>(
  p1: T,
  p2: T,
  name: K
): void => {
  const temp = p1[name];
  p1[name] = p2[name];
  p2[name] = temp;
};

const p1: IKorean = {
  name: "april",
  age: 20,
  liveInSeoul: true,
};

const p2: IKorean = {
  name: "yrkim",
  age: 20,
  liveInSeoul: true,
};

swapProperty(p1, p2, "age"); // p1, p2๋Š” IPerson์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํƒ€์ž…์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค