๐Ÿ›ก๏ธ 03. ํƒ€์ž… ์ขํžˆ๊ธฐ(Narrowing): ์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ ๊ฐ์ฒด๋ฅผ ๊ธธ๋“ค์ด๊ธฐ

2026๋…„ 3์›” 5์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

typeof๋ถ€ํ„ฐ ์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ์œ ๋‹ˆ์–ธ(Discriminated Unions)๊นŒ์ง€, ํ๋ฆ„ ์ œ์–ด์˜ ๋งˆ๋ฒ•

๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 15๋ถ„(์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 8๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
[๋ฌด์—‡์ด๋“  ๋  ์ˆ˜ ์žˆ๋Š” unknown์˜ ๊ณตํฌ] โ†’ [TS๋ฅผ ๋‚ฉ๋“์‹œํ‚ค๋Š” 4๊ฐ€์ง€ ๊ฐ€๋“œ(Guard)] โ†’ [์ตœ์ข… ๋ณ‘๊ธฐ: ์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ์œ ๋‹ˆ์–ธ]

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • try...catch์˜ error ๊ฐ์ฒด๊ฐ€ ์™œ ์ž๋™์œผ๋กœ any๋‚˜ unknown์ด ๋˜๋Š”์ง€ ์ดํ•ดํ•œ๋‹ค.
  • typeof, in, instanceof ์—ฐ์‚ฐ์ž๋กœ ํƒ€์ž…์„ ์ขํžˆ๋Š” ์›๋ฆฌ(Control Flow Analysis)๋ฅผ ์ฒด๋“ํ•œ๋‹ค.
  • ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๋ณต์žกํ•œ API ์—๋Ÿฌ๋ฅผ "์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ์œ ๋‹ˆ์–ธ" ํŒจํ„ด์œผ๋กœ ์™„๋ฒฝํ•˜๊ฒŒ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ๐ŸŽจ ์˜์ˆ™ ( UX ๋ฆฌ๋ทฐ์–ด ): "์˜์ฒ  ๋‹˜, ๋กœ๊ทธ์ธ ์‹คํŒจํ•  ๋•Œ ์–ผ๋Ÿฟ ์ฐฝ์ด ์™œ ์ž๊พธ [object Object] Error ๋ผ๊ณ  ๋œจ๋‚˜์š”? ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ฆฐ ๊ฑด์ง€, ๊ณ„์ •์ด ์—†๋Š” ๊ฑด์ง€ ์‚ฌ์šฉ์žํ•œํ…Œ ์˜ˆ์œ ๋ฉ”์‹œ์ง€๋กœ ์•Œ๋ ค์ค˜์•ผ์ฃ !"
  • ๐Ÿฃ ์˜์ฒ  ( ์‹ ์ž… ): "์ €๋„ ๊ทธ๋Ÿฌ๊ณ  ์‹ถ์€๋ฐ... catch (err)๋กœ ๋–จ์–ด์ง€๋Š” ์ด err ๋…€์„์ด ๋ฌด์Šจ ํƒ€์ž…์ธ์ง€ TS๊ฐ€ ์•„์˜ˆ ๋ชฐ๋ผ์š”! ์†์„ฑ์— ์ ‘๊ทผํ•˜๋ ค๊ณ ๋งŒ ํ•˜๋ฉด ๊ณ„์† ๋นจ๊ฐ„ ์ค„์„ ๋ฑ‰๋Š”๋‹ค๋‹ˆ๊นŒ์š”?!"
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์˜์ฒ  ๋‹˜, ์„ธ์ƒ์˜ ๋ชจ๋“  ์˜ˆ์™ธ๊ฐ€ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ๊ทœ์น™๋Œ€๋กœ๋งŒ ํ„ฐ์งˆ๊นŒ์š”? TS๊ฐ€ unknown์œผ๋กœ ๋ง‰์•„๋‘” ๊ฑด ์˜คํžˆ๋ ค ์ถ•๋ณต์ž…๋‹ˆ๋‹ค. ์ด์ œ ์ด ๊ฑฐ์นœ ์•ผ์ƒ๋งˆ๋ฅผ ํƒ€์ž… ์ขํžˆ๊ธฐ(Narrowing) ๋กœ ์กฐ๋ จํ•ด ๋ณผ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค."

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: '๋ชจ๋ฅธ๋‹ค'๋Š” ๊ฒƒ์„ ์ธ์ •ํ•˜๊ธฐ

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

  1. DOM ์š”์†Œ๋ฅผ ์ฐพ์•˜๋Š”๋ฐ ๊ทธ๊ฒŒ null์ผ ์ˆ˜๋„ ์žˆ์„ ๋•Œ (obj is possibly 'null')
  2. try...catch ๋ธ”๋ก์ด๋‚˜ ์™ธ๋ถ€ API์—์„œ ์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ๊ฐ€ ํ„ฐ์กŒ์„ ๋•Œ (Object is of type 'unknown')

๊ณผ๊ฑฐ์˜ ์˜์ฒ ์ด(๊ทธ๋ฆฌ๊ณ  1๋…„ ์ฐจ ์‹œ์ ˆ์˜ ์šฐ๋ฆฌ)๋Š” ์ด๋Ÿด ๋•Œ ์ผ๋‹จ as any ๋‚˜ as MyCustomError๋ฅผ ๋ฐ•์•„๋ฒ„๋ฆฌ๊ณ  ํ‡ด๊ทผํ•˜๊ณค ํ–ˆ์Šต๋‹ˆ๋‹ค.

// ๐Ÿ’ฃ ๊ณผ๊ฑฐ์˜ ์˜์ฒ ์ด๊ฐ€ ์ง  ์‹œํ•œํญํƒ„ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
} catch (err) {
  // TS: "๋‚˜๋Š” err๊ฐ€ ๋ญ”์ง€ ์˜ฌ๋ผ. string์ผ ์ˆ˜๋„ ์žˆ๊ณ , Error ๊ฐ์ฒด์ผ ์ˆ˜๋„ ์žˆ์–ด!"
  // ๐Ÿฃ ์˜์ฒ : "์•„ ์‹œ๋„๋Ÿฌ์›Œ, ๋ฐฑ์—”๋“œ์—์„œ ์ค€ MyApiError ๋‹ˆ๊นŒ ๊ทธ๋ƒฅ ๊ฐ•์ œ ํ• ๋‹นํ•ด!"
  
  alert((err as MyApiError).message); // ๋งŒ์•ฝ ๋„คํŠธ์›Œํฌ ๋‹จ์ ˆ(NetworkError)์ด์—ˆ๋‹ค๋ฉด? -> Undefined ํญํƒ„!
}

์•ˆ์ „ํ•œ ํ”„๋ก ํŠธ์—”๋“œ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•˜๋ ค๋ฉด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—๊ฒŒ "์ด ๊ฐ’์€ ํ™•์‹คํžˆ OOO์•ผ" ๋ผ๊ณ  ์ฆ๋ช…ํ•˜๋Š” ๊ณผ์ •(Control Flow Analysis) ์„ ๋ฐŸ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฆ๋ช… ๊ณผ์ •์ด ๋ฐ”๋กœ ํƒ€์ž… ์ขํžˆ๊ธฐ(Narrowing) ์ž…๋‹ˆ๋‹ค.


๐Ÿ” 1. ๊ฐ€์žฅ ํ”ํ•œ ๋ฐฉํŒจ: typeof์™€ Truthiness

๊ฐ€์žฅ ์›์‹œ์ ์ด์ง€๋งŒ ๋นˆ๋ฒˆํ•˜๊ฒŒ ์“ฐ์ด๋Š” ๊ฐ€๋“œ(Guard)์ž…๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ํ”ผ๋„ ๋ˆˆ๋ฌผ๋„ ์—†๋Š” ๋Ÿฐํƒ€์ž„ ์—ฐ์‚ฐ์ž์ธ typeof๋ฅผ TS๋„ ๋˜‘๋˜‘ํ•˜๊ฒŒ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ ๊ฒŒ์‹œํŒ ID ํŒŒ์‹ฑ๊ธฐ

function printBoardId(id: string | number | null) {
  // 1. Truthiness narrowing: null ์ œ๊ฑฐ
  if (!id) {
    console.log("ID๊ฐ€ ์ œ๊ณต๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.");
    return;
  }
 
  // ์ด ์‹œ์ ์—์„œ TS๋Š” id๊ฐ€ (string | number) ์ž„์„ ์•ˆ๋‹ค!
 
  // 2. typeof narrowing: string๊ณผ number ๋ถ„๋ฆฌ
  if (typeof id === "string") {
    // ์—ฌ๊ธฐ์„œ id๋Š” ๋ฌด์กฐ๊ฑด string!
    console.log(`๋ฌธ์ž์—ด ID์ž…๋‹ˆ๋‹ค: ${id.toUpperCase()}`);
  } else {
    // ์—ฌ๊ธฐ์„œ id๋Š” ๋ฌด์กฐ๊ฑด number!
    console.log(`์ˆซ์ž ID์ž…๋‹ˆ๋‹ค: ${id.toFixed(2)}`);
  }
}

๐Ÿ’ก ์ฃผ์˜ํ•  ์ : JS์˜ ํƒœ์ƒ์  ํ•œ๊ณ„๋กœ typeof null === "object" ์ด๊ณ , typeof [] === "object" ์ž…๋‹ˆ๋‹ค. ๋ฐฐ์—ด์ด๋‚˜ ์ปค์Šคํ…€ ๊ฐ์ฒด๋ฅผ ๊ตฌ๋ณ„ํ•  ๋•Œ๋Š” typeof๊ฐ€ ์ „ํ˜€ ์“ธ๋ชจ๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์„ ๋ช…์‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ›ก๏ธ 2. ๊ฐ์ฒด๋ฅผ ๊ตฌ๋ณ„ํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ์นผ: in ๊ณผ instanceof

๊ทธ๋Ÿผ ์˜์ฒ ์ด์˜ ๊ทผ๋ณธ์ ์ธ ๊ณ ๋ฏผ, "์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ ๊ฐ์ฒด"๋Š” ์–ด๋–ป๊ฒŒ ๊ตฌ๋ณ„ํ• ๊นŒ์š”? ์—ฌ๊ธฐ์„œ in ํ”ผ์—ฐ์‚ฐ์ž์™€ instanceof๊ฐ€ ๋“ฑํŒํ•ฉ๋‹ˆ๋‹ค.

์ผ€์ด์Šค A: instanceof (ํด๋ž˜์Šค๋กœ ์ฐ์–ด๋‚ธ ๊ฐ์ฒด์ผ ๋•Œ)

JS์˜ ๋‚ด์žฅ Error ํด๋ž˜์Šค๋‚˜ ์ปค์Šคํ…€ ํด๋ž˜์Šค๋ฅผ ๊ตฌ๋ณ„ํ•  ๋•Œ ๊ฐ•๋ ฅํ•ฉ๋‹ˆ๋‹ค.

} catch (err) {
  // err๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ unknown ํƒ€์ž…
  
  if (err instanceof TypeError) {
    console.log("๋ณ€์ˆ˜ ํƒ€์ž… ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ!", err.message);
  } else if (err instanceof Error) {
    console.log("์ผ๋ฐ˜์ ์ธ JS ์—๋Ÿฌ!", err.message);
  } else {
    // ๋ฌธ์ž์—ด์„ ๋˜์งˆ ์ˆ˜๋„, ์Œฉ ๊ฐ์ฒด๋ฅผ ๋˜์งˆ ์ˆ˜๋„ ์žˆ๋Š” ์•ผ์ˆ˜ ๊ฐ™์€ JS์˜ ๋Ÿฐํƒ€์ž„
    console.log("์•Œ ์ˆ˜ ์—†๋Š” ์˜ˆ์™ธ ๋ฐœ์ƒ: ", err);
  }
}

์ผ€์ด์Šค B: in ์—ฐ์‚ฐ์ž (์†์„ฑ์˜ ์กด์žฌ ์œ ๋ฌด๋กœ ๊ฐ์ฒด ํŒ๋ณ„)

๋ฐฑ์—”๋“œ์—์„œ ์˜ค๋Š” JSON ์‘๋‹ต ๊ฐ™์€ ์ˆœ์ˆ˜ ๊ฐ์ฒด(Plain Object)๋Š” ํด๋ž˜์Šค๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ด๋•Œ๋Š” "ํŠน์ • ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ด ๊ฐ์ฒด ์•ˆ์—(in) ์žˆ๋А๋ƒ?" ๋กœ ์ขํ˜€๋ƒ…๋‹ˆ๋‹ค.

type BackendError = { statusCode: number; developerMessage: string };
type ValidationError = { invalidFields: string[]; userMessage: string };
 
function handleApiError(error: BackendError | ValidationError) {
  // ๐Ÿฆ ์˜ํ˜ธ: "error ์•ˆ์— 'statusCode' ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด, ๊ทธ๊ฑด BackendError ์•ผ!"
  if ("statusCode" in error) {
    console.log(`์„œ๋ฒ„์—์„œ ${error.statusCode} ์—๋Ÿฌ๋ฅผ ๋ฑ‰์—ˆ์Šต๋‹ˆ๋‹ค.`);
  } else {
    // ๋‚จ์€ ๊ฑด ํ•˜๋‚˜๋ฟ์ด๋‹ˆ ์—ฌ๊ธฐ์„  ๋ฌด์กฐ๊ฑด ValidationError!
    console.log(`์ž…๋ ฅ๊ฐ’์ด ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค: ${error.invalidFields.join(', ')}`);
  }
}

๐Ÿ‘‘ 3. 5๋…„ ์ฐจ์˜ ์ตœ์ข… ๋ณ‘๊ธฐ: ์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ์œ ๋‹ˆ์–ธ (Discriminated Unions)

in ์—ฐ์‚ฐ์ž๋„ ์ข‹์ง€๋งŒ, ๊ถŒํ•œ(๊ถŒํ•œA, ๊ถŒํ•œB, ๊ถŒํ•œC...)์ด๋‚˜ ๋ณต์žกํ•œ ์ƒํƒœ(๋กœ๋”ฉ, ์„ฑ๊ณต, ์‹คํŒจ)๊ฐ€ 3~4๊ฐœ ์ด์ƒ ๋„˜์–ด๊ฐ€๋ฉด ์ฝ”๋“œ๊ฐ€ ๊ต‰์žฅํžˆ ์ง€์ €๋ถ„ํ•ด์ง‘๋‹ˆ๋‹ค.
์ด๋Ÿด ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์•„ํ‚คํ…์ฒ˜ ๋ ˆ๋ฒจ์˜ ํŒจํ„ด์ด ๋ฐ”๋กœ ์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ์œ ๋‹ˆ์–ธ(Discriminated Unions, ๋˜๋Š” Tagged Unions) ์ž…๋‹ˆ๋‹ค. ๋ฆฌ๋•์Šค์˜ ์•ก์…˜ ๊ฐ์ฒด๋‚˜ React์˜ useReducer๊ฐ€ ๋ฐ”๋กœ ์ด ํŒจํ„ด์„ ์”๋‹ˆ๋‹ค.

์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ์ฑ„ํŒ… ์ƒํƒœ ๊ด€๋ฆฌ๊ธฐ

ํ•ต์‹ฌ์€ ๊ณตํ†ต๋œ ์ด๋ฆ„์˜ ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…(Tag) ์„ ์‹ฌ์–ด๋‘๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ณดํ†ต type, kind, status๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

// 1. ๊ฐ๊ฐ์˜ ๊ฐ์ฒด์— 'status' ๋ผ๋Š” ์ด๋ฆ„ํ‘œ(Tag)๋ฅผ ๋ช…ํ™•ํ•œ ๋ฆฌํ„ฐ๋Ÿด ๋ฌธ์ž์—ด๋กœ ๋‹ฌ์•„์ค๋‹ˆ๋‹ค.
interface LoadingState {
  status: "loading"; // ์‹๋ณ„์ž
}
 
interface SuccessState {
  status: "success"; // ์‹๋ณ„์ž
  chatMessages: string[];
}
 
interface ErrorState {
  status: "error"; // ์‹๋ณ„์ž
  errorMessage: string;
}
 
// 2. ์ด๋“ค์„ ํ•˜๋‚˜์˜ ์œ ๋‹ˆ์–ธ(Union)์œผ๋กœ ๋ฌถ์Šต๋‹ˆ๋‹ค.
type ChatState = LoadingState | SuccessState | ErrorState;

์ด์ œ ์˜์ฒ ์ด๋Š” switch ๋ฌธ ํ•˜๋‚˜๋กœ TS๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ํ†ต์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function renderChatUI(state: ChatState) {
  // ๐Ÿฆ ์˜ํ˜ธ: "TS๋Š” switch ๋ฌธ์˜ 'status'๋ฅผ ๋ณด๋Š” ์ˆœ๊ฐ„, ๋‚จ์€ ๊ฐ€์ง€์น˜๊ธฐ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ํ•ด๋ƒ…๋‹ˆ๋‹ค."
  switch (state.status) {
    case "loading":
      // state๋Š” LoadingState ๋กœ ์ขํ˜€์ง!
      return `<p>๋กœ๋”ฉ ์ค‘...</p>`;
      
    case "success":
      // state๋Š” ์ž๋™์œผ๋กœ SuccessState ๊ฐ€ ๋˜์–ด chatMessages์— ์•ˆ์ „ํ•˜๊ฒŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ!
      return `<ul>${state.chatMessages.map(m => `<li>${m}</li>`).join('')}</ul>`;
      
    case "error":
      // state๋Š” ์ž๋™์œผ๋กœ ErrorState!
      return `<p color="red">${state.errorMessage}</p>`;
      
    default:
      // (๋ณด๋„ˆ์Šค) ๋ชจ๋“  ์ผ€์ด์Šค๋ฅผ ๋‹ค๋ฃจ์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” Exhaustiveness Check
      // ์—ฌ๊ธฐ์— ๋„๋‹ฌํ•œ๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ๋ฌด์–ธ๊ฐ€ ์‹ฌ๊ฐํ•œ ์‹ค์ˆ˜๋ฅผ ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
      const _exhaustiveCheck: never = state;
      return _exhaustiveCheck;
  }
}

์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ์œ ๋‹ˆ์–ธ์€ "์—๋Ÿฌ๊ฐ€ ๋‚˜์ง€ ์•Š๋Š” ์šฐ์•„ํ•œ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ"์˜ ์ƒ์ง•์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ๋ถ€ํ„ฐ kind, type ๊ฐ™์€ ๋ช…ํ™•ํ•œ ์‹ฌ๋ณผ์„ ๋งŒ๋“ค์–ด ๋‘๋Š” ์Šต๊ด€์„ ๋“ค์ด์„ธ์š”!


๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. typeof ์—ฐ์‚ฐ์ž๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ์˜ ํƒ€์ž…์„ ์ขํž ๋•Œ, typeof value === "object"๋ผ๋Š” ๊ฒ€์ฆ ๋กœ์ง์ด ๊ฐ€์ง„ ์น˜๋ช…์ ์ธ ์•ฝ์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

โœ… ์ •๋‹ต: null์ด๋‚˜ ๋ฐฐ์—ด(Array)๋„ object๋กœ ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋“ค์„ ์ผ๋ฐ˜ ๊ฐ์ฒด์™€ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์˜ค๋žœ ๋ฒ„๊ทธ์ด์ž ์ŠคํŽ™์œผ๋กœ, typeof null์€ "object"๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์ผ๋ฐ˜์ ์ธ ๊ฐ์ฒด(Dictionary)์ธ ์ค„ ์•Œ๊ณ  ์ขํ˜”๋‹ค๊ฐ€ TypeError: Cannot read properties of null์„ ๋งˆ์ฃผํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ์ฒด๋ฅผ ์ขํž ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ value !== null ๊ฐ™์€ Truthiness ๊ฒ€์ฆ์ด ๋จผ์ € ์„ ํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Q2. ์ˆœ์ˆ˜ํ•œ ๊ฐ์ฒด(Plain Object) ๋‘ ๊ฐœ๊ฐ€ ์œ ๋‹ˆ์–ธ์œผ๋กœ ๋ฌถ์—ฌ์žˆ์„ ๋•Œ(type A = { x: number } | { y: string }), ์ด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์—ฐ์‚ฐ์ž๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

โœ… ์ •๋‹ต: in ์—ฐ์‚ฐ์ž์ž…๋‹ˆ๋‹ค. (์˜ˆ: "x" in obj)

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋ผ๋ฉด instanceof๋ฅผ ์“ฐ๋ฉด ๋˜์ง€๋งŒ, ์„œ๋ฒ„์—์„œ API ์‘๋‹ต์œผ๋กœ ๋‚ด๋ ค์˜จ JSON ๊ฐ์ฒด๋Š” ํŠน์ • ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— instanceof๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ํŠน์ • ํ”„๋กœํผํ‹ฐ(x)๊ฐ€ ๊ฐ์ฒด ์•ˆ(in)์— ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ๋Ÿฐํƒ€์ž„์— ํ™•์ธํ•จ์œผ๋กœ์จ TS์—๊ฒŒ ์ด๊ฒŒ ์–ด๋–ค ํƒ€์ž…์ธ์ง€ ๋‚ฉ๋“์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Q3. [์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„: ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ๋”œ๋ ˆ๋งˆ] ์˜์ฒ ์ด๊ฐ€ ์˜์ˆ˜๋„ค ์‡ผํ•‘๋ชฐ์˜ ๊ฒฐ์ œ ์ˆ˜๋‹จ์„ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์นด์นด์˜คํŽ˜์ด, ๋„ค์ด๋ฒ„ํŽ˜์ด, ์‹ ์šฉ์นด๋“œ ๋“ฑ 5๊ฐ€์ง€ ๊ฒฐ์ œ ์ˆ˜๋‹จ์ด ์žˆ๊ณ , ๊ฐ ๊ฒฐ์ œ ์ˆ˜๋‹จ๋งˆ๋‹ค ์š”๊ตฌํ•˜๋Š” ๋ฐ์ดํ„ฐ ํผ(cardNumber vs authUrl ๋“ฑ)์ด ์•„์˜ˆ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ด๊ฑธ ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌํ•˜๋ ค๋Š”๋ฐ ์˜์ฒ ์ด๋Š” in ์—ฐ์‚ฐ์ž๋ฅผ 5๋ฒˆ์ด๋‚˜ ์“ฐ๋ฉด์„œ "์ด ์ฝ”๋“œ๋Š” ์ŠคํŒŒ๊ฒŒํ‹ฐ๊ฐ€ ๋˜์–ด๊ฐ€๊ณ  ์žˆ์–ด์š”"๋ผ๊ณ  ํ˜ธ์†Œํ•ฉ๋‹ˆ๋‹ค. ์˜ํ˜ธ ๋ฆฌ๋“œ๋ผ๋ฉด ์–ด๋–ค ํŒจํ„ด์œผ๋กœ ๋ฐ์ดํ„ฐ ์„ค๊ณ„๋ฅผ ๋ฆฌํŒฉํ† ๋งํ• ๊นŒ์š”?

โœ… ์ •๋‹ต: ๋ชจ๋“  ๊ฒฐ์ œ ํƒ€์ž… ๊ฐ์ฒด์— ๊ณตํ†ต๋œ method (์˜ˆ: "method": "KAKAO" | "method": "CARD") ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ, "์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ์œ ๋‹ˆ์–ธ(Discriminated Unions)" ํŒจํ„ด์œผ๋กœ ์ „ํ™˜ํ•œ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: ์†์„ฑ์˜ ์œ ๋ฌด(in ์—ฐ์‚ฐ์ž)๋กœ 5๊ฐœ๋‚˜ ๋˜๋Š” ํƒ€์ž…์„ ์ขํžˆ๋Š” ๊ฒƒ์€ ์œ ์ง€๋ณด์ˆ˜ ๊ด€์ ์—์„œ ์ฃ„์•…์ž…๋‹ˆ๋‹ค. ๊ฐ ์ธํ„ฐํŽ˜์ด์Šค์— ๊ณ ์ •๋œ ๋ฆฌํ„ฐ๋Ÿด ๋ฌธ์ž์—ด ์†์„ฑ(method๋‚˜ type ๋“ฑ)์„ ๋งŒ๋“ค๊ณ , ์ด๋ฅผ ๊ธฐ์ค€ ์‚ผ์•„ switch๋‚˜ if ๋ฌธ์œผ๋กœ ๋ถ„๊ธฐํ•˜๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์•Œ์•„์„œ ํƒ€์ž…์„ ๋งค๋ˆํ•˜๊ฒŒ ์ขํ˜€์ค๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ๋‚˜์ค‘์— ํ† ์ŠคํŽ˜์ด๋จผ์ธ ๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด in ์—ฐ์‚ฐ์ž ๋กœ์ง์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค ๋œฏ์–ด๊ณ ์น  ๊ฑด๊ฐ€์š”? ๊ธฐ์ค€์ (Tag) ํ•˜๋‚˜๋งŒ ๋ฐ•์•„๋‘๋ฉด TS๊ฐ€ ๋ชจ๋“  ๊ฑธ ๋‹ค ํ•ด์ค๋‹ˆ๋‹ค."
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๊ฐ์ฒด์˜ ๋ชจ์–‘์ด 3๊ฐœ ์ด์ƒ ๋‹ฌ๋ผ์ง€๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด, ๋ฌด์กฐ๊ฑด ๋ช…์ฐฐ(๊ณ ์œ ํ•œ ํ…์ŠคํŠธ ํ•„๋“œ) ๋ถ€ํ„ฐ ํ•˜๋‚˜ ๋ฐ•๊ณ  ์‹œ์ž‘ํ•˜์ž!

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

๐Ÿ’ก "์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ(unknown)๋Š” TS๊ฐ€ ๋‚  ๊ดด๋กญํžˆ๋ ค๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์„ธ์ƒ์˜ ๋ฌด์ˆ˜ํ•œ ์˜ˆ์™ธ๋กœ๋ถ€ํ„ฐ ๋‚  ๋ณดํ˜ธํ•ด ์ฃผ๋Š” ์•ˆ์ „๋ฒจํŠธ์˜€๋‹ค."

๊ทธ๋™์•ˆ catch(error: any)๋กœ ํŽธํ•˜๊ฒŒ ์‚ด์•˜๋˜ ๋‚˜๋‚ ์„ ์‚ฌ์ฃ„ํ•ฉ๋‹ˆ๋‹ค... ์˜์ˆ™ ๋‹˜์ด ์™œ ๊ทธ๋ ‡๊ฒŒ ํ™”๋ฅผ ๋ƒˆ๋Š”์ง€ ์ด์ œ ์กฐ๊ธˆ ์•Œ ๊ฒƒ ๊ฐ™๋‹ค. ๋‚ด๊ฐ€ any๋กœ ๋งˆ๋ฒ•์„ ๋ถ€๋ฆฐ๋‹ต์‹œ๊ณ  ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ๋ญ‰๋šฑ๊ทธ๋ ค ๋†“์•˜์œผ๋‹ˆ ์œ ์ €๋Š” ๋ฐ”๋ณด ๊ฐ™์€ ํŒ์—…๋งŒ ๋ด์•ผ ํ–ˆ๋˜ ๊ฑฐ๋‹ค.
์˜ค๋Š˜ ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๋ณด์—ฌ์ค€ '์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ์œ ๋‹ˆ์–ธ' ์ฝ”๋“œ๋Š” ์ •๋ง ์ถฉ๊ฒฉ์ ์ด์—ˆ๋‹ค. ๊ทธ์ € ํ•„๋“œ ํ•˜๋‚˜์— ๋ช…์ฐฐํ‘œ ๋‹ฌ์•„์คฌ์„ ๋ฟ์ธ๋ฐ, switch ๋ฌธ ์•ˆ์—์„œ TS๊ฐ€ ์ฒ™์ฒ™ ํƒ€์ž…์„ ์•Œ์•„๋‚ด์„œ ์–ด์‹œ์ŠคํŠธ๋ฅผ ํ•ด์ฃผ๋Š” ๊ฑธ ๋ณด๋‹ˆ ์ „์œจ๋งˆ์ € ๋Œ์•˜๋‹ค.
๋‚ด์ผ ์ถœ๊ทผํ•˜์ž๋งˆ์ž ๊ฒฐ์ œ ๋ชจ๋“ˆ ๋ถ„๊ธฐ๋ฌธ ๋‹ค ์—Ž๊ณ  ์ €๊ฑธ๋กœ ์‹น ๋ฆฌํŒฉํ† ๋งํ•ด์•ผ์ง€. ์•„, ์˜ค๋Š˜์€ ์ง„์งœ ๋˜‘๋˜‘ํ•ด์ง„ ๊ธฐ๋ถ„์ด๋‹ค ใ…‹ใ…‹ใ…‹ ์ฝ”๋”ฉ ์กด์žผ!