๐Ÿ’ก 12. ์˜ํ˜ธ์˜ ๋ฆฌํŒฉํ† ๋ง: ์ œ์–ด์˜ ์—ญ์ „ (Inversion of Control)

๐Ÿ“‹ ๊ฐœ์š”

if๋ฌธ ์ง€์˜ฅ์— ๋น ์ง„ ๋งŒ๋Šฅ ์ปดํฌ๋„ŒํŠธ์˜ ์ œ์–ด๊ถŒ์„ ๋ถ€๋ชจ์—๊ฒŒ ๋Œ๋ ค์ฃผ์–ด, ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜๋Š” '์ œ์–ด์˜ ์—ญ์ „(IoC)' ํŒจํ„ด์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ์š”๊ตฌ์‚ฌํ•ญ์ด ์ถ”๊ฐ€๋  ๋•Œ๋งˆ๋‹ค boolean prop์ด ๋Š˜์–ด๋‚˜๋ฉฐ ๊ธฐ๊ดดํ•ด์ง€๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ๋ฌธ์ œ์ ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • "์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ชจ๋“  ๊ฑธ ๋‹ค ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋‘”๋‹ค"๋Š” ๊ฐ•๋ฐ•์—์„œ ๋ฒ—์–ด๋‚œ๋‹ค.
  • ์ œ์–ด์˜ ์—ญ์ „(IoC) ๊ฐœ๋…์„ ์ดํ•ดํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ์˜ ๋‚ด๋ถ€ ์ œ์–ด๊ถŒ์„ ์‚ฌ์šฉํ•˜๋Š” ์ชฝ(์œ ์ €/๋ถ€๋ชจ)์œผ๋กœ ๋„˜๊ธธ ํŒ๋‹จ ๊ธฐ์ค€์„ ์„ธ์šด๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


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

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

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

  • ์˜์ˆ˜(PM):"์˜์ฒ ์”จ, ์ €๋ฒˆ์— ๋งŒ๋“  ๊ทธ <UserProfileCard /> ๋ง์ด์—์š”. ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„  ์‚ฌ์ง„์ด ์™ผ์ชฝ์—, ๋งˆ์ดํŽ˜์ด์ง€์—์„  ์˜ค๋ฅธ์ชฝ์—, ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€์—์„  ์•„์˜ˆ ์‚ฌ์ง„ ์—†์ด ํ…์ŠคํŠธ๋งŒ ๊ตต๊ฒŒ ๋‚˜์˜ค๊ฒŒ ํ•ด์ฃผ์„ธ์š”."
  • ์˜์ฒ (์‹ ์ž…):"๋„ท! ๊ทธ๋Ÿผ... isMain, isMyPage, isAdmin prop์„ 3๊ฐœ ์ถ”๊ฐ€ํ•ด์„œ ๋‚ด๋ถ€์—์„œ if๋ฌธ์œผ๋กœ ๋‹ค ๊ฐˆ๋ผ์น ๊ฒŒ์š”! ์™„๋ฒฝํ•ฉ๋‹ˆ๋‹ค!"
  • ์˜ํ˜ธ(๋ฆฌ๋“œ):(๊ฒฝ์•…ํ•˜๋ฉฐ) "์˜์ฒ  ๋‹˜, ์ œ๋ฐœ ๋ฉˆ์ถ”์„ธ์š”! ๊ทธ ์ปดํฌ๋„ŒํŠธ ํ˜ผ์ž์„œ ์„ธ์ƒ ๋ชจ๋“  ๊ฒฝ์šฐ์˜ ์ˆ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋งŒ๋“ค ์…ˆ์ž…๋‹ˆ๊นŒ?"

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: '๋งŒ๋Šฅ ํญ๊ตฐ' ์ปดํฌ๋„ŒํŠธ์˜ ํƒ„์ƒ๊ณผ ๋ชฐ๋ฝ

๋ณดํ†ต ์‹ ์ž… ๊ฐœ๋ฐœ์ž๋“ค์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ, "์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋‚ด๊ฐ€ ์ง  ์บก์А์ด๋‹ค. ๋ฐ–์—์„œ ์Šค์œ„์น˜(props)๋งŒ ๋”ธ๊น ๋ˆ„๋ฅด๋ฉด ์•Œ์•„์„œ ๋‚ด๋ถ€์—์„œ ํ˜•ํƒœ๋ฅผ ๋ฐ”๊พธ๋Š” ๋˜‘๋˜‘ํ•œ ๋†ˆ์œผ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ด!" ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ ๊ฑฐ๋Œ€ํ•œ ์ฐฉ๊ฐ์ž…๋‹ˆ๋‹ค.

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
์˜์ฒ ์ด์˜ ํญ๊ตฐ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณด์ž. ๋‚˜์ค‘์— "์‚ฌ์ง„์„ ์•„์˜ˆ ๋นผ๊ณ  ๋ฑƒ์ง€๋งŒ ๋„ฃ์–ด์ฃผ์„ธ์š”"๋ผ๋Š” ์š”๊ตฌ๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ์ด ์ฝ”๋“œ์— ๋˜ ๋ฌด์Šจ prop์ด ์ถ”๊ฐ€๋ ๊นŒ?

// โŒ ์˜์ฒ ์ด์˜ ๋งŒ๋Šฅ ๋”์ฐํ•œ ๊ดด๋ฌผ ์ปดํฌ๋„ŒํŠธ (Apocalypse Component)
function UserProfileCard({ 
  user, 
  isMainPage,     // ๋”ธ๊น ์Šค์œ„์น˜ 1
  isMyPage,       // ๋”ธ๊น ์Šค์œ„์น˜ 2
  isAdmin,        // ๋”ธ๊น ์Šค์œ„์น˜ 3
  showAvatar,     // ๋”ธ๊น ์Šค์œ„์น˜ 4
  showBadge       // ๋”ธ๊น ์Šค์œ„์น˜ 5
}) {
  return (
    <div className="card">
      {/* ๐Ÿ’ฅ ์ง€์˜ฅ์˜ if/else ๋ถ„๊ธฐ๋ฌธ ํŒŒํ‹ฐ ์‹œ์ž‘ */}
      {isAdmin && <div className="admin-ribbon">๊ด€๋ฆฌ์ž</div>}
      
      {isMainPage && showAvatar ? (
        <img src={user.avatar} className="avatar left" />
      ) : isMyPage && showAvatar ? (
        <img src={user.avatar} className="avatar right" />
      ) : null}
 
      <div className="info">
        <strong>{user.name}</strong>
        {showBadge && <span className="badge">์‹ ๊ทœ๊ฐ€์ž…</span>}
      </div>
    </div>
  );
}

์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹จ์ผ ์ฑ…์ž„ ์›์น™(SRP) ์„ ์ฒ˜์ฐธํ•˜๊ฒŒ ํŒŒ๊ดดํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด ๋…€์„์€ ๋งˆ์ดํŽ˜์ด์ง€๋„ ์•Œ์•„์•ผ ํ•˜๊ณ , ๋ฉ”์ธ ํŽ˜์ด์ง€๋„ ์•Œ์•„์•ผ ํ•˜๊ณ , ๊ด€๋ฆฌ์ž ๊ถŒํ•œ UI๊นŒ์ง€ ๋‹ค ์Šค์Šค๋กœ ํ†ต์ œ(Control)ํ•˜๋ ค ๋“ญ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋‚ด๋ถ€ ๋กœ์ง์ด ๊ฑฐ๋Œ€ํ•ด์ง€๋ฉด ๋‚˜์ค‘์—๋Š” ์ด ํŒŒ์ผ์„ ์—ฐ ์‚ฌ๋žŒ ๋ชจ๋‘๊ฐ€ ์ฝ”๋“œ๋ฅผ ๊ฑด๋“œ๋ฆฌ๊ธฐ ๋‘๋ ค์›Œํ•˜๋Š” '๋ ˆ๊ฑฐ์‹œ ์ง€๋ขฐ'๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.


๐Ÿ—๏ธ ๋น„์œ ๋กœ ๋จผ์ € ์ดํ•ดํ•˜๊ธฐ

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?
์˜์ฒ ์ด์˜ ๋ฐฉ์‹ (์ œ์–ด๋ฅผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋…์ ):
์‹๋‹น ์š”๋ฆฌ์‚ฌ(์ปดํฌ๋„ŒํŠธ)ํ•œํ…Œ ์ฃผ๋ฌธ์„ ํ•˜๋Š”๋ฐ ๋ฉ”๋‰ดํŒ์ด ๋„ˆ๋ฌด ๊ธฐ๊ดดํ•ด.
"์ €๊ธฐ์š”, '๋งค์šด๋ง› ์ถ”๊ฐ€ ์—ฌ๋ถ€: true', '์น˜์ฆˆ ๋นผ๊ธฐ ์—ฌ๋ถ€: false', '์–‘ํŒŒ๋Š” ๊ตฌ์šด ์–‘ํŒŒ๋กœ ๋ฐ”๊ฟ€์ง€ ์—ฌ๋ถ€: true'์ธ ์Œ์‹ ํ•˜๋‚˜ ์ฃผ์„ธ์š”."
์š”๋ฆฌ์‚ฌ๋Š” ๋ชจ๋“  ์˜ต์…˜์„ ๋‹ฌ๋‹ฌ ์™ธ์šฐ๊ณ  ๋ฉ”๋‰ดํŒ ์Šค์œ„์น˜๋ฅผ ์˜ฌ๋ ค๊ฐ€๋ฉฐ ์ง„๋•€ ๋นผ๋ฉฐ ์š”๋ฆฌํ•ด์•ผ ํ•ด.

์˜ํ˜ธ์˜ ๋ฐฉ์‹ (์ œ์–ด์˜ ์—ญ์ „ - Inversion of Control):
๋ท”ํŽ˜(์ œ์–ด๊ถŒ ์œ„์ž„)๋กœ ์‹๋‹น์„ ๋ฐ”๊ฟ”๋ฒ„๋ ธ์–ด!
์š”๋ฆฌ์‚ฌ๋Š” ๊ทธ๋ƒฅ ๋ฐฅ, ๊ณ ๊ธฐ, ์•ผ์ฑ„(์ž‘์€ ์ปดํฌ๋„ŒํŠธ ์กฐ๊ฐ๋“ค)๋งŒ ์ด์˜๊ฒŒ ์ฐ์–ด์„œ ์Ÿ๋ฐ˜์— ๋‹ด์•„์ค˜.
๋ฐฐ์น˜ํ•˜๊ณ  ์„ž์–ด ๋จน๋Š” ๊ถŒํ•œ(์ œ์–ด๊ถŒ)์€ ์†๋‹˜(๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์“ฐ๋Š” ์œ ์ €)ํ•œํ…Œ ์•„์˜ˆ ๋„˜๊ฒจ๋ฒ„๋ฆฌ๋Š” ๊ฑฐ์•ผ. ์†๋‹˜์ด ์ง์ ‘ ๋ฐฅ ์œ„์— ๊ณ ๊ธฐ๋ฅผ ์–น๋“  ์•ผ์ฑ„๋ฅผ ์–น๋“  ๋ง˜๋Œ€๋กœ ํ•˜๋ผ๊ณ ! ์š”๋ฆฌ์‚ฌ๋Š” ๋ง˜ ํŽธํ•˜๊ฒŒ ๊ธฐ๋ณธ ์žฌ๋ฃŒ๋งŒ ๋”ฑ๋”ฑ ๋‚ด์–ด์ฃผ๋ฉด ๋ผ.

์ด๊ฒƒ์ด ์˜ค๋Š˜ ๋ฐฐ์šธ IoC(์ œ์–ด์˜ ์—ญ์ „) ์ž…๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋ชจ๋“  ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ฒ ๋‹ค๋Š” ์˜ค๋งŒ์„ ๋ฒ„๋ฆฌ๊ณ , UI๋ฅผ ์–ด๋–ป๊ฒŒ ์กฐ๋ฆฝํ•  ๊ฒƒ์ธ๊ฐ€์— ๋Œ€ํ•œ ํ†ต์ œ๊ถŒ์„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ชฝ(๋ถ€๋ชจ)์œผ๋กœ ๋‚ด๋˜์ง€๋Š”(Invert) ์„ค๊ณ„๋ฒ• ์ž…๋‹ˆ๋‹ค.


๐Ÿงฉ ๊ทน์˜ ์ฒด๋“: IoC ํŒจํ„ด ์‹ค์ „ ๋ฆฌํŒฉํ† ๋ง

โœ… 1๋‹จ๊ณ„: ๋ Œ๋”๋ง ํ”„๋กญ์Šค (Render Props) / ์Šฌ๋กฏ ์—ด์–ด์ฃผ๊ธฐ

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ด๊ณ  ์ง๊ด€์ ์ธ ์ œ์–ด๊ถŒ ์–‘๋„ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ์•ˆ์— if ๋ถ„๊ธฐ๋ฅผ ์น˜๋Š” ๋Œ€์‹ , ๋ฐ”๊นฅ์—์„œ๋ถ€ํ„ฐ UI ์กฐ๊ฐ ์ž์ฒด๋ฅผ ํ”„๋กญ์Šค๋กœ ๊ฝ‚์•„ ๋„ฃ์„ ์ˆ˜ ์žˆ๊ฒŒ **'๊ตฌ๋ฉ(Slot)'**์„ ๋šซ์–ด์ค๋‹ˆ๋‹ค.

// โœ… ์˜ํ˜ธ์˜ 1์ฐจ ๋ฆฌํŒฉํ† ๋ง: ๊ตฌ๋ฉ ๋šซ๊ธฐ (IoC ์ ์šฉ)
// ๋” ์ด์ƒ isMainPage, isAdmin ๊ฐ™์€ ๋ณ€์ˆ˜๊ฐ€ ํ•„์š” ์—†๋‹ค!
function UserProfileCard({ user, avatarSlot, badgeSlot }) {
  return (
    <div className="card">
      {/* ๋‚˜๋Š” ๋ชจ์–‘๋งŒ ์žก์•„์ค„ ํ…Œ๋‹ˆ, ์ง„์งœ ์•Œ๋งน์ด๋Š” ์•Œ์•„์„œ ๋ผ์›Œ ๋„ฃ์–ด๋ผ! */}
      <div className="avatar-area">{avatarSlot}</div>
      <div className="info">
        <strong>{user.name}</strong>
        <div className="badge-area">{badgeSlot}</div>
      </div>
    </div>
  );
}
 
// ๐ŸŽฏ ์‚ฌ์šฉํ•˜๋Š” ๋ถ€๋ชจ ์ž…์žฅ (์ œ์–ด๊ถŒ์„ ํš๋“ํ•จ)
function AdminPage() {
  return (
    <UserProfileCard 
      user={{ name: "์˜์ˆ˜" }}
      // ๋ถ€๋ชจ๊ฐ€ "์ด ํŽ˜์ด์ง€์—์„  ์•„๋ฐ”ํƒ€ ์ž๋ฆฌ์— ๊ด€๋ฆฌ์ž ๋งˆํฌ๋ฅผ ๋„ฃ์ž!"๋ผ๊ณ  ์Šค์Šค๋กœ ์ œ์–ดํ•จ
      avatarSlot={<div className="admin-ribbon">๊ด€๋ฆฌ์ž</div>}
      badgeSlot={null}
    />
  );
}

์ด์ œ <UserProfileCard />๋Š” ์ž๊ธฐ๊ฐ€ ์–ด๋А ํŽ˜์ด์ง€์— ์žˆ๋Š”์ง€ ์ „ํ˜€ ์•Œ ํ•„์š”๋„, ์•Œ ๊ถŒ๋ฆฌ๋„ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ์ € ๋„˜๊ฒจ๋ฐ›์€ ๋ฉ์–ด๋ฆฌ๋ฅผ ๋ฌต๋ฌตํžˆ ์ •ํ•ด์ง„ ๋ ˆ์ด์•„์›ƒ์— ๋ผ์›Œ์ค„ ๋ฟ์ž…๋‹ˆ๋‹ค. ๋ณต์žก๋„๊ฐ€ 1/10๋กœ ์ค„์–ด๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.


โœ… 2๋‹จ๊ณ„: Children ํŒจํ„ด ์ฐข๊ธฐ (์™„์ „ํ•œ ํ•ด๋ฐฉ)

1๋‹จ๊ณ„์—์„œ ๋ฐœ์ „ํ•˜์—ฌ, ํ”„๋กญ์Šค ๊ตฌ๋ฉ์กฐ์ฐจ ๊ท€์ฐฎ๋‹ค๋ฉด ์• ์ดˆ์— ๋ถ€๋ชจ ๊ป๋ฐ๊ธฐ์™€ ์† ์•Œ๋งน์ด๋“ค์„ ์™„์ „ํžˆ ๋‹ค์ง„ ๊ณ ๊ธฐ์ฒ˜๋Ÿผ ์ฐข์–ด๋ฒ„๋ ค ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ ˆ๊ณ  ๋ธ”๋ก์œผ๋กœ ์ฅ์—ฌ ์ค๋‹ˆ๋‹ค. (์ด๊ฒƒ์ด 13๊ฐ•์—์„œ ๋ฐฐ์šธ ์ปดํŒŒ์šด๋“œ ์ปดํฌ๋„ŒํŠธ์˜ ๋ผˆ๋Œ€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.)

// โœ… ์˜ํ˜ธ์˜ 2์ฐจ ๋ฆฌํŒฉํ† ๋ง: ๊ทน๋‹จ์  ์ฐข๊ธฐ (IoC ์™„์„ฑ)
function CardWrapper({ children }) {
  return <div className="card shadow-md flex">{children}</div>;
}
 
function CardAvatar({ src, position = "left" }) {
  return <img src={src} className={`avatar ${position}`} />;
}
 
function CardInfo({ name, children }) {
  return (
    <div className="info">
      <strong>{name}</strong>
      {children}
    </div>
  );
}
 
// ๐ŸŽฏ ์‚ฌ์šฉํ•˜๋Š” ๋ถ€๋ชจ ์ž…์žฅ: ์ด์ œ ์™„๋ฒฝํ•œ ์กฐ๋ฆฝ์‹ ๋ ˆ๊ณ (DIY)๋ฅผ ์–ป์—ˆ๋‹ค.
function MyPage() {
  return (
    <CardWrapper>
      <CardInfo name="์˜์ˆ˜">
        <span className="badge">๋‹จ๊ณจ์†๋‹˜</span>
      </CardInfo>
      {/* ๋‚ด ๋ง˜๋Œ€๋กœ ์•„๋ฐ”ํƒ€๋ฅผ ์˜ค๋ฅธ์ชฝ์— ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค! Card ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—ด์–ด๋ณผ ํ•„์š”๊ฐ€ ์—†๋‹ค! */}
      <CardAvatar src="img.jpg" position="right" /> 
    </CardWrapper>
  );
}

์ด ์ฝ”๋“œ๋ฅผ ๋ณด์„ธ์š”. ์˜์ฒ ์ด๊ฐ€ ์ฒ˜์Œ์— ์ง  ๊ทธ ์ถ”์•…ํ•œ isMyPage, showBadge ๋“ฑ์˜ ๋ถˆ๋ฆฌ์–ธ ์Šค์œ„์น˜(Props) ๋”๋ฏธ๋“ค์ด ๋‹จ ํ•˜๋‚˜๋„ ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ๋Š” ๋ˆˆ๋ถ€์‹œ๊ฒŒ ๊นจ๋—ํ•ด์กŒ๊ณ , ๋ฏธ๋ž˜์— "์•„๋ฐ”ํƒ€ ๋‘ ๊ฐœ ๋„ฃ์–ด์ฃผ์„ธ์š”"๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด ์™€๋„ ๊ธฐ์กด ์ปดํฌ๋„ŒํŠธ ์ฝ”๋“œ๋Š” ํ•œ ์ค„๋„ ๊ฑด๋“œ๋ฆด ํ•„์š” ์—†์ด ์กฐ๋ฆฝ์‹์œผ๋กœ ํˆญ ๋ผ์›Œ ๋„ฃ์œผ๋ฉด ๊ทธ๋งŒ์ž…๋‹ˆ๋‹ค.


๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

์ปดํฌ๋„ŒํŠธ ์„ค๊ณ„ ๋ฐฉ์‹ํŠน์ง• ๋ฐ ๋ฌธ์ œ์ ํ•ด๊ฒฐ์ฑ… (IoC)
๋…์žฌ์ž ํŒจํ„ด (Anti-pattern)์ž๊ธฐ๊ฐ€ ๋ชจ๋“  ๊ฒฝ์šฐ์˜ ์ˆ˜(boolean props)๋ฅผ ๋‹ค ๋ฐ›์•„ ๋‚ด๋ถ€์—์„œ if๋ฌธ์œผ๋กœ ์…€ํ”„ ๋ Œ๋”๋ง. ์ˆ˜์ • ์‹œ ๋ ˆ๊ฑฐ์‹œ ์ง€๋ขฐ๊ฐ€ ๋จ.์ œ์–ด๊ถŒ์„ ๋ถ€๋ชจ๋กœ ์—ญ์ „์‹œํ‚ด.
IoC: Render Props / Slot๋ ˆ์ด์•„์›ƒ์€ ์œ ์ง€ํ•˜๋˜ ํŠน์ • ์˜์—ญ์— ๋ Œ๋”๋งํ•  JSX๋ฅผ ๋ถ€๋ชจ๊ฐ€ ํ”„๋กญ์Šค ๊ฐ์ฒด๋กœ ๋ฐ€์–ด ๋„ฃ์–ด ์คŒ.headerSlot={<Header/>}
IoC: Children ํ•ฉ์„ฑ๊ป๋ฐ๊ธฐ๋งŒ ์ œ๊ณตํ•˜๊ณ , ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ๋‚ด๋ถ€ ์•Œ๋งน์ด ๋ฐฐ์น˜๋ฅผ ์™„๋ฒฝํžˆ ์ž์œ ๋กญ๊ฒŒ ํ’€์–ด์คŒ.<Wrapper><A/><B/></Wrapper>

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
"๋ถˆ๋ฆฌ์–ธ(Boolean) ํ”„๋กญ์Šค ์Šค์œ„์น˜๊ฐ€ 3๊ฐœ๋ฅผ ๋„˜์–ด๊ฐ€๋Š” ์ˆœ๊ฐ„, ๊ทธ ์ปดํฌ๋„ŒํŠธ๋Š” ์ฃฝ์€ ๊ฒƒ์ด๋‹ค."
๊ทธ๋Ÿด ๋• ์–ต์ง€๋กœ ์Šค์œ„์น˜๋ฅผ ๋” ๋‹ฌ์ง€ ๋ง๊ณ , ๋‚ด๋ถ€ ๋กœ์ง์˜ ๋ฑƒ๊ตฌ๋ ˆ๋ฅผ ๊ฐ€๋ฅด๊ณ  ๋„์ง‘์–ด๋‚ด์–ด ๋ถ€๋ชจ๊ฐ€ ์ง์ ‘ ์กฐ๋ฆฝํ•˜๊ฒŒ ์ œ์–ด๊ถŒ(Control)์„ ๋˜์ ธ๋ฒ„๋ ค๋ผ(Invert).


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

๋‚ด๊ฐ€ ์ง  ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ ์  ๊ดด๋ฌผ์ด ๋˜์–ด๊ฐ€๋Š” ๊ฑธ ๋ณด๋ฉด์„œ '์›๋ž˜ ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ์ด๋Ÿฐ ๊ฑด๊ฐ€?' ๋‹จ๋…ํ–ˆ์—ˆ๋Š”๋ฐ, ๊ทธ๊ฒŒ ์•„๋‹ˆ๋ผ ๋‚ด๊ฐ€ ๋…์žฌ์ž์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ๋ชจ๋“  ์ƒ์‚ฌ์—ฌํƒˆ๊ถŒ์„ ์ฅ๊ณ  ํ”๋“ค๋ ค๋‹ค ๋งํ•œ ๊ฑฐ์˜€๋‹ค.

๐Ÿ’ก "์ปดํฌ๋„ŒํŠธ๋Š” ์™„์ œํ’ˆ ์š”๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ ˆ๊ณ  ๋ธ”๋ก์ด์–ด์•ผ ํ•œ๋‹ค. ์ œ์–ด๊ถŒ์„ ๋ถ€๋ชจ์—๊ฒŒ ๋’ค์ง‘์–ด ๋˜์ง€๋Š”(IoC) ์ˆœ๊ฐ„, ์ง€์˜ฅ ๊ฐ™์€ ๋ถ„๊ธฐ๋ฌธ์ด ๋ˆˆ ๋…น๋“ฏ ์‚ฌ๋ผ์ง„๋‹ค!"

๋ฉ”๋‰ดํŒ ์Šค์œ„์น˜ ๋น„์œ ๊ฐ€ ์ง„์งœ ์˜ˆ์ˆ ์ด๋‹ค. ๊ป๋ฐ๊ธฐ๋งŒ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ์•Œ๋งน์ด๋Š” ๋ถ€๋ชจ๊ฐ€ ์ง์ ‘ ๊ฝ‚์•„ ๋„ฃ๊ฒŒ ๋งŒ๋“œ๋Š” ๋ Œ๋” ํ”„๋กญ์Šค๋‚˜ Children ๊ตฌ๋ฉ ๋šซ๊ธฐ ํŒจํ„ด ํ•˜๋‚˜๋งŒ ์•Œ์•„๊ฐ€๋„ ์˜ค๋Š˜ ํ•˜๋ฃจ ๋ฐฅ๊ฐ’์€ ๋‹ค ํ•œ ๋“ฏ. ์กฐ๋งŒ๊ฐ„ ์‚ฌ๋‚ด ์Šคํ„ฐ๋”” ๋•Œ ๋‚ด๊ฐ€ ์ด '๋งŒ๋Šฅ ์ปดํฌ๋„ŒํŠธ ํญํŒŒ์„ค'๋กœ ๋ฐœํ‘œ ํ•œ๋ฒˆ ํ•ด์•ผ๊ฒ ๋‹ค.


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

Q1. ์˜์ฒ ์ด๊ฐ€ ๋‹ค์Œ์ฒ˜๋Ÿผ Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹(์ œ์–ด ๋…์ )์˜ ๊ฐ€์žฅ ์น˜๋ช…์ ์ธ ๋ฌธ์ œ์ ์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ง€์ ํ•œ ๊ฒƒ์€?

function Button({ isPrimary, isDanger, hasIcon, iconType, text }) {
  return (
    <button className={`btn ${isPrimary ? 'primary' : ''} ${isDanger ? 'red' : ''}`}>
      {hasIcon && iconType === 'save' && <SaveIcon />}
      {hasIcon && iconType === 'delete' && <TrashIcon />}
      {text}
    </button>
  );
}
  • A) ํด๋ž˜์Šค๋ช…์„ ํ•ฉ์น  ๋•Œ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ๋ฌธ๋ฒ•์ด ๋„ˆ๋ฌด ๊ธธ์–ด ์›นํŒฉ ๋ฒˆ๋“ค๋ง ์šฉ๋Ÿ‰์ด ํ„ฐ์ง€๊ธฐ ๋•Œ๋ฌธ.
  • B) ์ดํ›„ '์•„์ด์ฝ˜์ด ์˜ค๋ฅธ์ชฝ์— ๋ฐฐ์น˜๋˜๋Š” ๋ฒ„ํŠผ'์ด๋‚˜ '๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ ๋ฒ„ํŠผ' ๋“ฑ์˜ ์ƒˆ๋กœ์šด ์š”๊ตฌ์‚ฌํ•ญ์ด ์ƒ๊ธธ ๋•Œ๋งˆ๋‹ค ๋์—†์ด isLoading, iconPosition ๊ฐ™์€ ์Šค์œ„์น˜๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉฐ ํŒŒ๋ฉธ์  OCP(๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™) ์œ„๋ฐ˜ ์ฝ”๋“œ๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ.
  • C) button ํƒœ๊ทธ ๋‚ด๋ถ€์— ์ปค์Šคํ…€ ์ปดํฌ๋„ŒํŠธ(<SaveIcon />)๋ฅผ ๋ Œ๋”๋งํ•˜๋ฉด ๋ฆฌ์•กํŠธ ๊ฐ€์ƒ๋” ์—”์ง„์ด ์ด๋ฅผ SVG๋กœ ํŒŒ์‹ฑํ•˜์ง€ ๋ชปํ•˜๊ณ  ๋ฌดํ•œ ๋ฃจํ”„์— ๋น ์ง€๊ธฐ ๋•Œ๋ฌธ.

โœ… ์ •๋‹ต: B

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ๋งŒ๋Šฅ(God) ํ”„๋กญ์Šค ์„ค๊ณ„์˜ ๋น„๊ทน์ž…๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ํ•˜๋‚˜๊ฐ€ ์„ธ์ƒ์˜ ๋ชจ๋“  ๋ณ€ํ™”๋ฅผ ์ž๊ธฐ ๋ชธํ†ต ๋‚ด๋ถ€์—์„œ ํ˜ผ์ž ๊ฐ๋‹นํ•˜๋ ค ํ•˜๋Š” ์ˆœ๊ฐ„ ์ŠคํŒŒ๊ฒŒํ‹ฐ ์ฝ”๋“œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. B์˜ ์„ค๋ช…์ฒ˜๋Ÿผ ํ™•์žฅ์„ฑ(Open-Closed Principle)์ด ์™„๋ฒฝํžˆ ๋ฐ•์‚ด ๋‚œ ์ฝ”๋“œ๋ฉฐ, ๋‚˜์ค‘์—” isPrimaryHoveringAndSaveIconLeftWithRedText ๋”ฐ์œ„์˜ ๊ธฐ๊ดดํ•œ ์ƒํƒœ๋“ค๋งˆ์ € ์ƒ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์ด ์ฝ”๋“œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด <Button><SaveIcon /> ์ €์žฅ</Button> ์ฒ˜๋Ÿผ children ์œผ๋กœ ์•„์ด์ฝ˜ ๋ Œ๋”๋ง์„ ์™„์ „ํžˆ ๋ฐ€์–ด ๋„˜๊ธฐ๋Š” IoC ํŒจํ„ด์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.)

Q2. ์ œ์–ด์˜ ์—ญ์ „(IoC) ํŒจํ„ด์„ ์ ์šฉํ•˜์—ฌ, ์œ„ ์˜์ฒ ์ด์˜ ๋”๋Ÿฌ์šด Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถ€๋ชจ๊ฐ€ "์ง์ ‘ ๋‚ด๋ถ€ ๋ชจ์–‘ ์š”์†Œ๋“ค์„ ์ž์œ ์ž์žฌ๋กœ ์กฐ๋ฆฝ"ํ•˜๊ฒŒ ๋ฆฌํŒฉํ† ๋งํ•œ ์˜ฌ๋ฐ”๋ฅธ ๊ฒฐ๊ณผ๋ฌผ์€ ์–ด๋А ๊ฒƒ์ธ๊ฐ€์š”?

  • A) function Button({ type, label }) { return <button className={type}>{label}</button> }
  • B) function Button({ configObject }) { return <button>{configObject.render()}</button> }
  • C) function Button({ className, children }) { return <button className={btn $}>{children}</button> }

โœ… ์ •๋‹ต: C

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: C๊ฐ€ ๋ฐ”๋กœ '์™„๋ฒฝํ•œ ๋ Œ๋”๋ง ์ž์œ '์™€ ์ฑ…์ž„์„ ๋ถ€๋ชจ์—๊ฒŒ ๋„˜๊ฒจ๋ฒ„๋ฆฐ IoC ํ•ฉ์„ฑ(Composition) ํŒจํ„ด์˜ ์ •์„์ž…๋‹ˆ๋‹ค. ์ž์‹(Button)์€ ์˜ค์ง '๋ฒ„ํŠผ์ด๋ผ๋Š” ๋ผˆ๋Œ€'๋งŒ ๋”ฑ ์ฑ…์ž„์งˆ ๋ฟ, ๊ทธ ๋‚ด๋ถ€ ๋ชจ์–‘์ƒˆ๋Š” children ์ด๋ผ๋Š” ๊ตฌ๋ฉ์„ ๋šซ์–ด ๋ถ€๋ชจ๊ฐ€ ์•„์ด์ฝ˜์„ ๋„ฃ๋“  ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ’ˆ๋“  ์™„๋ฒฝํ•˜๊ฒŒ ๋งˆ์Œ๋Œ€๋กœ ํ†ต์ œํ•  ์ˆ˜ ์žˆ๋„๋ก (์ œ์–ด๊ถŒ ์–‘๋„) ๋น„์›Œ๋‘” ๋งˆ์Šคํ„ฐํ”ผ์Šค์ž…๋‹ˆ๋‹ค.

Q3. IoC(์ œ์–ด์˜ ์—ญ์ „)๋ฅผ ์ ์šฉํ•œ๋‹ค๊ณ  ํ•ด์„œ ๋ฌด์กฐ๊ฑด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž˜๊ฒŒ ์ชผ๊ฐœ์–ด children์œผ๋กœ๋งŒ ๋„˜๊ธฐ๋Š” ๊ฒŒ ํ•ญ์ƒ ์ •๋‹ต์ผ๊นŒ์š”? ์˜คํžˆ๋ ค ์ œ์–ด๊ถŒ์„ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ๊ฐ•๋ ฅํ•˜๊ฒŒ ๋ฌถ์–ด๋‘๊ณ (์บก์Аํ™”) ์‚ฌ์šฉ์ž(๋ถ€๋ชจ)๊ฐ€ ์•ˆ์„ ๋งˆ์Œ๋Œ€๋กœ ๋œฏ์–ด๊ณ ์น˜์ง€ ๋ชปํ•˜๊ฒŒ ๋ง‰์•„์•ผ ํ•˜๋Š” ํŠน์ • ์ƒํ™ฉ์„ ๋‹จ๋‹ตํ˜•์œผ๋กœ ์ ์–ด์ฃผ์„ธ์š”.

โœ… ์ •๋‹ต ๋ฐ ์ฃผ๊ด€์‹ ํ•ด์„ค:

์ •๋‹ต: "ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด๋‚˜ ๋””์ž์ธ ํ† ํฐ(์‚ฌ๋‚ด UI ๊ทœ๊ฒฉ)์ด ์ ˆ๋Œ€ ํ›ผ์†๋˜๋ฉด ์•ˆ ๋˜๋Š” ๊ณตํ†ต(๋””์ž์ธ ์‹œ์Šคํ…œ) ์ปดํฌ๋„ŒํŠธ์ผ ๊ฒฝ์šฐ."
IoC๋Š” ๋ฌดํ•œํžˆ ์œ ์—ฐํ•˜์ง€๋งŒ, ๋ฐ˜๋Œ€๋กœ ๋งํ•˜๋ฉด ์“ด ๋ถ€๋ชจ ๊ฐœ๋ฐœ์ž ๋งˆ์Œ๋Œ€๋กœ(์•ผ๋งค๋กœ) ์ด์ƒํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐํ•ฉํ•ด์„œ ๋ฐ•์•„๋„ฃ๋Š” ๊ฑธ ๋ง‰์„ ์ˆ˜ ์—†๋‹ค๋Š” ๋œป์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด ์€ํ–‰ ์•ฑ์˜ '๊ฒฐ์ œ ํ™•์ธ ๋ชจ๋‹ฌ์ฐฝ' ๊ฐ™์€ ๊ฒฝ์šฐ, ์œ ์ €๊ฐ€ ์‹ค์ˆ˜๋กœ '์ทจ์†Œ' ๋ฒ„ํŠผ ์Šฌ๋กฏ ์ž๋ฆฌ์— '๋™์˜' ๋ฒ„ํŠผ์„ ํ•˜๋‚˜ ๋” ๋ฐ•์•„๋„ฃ์œผ๋ฉด ์น˜๋ช…์  ์‚ฌ๊ณ ๊ฐ€ ํ„ฐ์ง‘๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ ˆ์ด์•„์›ƒ๊ณผ ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด ์—„๊ฒฉํžˆ ๊ณ ์ •, ๋ณด์žฅ๋˜์–ด์•ผ ํ•˜๋Š” ๋„๋ฉ”์ธ ์ปดํฌ๋„ŒํŠธ๋ผ๋ฉด ์˜คํžˆ๋ ค ๊ตฌ๋ฉ์„ ๋‹ซ์•„๋‘๊ณ  Props ๊ธฐ๋ฐ˜์œผ๋กœ ํ–‰๋™์„ ๊ฐ•์ œ(์บก์Аํ™”)ํ•˜๋Š” ๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅธ ์„ค๊ณ„์ž…๋‹ˆ๋‹ค. ์œ ์—ฐ์„ฑ(IoC)๊ณผ ๊ฒฌ๊ณ ํ•จ(Encapsulation)์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ์กฐ์œจํ•˜๋Š” ๊ฒƒ์ด 5๋…„ ์ฐจ์˜ ์‹ค๋ ฅ์ž…๋‹ˆ๋‹ค.