๐Ÿง  01. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๋ฉ˜ํƒˆ ๋ชจ๋ธ: JS์™€ TS์˜ ์•„์Šฌ์•„์Šฌํ•œ ๊ทœ์น™

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

๐Ÿ“‹ ๊ฐœ์š”

๋Ÿฐํƒ€์ž„๊ณผ ๋นŒ๋“œํƒ€์ž„ ๊ตฌ๋ถ„, ๊ตฌ์กฐ์  ํƒ€์ดํ•‘ ๋“ฑ 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(); // ๐Ÿ’ฃ ์ง€์˜ฅ์˜ ์‹œ์ž‘
  
  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) ์ฝ”๋“œ๋ฅผ ์ž‘๊ฒŒ ์ชผ๊ฐœ๋Š” ๊ฒƒ์ด ์ง„์งœ ์‹ค๋ ฅ์ž…๋‹ˆ๋‹ค.


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

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๋ฅผ ์ผ์ผ์ด ๋‹ค ์ ์–ด์ค˜์•ผ ํ•˜๋‚˜์š”?"๋ผ๊ณ  ๊ณ ๋ฏผํ•  ๋•Œ, ๊ตฌ์กฐ์  ํƒ€์ดํ•‘์„ ์ดํ•ดํ•œ ์˜ํ˜ธ ๋ฆฌ๋“œ๋Š” ์–ด๋–ค ํ•ด๊ฒฐ์ฑ…(๊ฐœ๋…)์„ ์ œ์‹œํ• ๊นŒ์š”?

โœ… ์ •๋‹ต: "์˜ค๋ฆฌ์ฒ˜๋Ÿผ ์ƒ๊ธด ๊ฑด ๋‹ค ๋ฐ›์•„์™€!" (extends๋ฅผ ํ†ตํ•œ HTML attributes ํ™•์žฅ)

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

  • ์›๋ฆฌ ์„ค๋ช…: ๊ตฌ์กฐ์  ํƒ€์ดํ•‘์˜ ์œ ์—ฐํ•จ์„ ํ™œ์šฉํ•˜๋ฉด, type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & { variant?: 'primary' | 'secondary' } ํ˜•ํƒœ๋กœ ์†์‰ฝ๊ฒŒ HTML ๊ธฐ๋ณธ ์†์„ฑ์„ ์‹น ๋‹ค ํก์ˆ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, onClick?: () => void๋ถ€ํ„ฐ onMouseEnter๊นŒ์ง€ 100๊ฐœ๊ฐ€ ๋„˜๋Š” ์ด๋ฒคํŠธ ์†์„ฑ์„ ์ˆ˜์ž‘์—…์œผ๋กœ ์ ๊ณ  ์žˆ๋Š” ๊ฑด ์•„๋‹ˆ์ฃ ?"
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๋‚ด๊ฐ€ ํ•„์š”ํ•œ ์ŠคํŽ™(variant)๋งŒ ์ •์˜ํ•˜๊ณ , ๋‚˜๋จธ์ง€๋Š” ๊ฒ€์ฆ๋œ ๋ฉ์–ด๋ฆฌ(HTMLAttributes)์™€ ํ•ฉ์น˜๋ฉด ๊ทธ๋งŒ์ด๋‹ค.

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

๐Ÿ’ก "ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์ฒœ์‚ฌ ๊ฐ™์€ ๊ฒฝ๋น„์›์ด์ง€๋งŒ, ์„ฑ๋ฌธ์„ ํ†ต๊ณผํ•œ ํ›„์˜ ์‚ฌ๊ณ ๋Š” ์ฑ…์ž„์ ธ ์ฃผ์ง€ ์•Š๋Š”๋‹ค."

์˜ค๋Š˜๋„ ํ•œ๋ฐ”ํƒ• ๋‚œ๋ฆฌ์˜€๋‹ค. any์™€ as๋ฅผ ๋•์ง€๋•์ง€ ๋ฐœ๋ผ์„œ VScode์˜ ๋นจ๊ฐ„ ์ค„๋งŒ ์ง€์šฐ๋ฉด ๋์ธ ์ค„ ์•Œ์•˜๋˜ ๋‚˜์˜ ์•ˆ์ผํ•œ ๊ณผ๊ฑฐ๋ฅผ ๋ฐ˜์„ฑํ•˜๊ฒŒ ๋œ ํ•˜๋ฃจ. ๋งˆ์น˜ ์Œ์ฃผ ์ธก์ •๋„ ์•ˆ ํ•˜๊ณ  ๋ฌด์กฐ๊ฑด ํ†ต๊ณผ์‹œ์ผœ ์ฃผ๋Š” ๊ฒฝ์ฐฐ๊ด€์„ ์ข‹์€ ์‚ฌ๋žŒ์ด๋ผ๊ณ  ์ฐฉ๊ฐํ•œ ๊ฒƒ๊ณผ ๊ฐ™๋‹ฌ๊นŒ?
๋‚ด์ผ๋ถ€ํ„ฐ๋Š” API ์‘๋‹ต๊ฐ’์„ ๋‹ค๋ฃฐ ๋•Œ ์ ˆ๋Œ€ ๋‚˜ ์Šค์Šค๋กœ๋ฅผ ๋ฏฟ์ง€ ๋ง์•„์•ผ๊ฒ ๋‹ค. ์ง‘์— ๊ฐ€์„œ ๋Ÿฐํƒ€์ž„ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌํ•ด ์ฃผ๋Š” Zod๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜ ์ข€ ์ฐพ์•„๋ณด๊ณ  ์ž์•ผ์ง€. ์•„, ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ํ‡ด๊ทผ๊ธธ์— ๋˜์ ธ์ฃผ์‹  ๋ฐ”๋‚˜๋‚˜ ์šฐ์œ  ๋‹ฌ๋‹ฌํ•˜๋„ค!