๐ŸŽญ Union Types: ์—ฌ๋Ÿฌ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜๋กœ ์ •์˜ํ•˜๊ธฐ ๐ŸŽญ

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽญ Union Types: ์—ฌ๋Ÿฌ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜๋กœ ์ •์˜ํ•˜๊ธฐ ๐ŸŽญ

 

 

์•ˆ๋…•, ์นœ๊ตฌ๋“ค! ์˜ค๋Š˜์€ TypeScript์˜ ๊ฟ€์žผ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ธ Union Types์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ๐Ÿฏ Union Types๋ผ๊ณ  ํ•˜๋ฉด ๋ญ”๊ฐ€ ๋…ธ๋™์กฐํ•ฉ ๊ฐ™์€ ๋Š๋‚Œ์ด ๋“ค์ง€ ์•Š์•„? ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” Union์€ ๊ทธ๋Ÿฐ ๊ฒŒ ์•„๋‹ˆ๋ผ, ์—ฌ๋Ÿฌ ํƒ€์ž…์„ ํ•˜๋‚˜๋กœ '๊ฒฐํ•ฉ'ํ•œ๋‹ค๋Š” ์˜๋ฏธ์•ผ. ๋งˆ์น˜ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์žฌ๋Šฅ์„ ๊ฐ€์ง„ ์‚ฌ๋žŒ๋“ค์ด ๋ชจ์—ฌ ์žˆ๋Š” ์žฌ๋Šฅ๋„ท์ฒ˜๋Ÿผ ๋ง์ด์ง€! ๐Ÿ˜‰

๐ŸŽจ Union Types๋ž€? ์—ฌ๋Ÿฌ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐฉ๋ฒ•์ด์•ผ. ์‰ฝ๊ฒŒ ๋งํ•ด, "์ด ๋ณ€์ˆ˜๋Š” ๋ฌธ์ž์—ด์ผ ์ˆ˜๋„ ์žˆ๊ณ , ์ˆซ์ž์ผ ์ˆ˜๋„ ์žˆ์–ด!"๋ผ๊ณ  ๋งํ•˜๋Š” ๊ฑฐ์ง€.

์ž, ์ด์ œ๋ถ€ํ„ฐ Union Types์˜ ์„ธ๊ณ„๋กœ ๋น ์ ธ๋ณผ๊นŒ? ์ค€๋น„๋์–ด? ๊ทธ๋Ÿผ ์ถœ๋ฐœ! ๐Ÿš€

๐ŸŒˆ Union Types์˜ ๊ธฐ๋ณธ

Union Types๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜์ผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์„ ์–ธํ•  ์ˆ˜ ์žˆ์–ด. ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ์“ฐ๋ƒ๊ณ ? ๊ฐ„๋‹จํ•ด! ํŒŒ์ดํ”„(|) ๊ธฐํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ผ.

let myVariable: string | number;

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด myVariable์€ ๋ฌธ์ž์—ด์ด๋‚˜ ์ˆซ์ž ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด. coolํ•˜์ง€? ๐Ÿ˜Ž

์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊นŒ? ์žฌ๋Šฅ๋„ท์—์„œ ์‚ฌ์šฉ์ž์˜ ID๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž. ์‚ฌ์šฉ์ž ID๋Š” ๋ฌธ์ž์—ด์ผ ์ˆ˜๋„ ์žˆ๊ณ , ์ˆซ์ž์ผ ์ˆ˜๋„ ์žˆ์–ด.

let userId: string | number;

userId = 123; // OK
userId = "abc123"; // ์ด๊ฒƒ๋„ OK
userId = true; // ์—๋Ÿฌ! boolean์€ ์•ˆ ๋ผ์š”.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด userId๋Š” ๋ฌธ์ž์—ด์ด๋‚˜ ์ˆซ์ž๋งŒ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ณ , ๋‹ค๋ฅธ ํƒ€์ž…์„ ํ• ๋‹นํ•˜๋ ค๊ณ  ํ•˜๋ฉด TypeScript๊ฐ€ "์•ผ, ๊ทธ๊ฑด ์•ˆ ๋ผ!"๋ผ๊ณ  ๋งํ•ด์ค„ ๊ฑฐ์•ผ.

๐Ÿšจ ์ฃผ์˜์‚ฌํ•ญ: Union Types๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•ด๋‹น ํƒ€์ž…๋“ค์˜ ๊ณตํ†ต ๋ฉ”์„œ๋“œ๋‚˜ ์†์„ฑ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด, string | number ํƒ€์ž…์˜ ๋ณ€์ˆ˜์—์„œ๋Š” toString() ๋ฉ”์„œ๋“œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, toUpperCase() ๊ฐ™์€ ๋ฌธ์ž์—ด ์ „์šฉ ๋ฉ”์„œ๋“œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด.

์ด์ œ ๊ธฐ๋ณธ์€ ์•Œ์•˜์œผ๋‹ˆ, ์ข€ ๋” ๊นŠ์ด ๋“ค์–ด๊ฐ€๋ณผ๊นŒ? ๐ŸŠโ€โ™‚๏ธ

๐ŸŽญ Union Types์™€ ํ•จ์ˆ˜

Union Types๋Š” ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋‚˜ ๋ฐ˜ํ™˜ ๊ฐ’์—๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•จ์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ ํƒ€์ž…์˜ ์ž…๋ ฅ์„ ๋ฐ›๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ ํƒ€์ž…์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์ง€.

์˜ˆ๋ฅผ ๋“ค์–ด, ์žฌ๋Šฅ๋„ท์—์„œ ์‚ฌ์šฉ์ž์˜ ํ”„๋กœํ•„ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž. ์‚ฌ์šฉ์ž ID๋กœ ๋ฌธ์ž์—ด์ด๋‚˜ ์ˆซ์ž๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ณ  ์‹ถ์–ด.

function getUserProfile(userId: string | number): object {
    // ์‚ฌ์šฉ์ž ํ”„๋กœํ•„์„ ๊ฐ€์ ธ์˜ค๋Š” ๋กœ์ง
    return {
        id: userId,
        name: "ํ™๊ธธ๋™",
        skills: ["ํ”„๋กœ๊ทธ๋ž˜๋ฐ", "๋””์ž์ธ", "๋งˆ์ผ€ํŒ…"]
    };
}

console.log(getUserProfile(123)); // OK
console.log(getUserProfile("user456")); // ์ด๊ฒƒ๋„ OK

์ด ํ•จ์ˆ˜๋Š” ๋ฌธ์ž์—ด์ด๋‚˜ ์ˆซ์ž ํ˜•ํƒœ์˜ userId๋ฅผ ๋ฐ›์•„์„œ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด. ์–ด๋–ค ํ˜•ํƒœ์˜ ID๋ฅผ ์‚ฌ์šฉํ•˜๋“  ํ•จ์ˆ˜๊ฐ€ ์ž˜ ์ž‘๋™ํ•œ๋‹ค๋Š” ๊ฑฐ์ง€. ๐Ÿ‘

๐Ÿ’ก ํŒ: ํ•จ์ˆ˜ ๋‚ด์—์„œ Union Type์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํƒ€์ž… ๊ฐ€๋“œ(Type Guard)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์•ˆ์ „ํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด. ํƒ€์ž… ๊ฐ€๋“œ์— ๋Œ€ํ•ด์„œ๋Š” ๋‚˜์ค‘์— ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ!

์ž, ์ด์ œ Union Types๊ฐ€ ํ•จ์ˆ˜์—์„œ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋˜๋Š”์ง€ ์•Œ์•˜์ง€? ๊ทผ๋ฐ ์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹ˆ์•ผ. Union Types๋Š” ๋” ๋ณต์žกํ•œ ์ƒํ™ฉ์—์„œ๋„ ์šฐ๋ฆฌ๋ฅผ ๊ตฌ์›ํ•ด์ค„ ์ˆ˜ ์žˆ์–ด! ๐Ÿฆธโ€โ™‚๏ธ

๐ŸŽจ ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…๊ณผ Union Types

Union Types์˜ ์ง„๊ฐ€๋Š” ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ ๋”์šฑ ๋น›์„ ๋ฐœํ•ด. ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์ด ๋ญ๋ƒ๊ณ ? ๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ ํŠน์ • ๊ฐ’ ์ž์ฒด๋ฅผ ํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์•ผ.

์˜ˆ๋ฅผ ๋“ค์–ด, ์žฌ๋Šฅ๋„ท์—์„œ ์‚ฌ์šฉ์ž์˜ ์—ญํ• ์„ ์ •์˜ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž. ์‚ฌ์šฉ์ž๋Š” "admin", "user", "guest" ์ค‘ ํ•˜๋‚˜์˜ ์—ญํ• ๋งŒ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์–ด.

type UserRole = "admin" | "user" | "guest";

let myRole: UserRole;
myRole = "admin"; // OK
myRole = "superuser"; // ์—๋Ÿฌ! "superuser"๋Š” UserRole์— ์—†์–ด์š”.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด myRole ๋ณ€์ˆ˜๋Š” ์˜ค์ง "admin", "user", "guest" ์ค‘ ํ•˜๋‚˜์˜ ๊ฐ’๋งŒ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์–ด. ๋‹ค๋ฅธ ๋ฌธ์ž์—ด์„ ํ• ๋‹นํ•˜๋ ค๊ณ  ํ•˜๋ฉด TypeScript๊ฐ€ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ๊ฑฐ์•ผ.

์ด๊ฑธ ํ™œ์šฉํ•ด์„œ ์žฌ๋Šฅ๋„ท์˜ ์‚ฌ์šฉ์ž ๊ด€๋ฆฌ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊นŒ?

function manageUser(userId: string | number, action: "ban" | "promote" | "demote"): void {
    console.log(`User ${userId} is being ${action}ed`);
    // ์‹ค์ œ ์‚ฌ์šฉ์ž ๊ด€๋ฆฌ ๋กœ์ง
}

manageUser("user123", "ban"); // OK
manageUser(456, "promote"); // OK
manageUser("admin789", "delete"); // ์—๋Ÿฌ! "delete"๋Š” ํ—ˆ์šฉ๋œ action์ด ์•„๋‹ˆ์—์š”.

์ด ํ•จ์ˆ˜๋Š” ์‚ฌ์šฉ์ž ID(๋ฌธ์ž์—ด ๋˜๋Š” ์ˆซ์ž)์™€ ์ˆ˜ํ–‰ํ•  ์ž‘์—…("ban", "promote", "demote" ์ค‘ ํ•˜๋‚˜)์„ ๋ฐ›์•„. ๋งŒ์•ฝ ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ์ž‘์—…์„ ์‹œ๋„ํ•˜๋ฉด TypeScript๊ฐ€ ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์ค˜. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‹ค์ˆ˜๋กœ ์ž˜๋ชป๋œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฑธ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์ง€!

๐ŸŽญ ์žฌ๋ฏธ์žˆ๋Š” ์‚ฌ์‹ค: ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…๊ณผ Union Types๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด, ๋งˆ์น˜ ์—ด๊ฑฐํ˜•(enum)์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ๋” ์œ ์—ฐํ•˜๊ณ , ๋•Œ๋กœ๋Š” ๋” ํƒ€์ž… ์•ˆ์ „ํ•ด!

์ž, ์ด์ œ Union Types์™€ ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์˜ ์กฐํ•ฉ์ด ์–ผ๋งˆ๋‚˜ ๊ฐ•๋ ฅํ•œ์ง€ ์•Œ๊ฒ ์ง€? ์ด๊ฑธ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ์„ ํฌ๊ฒŒ ๋†’์ผ ์ˆ˜ ์žˆ์–ด. ๊ทธ๋Ÿผ ์ด์ œ ์ข€ ๋” ๋ณต์žกํ•œ ์˜ˆ์ œ๋กœ ๋„˜์–ด๊ฐ€๋ณผ๊นŒ? ๐Ÿš€

๐Ÿงฉ ๊ฐ์ฒด ํƒ€์ž…๊ณผ Union Types

Union Types๋Š” ๋‹จ์ˆœํ•œ ๊ธฐ๋ณธ ํƒ€์ž…๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ณต์žกํ•œ ๊ฐ์ฒด ํƒ€์ž…์—๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ์ด๊ฑธ ์ด์šฉํ•˜๋ฉด ์ •๋ง ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์ง€.

์˜ˆ๋ฅผ ๋“ค์–ด, ์žฌ๋Šฅ๋„ท์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋“ฑ๋กํ•œ ์žฌ๋Šฅ์„ ๋‚˜ํƒ€๋‚ด๋Š” ํƒ€์ž…์„ ๋งŒ๋“ค์–ด๋ณด์ž. ์žฌ๋Šฅ์€ '๊ธฐ์ˆ  ์žฌ๋Šฅ'๊ณผ '์˜ˆ์ˆ  ์žฌ๋Šฅ' ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณผ๊ฒŒ.

type TechnicalSkill = {
    type: "technical";
    language: string;
    yearsOfExperience: number;
};

type ArtisticSkill = {
    type: "artistic";
    medium: string;
    style: string;
};

type Skill = TechnicalSkill | ArtisticSkill;

function describeSkill(skill: Skill): string {
    switch(skill.type) {
        case "technical":
            return `${skill.language} ๊ฐœ๋ฐœ์ž (${skill.yearsOfExperience}๋…„ ๊ฒฝ๋ ฅ)`;
        case "artistic":
            return `${skill.medium} ${skill.style} ์•„ํ‹ฐ์ŠคํŠธ`;
    }
}

const coding: Skill = { type: "technical", language: "TypeScript", yearsOfExperience: 3 };
const painting: Skill = { type: "artistic", medium: "์œ ํ™”", style: "์ธ์ƒ์ฃผ์˜" };

console.log(describeSkill(coding)); // "TypeScript ๊ฐœ๋ฐœ์ž (3๋…„ ๊ฒฝ๋ ฅ)"
console.log(describeSkill(painting)); // "์œ ํ™” ์ธ์ƒ์ฃผ์˜ ์•„ํ‹ฐ์ŠคํŠธ"

์—ฌ๊ธฐ์„œ Skill ํƒ€์ž…์€ TechnicalSkill๊ณผ ArtisticSkill์˜ Union Type์ด์•ผ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•˜๋‚˜์˜ Skill ๋ณ€์ˆ˜๊ฐ€ ๋‘ ๊ฐ€์ง€ ํƒ€์ž…์˜ ๊ฐ์ฒด ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์ง€.

๊ทธ๋ฆฌ๊ณ  describeSkill ํ•จ์ˆ˜๋Š” ์ด Union Type์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„์„œ ์ ์ ˆํ•œ ์„ค๋ช…์„ ๋ฐ˜ํ™˜ํ•ด. ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋Š” skill.type์„ ํ™•์ธํ•ด์„œ ์–ด๋–ค ์ข…๋ฅ˜์˜ ์žฌ๋Šฅ์ธ์ง€ ๊ตฌ๋ถ„ํ•˜๊ณ , ๊ทธ์— ๋งž๋Š” ์„ค๋ช…์„ ๋งŒ๋“ค์–ด๋‚ด์ง€.

๐Ÿ” ์ฃผ๋ชฉํ•  ์ : ์ด๋Ÿฐ ๋ฐฉ์‹์„ 'ํƒœ๊ทธ๋œ ์œ ๋‹ˆ์˜จ(Tagged Union)' ๋˜๋Š” 'ํŒ๋ณ„ ์œ ๋‹ˆ์˜จ(Discriminated Union)'์ด๋ผ๊ณ  ๋ถˆ๋Ÿฌ. type ์†์„ฑ์ด ์–ด๋–ค ์ข…๋ฅ˜์˜ ๊ฐ์ฒด์ธ์ง€ 'ํƒœ๊ทธ'ํ•˜๋Š” ์—ญํ• ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์ง€.

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

type UserProfile = {
    id: string | number;
    name: string;
    skills: Skill[];
};

const myProfile: UserProfile = {
    id: "user123",
    name: "๊น€์žฌ๋Šฅ",
    skills: [
        { type: "technical", language: "JavaScript", yearsOfExperience: 5 },
        { type: "artistic", medium: "๋””์ง€ํ„ธ ์•„ํŠธ", style: "๋ฏธ๋‹ˆ๋ฉ€๋ฆฌ์ฆ˜" }
    ]
};

myProfile.skills.forEach(skill => console.log(describeSkill(skill)));

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‚ฌ์šฉ์ž์˜ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜๊ณ  ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด. ๋ฉ‹์ง€์ง€ ์•Š์•„? ๐Ÿ˜Ž

์ž, ์ด์ œ Union Types๊ฐ€ ์–ผ๋งˆ๋‚˜ ๊ฐ•๋ ฅํ•œ์ง€ ์•Œ๊ฒ ์ง€? ํ•˜์ง€๋งŒ ์•„์ง ๋” ์žˆ์–ด! Union Types๋ฅผ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜๋ ค๋ฉด 'ํƒ€์ž… ๊ฐ€๋“œ'๋ผ๋Š” ๊ฐœ๋…๋„ ์•Œ์•„์•ผ ํ•ด. ๋‹ค์Œ ์„น์…˜์—์„œ ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž! ๐Ÿš€

๐Ÿ›ก๏ธ ํƒ€์ž… ๊ฐ€๋“œ์™€ Union Types

ํƒ€์ž… ๊ฐ€๋“œ(Type Guard)๋Š” Union Type์˜ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํŠน์ • ํƒ€์ž…์ž„์„ ๋ณด์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์•ผ. ์ด๊ฑธ ์‚ฌ์šฉํ•˜๋ฉด Union Type์˜ ๋ณ€์ˆ˜๋ฅผ ๋” ์•ˆ์ „ํ•˜๊ณ  ํšจ๊ณผ์ ์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์ง€.

ํƒ€์ž… ๊ฐ€๋“œ์—๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์–ด. ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๊ฒƒ๋ถ€ํ„ฐ ์‚ดํŽด๋ณผ๊นŒ?

1. typeof ์—ฐ์‚ฐ์ž ์‚ฌ์šฉํ•˜๊ธฐ

function printId(id: string | number) {
    if (typeof id === "string") {
        console.log(id.toUpperCase());
    } else {
        console.log(id.toFixed(2));
    }
}

printId("user123"); // USER123
printId(123.456); // 123.46

์ด ์˜ˆ์ œ์—์„œ typeof ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•ด id๊ฐ€ ๋ฌธ์ž์—ด์ธ์ง€ ์ˆซ์ž์ธ์ง€ ํ™•์ธํ•˜๊ณ  ์žˆ์–ด. TypeScript๋Š” ์ด ๊ฒ€์‚ฌ๋ฅผ ์ธ์‹ํ•˜๊ณ , ๊ฐ ๋ธ”๋ก ๋‚ด์—์„œ id๋ฅผ ์ ์ ˆํ•œ ํƒ€์ž…์œผ๋กœ ์ฒ˜๋ฆฌํ•ด.

2. instanceof ์—ฐ์‚ฐ์ž ์‚ฌ์šฉํ•˜๊ธฐ

๊ฐ์ฒด์˜ ๊ฒฝ์šฐ instanceof๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท์˜ ์‚ฌ์šฉ์ž ์‹œ์Šคํ…œ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊นŒ?

class NormalUser {
    constructor(public username: string) {}
    sayHello() {
        console.log(`์•ˆ๋…•ํ•˜์„ธ์š”, ${this.username}์ž…๋‹ˆ๋‹ค!`);
    }
}

class AdminUser {
    constructor(public username: string, public adminLevel: number) {}
    performAdminTask() {
        console.log(`${this.username} ๊ด€๋ฆฌ์ž๊ฐ€ ๊ด€๋ฆฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.`);
    }
}

function greetUser(user: NormalUser | AdminUser) {
    if (user instanceof AdminUser) {
        console.log(`๊ด€๋ฆฌ์ž ${user.username}๋‹˜, ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!`);
        user.performAdminTask();
    } else {
        user.sayHello();
    }
}

const normalUser = new NormalUser("๊น€์žฌ๋Šฅ");
const adminUser = new AdminUser("๋ฐ•๊ด€๋ฆฌ", 1);

greetUser(normalUser); // ์•ˆ๋…•ํ•˜์„ธ์š”, ๊น€์žฌ๋Šฅ์ž…๋‹ˆ๋‹ค!
greetUser(adminUser); // ๊ด€๋ฆฌ์ž ๋ฐ•๊ด€๋ฆฌ๋‹˜, ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค! ๋ฐ•๊ด€๋ฆฌ ๊ด€๋ฆฌ์ž๊ฐ€ ๊ด€๋ฆฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ด ์˜ˆ์ œ์—์„œ instanceof๋ฅผ ์‚ฌ์šฉํ•ด user๊ฐ€ AdminUser์˜ ์ธ์Šคํ„ด์Šค์ธ์ง€ ํ™•์ธํ•˜๊ณ  ์žˆ์–ด. ์ด๋ฅผ ํ†ตํ•ด ๊ฐ ์‚ฌ์šฉ์ž ํƒ€์ž…์— ๋งž๋Š” ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์ง€.

3. in ์—ฐ์‚ฐ์ž ์‚ฌ์šฉํ•˜๊ธฐ

๊ฐ์ฒด์— ํŠน์ • ํ”„๋กœํผํ‹ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท์˜ ์žฌ๋Šฅ ์‹œ์Šคํ…œ์„ ๋‹ค์‹œ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ.

type TechnicalSkill = { type: "technical", language: string };
type ArtisticSkill = { type: "artistic", medium: string };

function describeSkill(skill: TechnicalSkill | ArtisticSkill) {
    if ("language" in skill) {
        console.log(`๊ธฐ์ˆ  ์žฌ๋Šฅ: ${skill.language}`);
    } else {
        console.log(`์˜ˆ์ˆ  ์žฌ๋Šฅ: ${skill.medium}`);
    }
}

describeSkill({ type: "technical", language: "TypeScript" }); // ๊ธฐ์ˆ  ์žฌ๋Šฅ: TypeScript
describeSkill({ type: "artistic", medium: "์ˆ˜์ฑ„ํ™”" }); // ์˜ˆ์ˆ  ์žฌ๋Šฅ: ์ˆ˜์ฑ„ํ™”

์—ฌ๊ธฐ์„œ๋Š” 'in' ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•ด skill ๊ฐ์ฒด์— 'language' ํ”„๋กœํผํ‹ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์žˆ์–ด. ์ด ๋ฐฉ๋ฒ•์€ ๊ฐ์ฒด์˜ ๊ตฌ์กฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํƒ€์ž…์„ ๊ตฌ๋ถ„ํ•  ๋•Œ ์œ ์šฉํ•ด.

4. ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œ

๋•Œ๋กœ๋Š” ๋” ๋ณต์žกํ•œ ๋กœ์ง์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์–ด. ์ด๋Ÿด ๋•Œ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€.

function isTechnicalSkill(skill: TechnicalSkill | ArtisticSkill): skill is TechnicalSkill {
    return (skill as TechnicalSkill).language !== undefined;
}

function enhanceSkill(skill: TechnicalSkill | ArtisticSkill) {
    if (isTechnicalSkill(skill)) {
        console.log(`${skill.language} ์‹ค๋ ฅ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.`);
    } else {
        console.log(`${skill.medium} ๊ธฐ๋ฒ•์„ ์—ฐ๋งˆํ•ฉ๋‹ˆ๋‹ค.`);
    }
}

enhanceSkill({ type: "technical", language: "Python" }); // Python ์‹ค๋ ฅ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
enhanceSkill({ type: "artistic", medium: "์กฐ๊ฐ" }); // ์กฐ๊ฐ ๊ธฐ๋ฒ•์„ ์—ฐ๋งˆํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ isTechnicalSkill ํ•จ์ˆ˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œ์•ผ. ์ด ํ•จ์ˆ˜๋Š” skill์ด TechnicalSkill ํƒ€์ž…์ธ์ง€ ํ™•์ธํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ TypeScript์—๊ฒŒ ์•Œ๋ ค์ค˜. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋” ๋ณต์žกํ•œ ํƒ€์ž… ์ฒดํฌ๋„ ๊ฐ€๋Šฅํ•ด์ง€์ง€.

๐Ÿ’ก ํŒ: ํƒ€์ž… ๊ฐ€๋“œ๋ฅผ ์ž˜ ์‚ฌ์šฉํ•˜๋ฉด Union Types๋ฅผ ๋”์šฑ ์•ˆ์ „ํ•˜๊ณ  ํšจ๊ณผ์ ์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด. ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๋„ ์ข‹์•„์ง€๊ณ , ๋ฒ„๊ทธ๋„ ์ค„์ผ ์ˆ˜ ์žˆ์ง€!

์ž, ์ด์ œ ํƒ€์ž… ๊ฐ€๋“œ์— ๋Œ€ํ•ด ์•Œ์•˜์œผ๋‹ˆ Union Types๋ฅผ ๋” ์ž์‹ ์žˆ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€? ํ•˜์ง€๋งŒ ์•„์ง ๋์ด ์•„๋‹ˆ์•ผ. Union Types์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์ด ๋” ๋‚จ์•„์žˆ์–ด! ๋‹ค์Œ ์„น์…˜์—์„œ ๊ณ„์† ์•Œ์•„๋ณด์ž! ๐Ÿš€

๐ŸŽ“ Union Types์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ

์ž, ์ด์ œ Union Types์˜ ๊ธฐ๋ณธ์„ ๋งˆ์Šคํ„ฐํ–ˆ์œผ๋‹ˆ ์ข€ ๋” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์œผ๋กœ ๋„˜์–ด๊ฐ€๋ณผ๊นŒ? ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ๋Š” ์ง„์งœ TypeScript์˜ ๊ฐ•๋ ฅํ•จ์„ ๋Š๋‚„ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿ˜Ž

1. Discriminated Unions (ํŒ๋ณ„ ์œ ๋‹ˆ์˜จ)

์ด๋ฏธ ๊ฐ„๋‹จํžˆ ๋‹ค๋ค˜์ง€๋งŒ, ํŒ๋ณ„ ์œ ๋‹ˆ์˜จ์€ ์ •๋ง ์œ ์šฉํ•ด์„œ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณผ ๊ฐ€์น˜๊ฐ€ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท์˜ ๊ฒฐ์ œ ์‹œ์Šคํ…œ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ.

type Cash = {
    kind: "cash";
    amount: number;
};

type CreditCard = {
    kind: "credit";
    cardNumber: string;
    securityCode: string;
};

type BankTransfer = {
    kind: "transfer";
    accountNumber: string;
    bankCode: string;
};

type Payment = Cash | CreditCard | BankTransfer;

function processPayment(payment: Payment) {
    switch(payment.kind) {
        case "cash":
            console.log(`${payment.amount}์›์„ ํ˜„๊ธˆ์œผ๋กœ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.`);
            break;
        case "credit":
            console.log(`์นด๋“œ ๋ฒˆํ˜ธ ${payment.cardNumber}๋กœ ๊ฒฐ์ œ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.`);
            break;
        case "transfer":
            console.log(`${payment.bankCode} ์€ํ–‰์˜ ๊ณ„์ขŒ ${payment.accountNumber}๋กœ ์ด์ฒด๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.`);
            break;
    }
}

processPayment({ kind: "cash", amount: 50000 });
processPayment({ kind: "credit", cardNumber: "1234-5678-9012-3456", securityCode: "123" });
processPayment({ kind: "transfer", accountNumber: "987-65-43210", bankCode: "WB" });

์—ฌ๊ธฐ์„œ 'kind' ์†์„ฑ์ด ํŒ๋ณ„์ž(discriminator) ์—ญํ• ์„ ํ•ด. TypeScript๋Š” ์ด๋ฅผ ํ†ตํ•ด ๊ฐ case์—์„œ ์ •ํ™•ํ•œ ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ์ง€. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ๋‹ค์–‘ํ•œ ๊ฒฐ์ œ ๋ฐฉ์‹์„ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.

2. Exhaustiveness Checking (์™„์ „์„ฑ ๊ฒ€์‚ฌ)

ํŒ๋ณ„ ์œ ๋‹ˆ์˜จ์„ ์‚ฌ์šฉํ•  ๋•Œ, ๋ชจ๋“  ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ–ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด. TypeScript๋Š” ์ด๋ฅผ ์œ„ํ•œ ํŠธ๋ฆญ์„ ์ œ๊ณตํ•ด.

function assertNever(x: never): never {
    throw new Error("Unexpected object: " + x);
}

function processPayment(payment: Payment) {
    switch(payment.kind) {
        case "cash":
            console.log(`${payment.amount}์›์„ ํ˜„๊ธˆ์œผ๋กœ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.`);
            break;
        case "credit":
            console.log(`์นด๋“œ ๋ฒˆํ˜ธ ${payment.cardNumber}๋กœ ๊ฒฐ์ œ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.`);
            break;
        case "transfer":
            console.log(`${payment.bankCode} ์€ํ–‰์˜ ๊ณ„์ขŒ ${payment.accountNumber}๋กœ ์ด์ฒด๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.`);
            break;
        default:
            assertNever(payment); // ์—ฌ๊ธฐ์— ๋„๋‹ฌํ•˜๋ฉด ์ปดํŒŒ์ผ ์—๋Ÿฌ!
    }
}

assertNever ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ๋ชจ๋“  ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์•˜์„ ๋•Œ ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‚˜์ค‘์— Payment ํƒ€์ž…์— ์ƒˆ๋กœ์šด ๊ฒฐ์ œ ๋ฐฉ์‹์„ ์ถ”๊ฐ€ํ–ˆ์„ ๋•Œ, ๋ฐ˜๋“œ์‹œ ๊ทธ ์ผ€์ด์Šค๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์ง€.

3. Intersection Types์™€์˜ ์กฐํ•ฉ

Union Types๋Š” Intersection Types์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋  ๋•Œ ๋”์šฑ ๊ฐ•๋ ฅํ•ด์ ธ. ์žฌ๋Šฅ๋„ท์˜ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์‹œ์Šคํ…œ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ.

type BasicProfile = {
    name: string;
    age: number;
};

type SkillProfile = {
    skills: string[];
};

type SocialProfile = {
    socialLinks: { [key: string]: string };
};

type UserProfile = BasicProfile & (SkillProfile | SocialProfile);

function displayProfile(profile: UserProfile) {
    console.log(`์ด๋ฆ„: ${profile.name}, ๋‚˜์ด: ${profile.age}`);
    if ("skills" in profile) {
        console.log(`๋ณด์œ  ๊ธฐ์ˆ : ${profile.skills.join(", ")}`);
    } else {
        console.log(`์†Œ์…œ ๋งํฌ: ${Object.keys(profile.socialLinks).join(", ")}`);
    }
}

displayProfile({
    name: "๊น€์žฌ๋Šฅ",
    age: 28,
    skills: ["TypeScript", "React", "Node.js"]
});

displayProfile({
    name: "์ด์†Œ์…œ",
    age: 32,
    socialLinks: {
        twitter: "https://twitter.com/leesocial",
        instagram: "https://instagram.com/leesocial"
    }
});

์—ฌ๊ธฐ์„œ UserProfile์€ BasicProfile๊ณผ (SkillProfile ๋˜๋Š” SocialProfile)์˜ ์กฐํ•ฉ์ด์•ผ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ ๊ธฐ๋ณธ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๋ฉด์„œ, ์ถ”๊ฐ€๋กœ ๊ธฐ์ˆ  ์ •๋ณด๋‚˜ ์†Œ์…œ ์ •๋ณด ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ฒŒ ๋ผ. ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ๋ณต์žกํ•œ ํƒ€์ž… ๊ตฌ์กฐ๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์ง€.

4. Conditional Types์™€์˜ ํ™œ์šฉ

Union Types๋Š” Conditional Types์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋  ๋•Œ ๋”์šฑ ๊ฐ•๋ ฅํ•œ ํƒ€์ž… ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท์˜ ๊ฒ€์ƒ‰ ์‹œ์Šคํ…œ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ.

type SearchBy<T> = T extends "skill" ? { skill: string } :
                   T extends "name" ? { name: string } :
                   T extends "location" ? { city: string, country: string } :
                   never;

function search<T extends "skill" | "name" | "location">(searchType: T, query: SearchBy<T>) {
    // ๊ฒ€์ƒ‰ ๋กœ์ง
    console.log(`Searching by ${searchType}:`, query);
}

search("skill", { skill: "TypeScript" });
search("name", { name: "๊น€์žฌ๋Šฅ" });
search("location", { city: "์„œ์šธ", country: "๋Œ€ํ•œ๋ฏผ๊ตญ" });
// search("age", { age: 30 }); // ์ปดํŒŒ์ผ ์—๋Ÿฌ!

์ด ์˜ˆ์ œ์—์„œ SearchBy๋Š” Conditional Type์„ ์‚ฌ์šฉํ•ด ๊ฒ€์ƒ‰ ํƒ€์ž…์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์ฟผ๋ฆฌ ๊ฐ์ฒด ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ์œ ์—ฐํ•œ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์ง€.

๐Ÿš€ ๊ณ ๊ธ‰ ํŒ: Union Types, Intersection Types, Conditional Types๋ฅผ ์กฐํ•ฉํ•˜๋ฉด ์ •๋ง ๋ณต์žกํ•œ ํƒ€์ž… ๋กœ์ง๋„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ๋„ˆ๋ฌด ๋ณต์žกํ•ด์ง€๋ฉด ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ ์ ˆํ•œ ๊ท ํ˜•์„ ์ฐพ๋Š” ๊ฒŒ ์ค‘์š”ํ•ด!

5. Mapped Types์™€ Union Types

Mapped Types๋ฅผ Union Types์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๋”์šฑ ๊ฐ•๋ ฅํ•œ ํƒ€์ž… ๋ณ€ํ™˜์„ ํ•  ์ˆ˜ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท์˜ ์‚ฌ์šฉ์ž ์„ค์ • ์‹œ์Šคํ…œ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ.

type UserPreferences = {
    theme: "light" | "dark";
    fontSize: "small" | "medium" | "large";
    notifications: "all" | "important" | "none";
};

type PreferenceToggle<T> = {
    [K in keyof T]: {
        enable: boolean;
        value: T[K];
    }
};

type UserPreferencesToggle = PreferenceToggle<UserPreferences>;

const userSettings: UserPreferencesToggle = {
    theme: { enable: true, value: "dark" },
    fontSize: { enable: false, value: "medium" },
    notifications: { enable: true, value: "important" }
};

function applySettings(settings: UserPreferencesToggle) {
    for (const [key, { enable, value }] of Object.entries(settings)) {
        if (enable) {
            console.log(`Applying ${key}: ${value}`);
        } else {
            console.log(`Skipping ${key}`);
        }
    }
}

applySettings(userSettings);

์ด ์˜ˆ์ œ์—์„œ PreferenceToggle์€ Mapped Type์„ ์‚ฌ์šฉํ•ด ๊ฐ ์„ค์ •์— enable ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ ์„ค์ •์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ํ™œ์„ฑํ™”ํ•˜๊ฑฐ๋‚˜ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์ง€.

๋งˆ๋ฌด๋ฆฌ

์ž, ์ด์ œ Union Types์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค๊นŒ์ง€ ์‚ดํŽด๋ดค์–ด. ์ด๋Ÿฐ ๊ธฐ๋Šฅ๋“ค์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ๊ฐ•๋ ฅํ•˜๊ณ  ์œ ์—ฐํ•œ ํƒ€์ž… ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์ง€. TypeScript์˜ ์ง„์ •ํ•œ ํž˜์€ ์ด๋Ÿฐ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์„ ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•  ๋•Œ ๋‚˜ํƒ€๋‚˜!

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

๊ณ„์† ์—ฐ์Šตํ•˜๊ณ  ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด๋ฉด์„œ ์ด ๊ฐœ๋…๋“ค์„ ๋งˆ์Šคํ„ฐํ•ด๋‚˜๊ฐ€๊ธธ ๋ฐ”๋ผ! TypeScript์˜ ์„ธ๊ณ„๋Š” ์ •๋ง ๊นŠ๊ณ  ๋„“์œผ๋‹ˆ๊นŒ, ํ•ญ์ƒ ์ƒˆ๋กœ์šด ๊ฒƒ์„ ๋ฐฐ์šธ ์ค€๋น„๋ฅผ ํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•ด. ํ™”์ดํŒ…! ๐Ÿš€๐ŸŒŸ

๐ŸŽ‰ ๋งˆ๋ฌด๋ฆฌ: Union Types ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

์šฐ์™€, ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€๋งŒ ๋“œ๋””์–ด Union Types์˜ ์„ธ๊ณ„๋ฅผ ํƒํ—˜ํ–ˆ์–ด! ๐Ÿ‘ ์ด์ œ Union Types๊ฐ€ ์–ผ๋งˆ๋‚˜ ๊ฐ•๋ ฅํ•˜๊ณ  ์œ ์—ฐํ•œ ๋„๊ตฌ์ธ์ง€ ์•Œ๊ฒŒ ๋์„ ๊ฑฐ์•ผ. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ๋ณต์žกํ•œ ํ”Œ๋žซํผ์„ ๊ฐœ๋ฐœํ•  ๋•Œ ์ด๋Ÿฐ ์ง€์‹์€ ์ •๋ง ํฐ ๋„์›€์ด ๋  ๊ฑฐ์•ผ.

์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•ด๋ณผ๊นŒ?

  • Union Types์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๊ณผ ์‚ฌ์šฉ๋ฒ•
  • ํ•จ์ˆ˜์—์„œ Union Types ํ™œ์šฉํ•˜๊ธฐ
  • ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…๊ณผ Union Types์˜ ์กฐํ•ฉ
  • ๊ฐ์ฒด ํƒ€์ž…๊ณผ Union Types
  • ํƒ€์ž… ๊ฐ€๋“œ๋ฅผ ์ด์šฉํ•œ ์•ˆ์ „ํ•œ Union Types ์‚ฌ์šฉ
  • Discriminated Unions (ํŒ๋ณ„ ์œ ๋‹ˆ์˜จ)
  • Exhaustiveness Checking (์™„์ „์„ฑ ๊ฒ€์‚ฌ)
  • Intersection Types์™€์˜ ์กฐํ•ฉ
  • Conditional Types์™€ Union Types
  • Mapped Types์™€ Union Types

์ด ๋ชจ๋“  ๊ฐœ๋…๋“ค์„ ์™„์ „ํžˆ ์ดํ•ดํ•˜๊ณ  ํ™œ์šฉํ•˜๋Š” ๋ฐ๋Š” ์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ๊ฑฐ์•ผ. ํ•˜์ง€๋งŒ ๊ฑฑ์ •ํ•˜์ง€ ๋งˆ! ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ๊ณ„์† ์—ฐ์Šตํ•˜๊ณ  ๊ฒฝํ—˜์„ ์Œ“์•„๊ฐ€๋Š” ๊ณผ์ •์ด๋‹ˆ๊นŒ. ๐Ÿ˜Š

๐Ÿ’ก ์•ž์œผ๋กœ์˜ ํ•™์Šต ํŒ:

  • ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— Union Types๋ฅผ ์ ์šฉํ•ด๋ณด์„ธ์š”.
  • ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค์˜ ์ฝ”๋“œ๋ฅผ ์ฝ๊ณ  Union Types๊ฐ€ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋˜๋Š”์ง€ ๊ด€์ฐฐํ•˜์„ธ์š”.
  • ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ Union Types๋กœ ๋ชจ๋ธ๋งํ•ด๋ณด๋Š” ์—ฐ์Šต์„ ํ•˜์„ธ์š”.
  • TypeScript์˜ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ž์ฃผ ์ฐธ๊ณ ํ•˜์„ธ์š”. ํ•ญ์ƒ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋˜๊ณ  ์žˆ์–ด์š”!

Union Types๋Š” TypeScript์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์•ผ. ์ด๋ฅผ ๋งˆ์Šคํ„ฐํ•˜๋ฉด ๋” ์•ˆ์ „ํ•˜๊ณ , ๋” ์œ ์—ฐํ•˜๋ฉฐ, ๋” ํ‘œํ˜„๋ ฅ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์„ ๊ฐœ๋ฐœํ•  ๋•Œ, ์ด๋Ÿฐ ํƒ€์ž… ์‹œ์Šคํ…œ์˜ ํž˜์„ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ํฐ ์ฐจ์ด๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€.

์ž, ์ด์ œ ๋‹น์‹ ์€ Union Types์˜ ์ง„์ •ํ•œ ๋งˆ์Šคํ„ฐ๋กœ ๊ฑฐ๋“ญ๋‚ฌ์–ด! ๐ŸŽ“ ์ด ์ง€์‹์„ ๊ฐ€์ง€๊ณ  ๋” ๋ฉ‹์ง„ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋‚˜๊ฐ€๊ธธ ๋ฐ”๋ผ. ํ•ญ์ƒ ์ƒˆ๋กœ์šด ๊ฒƒ์„ ๋ฐฐ์šฐ๊ณ , ๋„์ „ํ•˜๋Š” ์ž์„ธ๋ฅผ ์žƒ์ง€ ๋งˆ์„ธ์š”. TypeScript์˜ ์„ธ๊ณ„๋Š” ์•„์ง ๋” ๋งŽ์€ ํฅ๋ฏธ๋กœ์šด ๊ธฐ๋Šฅ๋“ค๋กœ ๊ฐ€๋“ํ•˜๋‹ˆ๊นŒ์š”!

๋‹ค์Œ์— ๋˜ ๋‹ค๋ฅธ ํฅ๋ฏธ๋กœ์šด TypeScript ์ฃผ์ œ๋กœ ๋งŒ๋‚˜๊ธธ ๋ฐ”๋ผ์š”. ์ฝ”๋”ฉ ์žฌ๋Šฅ ๊ฐ€๋“ํ•œ ์—ฌ๋Ÿฌ๋ถ„, ๋ชจ๋‘ ํ™”์ดํŒ…! ๐Ÿš€๐Ÿ’ปโœจ