๐Ÿง  01. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๋ฉ˜ํƒˆ ๋ชจ๋ธ: JS์™€ TS์˜ ์‹คํ–‰ ๊ฒฝ๊ณ„

2026๋…„ 4์›” 30์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

๋Ÿฐํƒ€์ž„๊ณผ ๋นŒ๋“œํƒ€์ž„ ๊ตฌ๋ถ„, ๊ตฌ์กฐ์  ํƒ€์ดํ•‘ ๋“ฑ TS์˜ ํ•ต์‹ฌ ๋™์ž‘ ์›๋ฆฌ ํŒŒ์•…ํ•˜๊ธฐ

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

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

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
[๋Ÿฐํƒ€์ž„๊ณผ ๋นŒ๋“œํƒ€์ž„์˜ ์ดํ•ด] โ†’ [๊ตฌ์กฐ์  ํƒ€์ดํ•‘(Duck Typing)์˜ ๋น„๋ฐ€] โ†’ [ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๋ฐฐ์‹ (any)]

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

  • ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ์™€ TS ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๊ฒ€์‚ฌํ•˜๋Š” ์ฝ”๋“œ์˜ ์ฐจ์ด๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ตฌ์กฐ์  ํƒ€์ดํ•‘์ด ์™œ ์‹ค๋ฌด์—์„œ ์œ ์šฉํ•œ์ง€ (๊ทธ๋ฆฌ๊ณ  ์™œ ๊ฐ€๋” ์œ„ํ—˜ํ•œ์ง€) ์ดํ•ดํ•œ๋‹ค.
  • ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ์ฝ”๋“œ์—์„œ ํ”ํžˆ ๋ฐœ์ƒํ•˜๋Š” ํƒ€์ž… ๊ตฌ๋ฉ์„ ์–ด๋–ป๊ฒŒ ๋ง‰๋Š”์ง€ ์ฒด๋“ํ•œ๋‹ค.

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

  • ๐Ÿฃ ์˜์ฒ  ( ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ž…๋ฌธ ๋‹จ๊ณ„ ): "๋ฆฌ๋“œ ๋‹˜! ๋ฐฉ๊ธˆ ๋ฐฐํฌํ•œ ํ”„๋กœํ•„ ์ˆ˜์ • ๊ธฐ๋Šฅ์—์„œ Cannot read properties of undefined (reading 'nickname') ์—๋Ÿฌ ๋‚˜๋Š”๋ฐ์š”?! ์ปดํŒŒ์ผํ•  ๋•Œ๋Š” ์•„๋ฌด ์—๋Ÿฌ ์—†์—ˆ๋‹จ ๋ง์ด์—์š”!"
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์˜์ฒ  ๋‹˜, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์„ ์–ธ๋œ ํƒ€์ž…์„ ๊ธฐ์ค€์œผ๋กœ ๊ฒ€์‚ฌํ•ด์š”. ๊ทธ๋Ÿฐ๋ฐ API ์‘๋‹ต๊ฐ’์€ ๋Ÿฐํƒ€์ž„์— ๋„์ฐฉํ•˜๋‹ˆ, ์„ ์–ธ๋งŒ์œผ๋กœ๋Š” ์‹ค์ œ ๊ตฌ์กฐ๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์—†์ฃ . ๋นŒ๋“œํƒ€์ž„๊ณผ ๋Ÿฐํƒ€์ž„์˜ ์ฐจ์ด์ , ์˜ค๋Š˜ ํ™•์‹คํ•˜๊ฒŒ ์งš๊ณ  ๋„˜์–ด๊ฐ‘์‹œ๋‹ค."

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: '์—๋Ÿฌ'๋Š” ๋ˆ„๊ตฌ์˜ ์ž˜๋ชป์ธ๊ฐ€?

์˜์ฒ ์ด์˜ ์–ต์šธํ•จ์€ ๋‹น์—ฐํ•ฉ๋‹ˆ๋‹ค. VScode์—๋Š” ๋นจ๊ฐ„์ค„ ํ•˜๋‚˜ ์—†์—ˆ๊ณ , npm run build๋„ ์™„๋ฒฝํžˆ ํ†ต๊ณผํ–ˆ์œผ๋‹ˆ๊นŒ์š”. ํ•˜์ง€๋งŒ ์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ํ”„๋กœํ•„ ํ™”๋ฉด์ด ์˜ˆ์™ธ๋ฅผ ๋งŒ๋‚˜ ๋น„์–ด ๋ณด์ž…๋‹ˆ๋‹ค. ๋„๋Œ€์ฒด ๋ฌด์—‡์ด ๋ฌธ์ œ์˜€์„๊นŒ์š”?

์ด ํ˜„์ƒ์„ ์ดํ•ดํ•˜๋ ค๋ฉด, ๋จผ์ € ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰๋˜๋Š” ์ง„์งœ ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ์‚ฌ์‹ค์„ ๋ผˆ์ €๋ฆฌ๊ฒŒ ๋А๊ปด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋‹จ์ง€ ์ปดํŒŒ์ผ๋Ÿฌ(๊ฒ€๋ฌธ์†Œ)์ผ ๋ฟ, ์‹ค์ œ๋กœ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋‹ฌ๋ฆฌ๋Š” ๊ฑด 100% ์ˆœ์ˆ˜ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์ž…๋‹ˆ๋‹ค.

์ด **๋ฉ˜ํƒˆ ๋ชจ๋ธ(Mental Model)**์„ ์ œ๋Œ€๋กœ ์ •์ฐฉ์‹œํ‚ค์ง€ ๋ชปํ•˜๋ฉด, 5๋…„ ์ฐจ๊ฐ€ ๋˜์–ด๋„ any์™€ as๋ฅผ ๋‚จ๋ฐœํ•˜๋ฉฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์™€ ๋งค์ผ ์‹ธ์šฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


๐Ÿ—๏ธ 1. ๋นŒ๋“œํƒ€์ž„ vs ๋Ÿฐํƒ€์ž„ (ํ‰ํ–‰์„ธ๊ณ„์˜ ์ดํ•ด)

๊ฐ€์žฅ ํ”ํ•˜๊ฒŒ ์ฐฉ๊ฐํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ํƒ€์ž… ํ…์ŠคํŠธ๋Š” ์˜ค๋กœ์ง€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ(๋นŒ๋“œ ํƒ€์ž„) ์—๋งŒ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ ์˜คํ•ด ์ฝ”๋“œ

// ๐Ÿฃ ์˜์ฒ : "์œ ์ € ์ •๋ณด๋Š” ๋ฌด์กฐ๊ฑด User ํƒ€์ž…์ด๋‹ˆ๊นŒ ์•ˆ์ „ํ•˜๊ฒ ์ง€!"
type User = {
  id: number;
  nickname: string;
};
 
// ๋ฐฑ์—”๋“œ(์˜์ˆ˜)๊ฐ€ ๋ณด๋‚ด์ค€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€์ˆ˜์— ํ• ๋‹น
async function fetchUser() {
  const response = await fetch('/api/user/1');
  const data: User = await response.json(); // ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ ์—†์ด User๋ผ๊ณ  ์•ฝ์†ํ•œ ์ƒํƒœ
 
  return data;
}

๐Ÿฆ ์˜ํ˜ธ์˜ ๋ฆฌ๋ทฐ ์ฝ”๋ฉ˜ํŠธ

์œ„ ์ฝ”๋“œ์—์„œ const data: User๋Š” ๋ฐ์ดํ„ฐ ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ด ๋ฌธ์žฅ์€ ์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒŒ ์ด๋ ‡๊ฒŒ ์†์‚ญ์ด๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
"TS, ์ด API ์‘๋‹ต์€ User ๋ชจ์–‘์ด๋ผ๊ณ  ๋‚ด๊ฐ€ ๋ณด์žฅํ• ๊ฒŒ. ์—ฌ๊ธฐ์„œ๋Š” ๋” ์˜์‹ฌํ•˜์ง€ ์•Š์•„๋„ ๋ผ."

์‹ค์ œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋ณ€ํ™˜๋œ ์ฝ”๋“œ๋Š” ์ถฉ๊ฒฉ์ ์ผ ๋งŒํผ ํ—ˆ๋ฌดํ•ฉ๋‹ˆ๋‹ค.

// ํŠธ๋žœ์ŠคํŒŒ์ผ ํ›„์˜ ์ˆœ์ˆ˜ JS ์ฝ”๋“œ
async function fetchUser() {
  const response = await fetch('/api/user/1');
  const data = await response.json(); // ํƒ€์ž…์ด ํ”์ ๋„ ์—†์ด ์‚ฌ๋ผ์ง!
 
  return data;
}

๋งŒ์•ฝ ๋ฐฑ์—”๋“œ์˜ ์˜์ˆ˜ ๋‹˜์ด ์‹ค์ˆ˜๋กœ nickname ๋Œ€์‹  name์„ ๋„˜๊ฒจ์ฃผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

  • ๋นŒ๋“œํƒ€์ž„ (TS): ์˜์ฒ ์ด๊ฐ€ User๋ผ๊ณ  ์žฅ๋‹ดํ–ˆ์œผ๋‹ˆ ํ†ต๊ณผ.
  • ๋Ÿฐํƒ€์ž„ (๋ธŒ๋ผ์šฐ์ €): data.nickname์— ์ ‘๊ทผํ•˜๋Š” ์ˆœ๊ฐ„ undefined ๋•Œ๋ฌธ์— ํ™”๋ฉด ๋กœ์ง์ด ์‹คํŒจํ•  ์ˆ˜ ์žˆ์Œ.

๐Ÿ’ก TS์˜ ํ•œ๊ณ„: ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋„คํŠธ์›Œํฌ ํ†ต์‹  ๊ฒฐ๊ณผ, ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€ ๋ฐ์ดํ„ฐ, ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’ ๊ฐ™์€ '๋Ÿฐํƒ€์ž„'์˜ ๋™์  ๋ฐ์ดํ„ฐ๋ฅผ ์Šค์Šค๋กœ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ์ •ํ™•ํžˆ ๊ฐ€์ด๋“œํ•ด์ฃผ๊ฑฐ๋‚˜, Zod ๊ฐ™์€ ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿฆ† 2. ๊ตฌ์กฐ์  ํƒ€์ดํ•‘ (Duck Typing): ๋„Œ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋‹ˆ?

C#์ด๋‚˜ Java ์ถœ์‹ ์˜ ๊ฐœ๋ฐœ์ž๋“ค์ด TS๋ฅผ ์ฒ˜์Œ ์ ‘ํ•  ๋•Œ ๊ฐ€์žฅ ํ—ท๊ฐˆ๋ฆฌ๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.
ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๊ตฌ์กฐ์  ํƒ€์ดํ•‘(Structural Typing) ์„ ์ฑ„ํƒํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•ด "์˜ค๋ฆฌ์ฒ˜๋Ÿผ ์ƒ๊ฒผ๊ณ , ์˜ค๋ฆฌ์ฒ˜๋Ÿผ ๊ฝฅ๊ฝฅ๊ฑฐ๋ฆฌ๋ฉด, ๋„Œ ์˜ค๋ฆฌ๋‹ค" ๋ผ๋Š” ๋…ผ๋ฆฌ์ž…๋‹ˆ๋‹ค.

์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ถŒํ•œ ๋ถ€์—ฌ ์˜ˆ์‹œ

type BoardAdmin = {
  internalId: number;
  manageBoard: () => void;
};
 
type AppSuperUser = {
  internalId: number;
  manageBoard: () => void;
  deleteApp: () => void;
};
 
// ๊ฒŒ์‹œํŒ ๊ด€๋ฆฌ์ž๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ํ•จ์ˆ˜
function grantBoardAccess(admin: BoardAdmin) {
  admin.manageBoard();
}
 
// ๐Ÿฃ ์˜์ฒ ์˜ ์งˆ๋ฌธ
const superMan: AppSuperUser = {
  internalId: 100,
  manageBoard: () => console.log("๊ฒŒ์‹œํŒ ๊ถŒํ•œ ํš๋“"),
  deleteApp: () => console.log("์•ฑ ์‚ญ์ œ ์‹คํ–‰"),
};
 
// "BoardAdmin ํƒ€์ž…์ด ์•„๋‹Œ๋ฐ ์ด๊ฒŒ ๋“ค์–ด๊ฐ€์š”?"
grantBoardAccess(superMan); // โœ… ํ†ต๊ณผ! (์—๋Ÿฌ ์—†์Œ)

grantBoardAccess ํ•จ์ˆ˜๋Š” BoardAdmin์„ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” AppSuperUser๋ฅผ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค.
๋‹ค๋ฅธ ์–ธ์–ด๋ผ๋ฉด ์ƒ์†(extends) ๊ด€๊ณ„๊ฐ€ ๋ช…์‹œ๋˜์ง€ ์•Š์•„ ์—๋Ÿฌ๋ฅผ ๋ฟœ์—ˆ๊ฒ ์ง€๋งŒ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ํ†ต๊ณผ์‹œํ‚ต๋‹ˆ๋‹ค.

์™œ์ผ๊นŒ์š”? superMan์ด ์ตœ์†Œํ•œ BoardAdmin์ด ์š”๊ตฌํ•˜๋Š” ๋ชจ๋“  ์ŠคํŽ™(internalId, manageBoard) ์„ ๊ฐ–์ถ”๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ถ”๊ฐ€ ์†์„ฑ(deleteApp)์ด ๋” ์žˆ์–ด๋„, ๋ณ€์ˆ˜์— ๋‹ด๊ธด ๊ฐ’์„ ๋„˜๊ธฐ๋Š” ์ƒํ™ฉ์—์„œ๋Š” ํ•„์š”ํ•œ ๊ตฌ์กฐ๋ฅผ ๋งŒ์กฑํ•˜๋ฉด ํ˜ธํ™˜๋ฉ๋‹ˆ๋‹ค.

์ด ์œ ์—ฐ์„ฑ ๋•๋ถ„์— TS๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๋™์ ์ด๊ณ  ์ž์œ ๋ถ„๋ฐฉํ•œ ๊ฐ์ฒด ํ• ๋‹น ํŒจํ„ด์„ ๋ฌด๋ฆฌ ์—†์ด ํ’ˆ์–ด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿšซ 3. any์™€ as: ์ œ์–ด๊ถŒ์„ ํฌ๊ธฐํ•˜๋Š” ํ–‰์œ„

์•„ํ‚คํ…์ฒ˜ ๊ด€์ ์—์„œ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๋ฌด๊ธฐ๋Š” any๋„ ์•„๋‹ˆ๊ณ  Interface๋„ ์•„๋‹™๋‹ˆ๋‹ค. ๋ฐ”๋กœ ํƒ€์ž… ์ถ”๋ก (Type Inference) ์ž…๋‹ˆ๋‹ค.

โŒ ๋‚˜์œ ์˜ˆ (์˜์ฒ ์ด์˜ ํƒ€์ž… ๊ฐ•์ œ ์‚ฝ์ž…)

// ๐Ÿฃ ์˜์ฒ : "๋‚œ TS๊ฐ€ ๋ถˆ์•ˆํ•˜๋‹ˆ๊นŒ ๋ชจ๋“  ๋ณ€์ˆ˜์— ํƒ€์ž…์„ ๋‹ฌ์•„์ค„ ๊ฑฐ์•ผ!"
const message: string = "์•ˆ๋…•ํ•˜์„ธ์š”!";
const isLoading: boolean = true;
const userIds: number[] = [1, 2, 3];
 
const result = "๋น„๋ฐ€ ๋ฌธ์„œ" as any; // ๐Ÿ’ฃ ๊ธ‰ํ•  ๋•Œ ์“ฐ๋Š” ๋งŒ๋Šฅ์—ด์‡ 

โœ… ์ข‹์€ ์˜ˆ (์˜ํ˜ธ์˜ ์šฐ์•„ํ•œ ์ถ”๋ก )

// ๐Ÿฆ ์˜ํ˜ธ: "TS๋Š” ์ƒ๊ฐ๋ณด๋‹ค ๋˜‘๋˜‘ํ•ด์š”. ๋ช…๋ฐฑํ•œ ๊ฑด ๋†”๋‘๊ณ , ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ํž˜์„ ์ฃผ์ฃ ."
const message = "์•ˆ๋…•ํ•˜์„ธ์š”!"; // TS๊ฐ€ ์ด๋ฏธ string์œผ๋กœ ์•”
const isLoading = true; // boolean์œผ๋กœ ์•”
const userIds = [1, 2, 3]; // number[]๋กœ ์•”
 
// ๋ช…์‹œ์  ํƒ€์ž…์€ ์ธ์ž, ๋ฐ˜ํ™˜๊ฐ’, ๋ณต์žกํ•œ ๊ฐ์ฒด ์ƒ์„ฑ ์‹œ์— ์ง‘์ค‘!
function createProfile(name: string, age: number): User {
  return { id: 1, nickname: name };
}

ํŠนํžˆ as (Type Assertion)๋Š” "TS๊ฐ€ ์ถ”๋ก ํ•œ ๊ฒƒ๋ณด๋‹ค ๋‚ด๊ฐ€ ์ด ๊ฐ’์˜ ๋ชจ์–‘์„ ๋” ์ •ํ™•ํžˆ ์•Œ๊ณ  ์žˆ๋‹ค"๋ผ๊ณ  ์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒŒ ๊ฒ€์‚ฌ ์ฑ…์ž„์„ ๋„˜๊ธฐ๋Š” ์„ ์–ธ์ž…๋‹ˆ๋‹ค. ์›ฌ๋งŒํ•˜๋ฉด as ์—†์ด ํƒ€์ž…์ด ์œ ์—ฐํ•˜๊ฒŒ ํ˜๋Ÿฌ๊ฐ€๋„๋ก(Flow) ์ฝ”๋“œ๋ฅผ ์ž‘๊ฒŒ ์ชผ๊ฐœ๋Š” ๊ฒƒ์ด ์ง„์งœ ์‹ค๋ ฅ์ž…๋‹ˆ๋‹ค.

์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋Š” unknown์œผ๋กœ ๋ฐ›์•„ ์ž‘์€ ๊ฒ€์ฆ ๊ฒฝ๊ณ„๋ฅผ ํ†ต๊ณผ์‹œํ‚ค๋ฉด, ํƒ€์ž… ์„ ์–ธ๊ณผ ๋Ÿฐํƒ€์ž„ ์‚ฌ์‹ค์ด ์–ด๊ธ‹๋‚˜๋Š” ์ง€์ ์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type User = {
  id: number;
  nickname: string;
};
 
function isUser(value: unknown): value is User {
  if (typeof value !== "object" || value === null) return false;
 
  const user = value as Record<string, unknown>;
 
  return (
    typeof user.id === "number" &&
    typeof user.nickname === "string"
  );
}
 
async function fetchUserSafely() {
  const response = await fetch("/api/user/1");
  const data: unknown = await response.json();
 
  if (!isUser(data)) {
    throw new Error("ํ”„๋กœํ•„ API ์‘๋‹ต ํ˜•์‹์ด User์™€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.");
  }
 
  return data;
}

์ด ์ฝ”๋“œ๋Š” "๋นจ๊ฐ„ ์ค„์„ ์—†์• ๊ธฐ"๋ณด๋‹ค "์™ธ๋ถ€์—์„œ ๋“ค์–ด์˜จ ๊ฐ’์„ ์–ธ์ œ ์‹ ๋ขฐํ• ์ง€"๋ฅผ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค. ์˜์ฒ ์ด๋Š” ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ฌธ๋ฒ•์ด ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ ๊ฒฝ๊ณ„ ์„ค๊ณ„ ๋„๊ตฌ๋กœ ๋ณด๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.


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

Q1. ์™ธ๋ถ€ API์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ const data: User = await res.json()์œผ๋กœ ํƒ€์ดํ•‘ํ•˜๋Š” ๊ฒƒ์˜ ์œ„ํ—˜์„ฑ์€ ๋ฌด์—‡์ธ๊ฐ€?

โœ… ์ •๋‹ต: ๋นŒ๋“œํƒ€์ž„ ๊ฒ€์‚ฌ๋งŒ ํ†ต๊ณผํ•  ๋ฟ, ์‹ค์ œ ๋Ÿฐํƒ€์ž„ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๋ณด์žฅํ•˜์ง€ ๋ชปํ•จ.

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

  • res.json()์€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๊ด€์ ์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ any๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ณ€์ˆ˜์— : User๋ฅผ ๋‹จ๋‹ค๊ณ  ํ•ด์„œ ๊ฐ์ฒด ๋‚ด์šฉ์ด ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„(๋ธŒ๋ผ์šฐ์ €)์—์„œ ์‹ค์ œ๋กœ nickname ํ”„๋กœํผํ‹ฐ๊ฐ€ ์—†์œผ๋ฉด ๋‚˜์ค‘์— ์ ‘๊ทผ ์‹œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

Q2. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋Ÿฐํƒ€์ž„ ์–ธ์–ด์ž…๋‹ˆ๋‹ค. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋Ÿฐํƒ€์ž„์— ์—๋Ÿฌ๋ฅผ ๋ง‰์•„์ฃผ๋Š” ๋ฐฉํŒจ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๋‚˜์š”?

โœ… ์ •๋‹ต: ์•„๋‹ˆ์š”. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋ธŒ๋ผ์šฐ์ €(๋Ÿฐํƒ€์ž„)์—์„œ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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

  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๊ฒ€์‚ฌ๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ(๋นŒ๋“œ ํƒ€์ž„, VSCode ํŽธ์ง‘ ์ค‘)๊นŒ์ง€๋งŒ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค.
  • ์‹คํ–‰๋˜๋Š” ์‹œ์ ์—๋Š” ๋ชจ๋“  type๊ณผ interface ๊ป๋ฐ๊ธฐ ์ฝ”๋“œ๊ฐ€ ์ฆ๋ฐœํ•˜๊ณ  ์ˆœ์ˆ˜ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋งŒ ๋‚จ์Šต๋‹ˆ๋‹ค. ์ด ํ‰ํ–‰ ์„ธ๊ณ„๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ๋ฉ˜ํƒˆ ๋ชจ๋ธ์˜ ์ฒซ ๋‹จ์ถ”์ž…๋‹ˆ๋‹ค.

Q3. [์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„: ์‹ค๋ฌด ๋”œ๋ ˆ๋งˆ] ์˜์ฒ ์ด๊ฐ€ ๋””์ž์ธ ์‹œ์Šคํ…œ ์ปดํฌ๋„ŒํŠธ์ธ Button์„ ๋งŒ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด HTML <button>์˜ ๋ชจ๋“  ์†์„ฑ(onClick, disabled ๋“ฑ)์„ ๋‹ค ๋ฐ›์•„์˜ค๋ฉด์„œ ์ปค์Šคํ…€ ์†์„ฑ์ธ variant๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์–ด ํ•ฉ๋‹ˆ๋‹ค. "์ด๊ฑฐ Props๋ฅผ ์ผ์ผ์ด ๋‹ค ์ ์–ด์ค˜์•ผ ํ•˜๋‚˜์š”?"๋ผ๊ณ  ๊ณ ๋ฏผํ•  ๋•Œ, ๊ตฌ์กฐ์  ํƒ€์ดํ•‘์„ ์ดํ•ดํ•œ ์˜ํ˜ธ ๋ฆฌ๋“œ๋Š” ์–ด๋–ค ํ•ด๊ฒฐ์ฑ…(๊ฐœ๋…)์„ ์ œ์‹œํ• ๊นŒ์š”?

โœ… ์ •๋‹ต: ๊ธฐ์กด ๋ฒ„ํŠผ ์†์„ฑ ํƒ€์ž…์„ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ๊ต์ฐจํ•ด์„œ ์žฌ์‚ฌ์šฉํ•œ๋‹ค. ์˜ˆ: React.ButtonHTMLAttributes<HTMLButtonElement>์— variant๋ฅผ ๋”ํ•œ๋‹ค.

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

  • ๊ตฌ์กฐ์  ํƒ€์ดํ•‘ ๋•๋ถ„์— ButtonProps๊ฐ€ HTML ๋ฒ„ํŠผ์ด ์š”๊ตฌํ•˜๋Š” ์†์„ฑ ๊ตฌ์กฐ๋ฅผ ํฌํ•จํ•˜๋ฉด, ๋ณ„๋„์˜ ์ƒ์† ๊ณ„์ธต ์—†์ด๋„ ์•ˆ์ „ํ•˜๊ฒŒ ํ˜ธํ™˜๋ฉ๋‹ˆ๋‹ค.
  • type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & { variant?: "primary" | "secondary" }์ฒ˜๋Ÿผ ์ž‘์„ฑํ•˜๋ฉด ๊ธฐ์กด ์ด๋ฒคํŠธ, ์ ‘๊ทผ์„ฑ ์†์„ฑ, disabled ๊ฐ™์€ ํ‘œ์ค€ ์†์„ฑ์„ ๋น ๋œจ๋ฆด ์œ„ํ—˜์ด ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.
  • ํ•ต์‹ฌ์€ "ํƒ€์ž…์„ ์ „๋ถ€ ์ง์ ‘ ์“ฐ๊ธฐ"๊ฐ€ ์•„๋‹ˆ๋ผ, ์ด๋ฏธ ๊ฒ€์ฆ๋œ ํƒ€์ž… ๋ฉ์–ด๋ฆฌ์— ์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ์˜๋ฏธ ์žˆ๋Š” ์†์„ฑ๋งŒ ์–น๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

์˜ค๋Š˜์€ ๋นจ๊ฐ„ ์ค„์ด ์—†๋‹ค๋Š” ์‚ฌ์‹ค๊ณผ ๋Ÿฐํƒ€์ž„์ด ์•ˆ์ „ํ•˜๋‹ค๋Š” ์‚ฌ์‹ค์ด ๋‹ค๋ฅด๋‹ค๋Š” ๊ฑธ ์ œ๋Œ€๋กœ ๋ฐฐ์› ๋‹ค. const data: User = await res.json()์€ ๊ฒ€์ฆ์ด ์•„๋‹ˆ๋ผ ๋‚ด๊ฐ€ TS์—๊ฒŒ ๋ณด๋‚ธ ์•ฝ์†์ด์—ˆ๋‹ค. ๊ทธ ์•ฝ์†์ด ํ‹€๋ฆฌ๋ฉด ํ”ผํ•ด๋Š” ์‚ฌ์šฉ์ž ๋ธŒ๋ผ์šฐ์ €์—์„œ ์˜ˆ์™ธ๋กœ ๋“œ๋Ÿฌ๋‚œ๋‹ค.

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

๐Ÿ’ก "ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋Ÿฐํƒ€์ž„ ๋ฐ์ดํ„ฐ๋ฅผ ๋Œ€์‹  ๊ฒ€์ฆํ•˜์ง€ ์•Š๋Š”๋‹ค. ์™ธ๋ถ€์—์„œ ๋“ค์–ด์˜ค๋Š” ๊ฐ’์€ ํƒ€์ž… ์„ ์–ธ์ด ์•„๋‹ˆ๋ผ ๊ฒ€์ฆ ๊ฒฝ๊ณ„๋กœ ๋‹ค๋ค„์•ผ ํ•œ๋‹ค."

๋‚ด์ผ์€ ํ”„๋กœํ•„ API ์‘๋‹ต์„ ๋‹ค์‹œ ๋ณด๋ฉด์„œ unknown์œผ๋กœ ๋ฐ›์€ ๋’ค ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ ํ™•์ธํ•˜๋Š” ์ž‘์€ ์œ ํ‹ธ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด๋ณผ ์ƒ๊ฐ์ด๋‹ค. ๊ตฌ์กฐ์  ํƒ€์ดํ•‘์€ ํŽธํ•˜์ง€๋งŒ, ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ์—๋Š” ์‹ ๋ขฐ์˜ ์ถœ์ฒ˜๋ฅผ ๋‚จ๊ฒจ์•ผ ํ•œ๋‹ค๋Š” ๊ฑธ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ์ฒดํฌ๋ฆฌ์ŠคํŠธ์— ์ ์–ด๋‘ฌ์•ผ๊ฒ ๋‹ค.