React์ ์ปดํฌ๋ํธ ์ํคํ ์ฒ๋ฅผ ์ฌ์ธต์ ์ผ๋ก ์ดํด๋ณด๊ณ , Composition๊ณผ ์์์ ๋น๊ตํฉ๋๋ค. React๊ฐ Composition์ ์ ํธํ๋ ์ด์ ์ HOC, Render Props, Hooks์ ๊ฐ์ ํจํด์ ํ๊ตฌํ์ฌ ํ์ฅ ๊ฐ๋ฅํ๊ณ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์.
React ์ปดํฌ๋ํธ ์ํคํ ์ฒ: Why Composition Triumphs Over Inheritance
์ํํธ์จ์ด ๊ฐ๋ฐ์์ ์ํคํ ์ฒ๋ ๊ฐ์ฅ ์ค์ํฉ๋๋ค. ์ฝ๋๋ฅผ ๊ตฌ์ฑํ๋ ๋ฐฉ์์ ํ์ฅ์ฑ, ์ ์ง ๊ด๋ฆฌ์ฑ ๋ฐ ์ฌ์ฌ์ฉ ๊ฐ๋ฅ์ฑ์ ๊ฒฐ์ ํฉ๋๋ค. React๋ฅผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์์ ๊ฒฝ์ฐ, ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ํคํ ์ฒ ๊ฒฐ์ ์ค ํ๋๋ ์ปดํฌ๋ํธ ๊ฐ์ ๋ก์ง๊ณผ UI๋ฅผ ๊ณต์ ํ๋ ๋ฐฉ์์ ๊ดํ ๊ฒ์ ๋๋ค. ์ด๋ React์ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ์ธ์์ ๋ง์ถฐ ์ฌํด์๋ ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ๊ณ ์ ์ ์ธ ๋ ผ์, ์ฆ Composition vs. Inheritance๋ก ์ด์ด์ง๋๋ค.
Java ๋๋ C++์ ๊ฐ์ ๊ณ ์ ์ ์ธ ๊ฐ์ฒด ์งํฅ ์ธ์ด๋ฅผ ์ฌ์ฉํ ๊ฒฝํ์ด ์๋ค๋ฉด ์์์ด ์์ฐ์ค๋ฌ์ด ์ฒซ ๋ฒ์งธ ์ ํ์ผ๋ก ๋๊ปด์ง ์ ์์ต๋๋ค. 'is-a' ๊ด๊ณ๋ฅผ ๋ง๋๋ ๋ฐ ๊ฐ๋ ฅํ ๊ฐ๋ ์ ๋๋ค. ๊ทธ๋ฌ๋ ๊ณต์ React ์ค๋ช ์์์๋ ๋ช ํํ๊ณ ๊ฐ๋ ฅํ ๊ถ์ฅ ์ฌํญ์ ์ ๊ณตํฉ๋๋ค. "Facebook์์๋ ์์ฒ ๊ฐ์ ์ปดํฌ๋ํธ์์ React๋ฅผ ์ฌ์ฉํ๋ฉฐ ์ปดํฌ๋ํธ ์์ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๋ง๋๋ ๊ฒ์ ๊ถ์ฅํ๋ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค."
์ด ๊ฒ์๋ฌผ์์๋ ์ด๋ฌํ ์ํคํ ์ฒ ์ ํ์ ๋ํ ํฌ๊ด์ ์ธ ํ๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. React ์ปจํ ์คํธ์์ ์์๊ณผ Composition์ด ์๋ฏธํ๋ ๋ฐ๋ฅผ ํ๊ณ , Composition์ด ๊ด์ฉ์ ์ด๊ณ ์ฐ์ํ ์ ๊ทผ ๋ฐฉ์์ธ ์ด์ ๋ฅผ ์ค๋ช ํ๊ณ , ๊ณ ์ฐจ ์ปดํฌ๋ํธ์์ ํ๋ Hooks์ ์ด๋ฅด๊ธฐ๊น์ง ๊ฐ๋ ฅํ ํจํด์ ์ดํด๋ณด๊ณ , ์ด๋ฅผ ํตํด Composition์ ์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํ ๊ฐ๋ ฅํ๊ณ ์ ์ฐํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ๊ฐ๋ฐ์์ ๊ฐ์ฅ ์นํ ์น๊ตฌ๋ก ๋ง๋ค ๊ฒ์ ๋๋ค.
์ค๋๋ ๊ฒฝ๋น๋๋ฅผ ์ดํดํ๊ธฐ: ์์์ด๋ ๋ฌด์์ธ๊ฐ?
์์์ ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ(OOP)์ ํต์ฌ ๊ธฐ๋ฅ์
๋๋ค. ์ ํด๋์ค(ํ์ ํด๋์ค ๋๋ ์์)๊ฐ ๊ธฐ์กด ํด๋์ค(์ํผํด๋์ค ๋๋ ๋ถ๋ชจ)์ ์์ฑ๊ณผ ๋ฉ์๋๋ฅผ ์ป์ ์ ์๋๋ก ํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ฐ์ ํ๊ฒ ๊ฒฐํฉ๋ 'is-a' ๊ด๊ณ๊ฐ ์์ฑ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด, GoldenRetriever๋ Dog์ด๊ณ , Dog๋ Animal์
๋๋ค.
React๊ฐ ์๋ ์ปจํ ์คํธ์์์ ์์
๊ฐ๋ ์ ํ์คํ ํ๊ธฐ ์ํด ๊ฐ๋จํ JavaScript ํด๋์ค ์๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Calls the parent constructor
this.breed = breed;
}
speak() { // Overrides the parent method
console.log(`${this.name} barks.`);
}
fetch() {
console.log(`${this.name} is fetching the ball!`);
}
}
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak(); // Output: "Buddy barks."
myDog.fetch(); // Output: "Buddy is fetching the ball!"
์ด ๋ชจ๋ธ์์ Dog ํด๋์ค๋ ์๋์ผ๋ก Animal์์ name ์์ฑ๊ณผ speak ๋ฉ์๋๋ฅผ ๊ฐ์ ธ์ต๋๋ค. ๋ํ ์์ฒด ๋ฉ์๋(fetch)๋ฅผ ์ถ๊ฐํ๊ณ ๊ธฐ์กด ๋ฉ์๋๋ฅผ ์ฌ์ ์ํ ์๋ ์์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์๊ฒฉํ ๊ณ์ธต ๊ตฌ์กฐ๊ฐ ์์ฑ๋ฉ๋๋ค.
React์์ ์์์ด ์คํจํ๋ ์ด์
์ด 'is-a' ๋ชจ๋ธ์ ์ผ๋ถ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ์ ํฉํ์ง๋ง React์ UI ์ปดํฌ๋ํธ์ ์ ์ฉํ๋ฉด ์ฌ๊ฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
- ๊ฐํ ๊ฒฐํฉ: ์ปดํฌ๋ํธ๊ฐ ๊ธฐ๋ณธ ์ปดํฌ๋ํธ์์ ์์๋๋ฉด ๋ถ๋ชจ์ ๊ตฌํ์ ๋ฐ์ ํ๊ฒ ๊ฒฐํฉ๋ฉ๋๋ค. ๊ธฐ๋ณธ ์ปดํฌ๋ํธ์ ๋ณ๊ฒฝ์ผ๋ก ์ธํด ์ฒด์ธ ์๋์ ์ฌ๋ฌ ์์ ์ปดํฌ๋ํธ๊ฐ ์๊ธฐ์น ์๊ฒ ์์๋ ์ ์์ต๋๋ค. ์ด๋ก ์ธํด ๋ฆฌํฉํฐ๋ง ๋ฐ ์ ์ง ๊ด๋ฆฌ๊ฐ ์ทจ์ฝํ ํ๋ก์ธ์ค๊ฐ ๋ฉ๋๋ค.
- ์ ์ฐํ์ง ์์ ๋ก์ง ๊ณต์ : ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ์ ๊ฐ์ ํน์ ๊ธฐ๋ฅ์ ๋์ผํ 'is-a' ๊ณ์ธต ๊ตฌ์กฐ์ ๋ง์ง ์๋ ์ปดํฌ๋ํธ์ ๊ณต์ ํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น์? ์๋ฅผ ๋ค์ด,
UserProfile๊ณผProductList๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ํ ์ ์์ง๋ง ๊ณตํตDataFetchingComponent์์ ์์ํ๋ ๊ฒ์ ์๋ฏธ๊ฐ ์์ต๋๋ค. - Prop-Drilling Hell: ๊น์ ์์ ์ฒด์ธ์์ ์ต์์ ์ปดํฌ๋ํธ์์ ๊น์ด ์ค์ฒฉ๋ ์์์ผ๋ก ํ๋กญ์ ์ ๋ฌํ๋ ๊ฒ์ด ์ด๋ ค์์ง๋๋ค. ์ฌ์ฉํ์ง ์๋ ์ค๊ฐ ์ปดํฌ๋ํธ๋ฅผ ํตํด ํ๋กญ์ ์ ๋ฌํด์ผ ํ ์ ์์ผ๋ฉฐ, ์ด๋ก ์ธํด ํผ๋์ค๋ฝ๊ณ ๋ถํ์ํ ์ฝ๋๊ฐ ์์ฑ๋ฉ๋๋ค.
- "Gorilla-Banana Problem": OOP ์ ๋ฌธ๊ฐ์ธ Joe Armstrong์ ์ ๋ช ํ ์ธ์ฉ๋ฌธ์ ์ด ๋ฌธ์ ๋ฅผ ์๋ฒฝํ๊ฒ ์ค๋ช ํฉ๋๋ค. "๋ฐ๋๋๋ฅผ ์ํ์ง๋ง, ์ป์ ๊ฒ์ ๋ฐ๋๋๋ฅผ ๋ค๊ณ ์๋ ๊ณ ๋ฆด๋ผ์ ์ ๊ธ ์ ์ฒด์์ต๋๋ค." ์์์ ์ฌ์ฉํ๋ฉด ์ํ๋ ๊ธฐ๋ฅ๋ง ์ป์ ์ ์์ต๋๋ค. ์ ์ฒด ์ํผํด๋์ค๋ฅผ ํจ๊ป ๊ฐ์ ธ์์ผ ํฉ๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ก ์ธํด React ํ์ ๋ณด๋ค ์ ์ฐํ๊ณ ๊ฐ๋ ฅํ ํจ๋ฌ๋ค์์ธ Composition์ ์ค์ฌ์ผ๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค๊ณํ์ต๋๋ค.
React ๋ฐฉ์ ์์ฉ: Composition์ ํ
Composition์ 'has-a' ๋๋ 'uses-a' ๊ด๊ณ๋ฅผ ์ ํธํ๋ ์ค๊ณ ์์น์ ๋๋ค. ์ปดํฌ๋ํธ๊ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ธ ๋์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ง๊ฑฐ๋ ํด๋น ๊ธฐ๋ฅ์ ์ฌ์ฉํฉ๋๋ค. ์ปดํฌ๋ํธ๋ ๋ ๊ณ ๋ธ๋ก์ฒ๋ผ ์๊ฒฉํ ๊ณ์ธต ๊ตฌ์กฐ์ ๊ฐํ์ง ์๊ณ ๋ค์ํ ๋ฐฉ์์ผ๋ก ๊ฒฐํฉํ์ฌ ๋ณต์กํ UI๋ฅผ ๋ง๋ค ์ ์๋ ๋น๋ฉ ๋ธ๋ก์ผ๋ก ์ทจ๊ธ๋ฉ๋๋ค.
React์ Composition ๋ชจ๋ธ์ ๋งค์ฐ ๋ค์ฌ๋ค๋ฅํ๋ฉฐ, ๋ช ๊ฐ์ง ์ฃผ์ ํจํด์ผ๋ก ๋ํ๋ฉ๋๋ค. ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๊ฒ๋ถํฐ ๊ฐ์ฅ ํ๋์ ์ด๊ณ ๊ฐ๋ ฅํ ๊ฒ๊น์ง, ์ด๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๊ธฐ์ 1: props.children์ ์ฌ์ฉํ Containment
๊ฐ์ฅ ๊ฐ๋จํ ํํ์ Composition์ Containment์
๋๋ค. ์ด๋ ์ปดํฌ๋ํธ๊ฐ ์ผ๋ฐ ์ปจํ
์ด๋ ๋๋ '์์' ์ญํ ์ ํ๊ณ ํด๋น ์ฝํ
์ธ ๊ฐ ์์ ์ปดํฌ๋ํธ์์ ์ ๋ฌ๋๋ ๊ฒฝ์ฐ์
๋๋ค. React์๋ ์ด๋ฅผ ์ํ ํน๋ณํ ๊ธฐ๋ณธ ์ ๊ณต prop์ด ์์ต๋๋ค. props.children์
๋๋ค.
์ผ๊ด๋ ํ
๋๋ฆฌ์ ๊ทธ๋ฆผ์๊ฐ ์๋ ๋ชจ๋ ์ฝํ
์ธ ๋ฅผ ๋ํํ ์ ์๋ Card ์ปดํฌ๋ํธ๊ฐ ํ์ํ๋ค๊ณ ์์ํด ๋ณด์ญ์์ค. ์์์ ํตํด TextCard, ImageCard ๋ฐ ProfileCard ๋ณํ์ ๋ง๋๋ ๋์ ํ๋์ ์ผ๋ฐ Card ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ญ๋๋ค.
// Card.js - A generic container component
function Card(props) {
return (
<div className="card">
{props.children}
</div>
);
}
// App.js - Using the Card component
function App() {
return (
<div>
<Card>
<h1>Welcome!</h1>
<p>This content is inside a Card component.</p>
</Card>
<Card>
<img src="/path/to/image.jpg" alt="An example image" />
<p>This is an image card.</p>
</Card>
</div>
);
}
์ฌ๊ธฐ์ Card ์ปดํฌ๋ํธ๋ ํฌํจ๋ ๋ด์ฉ์ ์๊ฑฐ๋ ์ ๊ฒฝ ์ฐ์ง ์์ต๋๋ค. ๋จ์ํ ๋ํผ ์คํ์ผ์ ์ ๊ณตํฉ๋๋ค. ์ฌ๋ ํ๊ทธ์ ๋ซ๋ <Card> ํ๊ทธ ์ฌ์ด์ ์ฝํ
์ธ ๋ ์๋์ผ๋ก props.children์ผ๋ก ์ ๋ฌ๋ฉ๋๋ค. ์ด๋ ๋ถ๋ฆฌ ๋ฐ ์ฌ์ฌ์ฉ์ฑ์ ํ๋ฅญํ ์์
๋๋ค.
๊ธฐ์ 2: Props๋ฅผ ์ฌ์ฉํ ํน์ํ
๊ฒฝ์ฐ์ ๋ฐ๋ผ ์ปดํฌ๋ํธ์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๊ฐ ์ฑ์์ผ ํ๋ ์ฌ๋ฌ ๊ฐ์ '๊ตฌ๋ฉ'์ด ํ์ํฉ๋๋ค. props.children์ ์ฌ์ฉํ ์ ์์ง๋ง, ์ปดํฌ๋ํธ๋ฅผ ์ผ๋ฐ prop์ผ๋ก ์ ๋ฌํ๋ ๊ฒ์ด ๋ ๋ช
์์ ์ด๊ณ ๊ตฌ์กฐํ๋ ๋ฐฉ๋ฒ์
๋๋ค. ์ด ํจํด์ ์ข
์ข
ํน์ํ๋ผ๊ณ ํฉ๋๋ค.
Modal ์ปดํฌ๋ํธ๋ฅผ ์๊ฐํด ๋ณด์ธ์. ๋ชจ๋ฌ์๋ ์ผ๋ฐ์ ์ผ๋ก ์ ๋ชฉ ์น์
, ์ฝํ
์ธ ์น์
๋ฐ ์์
์น์
(์: "ํ์ธ" ๋๋ "์ทจ์"์ ๊ฐ์ ๋ฒํผ ํฌํจ)์ด ์์ต๋๋ค. ์ด๋ฌํ ์น์
์ prop์ผ๋ก ํ์ฉํ๋๋ก Modal์ ์ค๊ณํ ์ ์์ต๋๋ค.
// Modal.js - A more specialized container
function Modal(props) {
return (
<div className="modal-backdrop">
<div className="modal-content">
<div className="modal-header">{props.title}</div>
<div className="modal-body">{props.body}</div>
<div className="modal-footer">{props.actions}</div>
</div>
</div>
);
}
// App.js - Using the Modal with specific components
function App() {
const confirmationTitle = <h2>Confirm Action</h2>;
const confirmationBody = <p>Are you sure you want to proceed with this action?</p>;
const confirmationActions = (
<div>
<button>Confirm</button>
<button>Cancel</button>
</div>
);
return (
<Modal
title={confirmationTitle}
body={confirmationBody}
actions={confirmationActions}
/>
);
}
์ด ์์์ Modal์ ๊ณ ๋๋ก ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ ์ด์์ ์ปดํฌ๋ํธ์
๋๋ค. title, body ๋ฐ actions์ ๋ํด ํน์ JSX ์์๋ฅผ ์ ๋ฌํ์ฌ ํน์ํํฉ๋๋ค. ์ด๋ ConfirmationModal ๋ฐ WarningModal ํ์ ํด๋์ค๋ฅผ ๋ง๋๋ ๊ฒ๋ณด๋ค ํจ์ฌ ์ ์ฐํฉ๋๋ค. ํ์์ ๋ฐ๋ผ ๋ค๋ฅธ ์ฝํ
์ธ ๋ก Modal์ Compositionํ๋ฉด ๋ฉ๋๋ค.
๊ธฐ์ 3: ๊ณ ์ฐจ ์ปดํฌ๋ํธ(HOC)
๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ, ์ธ์ฆ ๋๋ ๋ก๊น ๊ณผ ๊ฐ์ ๋น UI ๋ก์ง์ ๊ณต์ ํ๊ธฐ ์ํด React ๊ฐ๋ฐ์๋ ์ญ์ฌ์ ์ผ๋ก ๊ณ ์ฐจ ์ปดํฌ๋ํธ(HOC)๋ผ๋ ํจํด์ ์ฌ์ฉํ์ต๋๋ค. ํ๋ React์์๋ Hooks๋ก ๋์ฒด๋์์ง๋ง, React์ Composition ์คํ ๋ฆฌ์์ ํต์ฌ์ ์ธ ์งํ ๋จ๊ณ๋ฅผ ๋ํ๋ด๋ฉฐ ๋ง์ ์ฝ๋๋ฒ ์ด์ค์ ์ฌ์ ํ ์กด์ฌํ๋ฏ๋ก ์ด๋ฅผ ์ดํดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
HOC๋ ์ปดํฌ๋ํธ๋ฅผ ์ธ์๋ก ์ฌ์ฉํ๊ณ ์๋กญ๊ฒ ํฅ์๋ ์ปดํฌ๋ํธ๋ฅผ ๋ฐํํ๋ ํจ์์ ๋๋ค.
์ปดํฌ๋ํธ๊ฐ ์
๋ฐ์ดํธ๋ ๋๋ง๋ค ์ปดํฌ๋ํธ์ ํ๋กญ์ ๊ธฐ๋กํ๋ withLogger๋ผ๋ HOC๋ฅผ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค. ์ด๋ ๋๋ฒ๊น
์ ์ ์ฉํฉ๋๋ค.
// withLogger.js - The HOC
import React, { useEffect } from 'react';
function withLogger(WrappedComponent) {
// It returns a new component...
return function EnhancedComponent(props) {
useEffect(() => {
console.log('Component updated with new props:', props);
}, [props]);
// ... that renders the original component with the original props.
return <WrappedComponent {...props} />;
};
}
// MyComponent.js - A component to be enhanced
function MyComponent({ name, age }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old.</p>
</div>
);
}
// Exporting the enhanced component
export default withLogger(MyComponent);
withLogger ํจ์๋ MyComponent๋ฅผ ๋ํํ์ฌ MyComponent์ ๋ด๋ถ ์ฝ๋๋ฅผ ์์ ํ์ง ์๊ณ ๋ ์๋ก์ด ๋ก๊น
๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ด ๋์ผํ HOC๋ฅผ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์ ์ฉํ์ฌ ๋์ผํ ๋ก๊น
๊ธฐ๋ฅ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
HOC์ ๊ณผ์ :
- ๋ํผ ์ง์ฅ: ๋จ์ผ ์ปดํฌ๋ํธ์ ์ฌ๋ฌ HOC๋ฅผ ์ ์ฉํ๋ฉด React DevTools์์ ๊น์ด ์ค์ฒฉ๋ ์ปดํฌ๋ํธ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค(์:
withAuth(withRouter(withLogger(MyComponent)))). ์ด๋ก ์ธํด ๋๋ฒ๊น ์ด ์ด๋ ค์์ง๋๋ค. - Prop ์ด๋ฆ ์ถฉ๋: HOC๊ฐ ๋ํ๋ ์ปดํฌ๋ํธ์์ ์ด๋ฏธ ์ฌ์ฉ ์ค์ธ prop(์:
data)์ ์ฝ์ ํ๋ ๊ฒฝ์ฐ ์ค์๋ก ๋ฎ์ด์ธ ์ ์์ต๋๋ค. - ์์์ ๋ก์ง: ์ปดํฌ๋ํธ์ ์ฝ๋์์ ํด๋น ํ๋กญ์ด ์ด๋์์ ์ค๋์ง ํญ์ ๋ช ํํ์ง๋ ์์ต๋๋ค. ๋ก์ง์ HOC ๋ด์ ์จ๊ฒจ์ ธ ์์ต๋๋ค.
๊ธฐ์ 4: Render Props
Render Prop ํจํด์ HOC์ ๋ช ๊ฐ์ง ๋จ์ ์ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฑ์ฅํ์ต๋๋ค. ๋ณด๋ค ๋ช ์์ ์ธ ๋ฐฉ์์ผ๋ก ๋ก์ง์ ๊ณต์ ํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
render prop์ด ์๋ ์ปดํฌ๋ํธ๋ ํจ์๋ฅผ prop์ผ๋ก ์ฌ์ฉํ๊ณ (์ผ๋ฐ์ ์ผ๋ก render๋ผ๋ ์ด๋ฆ) ํด๋น ํจ์๋ฅผ ํธ์ถํ์ฌ ๋ ๋๋งํ ๋ด์ฉ์ ๊ฒฐ์ ํ๊ณ , ๋ชจ๋ ์ํ ๋๋ ๋ก์ง์ ์ธ์๋ก ์ ๋ฌํฉ๋๋ค.
๋ง์ฐ์ค์ X ๋ฐ Y ์ขํ๋ฅผ ์ถ์ ํ๊ณ ์ด๋ฅผ ์ฌ์ฉํ๋ ค๋ ๋ชจ๋ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ ์ ์๋๋ก ํ๋ MouseTracker ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
// MouseTracker.js - Component with a render prop
import React, { useState, useEffect } from 'react';
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (event) => {
setPosition({ x: event.clientX, y: event.clientY });
};
useEffect(() => {
window.addEventListener('mousemove', handleMouseMove);
return () => {
window.removeEventListener('mousemove', handleMouseMove);
};
}, []);
// Call the render function with the state
return render(position);
}
// App.js - Using the MouseTracker
function App() {
return (
<div>
<h1>Move your mouse around!</h1>
<MouseTracker
render={mousePosition => (
<p>The current mouse position is ({mousePosition.x}, {mousePosition.y})</p>
)}
/>
</div>
);
}
์ฌ๊ธฐ์ MouseTracker๋ ๋ง์ฐ์ค ์ด๋์ ์ถ์ ํ๊ธฐ ์ํ ๋ชจ๋ ๋ก์ง์ ์บก์ํํฉ๋๋ค. ์์ฒด์ ์ผ๋ก๋ ์๋ฌด๊ฒ๋ ๋ ๋๋งํ์ง ์์ต๋๋ค. ๋์ , ๋ ๋๋ง ๋ก์ง์ render prop์ ์์ํฉ๋๋ค. ์ด๋ HOC๋ณด๋ค ๋ช
์์ ์
๋๋ค. JSX ๋ด์์ mousePosition ๋ฐ์ดํฐ๊ฐ ์ ํํ ์ด๋์์ ์ค๋์ง ํ์ธํ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
children prop์ ์ด ํจํด์ ์ผ๋ฐ์ ์ด๊ณ ์ฐ์ํ ๋ณํ์ธ ํจ์๋ก๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
// Using children as a function
<MouseTracker>
{mousePosition => (
<p>The current mouse position is ({mousePosition.x}, {mousePosition.y})</p>
)}
</MouseTracker>
๊ธฐ์ 5: Hooks(ํ๋์ ์ด๊ณ ์ ํธ๋๋ ์ ๊ทผ ๋ฐฉ์)
React 16.8์ ๋์ ๋ Hooks๋ React ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๋ ๋ฐฉ์์ ํ์ ํ์ต๋๋ค. ์ด๋ฅผ ํตํด ํจ์ํ ์ปดํฌ๋ํธ์์ ์ํ ๋ฐ ๊ธฐํ React ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ฐ์ฅ ์ค์ํ ๊ฒ์, ์ฌ์ฉ์ ์ง์ Hooks๋ ์ปดํฌ๋ํธ ๊ฐ์ ์ํ ๋ก์ง์ ๊ณต์ ํ๊ธฐ ์ํ ๊ฐ์ฅ ์ฐ์ํ๊ณ ์ง์ ์ ์ธ ์๋ฃจ์ ์ ์ ๊ณตํฉ๋๋ค.
Hooks๋ HOC ๋ฐ Render Props์ ๋ฌธ์ ๋ฅผ ํจ์ฌ ๋ ๊น๋ํ๊ฒ ํด๊ฒฐํฉ๋๋ค. MouseTracker ์์ ๋ฅผ useMousePosition์ด๋ผ๋ ์ฌ์ฉ์ ์ง์ Hook์ผ๋ก ๋ฆฌํฉํฐ๋งํด ๋ณด๊ฒ ์ต๋๋ค.
// hooks/useMousePosition.js - A custom Hook
import { useState, useEffect } from 'react';
export function useMousePosition() {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMouseMove = (event) => {
setPosition({ x: event.clientX, y: event.clientY });
};
window.addEventListener('mousemove', handleMouseMove);
return () => {
window.removeEventListener('mousemove', handleMouseMove);
};
}, []); // Empty dependency array means this effect runs only once
return position;
}
// DisplayMousePosition.js - A component using the Hook
import { useMousePosition } from './hooks/useMousePosition';
function DisplayMousePosition() {
const position = useMousePosition(); // Just call the hook!
return (
<p>
The mouse position is ({position.x}, {position.y})
</p>
);
}
// Another component, maybe an interactive element
import { useMousePosition } from './hooks/useMousePosition';
function InteractiveBox() {
const { x, y } = useMousePosition();
const style = {
position: 'absolute',
top: y - 25, // Center the box on the cursor
left: x - 25,
width: '50px',
height: '50px',
backgroundColor: 'lightblue',
};
return <div style={style} />;
}
์ด๋ ์์ฒญ๋ ๊ฐ์ ์
๋๋ค. '๋ํผ ์ง์ฅ'๋, prop ์ด๋ฆ ์ถฉ๋๋, ๋ณต์กํ ๋ ๋ prop ํจ์๋ ์์ต๋๋ค. ๋ก์ง์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ํจ์(useMousePosition)๋ก ์์ ํ ๋ถ๋ฆฌ๋์์ผ๋ฉฐ, ๋ชจ๋ ์ปดํฌ๋ํธ๋ ๋ช
ํํ ํ ์ค์ ์ฝ๋๋ง์ผ๋ก ํด๋น ์ํ ๋ก์ง์ '์ฐ๊ฒฐ'ํ ์ ์์ต๋๋ค. ์ฌ์ฉ์ ์ง์ Hooks๋ ํ๋ React์์ Composition์ ๊ถ๊ทน์ ์ธ ํํ์ผ๋ก, ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ก์ง ๋ธ๋ก์ ์์ฒด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ตฌ์ถํ ์ ์๋๋ก ํฉ๋๋ค.
๊ฐ๋จํ ๋น๊ต: React์์ Composition vs. Inheritance
React ์ปจํ ์คํธ์์ ์ฃผ์ ์ฐจ์ด์ ์ ์์ฝํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ง์ ๋น๊ต๊ฐ ์์ต๋๋ค.
| ์ธก๋ฉด | ์์(React์์ Anti-Pattern) | Composition(React์์ ์ ํธ) |
|---|---|---|
| <strong>๊ด๊ณ</strong> | 'is-a' ๊ด๊ณ์ ๋๋ค. ํน์ํ๋ ์ปดํฌ๋ํธ๋ ๊ธฐ๋ณธ ์ปดํฌ๋ํธ์ a ๋ฒ์ ์ ๋๋ค. | 'has-a' ๋๋ 'uses-a' ๊ด๊ณ์ ๋๋ค. ๋ณต์กํ ์ปดํฌ๋ํธ๋ ๋ ์์ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ง๊ฑฐ๋ ๊ณต์ ๋ก์ง์ ์ฌ์ฉํฉ๋๋ค. |
| <strong>๊ฒฐํฉ</strong> | <strong>๋์.</strong> ์์ ์ปดํฌ๋ํธ๋ ๋ถ๋ชจ์ ๊ตฌํ์ ๋ฐ์ ํ๊ฒ ๊ฒฐํฉ๋ฉ๋๋ค. | <strong>๋ฎ์.</strong> ์ปดํฌ๋ํธ๋ ๋ ๋ฆฝ์ ์ด๋ฉฐ ์์ ์์ด ๋ค๋ฅธ ์ปจํ ์คํธ์์ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค. |
| <strong>์ ์ฐ์ฑ</strong> | <strong>๋ฎ์.</strong> ์๊ฒฉํ ํด๋์ค ๊ธฐ๋ฐ ๊ณ์ธต ๊ตฌ์กฐ๋ก ์ธํด ๋ค๋ฅธ ์ปดํฌ๋ํธ ํธ๋ฆฌ์ ๋ก์ง์ ๊ณต์ ํ๊ธฐ๊ฐ ์ด๋ ต์ต๋๋ค. | <strong>๋์.</strong> ๋ก์ง๊ณผ UI๋ ๋ง์น ๋น๋ฉ ๋ธ๋ก์ฒ๋ผ ๋ฌด์ํ ๋ง์ ๋ฐฉ์์ผ๋ก ๊ฒฐํฉํ๊ณ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค. |
| <strong>์ฝ๋ ์ฌ์ฌ์ฉ์ฑ</strong> | ๋ฏธ๋ฆฌ ์ ์๋ ๊ณ์ธต ๊ตฌ์กฐ๋ก ์ ํ๋ฉ๋๋ค. '๋ฐ๋๋'๋ง ์ํ ๋ ์ ์ฒด '๊ณ ๋ฆด๋ผ'๋ฅผ ์ป์ต๋๋ค. | ์ฐ์ํฉ๋๋ค. ์๊ณ , ์ง์ค๋ ์ปดํฌ๋ํธ์ Hooks๋ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค. |
| <strong>React ๊ด์ฉ๊ตฌ</strong> | React ๊ณต์ ํ์์ ๊ถ์ฅํ์ง ์์ต๋๋ค. | React ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ๊ถ์ฅ๋๊ณ ๊ด์ฉ์ ์ธ ์ ๊ทผ ๋ฐฉ์์ ๋๋ค. |
๊ฒฐ๋ก : Composition์ผ๋ก ์๊ฐํ์ญ์์ค
Composition๊ณผ ์์ ๊ฐ์ ๋ ผ์์ ์ํํธ์จ์ด ์ค๊ณ์ ๊ธฐ๋ณธ์ ์ธ ์ฃผ์ ์ ๋๋ค. ์์์ ๊ณ ์ ์ ์ธ OOP์์ ์๋ฆฌ๋ฅผ ์ก์์ง๋ง UI ๊ฐ๋ฐ์ ๋์ ์ด๊ณ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ํน์ฑ์ผ๋ก ์ธํด React์ ์ ํฉํ์ง ์์ต๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ทผ๋ณธ์ ์ผ๋ก Composition์ ์์ฉํ๋๋ก ์ค๊ณ๋์์ต๋๋ค.
Composition์ ์ ํธํจ์ผ๋ก์จ ๋ค์๊ณผ ๊ฐ์ ์ด์ ์ ์ป์ ์ ์์ต๋๋ค.
- ์ ์ฐ์ฑ: ํ์ํ ๋๋ก UI์ ๋ก์ง์ ํผํฉํ๊ณ ์ผ์น์ํฌ ์ ์์ต๋๋ค.
- ์ ์ง ๊ด๋ฆฌ์ฑ: ๋์จํ๊ฒ ๊ฒฐํฉ๋ ์ปดํฌ๋ํธ๋ ๊ฒฉ๋ฆฌ๋ ์ํ์์ ์ดํด, ํ ์คํธ ๋ฐ ๋ฆฌํฉํฐ๋งํ๊ธฐ๊ฐ ๋ ์ฝ์ต๋๋ค.
- ํ์ฅ์ฑ: Composition ๋ง์ธ๋๋ ํฌ๊ณ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์ ํจ์จ์ ์ผ๋ก ๊ตฌ์ถํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ์๊ณ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ ๋ฐ Hooks์ ์ค๊ณ ์์คํ ์ ๋ง๋๋ ๊ฒ์ ์ฅ๋ คํฉ๋๋ค.
์ ์ธ๊ณ React ๊ฐ๋ฐ์๋ก์ Composition์ ๋ง์คํฐํ๋ ๊ฒ์ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ฅด๋ ๊ฒ๋ง์ด ์๋๋ผ React๋ฅผ ๊ฐ๋ ฅํ๊ณ ์์ฐ์ ์ธ ๋๊ตฌ๋ก ๋ง๋๋ ํต์ฌ ์ฒ ํ์ ์ดํดํ๋ ๊ฒ์
๋๋ค. ์๊ณ , ์ง์ค๋ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๊ฒ๋ถํฐ ์์ํ์ญ์์ค. ์ผ๋ฐ ์ปจํ
์ด๋์๋ props.children์ ์ฌ์ฉํ๊ณ ํน์ํ์๋ props๋ฅผ ์ฌ์ฉํ์ญ์์ค. ๋ก์ง์ ๊ณต์ ํ๋ ค๋ฉด ๋จผ์ ์ฌ์ฉ์ ์ง์ Hooks๋ฅผ ์ฌ์ฉํ์ญ์์ค. Composition์ผ๋ก ์๊ฐํจ์ผ๋ก์จ ์๊ฐ์ด ์ง๋๋ ๋ณ์น ์๋ ์ฐ์ํ๊ณ , ๊ฐ๋ ฅํ๋ฉฐ, ํ์ฅ ๊ฐ๋ฅํ React ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ๋ ๋ฐ ๋์์ด ๋ ๊ฒ์
๋๋ค.