๐ก 12. ์ํธ์ ๋ฆฌํฉํ ๋ง: ์ ์ด์ ์ญ์ (Inversion of Control)
๐ ๊ฐ์
if๋ฌธ ์ง์ฅ์ ๋น ์ง ๋ง๋ฅ ์ปดํฌ๋ํธ์ ์ ์ด๊ถ์ ๋ถ๋ชจ์๊ฒ ๋๋ ค์ฃผ์ด, ์ฌ์ฌ์ฉ์ฑ๊ณผ ํ์ฅ์ฑ์ ๊ทน๋ํํ๋ '์ ์ด์ ์ญ์ (IoC)' ํจํด์ ๋ฐฐ์๋๋ค.
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์๊ตฌ์ฌํญ์ด ์ถ๊ฐ๋ ๋๋ง๋ค
booleanprop์ด ๋์ด๋๋ฉฐ ๊ธฐ๊ดดํด์ง๋ ์ปดํฌ๋ํธ์ ๋ฌธ์ ์ ์ ํ์ ํ ์ ์๋ค.- "์ปดํฌ๋ํธ๊ฐ ๋ชจ๋ ๊ฑธ ๋ค ์ฒ๋ฆฌํ๊ฒ ๋๋ค"๋ ๊ฐ๋ฐ์์ ๋ฒ์ด๋๋ค.
- ์ ์ด์ ์ญ์ (IoC) ๊ฐ๋ ์ ์ดํดํ๊ณ , ์ปดํฌ๋ํธ์ ๋ด๋ถ ์ ์ด๊ถ์ ์ฌ์ฉํ๋ ์ชฝ(์ ์ /๋ถ๋ชจ)์ผ๋ก ๋๊ธธ ํ๋จ ๊ธฐ์ค์ ์ธ์ด๋ค.
๐ ๋ชฉ์ฐจ
- ๐ค ์ ์์์ผ ํ๋๊ฐ: '๋ง๋ฅ ํญ๊ตฐ' ์ปดํฌ๋ํธ์ ํ์๊ณผ ๋ชฐ๋ฝ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐งฉ ๊ทน์ ์ฒด๋: IoC ํจํด ์ค์ ๋ฆฌํฉํ ๋ง
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 10๋ถ / ํต์ฌ ํํธ: 6๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ์์(PM):"์์ฒ ์จ, ์ ๋ฒ์ ๋ง๋ ๊ทธ
<UserProfileCard />๋ง์ด์์. ๋ฉ์ธ ํ์ด์ง์์ ์ฌ์ง์ด ์ผ์ชฝ์, ๋ง์ดํ์ด์ง์์ ์ค๋ฅธ์ชฝ์, ๊ด๋ฆฌ์ ํ์ด์ง์์ ์์ ์ฌ์ง ์์ด ํ ์คํธ๋ง ๊ตต๊ฒ ๋์ค๊ฒ ํด์ฃผ์ธ์." - ์์ฒ (์ ์
):"๋ท! ๊ทธ๋ผ...
isMain,isMyPage,isAdminprop์ 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๋
์ฐจ์ ์ค๋ ฅ์
๋๋ค.